diff --git a/.github/workflows/autowiki.yml b/.github/workflows/autowiki.yml
index 0a431d520ba..91ab12cdb19 100644
--- a/.github/workflows/autowiki.yml
+++ b/.github/workflows/autowiki.yml
@@ -30,9 +30,6 @@ jobs:
- name: Install rust-g
if: steps.secrets_set.outputs.SECRETS_ENABLED
run: |
- sudo dpkg --add-architecture i386
- sudo apt update || true
- sudo apt install -o APT::Immediate-Configure=false libssl1.1:i386
bash tools/ci/install_rust_g.sh
- name: Compile and generate Autowiki files
if: steps.secrets_set.outputs.SECRETS_ENABLED
diff --git a/.github/workflows/run_integration_tests.yml b/.github/workflows/run_integration_tests.yml
index 0c4ba61a8a4..404119d9880 100644
--- a/.github/workflows/run_integration_tests.yml
+++ b/.github/workflows/run_integration_tests.yml
@@ -18,7 +18,7 @@ on:
type: string
jobs:
run_integration_tests:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
services:
mysql:
image: mysql:latest
@@ -43,9 +43,6 @@ jobs:
mysql -u root -proot tg_ci_prefixed < SQL/tgstation_schema_prefixed.sql
- name: Install rust-g
run: |
- sudo dpkg --add-architecture i386
- sudo apt update || true
- sudo apt install -o APT::Immediate-Configure=false libssl1.1:i386
bash tools/ci/install_rust_g.sh
- name: Install auxlua
run: |
diff --git a/_maps/RandomRuins/IceRuins/skyrat/icemoon_underground_icewalker_lower.dmm b/_maps/RandomRuins/IceRuins/skyrat/icemoon_underground_icewalker_lower.dmm
index 8ea8983ce52..4e5a3e21f19 100644
--- a/_maps/RandomRuins/IceRuins/skyrat/icemoon_underground_icewalker_lower.dmm
+++ b/_maps/RandomRuins/IceRuins/skyrat/icemoon_underground_icewalker_lower.dmm
@@ -1873,6 +1873,7 @@
/obj/item/seeds/tower{
pixel_y = -3
},
+/obj/item/seeds/glowshroom,
/obj/item/seeds/poppy,
/obj/item/seeds/reishi,
/obj/item/seeds/reishi,
diff --git a/_maps/RandomRuins/SpaceRuins/mimesvsclowns.dmm b/_maps/RandomRuins/SpaceRuins/mimesvsclowns.dmm
index d7186f44776..6a7c71ad938 100644
--- a/_maps/RandomRuins/SpaceRuins/mimesvsclowns.dmm
+++ b/_maps/RandomRuins/SpaceRuins/mimesvsclowns.dmm
@@ -6,6 +6,11 @@
/area/ruin)
"dI" = (
/obj/item/grown/bananapeel,
+/obj/item/ammo_casing/a357/spent{
+ dir = 9;
+ pixel_x = -13;
+ pixel_y = 10
+ },
/turf/open/floor/iron/checker/airless,
/area/ruin)
"ef" = (
@@ -71,20 +76,21 @@
/obj/machinery/atmospherics/components/unary/passive_vent{
dir = 4
},
-/obj/item/ammo_casing/energy/c3dbullet{
- pixel_y = 10;
- pixel_x = 115;
- dir = 9
- },
/obj/effect/decal/cleanable/blood/gibs,
/obj/machinery/light/small/broken/directional/south,
+/obj/item/ammo_casing/a357/spent,
/turf/open/floor/iron/checker/airless,
/area/ruin)
"uc" = (
-/obj/item/ammo_casing/energy/c3dbullet,
/obj/effect/decal/cleanable/blood/footprints{
dir = 4
},
+/obj/item/ammo_casing/a357/spent{
+ pixel_x = -5;
+ dir = 5;
+ pixel_y = 6
+ },
+/obj/item/ammo_casing/a357/spent,
/obj/item/gps/spaceruin,
/turf/open/floor/plating/airless,
/area/ruin)
@@ -139,11 +145,6 @@
/turf/open/floor/iron/checker/airless,
/area/ruin)
"Ar" = (
-/obj/item/ammo_casing/energy/c3dbullet{
- dir = 5;
- pixel_x = 59;
- pixel_y = 6
- },
/obj/item/clothing/mask/gas/clown_hat{
pixel_y = 39
},
@@ -216,7 +217,6 @@
/area/ruin)
"JK" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber,
-/obj/item/ammo_casing/energy/c3dbullet,
/obj/effect/decal/cleanable/blood/gibs/up,
/turf/open/floor/plating/airless,
/area/ruin)
@@ -252,8 +252,8 @@
/area/ruin)
"Pq" = (
/obj/machinery/light/broken/directional/north,
-/obj/item/ammo_casing/energy/c3dbullet,
/obj/structure/reagent_dispensers/watertank,
+/obj/item/ammo_casing/a357/spent,
/turf/open/floor/iron/checker/airless,
/area/ruin)
"Qb" = (
@@ -267,7 +267,6 @@
/turf/open/floor/iron/checker/airless,
/area/ruin)
"Vj" = (
-/obj/structure/grille,
/obj/effect/spawner/structure/window/reinforced,
/obj/effect/mapping_helpers/damaged_window,
/obj/effect/decal/cleanable/blood/splatter/over_window,
diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm
index d2e8f5c89f7..2677923ac53 100644
--- a/_maps/map_files/Birdshot/birdshot.dmm
+++ b/_maps/map_files/Birdshot/birdshot.dmm
@@ -16109,7 +16109,7 @@
/obj/structure/chair/office/light{
dir = 4
},
-/mob/living/simple_animal/parrot/poly,
+/mob/living/basic/parrot/poly,
/obj/effect/decal/cleanable/dirt,
/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{
dir = 4
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index 0c73cb0f482..142e2ea32c7 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -34717,7 +34717,7 @@
/obj/structure/filingcabinet/chestdrawer,
/obj/effect/turf_decal/tile/neutral/fourcorners,
/obj/machinery/newscaster/directional/north,
-/mob/living/simple_animal/parrot/poly,
+/mob/living/basic/parrot/poly,
/turf/open/floor/iron/dark,
/area/station/command/heads_quarters/ce)
"iDq" = (
diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
index 4c286912a63..e37c8699be8 100644
--- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm
+++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
@@ -4163,16 +4163,6 @@
/obj/structure/sign/poster/contraband/the_griffin/directional/south,
/turf/open/floor/iron/grimy,
/area/station/commons/vacant_room/office)
-"boj" = (
-/obj/machinery/airalarm/directional/north,
-/obj/structure/closet/secure_closet/personal,
-/obj/item/storage/briefcase/secure,
-/obj/effect/turf_decal/tile/neutral/opposingcorners,
-/obj/effect/turf_decal/tile/brown/opposingcorners{
- dir = 1
- },
-/turf/open/floor/iron,
-/area/station/commons/vacant_room/commissary)
"bol" = (
/turf/open/floor/iron/dark/textured,
/area/station/security/prison)
@@ -4594,12 +4584,6 @@
},
/turf/open/floor/plating,
/area/station/engineering/atmos/pumproom)
-"bup" = (
-/obj/structure/filingcabinet/chestdrawer,
-/obj/effect/turf_decal/tile/neutral/fourcorners,
-/mob/living/simple_animal/parrot/poly,
-/turf/open/floor/iron/dark,
-/area/station/command/heads_quarters/ce)
"buv" = (
/obj/machinery/atmospherics/pipe/multiz/scrubbers/visible/layer2{
color = "#ff0000";
@@ -9346,11 +9330,6 @@
/area/station/cargo/warehouse)
"cLw" = (
/obj/structure/table/reinforced,
-/obj/machinery/door/window/left/directional/north{
- dir = 4;
- name = "Engineering Desk";
- req_access = list("engine_equip")
- },
/obj/machinery/door/firedoor,
/obj/item/paper_bin{
pixel_x = -6;
@@ -9367,6 +9346,11 @@
/obj/structure/desk_bell{
pixel_x = 6
},
+/obj/machinery/door/window/left/directional/north{
+ dir = 4;
+ name = "Engineering Desk";
+ req_access = list("engineering")
+ },
/turf/open/floor/iron,
/area/station/engineering/lobby)
"cLB" = (
@@ -10054,14 +10038,6 @@
},
/turf/open/floor/plating/snowed/icemoon,
/area/icemoon/surface/outdoors/nospawn)
-"cXb" = (
-/obj/structure/table/reinforced,
-/obj/structure/secure_safe/caps_spare/directional/east,
-/obj/item/papercutter{
- pixel_x = 7
- },
-/turf/open/floor/iron,
-/area/station/command/bridge)
"cXc" = (
/obj/effect/turf_decal/arrows,
/turf/open/floor/iron,
@@ -17140,6 +17116,12 @@
"fiL" = (
/turf/closed/wall/r_wall,
/area/station/security/evidence)
+"fiN" = (
+/obj/structure/table/wood,
+/obj/structure/secure_safe/directional/east,
+/obj/machinery/light/directional/east,
+/turf/open/floor/wood,
+/area/station/command/heads_quarters/captain)
"fiO" = (
/obj/structure/disposalpipe/segment{
dir = 6
@@ -17992,6 +17974,14 @@
/obj/structure/cable,
/turf/open/floor/iron/showroomfloor,
/area/station/engineering/atmos)
+"fwQ" = (
+/obj/structure/table/reinforced,
+/obj/structure/secure_safe/caps_spare/directional/east,
+/obj/item/papercutter{
+ pixel_x = 7
+ },
+/turf/open/floor/iron,
+/area/station/command/bridge)
"fwS" = (
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
@@ -27847,15 +27837,6 @@
/obj/effect/turf_decal/tile/yellow/opposingcorners,
/turf/open/floor/iron/white,
/area/station/maintenance/port/fore)
-"iBi" = (
-/obj/effect/turf_decal/stripes/line{
- dir = 6
- },
-/obj/structure/table,
-/obj/structure/secure_safe/directional/south,
-/obj/item/storage/briefcase/secure,
-/turf/open/floor/iron/smooth,
-/area/station/command/heads_quarters/rd)
"iBj" = (
/obj/effect/turf_decal/trimline/blue/filled/line{
dir = 1
@@ -28640,14 +28621,6 @@
/obj/effect/spawner/random/structure/steam_vent,
/turf/open/floor/plating,
/area/station/maintenance/port/aft)
-"iOQ" = (
-/obj/structure/table/reinforced,
-/obj/item/storage/briefcase/secure{
- pixel_y = 5
- },
-/obj/machinery/status_display/evac/directional/west,
-/turf/open/floor/iron,
-/area/station/command/bridge)
"iOS" = (
/obj/machinery/airalarm/directional/east,
/obj/structure/table,
@@ -30568,6 +30541,12 @@
},
/turf/open/floor/iron/white,
/area/station/medical/treatment_center)
+"juH" = (
+/obj/structure/filingcabinet/chestdrawer,
+/obj/effect/turf_decal/tile/neutral/fourcorners,
+/mob/living/basic/parrot/poly,
+/turf/open/floor/iron/dark,
+/area/station/command/heads_quarters/ce)
"juQ" = (
/obj/structure/rack,
/obj/item/stack/rods/fifty,
@@ -32043,11 +32022,6 @@
},
/turf/open/floor/iron,
/area/station/hallway/primary/aft)
-"jSc" = (
-/obj/structure/bookcase/random,
-/obj/structure/bookcase/random,
-/turf/open/floor/carpet/red,
-/area/station/security/prison/work)
"jSe" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -35568,6 +35542,13 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden,
/turf/open/floor/iron/showroomfloor,
/area/station/engineering/atmos)
+"kRy" = (
+/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
+/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
+/obj/effect/turf_decal/tile/red,
+/obj/structure/cable,
+/turf/open/floor/iron,
+/area/station/hallway/primary/central/fore)
"kRE" = (
/obj/machinery/computer/mech_bay_power_console{
dir = 8
@@ -38129,6 +38110,14 @@
/obj/structure/cable,
/turf/open/floor/plating,
/area/station/maintenance/starboard/aft)
+"lET" = (
+/obj/structure/table/reinforced,
+/obj/item/storage/briefcase/secure{
+ pixel_y = 5
+ },
+/obj/machinery/status_display/evac/directional/west,
+/turf/open/floor/iron,
+/area/station/command/bridge)
"lFe" = (
/obj/structure/bookcase/random/adult,
/turf/open/floor/iron/dark/textured,
@@ -41007,7 +40996,7 @@
"mCb" = (
/mob/living/basic/goat/pete{
desc = "Not known for their pleasant disposition. This one seems a bit more hardy to the cold.";
- habitable_atmos = list("min_oxy" = 1, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0);
+ habitable_atmos = list("min_oxy"=1,"max_oxy"=0,"min_plas"=0,"max_plas"=1,"min_co2"=0,"max_co2"=5,"min_n2"=0,"max_n2"=0);
minimum_survivable_temperature = 150;
name = "Snowy Pete"
},
@@ -42338,12 +42327,6 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/turf/open/floor/iron,
/area/station/commons/fitness)
-"mZO" = (
-/obj/structure/table/wood,
-/obj/structure/secure_safe/directional/east,
-/obj/machinery/light/directional/east,
-/turf/open/floor/wood,
-/area/station/command/heads_quarters/captain)
"mZS" = (
/obj/machinery/door/firedoor,
/obj/effect/turf_decal/tile/blue,
@@ -45872,6 +45855,7 @@
},
/obj/machinery/door/firedoor,
/obj/effect/mapping_helpers/airlock/access/all/security/general,
+/obj/structure/cable,
/turf/open/floor/iron/textured,
/area/station/security/brig)
"nZf" = (
@@ -52200,6 +52184,14 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/turf/open/floor/iron/cafeteria,
/area/station/commons/storage/art)
+"pRX" = (
+/obj/structure/secure_safe/directional/south,
+/obj/machinery/light/directional/south,
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
+/turf/open/floor/plating,
+/area/station/commons/vacant_room/commissary)
"pRZ" = (
/obj/machinery/shower/directional/south,
/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4,
@@ -53283,6 +53275,15 @@
/obj/machinery/firealarm/directional/north,
/turf/open/floor/iron,
/area/station/science/explab)
+"qlO" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 6
+ },
+/obj/structure/table,
+/obj/structure/secure_safe/directional/south,
+/obj/item/storage/briefcase/secure,
+/turf/open/floor/iron/smooth,
+/area/station/command/heads_quarters/rd)
"qlP" = (
/obj/machinery/door/airlock/external/glass,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
@@ -54822,29 +54823,6 @@
/obj/item/cigbutt,
/turf/open/floor/wood/large,
/area/mine/eva/lower)
-"qJO" = (
-/obj/structure/table/wood,
-/obj/item/reagent_containers/cup/glass/bottle/vodka/badminka{
- pixel_x = 7;
- pixel_y = 20
- },
-/obj/item/taperecorder{
- pixel_x = -5;
- pixel_y = 1
- },
-/obj/item/reagent_containers/cup/glass/drinkingglass/shotglass{
- pixel_x = 7;
- pixel_y = 8
- },
-/obj/item/reagent_containers/cup/glass/drinkingglass/shotglass{
- pixel_x = 6
- },
-/obj/structure/secure_safe/hos{
- pixel_x = 35
- },
-/obj/machinery/firealarm/directional/south,
-/turf/open/floor/iron/dark/smooth_large,
-/area/station/command/heads_quarters/hos)
"qJT" = (
/obj/machinery/light/small/directional/south,
/turf/open/floor/plating/snowed/icemoon,
@@ -59015,6 +58993,17 @@
/obj/structure/cable,
/turf/open/floor/carpet,
/area/station/command/heads_quarters/captain)
+"rXj" = (
+/obj/structure/bed{
+ dir = 4
+ },
+/obj/item/bedsheet/medical{
+ dir = 4
+ },
+/obj/structure/secure_safe/directional/south,
+/obj/effect/turf_decal/tile/green/full,
+/turf/open/floor/iron/dark/smooth_large,
+/area/station/medical/virology)
"rXr" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{
dir = 4
@@ -59430,6 +59419,10 @@
/obj/effect/turf_decal/tile/neutral/half/contrasted,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/command/storage/eva)
+"sen" = (
+/obj/structure/cable,
+/turf/open/floor/iron,
+/area/station/security/courtroom)
"seA" = (
/obj/effect/spawner/structure/window/reinforced,
/obj/structure/cable,
@@ -65563,6 +65556,16 @@
/obj/effect/decal/cleanable/cobweb,
/turf/open/floor/plating,
/area/station/maintenance/department/chapel)
+"uao" = (
+/obj/machinery/airalarm/directional/north,
+/obj/structure/closet/secure_closet/personal,
+/obj/item/storage/briefcase/secure,
+/obj/effect/turf_decal/tile/neutral/opposingcorners,
+/obj/effect/turf_decal/tile/brown/opposingcorners{
+ dir = 1
+ },
+/turf/open/floor/iron,
+/area/station/commons/vacant_room/commissary)
"uar" = (
/obj/structure/disposalpipe/segment{
dir = 4
@@ -69730,6 +69733,29 @@
dir = 10
},
/area/station/science/research)
+"vtZ" = (
+/obj/structure/table/wood,
+/obj/item/reagent_containers/cup/glass/bottle/vodka/badminka{
+ pixel_x = 7;
+ pixel_y = 20
+ },
+/obj/item/taperecorder{
+ pixel_x = -5;
+ pixel_y = 1
+ },
+/obj/item/reagent_containers/cup/glass/drinkingglass/shotglass{
+ pixel_x = 7;
+ pixel_y = 8
+ },
+/obj/item/reagent_containers/cup/glass/drinkingglass/shotglass{
+ pixel_x = 6
+ },
+/obj/structure/secure_safe/hos{
+ pixel_x = 35
+ },
+/obj/machinery/firealarm/directional/south,
+/turf/open/floor/iron/dark/smooth_large,
+/area/station/command/heads_quarters/hos)
"vuh" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
@@ -70540,6 +70566,7 @@
"vFO" = (
/obj/structure/window/reinforced/spawner/directional/west,
/obj/structure/window/reinforced/spawner/directional/east,
+/obj/structure/cable,
/turf/open/floor/iron/textured,
/area/station/security/brig)
"vFW" = (
@@ -70739,17 +70766,6 @@
/obj/machinery/light_switch/directional/west,
/turf/open/floor/iron/dark,
/area/station/science/breakroom)
-"vJX" = (
-/obj/structure/bed{
- dir = 4
- },
-/obj/item/bedsheet/medical{
- dir = 4
- },
-/obj/structure/secure_safe/directional/south,
-/obj/effect/turf_decal/tile/green/full,
-/turf/open/floor/iron/dark/smooth_large,
-/area/station/medical/virology)
"vJY" = (
/obj/structure/railing/corner{
dir = 8
@@ -73970,14 +73986,6 @@
/obj/effect/landmark/start/hangover,
/turf/open/floor/carpet,
/area/station/commons/dorms)
-"wIE" = (
-/obj/structure/secure_safe/directional/south,
-/obj/machinery/light/directional/south,
-/obj/structure/disposalpipe/segment{
- dir = 4
- },
-/turf/open/floor/plating,
-/area/station/commons/vacant_room/commissary)
"wIF" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -77379,6 +77387,7 @@
},
/obj/machinery/door/firedoor,
/obj/effect/mapping_helpers/airlock/access/all/security/general,
+/obj/structure/cable,
/turf/open/floor/iron/textured,
/area/station/security/courtroom)
"xGZ" = (
@@ -78425,7 +78434,7 @@
},
/obj/machinery/elevator_control_panel/directional/north{
linked_elevator_id = "publicElevator";
- preset_destination_names = list("3" = "Icemoon Level", "4" = "Station Level")
+ preset_destination_names = list("3"="Icemoon Level","4"="Station Level")
},
/turf/open/floor/plating/elevatorshaft,
/area/mine/storage)
@@ -169146,7 +169155,7 @@ scl
dck
scl
htB
-jSc
+scl
jty
cIc
nUL
@@ -169168,7 +169177,7 @@ jLB
qpB
qpB
siv
-qpB
+kRy
qpB
iwC
qzV
@@ -170460,8 +170469,8 @@ vFO
vFO
vFO
xGX
-aiX
-aiX
+sen
+sen
lQq
aiX
vrc
@@ -184135,7 +184144,7 @@ hzz
gmf
jUB
gyY
-vJX
+rXj
xDb
thA
thA
@@ -233195,7 +233204,7 @@ gpZ
vov
cAo
gst
-boj
+uao
plN
rTO
rTO
@@ -233457,7 +233466,7 @@ fXu
uOM
vzw
rTO
-wIE
+pRX
gst
byl
rgl
@@ -237817,7 +237826,7 @@ dnq
kgD
utR
uEQ
-iOQ
+lET
grA
cEv
nOH
@@ -239576,7 +239585,7 @@ jDt
wtg
diq
ehy
-qJO
+vtZ
mgU
fUj
kcc
@@ -240388,7 +240397,7 @@ nfk
utR
tmQ
qnV
-cXb
+fwQ
kBr
lhv
iGH
@@ -240442,7 +240451,7 @@ ami
oPU
wHj
mBB
-bup
+juH
ojv
htc
mDw
@@ -242454,7 +242463,7 @@ eEC
aTw
iFL
hpe
-mZO
+fiN
mBX
uEm
viQ
@@ -254813,7 +254822,7 @@ bHa
oMT
gaT
bZc
-iBi
+qlO
jbU
ily
hdH
diff --git a/_maps/map_files/KiloStation2/KiloStation2.dmm b/_maps/map_files/KiloStation2/KiloStation2.dmm
index beca1ca8175..25026c9cc6c 100644
--- a/_maps/map_files/KiloStation2/KiloStation2.dmm
+++ b/_maps/map_files/KiloStation2/KiloStation2.dmm
@@ -72595,7 +72595,7 @@
pixel_x = 12
},
/obj/item/radio/intercom/directional/east,
-/mob/living/simple_animal/parrot/poly,
+/mob/living/basic/parrot/poly,
/turf/open/floor/iron/dark,
/area/station/command/heads_quarters/ce)
"xjr" = (
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index e6ba2a8dda4..6b23ab29959 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -152,6 +152,14 @@
"adp" = (
/turf/closed/wall,
/area/station/hallway/primary/starboard)
+"adz" = (
+/obj/structure/closet/secure_closet/engineering_chief,
+/obj/machinery/airalarm/directional/east,
+/obj/structure/cable,
+/obj/item/storage/briefcase/secure,
+/obj/effect/turf_decal/tile/neutral/fourcorners,
+/turf/open/floor/iron/dark,
+/area/station/command/heads_quarters/ce)
"adD" = (
/obj/effect/spawner/random/structure/closet_maintenance,
/obj/effect/spawner/random/maintenance,
@@ -3102,12 +3110,6 @@
/obj/effect/mapping_helpers/broken_floor,
/turf/open/floor/plating,
/area/station/maintenance/fore/lesser)
-"bdV" = (
-/obj/structure/filingcabinet/chestdrawer,
-/obj/effect/turf_decal/tile/neutral/fourcorners,
-/mob/living/simple_animal/parrot/poly,
-/turf/open/floor/iron/dark,
-/area/station/command/heads_quarters/ce)
"beo" = (
/obj/effect/turf_decal/tile/brown/half/contrasted{
dir = 4
@@ -3312,6 +3314,17 @@
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2,
/turf/open/floor/iron/dark,
/area/station/security/execution/education)
+"bhv" = (
+/obj/effect/landmark/blobstart,
+/obj/machinery/camera/directional/north{
+ c_tag = "Security - Evidence Storage"
+ },
+/obj/structure/secure_safe/directional/north{
+ name = "evidence safe"
+ },
+/obj/effect/turf_decal/tile/neutral/fourcorners,
+/turf/open/floor/iron/dark,
+/area/station/security/evidence)
"bhM" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{
dir = 8
@@ -7406,6 +7419,14 @@
/obj/item/hand_labeler,
/turf/open/floor/wood,
/area/station/command/heads_quarters/hop)
+"cKW" = (
+/obj/structure/secure_safe/directional/north,
+/obj/machinery/camera/directional/north{
+ c_tag = "Chief Engineer's Office"
+ },
+/obj/effect/turf_decal/tile/neutral/fourcorners,
+/turf/open/floor/iron/dark,
+/area/station/command/heads_quarters/ce)
"cLa" = (
/obj/structure/weightmachine,
/obj/effect/turf_decal/tile/dark_red/half/contrasted,
@@ -7466,19 +7487,6 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/turf/open/floor/plating,
/area/station/maintenance/port)
-"cMc" = (
-/obj/machinery/recharger{
- pixel_x = 2;
- pixel_y = 3
- },
-/obj/structure/secure_safe/directional/east,
-/obj/structure/table/wood,
-/obj/item/flashlight/lamp/green{
- pixel_x = -12;
- pixel_y = 5
- },
-/turf/open/floor/wood,
-/area/station/command/heads_quarters/hop)
"cMd" = (
/obj/machinery/atmospherics/pipe/smart/simple/cyan/visible,
/turf/open/floor/iron,
@@ -7683,6 +7691,22 @@
/obj/structure/window/spawner/directional/south,
/turf/open/floor/iron,
/area/station/engineering/atmos)
+"cQz" = (
+/obj/structure/table/glass,
+/obj/machinery/light_switch/directional/north,
+/obj/item/storage/briefcase/secure{
+ pixel_x = 3;
+ pixel_y = 5
+ },
+/obj/item/storage/medkit/regular{
+ pixel_x = -3;
+ pixel_y = -3
+ },
+/obj/effect/turf_decal/tile/blue/anticorner/contrasted{
+ dir = 1
+ },
+/turf/open/floor/iron/white,
+/area/station/command/heads_quarters/cmo)
"cQQ" = (
/obj/machinery/meter,
/obj/machinery/atmospherics/pipe/smart/manifold/purple/visible{
@@ -8395,20 +8419,6 @@
/obj/structure/reagent_dispensers/fueltank,
/turf/open/floor/plating,
/area/station/maintenance/starboard/aft)
-"ddy" = (
-/obj/structure/rack,
-/obj/item/storage/briefcase{
- pixel_x = -3;
- pixel_y = 2
- },
-/obj/item/storage/briefcase/secure{
- pixel_x = 2;
- pixel_y = -2
- },
-/obj/item/taperecorder,
-/obj/item/clothing/glasses/sunglasses,
-/turf/open/floor/wood,
-/area/station/service/lawoffice)
"ddK" = (
/obj/structure/cable,
/obj/structure/disposalpipe/segment{
@@ -10749,6 +10759,12 @@
/obj/effect/mapping_helpers/turn_off_lights_with_lightswitch,
/turf/open/floor/iron,
/area/station/cargo/warehouse)
+"dXs" = (
+/obj/structure/secure_safe/directional/north{
+ name = "armory safe A"
+ },
+/turf/open/floor/iron/dark,
+/area/station/ai_monitored/security/armory)
"dXA" = (
/obj/structure/closet/crate/trashcart,
/obj/effect/spawner/random/contraband/prison,
@@ -11180,6 +11196,19 @@
},
/turf/open/floor/iron/dark,
/area/station/ai_monitored/security/armory)
+"eey" = (
+/obj/machinery/firealarm/directional/west,
+/obj/structure/rack,
+/obj/item/storage/briefcase{
+ pixel_x = -3;
+ pixel_y = 2
+ },
+/obj/item/storage/briefcase/secure{
+ pixel_x = 2;
+ pixel_y = -2
+ },
+/turf/open/floor/iron/grimy,
+/area/station/security/detectives_office)
"eeT" = (
/obj/machinery/vending/hydroseeds{
slogan_delay = 700
@@ -11292,6 +11321,14 @@
/obj/machinery/door/firedoor,
/turf/open/floor/iron,
/area/station/hallway/primary/starboard)
+"egp" = (
+/obj/item/storage/briefcase/secure,
+/obj/structure/table/wood,
+/obj/item/folder/blue,
+/obj/item/storage/briefcase/secure,
+/obj/item/assembly/flash/handheld,
+/turf/open/floor/wood,
+/area/station/command/heads_quarters/hop)
"egs" = (
/obj/effect/spawner/random/maintenance/two,
/obj/structure/rack,
@@ -12879,6 +12916,15 @@
"eKP" = (
/turf/closed/wall/r_wall,
/area/station/science/ordnance/freezerchamber)
+"eLa" = (
+/obj/structure/table/wood,
+/obj/item/storage/briefcase/secure{
+ desc = "A large briefcase with a digital locking system, and the Nanotrasen logo emblazoned on the sides.";
+ name = "\improper Nanotrasen-brand secure briefcase exhibit";
+ pixel_y = 2
+ },
+/turf/open/floor/carpet,
+/area/station/command/corporate_showroom)
"eLb" = (
/obj/structure/cable,
/obj/structure/disposalpipe/segment{
@@ -14076,14 +14122,6 @@
/obj/effect/spawner/random/maintenance/three,
/turf/open/floor/plating,
/area/station/maintenance/port/aft)
-"fhz" = (
-/obj/structure/secure_safe/directional/north,
-/obj/machinery/camera/directional/north{
- c_tag = "Chief Engineer's Office"
- },
-/obj/effect/turf_decal/tile/neutral/fourcorners,
-/turf/open/floor/iron/dark,
-/area/station/command/heads_quarters/ce)
"fhA" = (
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -14192,19 +14230,6 @@
},
/turf/open/floor/iron/white/smooth_large,
/area/station/medical/medbay/central)
-"fiI" = (
-/obj/machinery/firealarm/directional/west,
-/obj/structure/rack,
-/obj/item/storage/briefcase{
- pixel_x = -3;
- pixel_y = 2
- },
-/obj/item/storage/briefcase/secure{
- pixel_x = 2;
- pixel_y = -2
- },
-/turf/open/floor/iron/grimy,
-/area/station/security/detectives_office)
"fiK" = (
/obj/structure/table/glass,
/obj/item/clothing/gloves/latex,
@@ -15032,15 +15057,6 @@
},
/turf/open/floor/iron,
/area/station/engineering/break_room)
-"fze" = (
-/obj/structure/secure_safe/hos{
- pixel_x = 36;
- pixel_y = 28
- },
-/obj/machinery/status_display/evac/directional/north,
-/obj/structure/cable,
-/turf/open/floor/carpet,
-/area/station/command/heads_quarters/hos)
"fzi" = (
/obj/effect/turf_decal/trimline/neutral/filled/corner{
dir = 4
@@ -16533,6 +16549,15 @@
/obj/machinery/power/apc/auto_name/directional/east,
/turf/open/floor/iron/white,
/area/station/security/prison/visit)
+"gem" = (
+/obj/structure/table,
+/obj/item/storage/briefcase/secure{
+ pixel_x = -7;
+ pixel_y = 12
+ },
+/obj/effect/spawner/random/engineering/flashlight,
+/turf/open/floor/iron/dark,
+/area/station/security/office)
"gen" = (
/obj/structure/table/glass,
/obj/item/folder/blue{
@@ -16579,17 +16604,6 @@
},
/turf/open/floor/iron,
/area/station/service/hydroponics/garden)
-"gfc" = (
-/obj/structure/table/wood,
-/obj/machinery/light_switch/directional/west,
-/obj/item/storage/briefcase/secure{
- pixel_x = -2;
- pixel_y = 4
- },
-/obj/item/storage/lockbox/medal,
-/obj/structure/window/reinforced/spawner/directional/south,
-/turf/open/floor/wood,
-/area/station/command/heads_quarters/captain/private)
"gfe" = (
/obj/effect/decal/cleanable/cobweb/cobweb2,
/obj/structure/table,
@@ -17449,6 +17463,17 @@
},
/turf/open/floor/iron,
/area/station/service/janitor)
+"guO" = (
+/obj/structure/table/wood,
+/obj/machinery/light_switch/directional/west,
+/obj/item/storage/briefcase/secure{
+ pixel_x = -2;
+ pixel_y = 4
+ },
+/obj/item/storage/lockbox/medal,
+/obj/structure/window/reinforced/spawner/directional/south,
+/turf/open/floor/wood,
+/area/station/command/heads_quarters/captain/private)
"guR" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -17729,6 +17754,19 @@
/obj/effect/spawner/structure/window/reinforced,
/turf/open/floor/plating,
/area/station/maintenance/aft/greater)
+"gAk" = (
+/obj/machinery/recharger{
+ pixel_x = 2;
+ pixel_y = 3
+ },
+/obj/structure/secure_safe/directional/east,
+/obj/structure/table/wood,
+/obj/item/flashlight/lamp/green{
+ pixel_x = -12;
+ pixel_y = 5
+ },
+/turf/open/floor/wood,
+/area/station/command/heads_quarters/hop)
"gAt" = (
/obj/structure/reagent_dispensers/fueltank,
/turf/open/floor/plating,
@@ -19649,6 +19687,20 @@
},
/turf/open/floor/iron,
/area/station/cargo/sorting)
+"hlj" = (
+/obj/structure/table/wood,
+/obj/structure/secure_safe/directional/east,
+/obj/machinery/computer/security/wooden_tv{
+ pixel_x = 3;
+ pixel_y = 2
+ },
+/obj/machinery/button/door/directional/north{
+ id = "detective_shutters";
+ name = "detective's office shutters control";
+ req_access = list("detective")
+ },
+/turf/open/floor/carpet,
+/area/station/security/detectives_office)
"hlq" = (
/obj/structure/chair{
dir = 4
@@ -23602,13 +23654,6 @@
/obj/structure/reagent_dispensers/plumbed,
/turf/open/floor/plating,
/area/station/maintenance/port/aft)
-"iEs" = (
-/obj/structure/secure_safe/directional/south,
-/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{
- dir = 1
- },
-/turf/open/floor/plating,
-/area/station/commons/vacant_room/commissary)
"iEv" = (
/obj/structure/closet,
/obj/effect/turf_decal/tile/neutral/fourcorners,
@@ -26067,17 +26112,6 @@
/obj/effect/turf_decal/tile/neutral/fourcorners,
/turf/open/floor/iron/dark,
/area/station/engineering/storage/tech)
-"jtw" = (
-/obj/machinery/light/directional/south,
-/obj/machinery/firealarm/directional/south,
-/obj/structure/rack,
-/obj/item/storage/briefcase/secure,
-/obj/item/clothing/mask/cigarette/cigar,
-/obj/effect/turf_decal/tile/blue/half/contrasted{
- dir = 1
- },
-/turf/open/floor/iron/dark,
-/area/station/command/bridge)
"jtA" = (
/obj/structure/table/glass,
/obj/effect/turf_decal/siding/white{
@@ -36844,25 +36878,6 @@
},
/turf/open/floor/iron,
/area/station/hallway/primary/aft)
-"nka" = (
-/obj/structure/safe,
-/obj/item/storage/briefcase/secure/riches,
-/obj/item/storage/backpack/duffelbag/syndie/hitman,
-/obj/item/card/id/advanced/silver/reaper,
-/obj/item/lazarus_injector,
-/obj/item/gun/energy/disabler,
-/obj/item/gun/ballistic/revolver/russian,
-/obj/item/ammo_box/a357,
-/obj/item/clothing/neck/stethoscope,
-/obj/item/book{
- desc = "An undeniably handy book.";
- icon_state = "bookknock";
- name = "\improper A Simpleton's Guide to Safe-cracking with Stethoscopes"
- },
-/obj/effect/turf_decal/bot_white/left,
-/obj/effect/turf_decal/tile/neutral/fourcorners,
-/turf/open/floor/iron/dark,
-/area/station/ai_monitored/command/nuke_storage)
"nkj" = (
/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{
dir = 10
@@ -36925,14 +36940,6 @@
/obj/structure/cable,
/turf/open/floor/iron/white,
/area/station/science/research)
-"nlV" = (
-/obj/item/storage/briefcase/secure,
-/obj/structure/table/wood,
-/obj/item/folder/blue,
-/obj/item/storage/briefcase/secure,
-/obj/item/assembly/flash/handheld,
-/turf/open/floor/wood,
-/area/station/command/heads_quarters/hop)
"nmf" = (
/obj/machinery/smartfridge,
/obj/machinery/door/poddoor/shutters/preopen{
@@ -37349,12 +37356,6 @@
/obj/effect/turf_decal/bot,
/turf/open/floor/wood,
/area/station/service/theater)
-"nsx" = (
-/obj/machinery/light_switch/directional/east,
-/obj/structure/dresser,
-/obj/structure/secure_safe/directional/north,
-/turf/open/floor/wood,
-/area/station/command/heads_quarters/captain/private)
"nsA" = (
/turf/closed/wall,
/area/station/science/ordnance/testlab)
@@ -37526,6 +37527,13 @@
},
/turf/open/floor/iron/white,
/area/station/medical/office)
+"nuB" = (
+/obj/structure/secure_safe/directional/south,
+/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{
+ dir = 1
+ },
+/turf/open/floor/plating,
+/area/station/commons/vacant_room/commissary)
"nuI" = (
/obj/effect/turf_decal/stripes/line{
dir = 4
@@ -38292,14 +38300,6 @@
/obj/structure/easel,
/turf/open/floor/plating,
/area/station/maintenance/disposal)
-"nIx" = (
-/obj/structure/closet/secure_closet/engineering_chief,
-/obj/machinery/airalarm/directional/east,
-/obj/structure/cable,
-/obj/item/storage/briefcase/secure,
-/obj/effect/turf_decal/tile/neutral/fourcorners,
-/turf/open/floor/iron/dark,
-/area/station/command/heads_quarters/ce)
"nIP" = (
/obj/structure/table/glass,
/obj/item/paper_bin{
@@ -39590,6 +39590,20 @@
},
/turf/open/floor/iron/white,
/area/station/command/heads_quarters/captain/private)
+"oha" = (
+/obj/structure/rack,
+/obj/item/storage/briefcase{
+ pixel_x = -3;
+ pixel_y = 2
+ },
+/obj/item/storage/briefcase/secure{
+ pixel_x = 2;
+ pixel_y = -2
+ },
+/obj/item/taperecorder,
+/obj/item/clothing/glasses/sunglasses,
+/turf/open/floor/wood,
+/area/station/service/lawoffice)
"ohm" = (
/obj/effect/turf_decal/stripes/line{
dir = 1
@@ -40100,6 +40114,15 @@
/obj/structure/window/reinforced/spawner/directional/east,
/turf/open/space,
/area/space/nearstation)
+"osH" = (
+/obj/structure/secure_safe/hos{
+ pixel_x = 36;
+ pixel_y = 28
+ },
+/obj/machinery/status_display/evac/directional/north,
+/obj/structure/cable,
+/turf/open/floor/carpet,
+/area/station/command/heads_quarters/hos)
"ota" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -41723,6 +41746,25 @@
/obj/item/stack/cable_coil,
/turf/open/floor/plating,
/area/station/maintenance/solars/starboard/aft)
+"oXK" = (
+/obj/structure/safe,
+/obj/item/storage/briefcase/secure/riches,
+/obj/item/storage/backpack/duffelbag/syndie/hitman,
+/obj/item/card/id/advanced/silver/reaper,
+/obj/item/lazarus_injector,
+/obj/item/gun/energy/disabler,
+/obj/item/gun/ballistic/revolver/russian,
+/obj/item/ammo_box/a357,
+/obj/item/clothing/neck/stethoscope,
+/obj/item/book{
+ desc = "An undeniably handy book.";
+ icon_state = "bookknock";
+ name = "\improper A Simpleton's Guide to Safe-cracking with Stethoscopes"
+ },
+/obj/effect/turf_decal/bot_white/left,
+/obj/effect/turf_decal/tile/neutral/fourcorners,
+/turf/open/floor/iron/dark,
+/area/station/ai_monitored/command/nuke_storage)
"oXL" = (
/obj/machinery/disposal/bin,
/obj/structure/cable,
@@ -41810,17 +41852,6 @@
},
/turf/open/floor/engine/cult,
/area/station/service/library)
-"oZn" = (
-/obj/effect/landmark/blobstart,
-/obj/machinery/camera/directional/north{
- c_tag = "Security - Evidence Storage"
- },
-/obj/structure/secure_safe/directional/north{
- name = "evidence safe"
- },
-/obj/effect/turf_decal/tile/neutral/fourcorners,
-/turf/open/floor/iron/dark,
-/area/station/security/evidence)
"oZs" = (
/obj/structure/bed/medical/emergency{
dir = 4
@@ -42351,20 +42382,6 @@
/obj/effect/turf_decal/tile/neutral/fourcorners,
/turf/open/floor/iron/dark,
/area/station/hallway/primary/central)
-"pjK" = (
-/obj/structure/table/wood,
-/obj/structure/secure_safe/directional/east,
-/obj/machinery/computer/security/wooden_tv{
- pixel_x = 3;
- pixel_y = 2
- },
-/obj/machinery/button/door/directional/north{
- id = "detective_shutters";
- name = "detective's office shutters control";
- req_access = list("detective")
- },
-/turf/open/floor/carpet,
-/area/station/security/detectives_office)
"pjS" = (
/obj/machinery/vending/cigarette,
/obj/structure/extinguisher_cabinet/directional/east,
@@ -47076,15 +47093,6 @@
},
/turf/open/floor/iron,
/area/station/commons/locker)
-"qRs" = (
-/obj/structure/table/wood,
-/obj/item/storage/briefcase/secure{
- desc = "A large briefcase with a digital locking system, and the Nanotrasen logo emblazoned on the sides.";
- name = "\improper Nanotrasen-brand secure briefcase exhibit";
- pixel_y = 2
- },
-/turf/open/floor/carpet,
-/area/station/command/corporate_showroom)
"qRz" = (
/obj/machinery/atmospherics/components/unary/portables_connector/visible,
/obj/effect/spawner/random/trash/janitor_supplies,
@@ -48856,18 +48864,6 @@
/obj/structure/cable,
/turf/open/floor/plating/airless,
/area/station/solars/starboard/aft)
-"rxN" = (
-/obj/item/folder/white{
- pixel_x = 4;
- pixel_y = -3
- },
-/obj/structure/table/glass,
-/obj/structure/secure_safe/caps_spare/directional/west,
-/obj/effect/turf_decal/tile/green/half/contrasted{
- dir = 8
- },
-/turf/open/floor/iron/dark,
-/area/station/command/bridge)
"rxP" = (
/obj/machinery/computer/security,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -50570,6 +50566,12 @@
/obj/machinery/airalarm/directional/south,
/turf/open/floor/iron/white,
/area/station/medical/abandoned)
+"sby" = (
+/obj/structure/filingcabinet/chestdrawer,
+/obj/effect/turf_decal/tile/neutral/fourcorners,
+/mob/living/basic/parrot/poly,
+/turf/open/floor/iron/dark,
+/area/station/command/heads_quarters/ce)
"sbG" = (
/obj/structure/window/reinforced/spawner/directional/west,
/obj/structure/window/reinforced/spawner/directional/east,
@@ -55850,6 +55852,17 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/turf/open/floor/iron/dark,
/area/station/security/execution/education)
+"tVk" = (
+/obj/machinery/light/directional/south,
+/obj/machinery/firealarm/directional/south,
+/obj/structure/rack,
+/obj/item/storage/briefcase/secure,
+/obj/item/clothing/mask/cigarette/cigar,
+/obj/effect/turf_decal/tile/blue/half/contrasted{
+ dir = 1
+ },
+/turf/open/floor/iron/dark,
+/area/station/command/bridge)
"tVm" = (
/obj/structure/closet/secure_closet/brig,
/obj/effect/turf_decal/trimline/red/filled/line{
@@ -56865,22 +56878,6 @@
},
/turf/open/floor/engine,
/area/station/engineering/supermatter/room)
-"uno" = (
-/obj/structure/table/glass,
-/obj/machinery/light_switch/directional/north,
-/obj/item/storage/briefcase/secure{
- pixel_x = 3;
- pixel_y = 5
- },
-/obj/item/storage/medkit/regular{
- pixel_x = -3;
- pixel_y = -3
- },
-/obj/effect/turf_decal/tile/blue/anticorner/contrasted{
- dir = 1
- },
-/turf/open/floor/iron/white,
-/area/station/command/heads_quarters/cmo)
"unt" = (
/obj/item/radio/intercom/directional/south,
/obj/effect/turf_decal/tile/yellow/anticorner/contrasted,
@@ -57226,6 +57223,18 @@
},
/turf/open/floor/iron,
/area/station/commons/locker)
+"usQ" = (
+/obj/item/folder/white{
+ pixel_x = 4;
+ pixel_y = -3
+ },
+/obj/structure/table/glass,
+/obj/structure/secure_safe/caps_spare/directional/west,
+/obj/effect/turf_decal/tile/green/half/contrasted{
+ dir = 8
+ },
+/turf/open/floor/iron/dark,
+/area/station/command/bridge)
"uta" = (
/obj/structure/rack,
/obj/item/book/manual/wiki/security_space_law{
@@ -57842,7 +57851,9 @@
dir = 4;
id = "garbage"
},
-/obj/machinery/recycler,
+/obj/machinery/recycler{
+ dir = 8
+ },
/turf/open/floor/plating,
/area/station/maintenance/disposal)
"uEO" = (
@@ -65311,12 +65322,6 @@
/obj/effect/mapping_helpers/airlock/access/all/medical/cmo,
/turf/open/floor/plating,
/area/station/maintenance/department/medical/central)
-"xgh" = (
-/obj/structure/secure_safe/directional/north{
- name = "armory safe A"
- },
-/turf/open/floor/iron/dark,
-/area/station/ai_monitored/security/armory)
"xgi" = (
/obj/structure/table/reinforced,
/obj/effect/turf_decal/bot,
@@ -66082,6 +66087,23 @@
/obj/effect/spawner/random/structure/grille,
/turf/open/floor/plating,
/area/station/maintenance/port)
+"xuy" = (
+/obj/item/radio/intercom/directional/south,
+/obj/effect/turf_decal/tile/bar,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 8
+ },
+/obj/structure/table,
+/obj/machinery/reagentgrinder{
+ pixel_x = 6;
+ pixel_y = 6
+ },
+/obj/item/reagent_containers/condiment/enzyme{
+ layer = 5;
+ pixel_x = -6
+ },
+/turf/open/floor/iron/cafeteria,
+/area/station/service/kitchen)
"xuA" = (
/obj/effect/turf_decal/trimline/yellow/filled/line{
dir = 4
@@ -66116,6 +66138,16 @@
},
/turf/open/floor/iron/solarpanel/airless,
/area/station/solars/port/fore)
+"xuP" = (
+/obj/machinery/conveyor{
+ dir = 4;
+ id = "garbage"
+ },
+/obj/machinery/recycler{
+ dir = 8
+ },
+/turf/open/floor/plating,
+/area/station/maintenance/disposal)
"xuS" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -67419,15 +67451,6 @@
"xTw" = (
/turf/closed/wall/r_wall,
/area/station/medical/medbay/central)
-"xTH" = (
-/obj/structure/table,
-/obj/item/storage/briefcase/secure{
- pixel_x = -7;
- pixel_y = 12
- },
-/obj/effect/spawner/random/engineering/flashlight,
-/turf/open/floor/iron/dark,
-/area/station/security/office)
"xTO" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -67475,6 +67498,12 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/turf/open/floor/plating,
/area/station/maintenance/port/aft)
+"xUx" = (
+/obj/machinery/light_switch/directional/east,
+/obj/structure/dresser,
+/obj/structure/secure_safe/directional/north,
+/turf/open/floor/wood,
+/area/station/command/heads_quarters/captain/private)
"xUB" = (
/obj/structure/sign/directions/security{
dir = 1;
@@ -67633,8 +67662,8 @@
pixel_y = 6
},
/obj/item/reagent_containers/condiment/enzyme{
- layer = 5;
- pixel_x = -6
+ pixel_x = -6;
+ pixel_y = 5
},
/turf/open/floor/iron/cafeteria,
/area/station/service/kitchen)
@@ -86104,7 +86133,7 @@ gYC
xGm
isO
gYE
-uEC
+xuP
wgw
twr
bSm
@@ -90227,7 +90256,7 @@ rlU
vis
mCi
ixT
-nka
+oXK
rlU
aaa
aEH
@@ -92051,7 +92080,7 @@ wvo
xCl
nMz
pJR
-nlV
+egp
kfA
rWH
oOE
@@ -92086,7 +92115,7 @@ ePX
svQ
duu
pKP
-uno
+cQz
rQd
cJm
jGw
@@ -92306,9 +92335,9 @@ oIa
cpn
koa
srP
-iEs
+nuB
pJR
-cMc
+gAk
qAA
uFQ
eIy
@@ -94580,7 +94609,7 @@ aaa
aaa
aaa
rJB
-oZn
+bhv
dxe
wTO
xxZ
@@ -94619,7 +94648,7 @@ tKN
aaf
dsQ
mZL
-rxN
+usQ
aGQ
acf
hmq
@@ -94880,7 +94909,7 @@ nxO
qXF
qXF
aMB
-jtw
+tVk
duI
jnt
cdC
@@ -94896,7 +94925,7 @@ jzN
sYh
qeZ
nBs
-qRs
+eLa
vyi
nNY
htd
@@ -95608,7 +95637,7 @@ aaa
aaa
cTk
aeq
-xgh
+dXs
tJE
kVU
tJE
@@ -98479,7 +98508,7 @@ ogL
tyY
aPs
cUX
-gfc
+guO
nOq
xDa
vwP
@@ -98697,7 +98726,7 @@ kxA
xXh
ooG
wxj
-xTH
+gem
ipz
nOv
dgS
@@ -99220,7 +99249,7 @@ pBG
sOZ
uWo
jMy
-fiI
+eey
aHr
mhA
xNo
@@ -99741,7 +99770,7 @@ bHN
ilh
ilh
ilh
-ddy
+oha
fbs
itp
soX
@@ -100013,7 +100042,7 @@ wEG
htd
qBC
syo
-nsx
+xUx
cRU
oRn
sjS
@@ -100248,7 +100277,7 @@ lAM
dDe
lAM
sWV
-pjK
+hlj
dbj
jyQ
lGj
@@ -101515,7 +101544,7 @@ aaa
rrt
lMJ
mxn
-fze
+osH
gTt
sdu
fad
@@ -104145,7 +104174,7 @@ hPK
pgK
idR
dVm
-xWY
+xuy
uIs
rwa
aJI
@@ -108233,7 +108262,7 @@ vHs
iBm
uXd
sqE
-fhz
+cKW
dJP
bfO
pCt
@@ -108494,7 +108523,7 @@ nLz
jEh
hYE
rEd
-bdV
+sby
tUw
iqU
jLg
@@ -108751,7 +108780,7 @@ qtm
fyJ
qVD
cuc
-nIx
+adz
tUw
rHq
peX
diff --git a/_maps/map_files/NSSJourney/NSSJourney.dmm b/_maps/map_files/NSSJourney/NSSJourney.dmm
index c75e440de8d..88dc852d932 100644
--- a/_maps/map_files/NSSJourney/NSSJourney.dmm
+++ b/_maps/map_files/NSSJourney/NSSJourney.dmm
@@ -26469,7 +26469,7 @@
/obj/structure/filingcabinet/chestdrawer,
/obj/structure/disposalpipe/segment,
/obj/structure/cable,
-/mob/living/simple_animal/parrot/poly,
+/mob/living/basic/parrot/poly,
/obj/effect/turf_decal/siding/thinplating{
dir = 8
},
diff --git a/_maps/map_files/NSVBlueshift/Blueshift.dmm b/_maps/map_files/NSVBlueshift/Blueshift.dmm
index 6a2d5d1ab47..1f1c9541b5f 100644
--- a/_maps/map_files/NSVBlueshift/Blueshift.dmm
+++ b/_maps/map_files/NSVBlueshift/Blueshift.dmm
@@ -120550,7 +120550,7 @@
dir = 1
},
/obj/structure/filingcabinet/chestdrawer,
-/mob/living/simple_animal/parrot/poly,
+/mob/living/basic/parrot/poly,
/obj/machinery/light_switch/directional/north,
/turf/open/floor/iron/white/corner,
/area/station/command/heads_quarters/ce)
diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm
index 188d6cf2b4b..213928af553 100644
--- a/_maps/map_files/NorthStar/north_star.dmm
+++ b/_maps/map_files/NorthStar/north_star.dmm
@@ -6819,7 +6819,7 @@
/obj/machinery/computer/security/telescreen/engine{
name = "Engineering and atmospherics monitor"
},
-/mob/living/simple_animal/parrot/poly,
+/mob/living/basic/parrot/poly,
/turf/open/floor/catwalk_floor/iron_dark,
/area/station/command/heads_quarters/ce)
"bIQ" = (
diff --git a/_maps/map_files/VoidRaptor/VoidRaptor.dmm b/_maps/map_files/VoidRaptor/VoidRaptor.dmm
index 6d159c3afc7..c426ce32f48 100644
--- a/_maps/map_files/VoidRaptor/VoidRaptor.dmm
+++ b/_maps/map_files/VoidRaptor/VoidRaptor.dmm
@@ -35989,7 +35989,7 @@
/turf/open/floor/circuit/green,
/area/station/ai_monitored/command/nuke_storage)
"kmm" = (
-/mob/living/simple_animal/parrot/poly,
+/mob/living/basic/parrot/poly,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/turf/open/floor/carpet/orange,
diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm
index 8b19e237ac4..a41f196bc29 100644
--- a/_maps/map_files/tramstation/tramstation.dmm
+++ b/_maps/map_files/tramstation/tramstation.dmm
@@ -3298,6 +3298,18 @@
},
/turf/open/floor/iron/dark,
/area/station/engineering/atmospherics_engine)
+"asG" = (
+/obj/structure/table,
+/obj/item/storage/fancy/cigarettes{
+ pixel_x = 8;
+ pixel_y = 8
+ },
+/obj/item/storage/briefcase/secure{
+ pixel_x = -3;
+ pixel_y = 2
+ },
+/turf/open/floor/iron,
+/area/station/security/office)
"asQ" = (
/obj/structure/sign/warning/no_smoking,
/turf/closed/wall,
@@ -8257,6 +8269,7 @@
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 1
},
+/obj/effect/mapping_helpers/airlock/unres,
/turf/open/floor/iron/white,
/area/station/science/research)
"bNi" = (
@@ -11142,16 +11155,6 @@
/obj/machinery/light/directional/south,
/turf/open/floor/iron/dark,
/area/station/security/courtroom)
-"cMY" = (
-/obj/structure/table/reinforced,
-/obj/item/storage/briefcase/secure,
-/obj/effect/turf_decal/tile/blue{
- dir = 8
- },
-/turf/open/floor/iron/dark/side{
- dir = 8
- },
-/area/station/command/bridge)
"cNc" = (
/obj/effect/turf_decal/tile/blue/half/contrasted{
dir = 4
@@ -15547,6 +15550,16 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/turf/open/floor/iron,
/area/station/hallway/primary/tram/left)
+"esf" = (
+/obj/structure/table/reinforced,
+/obj/item/storage/briefcase/secure,
+/obj/effect/turf_decal/tile/blue{
+ dir = 8
+ },
+/turf/open/floor/iron/dark/side{
+ dir = 8
+ },
+/area/station/command/bridge)
"esi" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{
dir = 1
@@ -15690,14 +15703,6 @@
/obj/structure/cable,
/turf/open/floor/plating,
/area/station/engineering/supermatter/room)
-"evc" = (
-/obj/structure/displaycase/captain{
- pixel_y = 5
- },
-/obj/machinery/status_display/evac/directional/north,
-/obj/structure/secure_safe/directional/west,
-/turf/open/floor/wood,
-/area/station/command/heads_quarters/captain)
"evd" = (
/obj/effect/turf_decal/trimline/purple/filled/line{
dir = 1
@@ -16750,14 +16755,6 @@
/obj/machinery/airalarm/directional/west,
/turf/open/floor/plating,
/area/station/maintenance/central/greater)
-"eQO" = (
-/obj/structure/closet{
- name = "Evidence Closet 1"
- },
-/obj/structure/secure_safe/directional/east,
-/obj/effect/spawner/random/contraband/cannabis,
-/turf/open/floor/iron/dark,
-/area/station/security/evidence)
"eQR" = (
/obj/structure/railing/corner{
dir = 8
@@ -17608,6 +17605,16 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/turf/open/floor/iron/freezer,
/area/station/security/prison/shower)
+"fhK" = (
+/obj/structure/window/reinforced/spawner/directional/south,
+/obj/structure/table/wood,
+/obj/item/storage/briefcase/secure{
+ pixel_x = -2;
+ pixel_y = 4
+ },
+/obj/item/storage/lockbox/medal,
+/turf/open/floor/wood,
+/area/station/command/heads_quarters/captain)
"fhL" = (
/obj/effect/turf_decal/stripes/line{
dir = 4
@@ -18521,15 +18528,6 @@
/obj/effect/turf_decal/trimline/purple/filled/corner,
/turf/open/floor/iron,
/area/station/hallway/secondary/exit)
-"fyA" = (
-/obj/structure/table/reinforced,
-/obj/structure/secure_safe/caps_spare/directional/east,
-/obj/machinery/cell_charger{
- pixel_y = 4
- },
-/obj/item/stock_parts/cell/high,
-/turf/open/floor/iron,
-/area/station/command/bridge)
"fyF" = (
/obj/effect/landmark/event_spawn,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -22054,6 +22052,7 @@
/obj/effect/mapping_helpers/airlock/access/all/science/general,
/obj/effect/mapping_helpers/airlock/cyclelink_helper,
/obj/structure/disposalpipe/segment,
+/obj/effect/mapping_helpers/airlock/unres,
/turf/open/floor/iron/white/side,
/area/station/science/research)
"gPA" = (
@@ -24680,6 +24679,13 @@
},
/turf/open/floor/iron,
/area/station/hallway/secondary/command)
+"hPQ" = (
+/obj/structure/secure_safe/directional/north,
+/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2,
+/obj/effect/decal/cleanable/dirt,
+/obj/effect/turf_decal/tile/neutral/fourcorners,
+/turf/open/floor/iron/dark,
+/area/station/commons/vacant_room/commissary)
"hPW" = (
/obj/effect/turf_decal/trimline/red/filled/line,
/obj/effect/turf_decal/trimline/red/filled/corner{
@@ -24981,20 +24987,6 @@
},
/turf/open/floor/iron/white,
/area/station/medical/storage)
-"hWu" = (
-/obj/structure/rack,
-/obj/item/storage/briefcase{
- pixel_x = 3;
- pixel_y = 3
- },
-/obj/item/storage/briefcase/secure,
-/obj/effect/turf_decal/trimline/green/filled/corner,
-/obj/effect/turf_decal/trimline/green/filled/line{
- dir = 9
- },
-/obj/machinery/light/directional/north,
-/turf/open/floor/iron,
-/area/station/security/courtroom)
"hWI" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
@@ -27743,15 +27735,6 @@
/obj/structure/cable,
/turf/open/openspace,
/area/station/solars/starboard/fore)
-"iXr" = (
-/obj/structure/table/wood,
-/obj/item/storage/briefcase/secure{
- pixel_x = -2;
- pixel_y = 6
- },
-/obj/structure/cable,
-/turf/open/floor/wood,
-/area/station/command/heads_quarters/hop)
"iXx" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/visible/layer2,
/obj/structure/lattice,
@@ -28904,13 +28887,6 @@
},
/turf/open/floor/iron/white,
/area/station/science/research)
-"jqE" = (
-/obj/structure/secure_safe/directional/north,
-/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2,
-/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/tile/neutral/fourcorners,
-/turf/open/floor/iron/dark,
-/area/station/commons/vacant_room/commissary)
"jqK" = (
/obj/effect/spawner/structure/window/reinforced,
/obj/structure/cable,
@@ -32841,6 +32817,33 @@
"kHB" = (
/turf/open/floor/grass,
/area/station/medical/virology)
+"kHR" = (
+/obj/structure/filingcabinet/chestdrawer,
+/obj/effect/turf_decal/trimline/yellow/filled/line{
+ dir = 9
+ },
+/obj/machinery/button/door/directional/west{
+ id = "atmos";
+ name = "Atmospherics Lockdown";
+ pixel_y = 8;
+ req_access = list("atmospherics")
+ },
+/obj/machinery/button/door/directional/west{
+ id = "Secure Storage";
+ name = "Engineering Secure Storage";
+ req_access = list("engine_equip")
+ },
+/obj/machinery/button/door/directional/west{
+ id = "Engineering";
+ name = "Engineering Lockdown";
+ pixel_y = -8;
+ req_access = list("engineering")
+ },
+/obj/machinery/power/apc/auto_name/directional/north,
+/obj/structure/cable,
+/mob/living/basic/parrot/poly,
+/turf/open/floor/iron,
+/area/station/command/heads_quarters/ce)
"kHS" = (
/obj/machinery/door/firedoor,
/obj/machinery/door/airlock/public/glass{
@@ -33004,18 +33007,6 @@
},
/turf/open/floor/iron/white,
/area/station/science/explab)
-"kKw" = (
-/obj/structure/table,
-/obj/item/storage/fancy/cigarettes{
- pixel_x = 8;
- pixel_y = 8
- },
-/obj/item/storage/briefcase/secure{
- pixel_x = -3;
- pixel_y = 2
- },
-/turf/open/floor/iron,
-/area/station/security/office)
"kKB" = (
/obj/machinery/door/airlock{
name = "Law Office"
@@ -41944,14 +41935,6 @@
/obj/structure/cable,
/turf/open/floor/plating,
/area/station/maintenance/tram/left)
-"nQF" = (
-/obj/structure/closet/secure_closet/hos,
-/obj/structure/secure_safe/hos{
- pixel_x = 35
- },
-/obj/structure/sign/poster/official/space_cops/directional/north,
-/turf/open/floor/carpet,
-/area/station/command/heads_quarters/hos)
"nQG" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/turf/open/floor/iron,
@@ -42392,6 +42375,13 @@
/obj/item/stamp/head/cmo,
/turf/open/floor/iron/white,
/area/station/command/heads_quarters/cmo)
+"nXM" = (
+/obj/structure/table/glass,
+/obj/item/storage/briefcase/secure,
+/obj/effect/turf_decal/tile/blue/fourcorners,
+/obj/machinery/light/cold/directional/south,
+/turf/open/floor/iron/white,
+/area/station/command/heads_quarters/cmo)
"nXP" = (
/obj/structure/disposalpipe/segment,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -44178,6 +44168,11 @@
/obj/structure/sign/warning/fire,
/turf/closed/wall/r_wall,
/area/station/engineering/supermatter)
+"oMh" = (
+/obj/structure/secure_safe/directional/east,
+/obj/machinery/airalarm/directional/north,
+/turf/open/floor/carpet,
+/area/station/security/detectives_office)
"oMz" = (
/obj/effect/turf_decal/trimline/purple/filled/corner{
dir = 4
@@ -44726,13 +44721,6 @@
},
/turf/open/floor/iron/dark,
/area/station/ai_monitored/turret_protected/aisat/foyer)
-"oXx" = (
-/obj/structure/table/glass,
-/obj/item/storage/briefcase/secure,
-/obj/effect/turf_decal/tile/blue/fourcorners,
-/obj/machinery/light/cold/directional/south,
-/turf/open/floor/iron/white,
-/area/station/command/heads_quarters/cmo)
"oXz" = (
/obj/machinery/iv_drip,
/obj/effect/turf_decal/trimline/blue/filled/line{
@@ -46162,6 +46150,14 @@
/obj/structure/closet/toolcloset,
/turf/open/floor/iron,
/area/station/engineering/main)
+"pxf" = (
+/obj/structure/closet/secure_closet/hos,
+/obj/structure/secure_safe/hos{
+ pixel_x = 35
+ },
+/obj/structure/sign/poster/official/space_cops/directional/north,
+/turf/open/floor/carpet,
+/area/station/command/heads_quarters/hos)
"pxj" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/table,
@@ -46426,6 +46422,7 @@
dir = 1
},
/obj/structure/disposalpipe/segment,
+/obj/effect/mapping_helpers/airlock/unres,
/turf/open/floor/iron/white,
/area/station/science/research)
"pAR" = (
@@ -46643,6 +46640,7 @@
/obj/machinery/door/firedoor,
/obj/effect/mapping_helpers/airlock/access/all/science/general,
/obj/effect/mapping_helpers/airlock/cyclelink_helper,
+/obj/effect/mapping_helpers/airlock/unres,
/turf/open/floor/iron/white/side,
/area/station/science/research)
"pFm" = (
@@ -47542,33 +47540,6 @@
/obj/machinery/light/warm/directional/north,
/turf/open/floor/iron/dark,
/area/station/service/bar)
-"pWa" = (
-/obj/structure/filingcabinet/chestdrawer,
-/obj/effect/turf_decal/trimline/yellow/filled/line{
- dir = 9
- },
-/obj/machinery/button/door/directional/west{
- id = "atmos";
- name = "Atmospherics Lockdown";
- pixel_y = 8;
- req_access = list("atmospherics")
- },
-/obj/machinery/button/door/directional/west{
- id = "Secure Storage";
- name = "Engineering Secure Storage";
- req_access = list("engine_equip")
- },
-/obj/machinery/button/door/directional/west{
- id = "Engineering";
- name = "Engineering Lockdown";
- pixel_y = -8;
- req_access = list("engineering")
- },
-/obj/machinery/power/apc/auto_name/directional/north,
-/obj/structure/cable,
-/mob/living/simple_animal/parrot/poly,
-/turf/open/floor/iron,
-/area/station/command/heads_quarters/ce)
"pWt" = (
/obj/effect/turf_decal/trimline/brown/filled/line{
dir = 4
@@ -48216,6 +48187,15 @@
"qgt" = (
/turf/closed/wall/rock/porous,
/area/station/medical/chemistry)
+"qgy" = (
+/obj/structure/table/reinforced,
+/obj/structure/secure_safe/caps_spare/directional/east,
+/obj/machinery/cell_charger{
+ pixel_y = 4
+ },
+/obj/item/stock_parts/cell/high,
+/turf/open/floor/iron,
+/area/station/command/bridge)
"qgB" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/visible,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
@@ -48552,6 +48532,14 @@
/obj/machinery/light/directional/south,
/turf/open/floor/iron/dark,
/area/station/hallway/secondary/exit/departure_lounge)
+"qmC" = (
+/obj/structure/closet{
+ name = "Evidence Closet 1"
+ },
+/obj/structure/secure_safe/directional/east,
+/obj/effect/spawner/random/contraband/cannabis,
+/turf/open/floor/iron/dark,
+/area/station/security/evidence)
"qmH" = (
/obj/structure/closet/secure_closet/quartermaster,
/obj/effect/turf_decal/trimline/brown/filled/line{
@@ -48621,12 +48609,6 @@
},
/turf/open/floor/iron,
/area/station/science/explab)
-"qnC" = (
-/obj/structure/closet/secure_closet/hop,
-/obj/structure/sign/clock/directional/west,
-/obj/structure/secure_safe/directional/north,
-/turf/open/floor/carpet,
-/area/station/command/heads_quarters/hop)
"qnL" = (
/obj/machinery/light/directional/east,
/turf/open/floor/noslip/tram,
@@ -50910,6 +50892,14 @@
},
/turf/open/floor/iron/freezer,
/area/station/commons/toilet)
+"rdm" = (
+/obj/structure/displaycase/captain{
+ pixel_y = 5
+ },
+/obj/machinery/status_display/evac/directional/north,
+/obj/structure/secure_safe/directional/west,
+/turf/open/floor/wood,
+/area/station/command/heads_quarters/captain)
"rdo" = (
/obj/machinery/computer/shuttle/labor,
/obj/effect/turf_decal/trimline/red/filled/line{
@@ -54887,11 +54877,6 @@
"syv" = (
/turf/closed/wall/r_wall,
/area/station/security/checkpoint/science)
-"syB" = (
-/obj/structure/secure_safe/directional/east,
-/obj/machinery/airalarm/directional/north,
-/turf/open/floor/carpet,
-/area/station/security/detectives_office)
"syE" = (
/obj/machinery/rnd/production/circuit_imprinter/department/science,
/obj/effect/turf_decal/stripes/line{
@@ -57991,6 +57976,15 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/turf/open/floor/iron/white,
/area/station/medical/medbay/central)
+"tDR" = (
+/obj/structure/table/wood,
+/obj/item/storage/briefcase/secure{
+ pixel_x = -2;
+ pixel_y = 6
+ },
+/obj/structure/cable,
+/turf/open/floor/wood,
+/area/station/command/heads_quarters/hop)
"tDT" = (
/turf/open/openspace,
/area/station/commons/fitness/recreation)
@@ -60228,6 +60222,12 @@
/obj/machinery/telecomms/processor/preset_three,
/turf/open/floor/iron/dark/telecomms,
/area/station/tcommsat/server)
+"uqs" = (
+/obj/structure/closet/secure_closet/hop,
+/obj/structure/sign/clock/directional/west,
+/obj/structure/secure_safe/directional/north,
+/turf/open/floor/carpet,
+/area/station/command/heads_quarters/hop)
"uqA" = (
/obj/effect/turf_decal/siding/thinplating/dark/corner,
/obj/machinery/duct,
@@ -60886,6 +60886,20 @@
},
/turf/open/floor/engine/hull,
/area/station/solars/starboard/fore)
+"uBi" = (
+/obj/structure/rack,
+/obj/item/storage/briefcase{
+ pixel_x = 3;
+ pixel_y = 3
+ },
+/obj/item/storage/briefcase/secure,
+/obj/effect/turf_decal/trimline/green/filled/corner,
+/obj/effect/turf_decal/trimline/green/filled/line{
+ dir = 9
+ },
+/obj/machinery/light/directional/north,
+/turf/open/floor/iron,
+/area/station/security/courtroom)
"uBo" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{
dir = 8
@@ -68684,16 +68698,6 @@
/obj/effect/turf_decal/trimline/white/filled/line,
/turf/open/floor/iron/dark,
/area/station/engineering/storage/tech)
-"xsV" = (
-/obj/structure/window/reinforced/spawner/directional/south,
-/obj/structure/table/wood,
-/obj/item/storage/briefcase/secure{
- pixel_x = -2;
- pixel_y = 4
- },
-/obj/item/storage/lockbox/medal,
-/turf/open/floor/wood,
-/area/station/command/heads_quarters/captain)
"xsW" = (
/obj/machinery/porta_turret/ai{
dir = 4
@@ -99186,7 +99190,7 @@ aaa
hFr
nMu
hFr
-hWu
+uBi
abZ
ycB
jyA
@@ -102592,7 +102596,7 @@ fXQ
srY
roB
roB
-pWa
+kHR
tga
rnf
xBD
@@ -150871,7 +150875,7 @@ aBV
aBV
aBV
wHT
-qnC
+uqs
kcw
ruV
wHT
@@ -151905,7 +151909,7 @@ umf
qjW
dCk
iDR
-iXr
+tDR
wHT
omH
jKZ
@@ -155757,7 +155761,7 @@ uvu
aCC
ukE
hhc
-cMY
+esf
lPY
eaT
kiU
@@ -156011,7 +156015,7 @@ aQO
rup
wzm
rAh
-fyA
+qgy
eUy
mgq
nhV
@@ -157511,7 +157515,7 @@ aaa
aaa
aaa
rmB
-nQF
+pxf
bWb
toY
wpM
@@ -157522,7 +157526,7 @@ avl
fBk
myB
vhl
-kKw
+asG
vhl
gnL
lvm
@@ -157803,9 +157807,9 @@ ltw
pbx
pyA
ltw
-evc
+rdm
vjq
-xsV
+fhK
ktl
mjd
mjI
@@ -158838,7 +158842,7 @@ phl
sSx
xJH
xEo
-syB
+oMh
ixO
urP
kOE
@@ -159575,7 +159579,7 @@ aaa
abM
abM
tFJ
-eQO
+qmC
fsQ
jil
fsQ
@@ -166592,7 +166596,7 @@ aOF
wdj
wao
cHU
-oXx
+nXM
wdj
oGJ
oGJ
@@ -184809,7 +184813,7 @@ ebY
aGn
brm
nSI
-jqE
+hPQ
vDI
nSd
rMB
diff --git a/_maps/shuttles/emergency_hugcage.dmm b/_maps/shuttles/emergency_hugcage.dmm
index 80211ed6092..fc474764401 100644
--- a/_maps/shuttles/emergency_hugcage.dmm
+++ b/_maps/shuttles/emergency_hugcage.dmm
@@ -31,7 +31,7 @@
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/escape)
"dz" = (
-/mob/living/simple_animal/parrot/natural{
+/mob/living/basic/parrot{
melee_damage_upper = 5
},
/turf/open/floor/mineral/titanium/blue,
diff --git a/_maps/shuttles/ruin_pirate_cutter.dmm b/_maps/shuttles/ruin_pirate_cutter.dmm
index 7dd4be59ba7..77c4e37bc03 100644
--- a/_maps/shuttles/ruin_pirate_cutter.dmm
+++ b/_maps/shuttles/ruin_pirate_cutter.dmm
@@ -702,7 +702,7 @@
"Ry" = (
/obj/structure/rack,
/obj/item/storage/bag/money/vault,
-/mob/living/simple_animal/parrot{
+/mob/living/basic/parrot{
faction = list("pirate");
name = "Pegwing"
},
diff --git a/_maps/virtual_domains/pirates.dmm b/_maps/virtual_domains/pirates.dmm
index 952a8877d37..04be3d3a42d 100644
--- a/_maps/virtual_domains/pirates.dmm
+++ b/_maps/virtual_domains/pirates.dmm
@@ -801,7 +801,7 @@
/area/virtual_domain/fullbright)
"Pz" = (
/obj/structure/table/wood,
-/mob/living/simple_animal/parrot{
+/mob/living/basic/parrot{
name = "pepper"
},
/turf/open/floor/carpet/blue,
diff --git a/code/__DEFINES/_flags.dm b/code/__DEFINES/_flags.dm
index e0b88066d7d..f5fc50004cd 100644
--- a/code/__DEFINES/_flags.dm
+++ b/code/__DEFINES/_flags.dm
@@ -163,6 +163,11 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define FLOATING (1<<3)
/// When moving, will Cross() everything, but won't stop or Bump() anything.
#define PHASING (1<<4)
+/// The mob is walking on the ceiling. Or is generally just, upside down.
+#define UPSIDE_DOWN (1<<5)
+
+/// Combination flag for movetypes which, for all intents and purposes, mean the mob is not touching the ground
+#define MOVETYPES_NOT_TOUCHING_GROUND (FLYING|FLOATING|UPSIDE_DOWN)
//Fire and Acid stuff, for resistance_flags
#define LAVA_PROOF (1<<0)
diff --git a/code/__DEFINES/ai/pets.dm b/code/__DEFINES/ai/pets.dm
index b3ad67ecc06..e41c9ac0c3f 100644
--- a/code/__DEFINES/ai/pets.dm
+++ b/code/__DEFINES/ai/pets.dm
@@ -27,3 +27,27 @@
#define BB_FIND_MOM_TYPES "BB_find_mom_types"
///list of types of mobs we must ignore
#define BB_IGNORE_MOM_TYPES "BB_ignore_mom_types"
+
+/// The current string that this parrot will repeat back to someone
+#define BB_PARROT_REPEAT_STRING "BB_parrot_repeat_string"
+/// The odds that this parrot will repeat back a string
+#define BB_PARROT_REPEAT_PROBABILITY "BB_parrot_repeat_probability"
+/// The odds that this parrot will choose another string to repeat
+#define BB_PARROT_PHRASE_CHANGE_PROBABILITY "BB_parrot_phrase_change_probability"
+/// A copy of the string buffer that we end the shift with. DO NOT ACCESS THIS DIRECTLY - YOU SHOULD USE THE COMPONENT IN MOST CASES
+#define BB_EXPORTABLE_STRING_BUFFER_LIST "BB_parrot_repeat_string_buffer"
+/// The types of perches we desire to use
+#define BB_PARROT_PERCH_TYPES "BB_parrot_perch_types"
+/// key that holds our perch target
+#define BB_PERCH_TARGET "perch_target"
+/// key that holds our theft item target
+#define BB_HOARD_ITEM_TARGET "hoard_item_target"
+/// key that holds the mob we will steal from
+#define BB_THEFT_VICTIM "theft_victim"
+/// key that holds the turf we will be hauling stolen items to
+#define BB_HOARD_LOCATION "hoard_location"
+/// key that holds the minimum range we must be from the hoard spot
+#define BB_HOARD_LOCATION_RANGE "hoard_location_range"
+/// key that holds items we arent interested in hoarding
+#define BB_IGNORE_ITEMS "ignore_items"
+
diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm
index d26dd4e8c86..b836c811667 100644
--- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm
+++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm
@@ -47,13 +47,17 @@
#define COMSIG_MOVABLE_THROW_LANDED "movable_throw_landed"
///from base of atom/movable/on_changed_z_level(): (turf/old_turf, turf/new_turf, same_z_layer)
#define COMSIG_MOVABLE_Z_CHANGED "movable_ztransit"
+///called before hearing a message from atom/movable/Hear():
+#define COMSIG_MOVABLE_PRE_HEAR "movable_pre_hear"
+ ///cancel hearing the message because we're doing something else presumably
+ #define COMSIG_MOVABLE_CANCEL_HEARING (1<<0)
///from base of atom/movable/Hear(): (proc args list(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range))
#define COMSIG_MOVABLE_HEAR "movable_hear"
- //#define HEARING_MESSAGE 1 - (I'm pretty sure this is never really used and can be gutted)
+ #define HEARING_MESSAGE 1
#define HEARING_SPEAKER 2
#define HEARING_LANGUAGE 3
#define HEARING_RAW_MESSAGE 4
- //#define HEARING_RADIO_FREQ 5
+ #define HEARING_RADIO_FREQ 5
#define HEARING_SPANS 6
#define HEARING_MESSAGE_MODE 7
#define HEARING_RANGE 8
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm
index 85630c8e8f0..a04b8e751a0 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm
@@ -3,3 +3,4 @@
/// Signal sent when a blackboard key is cleared
#define COMSIG_AI_BLACKBOARD_KEY_CLEARED(blackboard_key) "ai_blackboard_key_clear_[blackboard_key]"
+
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm
index 18c6c651435..8420a486417 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm
@@ -5,3 +5,10 @@
///from the ranged_attacks component for basic mobs: (mob/living/basic/firer, atom/target, modifiers)
#define COMSIG_BASICMOB_POST_ATTACK_RANGED "basicmob_post_attack_ranged"
+
+/// Sent from /datum/ai_planning_subtree/parrot_as_in_repeat() : ()
+#define COMSIG_NEEDS_NEW_PHRASE "parrot_needs_new_phrase"
+ #define NO_NEW_PHRASE_AVAILABLE (1<<0) //! Cancel to try again later for when we actually get a new phrase
+
+/// Called whenever an animal is pet via the /datum/element/pet_bonus element: (mob/living/petter, modifiers)
+#define COMSIG_ANIMAL_PET "animal_pet"
diff --git a/code/__DEFINES/nozzle_define.dm b/code/__DEFINES/nozzle_define.dm
new file mode 100644
index 00000000000..06b43c1acc6
--- /dev/null
+++ b/code/__DEFINES/nozzle_define.dm
@@ -0,0 +1,4 @@
+/// 3 differnt modes for the firefighter extinquisher
+#define EXTINGUISHER 0
+#define RESIN_LAUNCHER 1
+#define RESIN_FOAM 2
diff --git a/code/__DEFINES/rust_g.dm b/code/__DEFINES/rust_g.dm
index cab4430a88d..76e5fa22d47 100644
--- a/code/__DEFINES/rust_g.dm
+++ b/code/__DEFINES/rust_g.dm
@@ -110,6 +110,12 @@
#define rustg_dmi_strip_metadata(fname) RUSTG_CALL(RUST_G, "dmi_strip_metadata")(fname)
#define rustg_dmi_create_png(path, width, height, data) RUSTG_CALL(RUST_G, "dmi_create_png")(path, width, height, data)
#define rustg_dmi_resize_png(path, width, height, resizetype) RUSTG_CALL(RUST_G, "dmi_resize_png")(path, width, height, resizetype)
+/**
+ * input: must be a path, not an /icon; you have to do your own handling if it is one, as icon objects can't be directly passed to rustg.
+ *
+ * output: json_encode'd list. json_decode to get a flat list with icon states in the order they're in inside the .dmi
+ */
+#define rustg_dmi_icon_states(fname) RUSTG_CALL(RUST_G, "dmi_icon_states")(fname)
#define rustg_file_read(fname) RUSTG_CALL(RUST_G, "file_read")(fname)
#define rustg_file_exists(fname) RUSTG_CALL(RUST_G, "file_exists")(fname)
@@ -158,8 +164,9 @@
#define rustg_time_milliseconds(id) text2num(RUSTG_CALL(RUST_G, "time_milliseconds")(id))
#define rustg_time_reset(id) RUSTG_CALL(RUST_G, "time_reset")(id)
+/// Returns the timestamp as a string
/proc/rustg_unix_timestamp()
- return text2num(RUSTG_CALL(RUST_G, "unix_timestamp")())
+ return RUSTG_CALL(RUST_G, "unix_timestamp")()
#define rustg_raw_read_toml_file(path) json_decode(RUSTG_CALL(RUST_G, "toml_file_to_json")(path) || "null")
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 697474bc9ef..136e1acf4ba 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -75,6 +75,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_GUN_NATURAL "gunnatural"
/// Causes death-like unconsciousness
#define TRAIT_DEATHCOMA "deathcoma"
+/// The mob has the stasis effect.
+/// Does nothing on its own, applied via status effect.
+#define TRAIT_STASIS "in_stasis"
/// Makes the owner appear as dead to most forms of medical examination
#define TRAIT_FAKEDEATH "fakedeath"
#define TRAIT_DISFIGURED "disfigured"
@@ -83,6 +86,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_NO_TRANSFORM "block_transformations"
/// Tracks whether we're gonna be a baby alien's mummy.
#define TRAIT_XENO_HOST "xeno_host"
+/// This parrot is currently perched
+#define TRAIT_PARROT_PERCHED "parrot_perched"
/// This mob is immune to stun causing status effects and stamcrit.
/// Prefer to use [/mob/living/proc/check_stun_immunity] over checking for this trait exactly.
#define TRAIT_STUNIMMUNE "stun_immunity"
@@ -161,8 +166,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_LIVERLESS_METABOLISM "liverless_metabolism"
/// Humans with this trait cannot be turned into zombies
#define TRAIT_NO_ZOMBIFY "no_zombify"
-/// Humans with this trait cannot be affected by changeling transformation stings
-#define TRAIT_NO_TRANSFORMATION_STING "no_transformation_sting"
/// Carbons with this trait can't have their DNA copied by diseases nor changelings
#define TRAIT_NO_DNA_COPY "no_dna_copy"
/// Carbons with this trait cant have their dna scrambled by genetics or a disease retrovirus.
@@ -531,6 +534,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_MOVE_VENTCRAWLING "move_ventcrawling"
#define TRAIT_MOVE_FLOATING "move_floating"
#define TRAIT_MOVE_PHASING "move_phasing"
+#define TRAIT_MOVE_UPSIDE_DOWN "move_upside_down"
/// Disables the floating animation. See above.
#define TRAIT_NO_FLOATING_ANIM "no-floating-animation"
@@ -846,7 +850,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// changelings with this trait can no longer talk over the hivemind
#define TRAIT_CHANGELING_HIVEMIND_MUTE "ling_mute"
#define TRAIT_HULK "hulk"
-
/// Isn't attacked harmfully by blob structures
#define TRAIT_BLOB_ALLY "blob_ally"
diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm
index 3ba0008acb2..6be8cdf3f8d 100644
--- a/code/__DEFINES/traits/sources.dm
+++ b/code/__DEFINES/traits/sources.dm
@@ -136,6 +136,7 @@
#define SPECIES_FLIGHT_TRAIT "species-flight"
#define FROSTMINER_ENRAGE_TRAIT "frostminer-enrage"
#define NO_GRAVITY_TRAIT "no-gravity"
+#define NEGATIVE_GRAVITY_TRAIT "negative-gravity"
/// A trait gained from a mob's leap action, like the leaper
#define LEAPING_TRAIT "leaping"
diff --git a/code/__DEFINES/~skyrat_defines/DNA.dm b/code/__DEFINES/~skyrat_defines/DNA.dm
index 8e9c220c9e1..6ac4d165ee6 100644
--- a/code/__DEFINES/~skyrat_defines/DNA.dm
+++ b/code/__DEFINES/~skyrat_defines/DNA.dm
@@ -34,6 +34,7 @@
// Defines for mutant bodyparts indexes
#define MUTANT_INDEX_NAME "name"
+#define MUTANT_INDEX_CAN_RANDOMIZE "can_randomize"
#define MUTANT_INDEX_COLOR_LIST "color"
#define MUTANT_INDEX_EMISSIVE_LIST "emissive"
diff --git a/code/__DEFINES/~skyrat_defines/atom_hud.dm b/code/__DEFINES/~skyrat_defines/atom_hud.dm
index 32862bfc7e4..b190157d0eb 100644
--- a/code/__DEFINES/~skyrat_defines/atom_hud.dm
+++ b/code/__DEFINES/~skyrat_defines/atom_hud.dm
@@ -1,6 +1,5 @@
//SR Security
#define SECHUD_CORRECTIONS_OFFICER "hudcorrectionsofficer"
-#define SECHUD_SECURITY_MEDIC "hudsecuritymedic"
//SR Departmental Guards
#define SECHUD_ENGINEERING_GUARD "hudengineeringguard"
#define SECHUD_ORDERLY "hudorderly"
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 8eacab9e2d5..c6fa9d09ac8 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -17,9 +17,9 @@
//SKYRAT EDIT REMOVAL BEGIN - CUSTOMIZATION
/*
init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.body_markings_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/tails, GLOB.tails_list, add_blank = TRUE)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/human, GLOB.tails_list_human, add_blank = TRUE)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard, GLOB.tails_list_lizard, add_blank = TRUE)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/monkey, GLOB.tails_list_monkey, add_blank = TRUE)
init_sprite_accessory_subtypes(/datum/sprite_accessory/snouts, GLOB.snouts_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/horns,GLOB.horns_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/ears, GLOB.ears_list)
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index df98ab953fa..5a408937a04 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -58,7 +58,7 @@
/proc/random_backpack()
return pick(GLOB.backpacklist)
-//SKYRAT EDIT REMOVAL - CUSTOMIZATION (moved to modular)
+// SKYRAT EDIT REMOVAL - CUSTOMIZATION (moved to modular)
/*
/proc/random_features()
if(!GLOB.tails_list.len)
@@ -113,9 +113,8 @@
"tail_monkey" = "Monkey",
"pod_hair" = pick(GLOB.pod_hair_list),
))
- */
- //SKYRAT EDIT REMOVAL END
-
+*/
+//SKYRAT EDIT REMOVAL END
/proc/random_hairstyle(gender)
switch(gender)
@@ -589,8 +588,6 @@ GLOBAL_LIST_EMPTY(species_list)
#define ISADVANCEDTOOLUSER(mob) (HAS_TRAIT(mob, TRAIT_ADVANCEDTOOLUSER) && !HAS_TRAIT(mob, TRAIT_DISCOORDINATED_TOOL_USER))
-#define IS_IN_STASIS(mob) (mob.has_status_effect(/datum/status_effect/grouped/stasis) || mob.has_status_effect(/datum/status_effect/embryonic))
-
/// Gets the client of the mob, allowing for mocking of the client.
/// You only need to use this if you know you're going to be mocking clients somewhere else.
#define GET_CLIENT(mob) (##mob.client || ##mob.mock_client)
@@ -631,7 +628,6 @@ GLOBAL_LIST_EMPTY(species_list)
moblist += mob_to_sort
// SKYRAT EDIT END - SOULCATCHERS
return moblist
-
///returns a mob type controlled by a specified ckey
/proc/get_mob_by_ckey(key)
if(!key)
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index 27c6de53b53..796def0de16 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -283,6 +283,7 @@ DEFINE_BITFIELD(movement_type, list(
"GROUND" = GROUND,
"PHASING" = PHASING,
"VENTCRAWLING" = VENTCRAWLING,
+ "UPSIDE_DOWN" = UPSIDE_DOWN,
))
DEFINE_BITFIELD(obj_flags, list(
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index dbf9a47f9f7..afb15912694 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -30,9 +30,9 @@ GLOBAL_LIST_EMPTY(legs_list)
GLOBAL_LIST_EMPTY(animated_spines_list)
//Mutant Human bits
-GLOBAL_LIST_EMPTY(tails_list)
-GLOBAL_LIST_EMPTY(tails_list_human) //Only exists for preference choices. Use "tails_list" otherwise.
-GLOBAL_LIST_EMPTY(tails_list_lizard) //See above!
+GLOBAL_LIST_EMPTY(tails_list_human)
+GLOBAL_LIST_EMPTY(tails_list_lizard)
+GLOBAL_LIST_EMPTY(tails_list_monkey)
GLOBAL_LIST_EMPTY(ears_list)
GLOBAL_LIST_EMPTY(wings_list)
GLOBAL_LIST_EMPTY(wings_open_list)
diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm
index 50575d24b5b..a54799b7bb1 100644
--- a/code/_globalvars/lists/maintenance_loot.dm
+++ b/code/_globalvars/lists/maintenance_loot.dm
@@ -256,10 +256,10 @@ GLOBAL_LIST_INIT(uncommon_loot, list(//uncommon: useful items
list(//computer disks
/obj/item/computer_disk/maintenance/scanner = 1,
- /obj/item/computer_disk/maintenance/camera = 1,
+ ///obj/item/computer_disk/maintenance/camera = 1, //SKYRAT EDIT REMOVAL - Available To Crew Now
/obj/item/computer_disk/maintenance/modsuit_control = 1,
/obj/item/computer_disk/maintenance/theme = 3,
- ) = 4,
+ ) = 3, //SKYRAT EDIT CHANGE - Original : 4
list(//modsuits
/obj/effect/spawner/random/mod/maint = 3,
diff --git a/code/_globalvars/phobias.dm b/code/_globalvars/phobias.dm
index 5bb8b4bf314..3aa6c41de4f 100644
--- a/code/_globalvars/phobias.dm
+++ b/code/_globalvars/phobias.dm
@@ -61,8 +61,8 @@ GLOBAL_LIST_INIT(phobia_mobs, list(
"birds" = typecacheof(list(
/mob/living/basic/chick,
/mob/living/basic/chicken,
+ /mob/living/basic/parrot,
/mob/living/basic/pet/penguin,
- /mob/living/simple_animal/parrot,
)),
"conspiracies" = typecacheof(list(
/mob/living/basic/drone,
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 1c48dd0dab4..c3d124999b5 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -48,6 +48,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_MOVE_GROUND" = TRAIT_MOVE_GROUND,
"TRAIT_MOVE_PHASING" = TRAIT_MOVE_PHASING,
"TRAIT_MOVE_VENTCRAWLING" = TRAIT_MOVE_VENTCRAWLING,
+ "TRAIT_MOVE_UPSIDE_DOWN" = TRAIT_MOVE_UPSIDE_DOWN,
"TRAIT_NO_FLOATING_ANIM" = TRAIT_NO_FLOATING_ANIM,
"TRAIT_NO_MANIFEST_CONTENTS_ERROR" = TRAIT_NO_MANIFEST_CONTENTS_ERROR,
"TRAIT_NO_MISSING_ITEM_ERROR" = TRAIT_NO_MISSING_ITEM_ERROR,
@@ -287,7 +288,6 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NO_JUMPSUIT" = TRAIT_NO_JUMPSUIT,
"TRAIT_NO_MINDSWAP" = TRAIT_NO_MINDSWAP,
"TRAIT_NO_MIRROR_REFLECTION" = TRAIT_NO_MIRROR_REFLECTION,
- //"TRAIT_NO_PLASMA_TRANSFORM" = TRAIT_NO_PLASMA_TRANSFORM, SKYRAT EDIT - TODO - These require transformation sting pr
"TRAIT_NO_SLIP_ALL" = TRAIT_NO_SLIP_ALL,
"TRAIT_NO_SLIP_ICE" = TRAIT_NO_SLIP_ICE,
"TRAIT_NO_SLIP_SLIDE" = TRAIT_NO_SLIP_SLIDE,
@@ -366,7 +366,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_ROCK_METAMORPHIC" = TRAIT_ROCK_METAMORPHIC,
"TRAIT_ROD_SUPLEX" = TRAIT_ROD_SUPLEX,
"TRAIT_SABRAGE_PRO" = TRAIT_SABRAGE_PRO,
- "TRAIT_SACRIFICED" = TRAIT_SACRIFICED, // SKYRAT EDIT ADDITION
+ "TRAIT_SACRIFICED" = TRAIT_SACRIFICED,
"TRAIT_SECURITY_HUD" = TRAIT_SECURITY_HUD,
"TRAIT_SEE_GLASS_COLORS" = TRAIT_SEE_GLASS_COLORS,
"TRAIT_SELF_AWARE" = TRAIT_SELF_AWARE,
@@ -394,7 +394,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_SPRAY_PAINTABLE" = TRAIT_SPRAY_PAINTABLE,
"TRAIT_STABLEHEART" = TRAIT_STABLEHEART,
"TRAIT_STABLELIVER" = TRAIT_STABLELIVER,
- //"TRAIT_STASIS" = TRAIT_STASIS, TODO - SKYRAT EDIT - TODO - These require transformation sting pr
+ "TRAIT_STASIS" = TRAIT_STASIS,
"TRAIT_STRONG_GRABBER" = TRAIT_STRONG_GRABBER,
"TRAIT_STUNIMMUNE" = TRAIT_STUNIMMUNE,
"TRAIT_SUCCUMB_OVERRIDE" = TRAIT_SUCCUMB_OVERRIDE,
@@ -423,11 +423,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_UNBREAKABLE" = TRAIT_UNBREAKABLE,
"TRAIT_UNDENSE" = TRAIT_UNDENSE,
"TRAIT_UNDERWATER_BASKETWEAVING_KNOWLEDGE" = TRAIT_UNDERWATER_BASKETWEAVING_KNOWLEDGE,
-<<<<<<< HEAD
- //"TRAIT_UNHUSKABLE" = TRAIT_UNHUSKABLE, SKYRAT EDIT - TODO - These require transformation sting pr
-=======
"TRAIT_UNHUSKABLE" = TRAIT_UNHUSKABLE,
->>>>>>> def96ed8d ([MIRROR] Golems cannot turn into Plasmamen [MDB IGNORE] (#24809))
"TRAIT_UNINTELLIGIBLE_SPEECH" = TRAIT_UNINTELLIGIBLE_SPEECH,
"TRAIT_UNKNOWN" = TRAIT_UNKNOWN,
"TRAIT_UNNATURAL_RED_GLOWY_EYES" = TRAIT_UNNATURAL_RED_GLOWY_EYES,
@@ -574,6 +570,7 @@ GLOBAL_LIST_INIT(movement_type_trait_to_flag, list(
TRAIT_MOVE_VENTCRAWLING = VENTCRAWLING,
TRAIT_MOVE_FLOATING = FLOATING,
TRAIT_MOVE_PHASING = PHASING,
+ TRAIT_MOVE_UPSIDE_DOWN = UPSIDE_DOWN,
))
GLOBAL_LIST_INIT(movement_type_addtrait_signals, set_movement_type_addtrait_signals())
diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm
index e3bf5ae97ef..26c419723cb 100644
--- a/code/_globalvars/traits/admin_tooling.dm
+++ b/code/_globalvars/traits/admin_tooling.dm
@@ -15,6 +15,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_MOVE_GROUND" = TRAIT_MOVE_GROUND,
"TRAIT_MOVE_PHASING" = TRAIT_MOVE_PHASING,
"TRAIT_MOVE_VENTCRAWLING" = TRAIT_MOVE_VENTCRAWLING,
+ "TRAIT_MOVE_UPSIDE_DOWN" = TRAIT_MOVE_UPSIDE_DOWN,
"TRAIT_RUNECHAT_HIDDEN" = TRAIT_RUNECHAT_HIDDEN,
"TRAIT_SNOWSTORM_IMMUNE" = TRAIT_SNOWSTORM_IMMUNE,
"TRAIT_VOIDSTORM_IMMUNE" = TRAIT_VOIDSTORM_IMMUNE,
@@ -128,11 +129,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_NO_BLOOD_OVERLAY" = TRAIT_NO_BLOOD_OVERLAY,
"TRAIT_NO_DNA_COPY" = TRAIT_NO_DNA_COPY,
"TRAIT_NO_GLIDE" = TRAIT_NO_GLIDE,
-<<<<<<< HEAD
- //"TRAIT_NO_PLASMA_TRANSFORM" = TRAIT_NO_PLASMA_TRANSFORM, SKYRAT EDIT - TODO - These require transformation sting pr
-=======
"TRAIT_NO_PLASMA_TRANSFORM" = TRAIT_NO_PLASMA_TRANSFORM,
->>>>>>> def96ed8d ([MIRROR] Golems cannot turn into Plasmamen [MDB IGNORE] (#24809))
"TRAIT_NO_SLIP_ALL" = TRAIT_NO_SLIP_ALL,
"TRAIT_NO_SLIP_ICE" = TRAIT_NO_SLIP_ICE,
"TRAIT_NO_SLIP_SLIDE" = TRAIT_NO_SLIP_SLIDE,
@@ -218,11 +215,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_UI_BLOCKED" = TRAIT_UI_BLOCKED,
"TRAIT_UNDENSE" = TRAIT_UNDENSE,
"TRAIT_UNDERWATER_BASKETWEAVING_KNOWLEDGE" = TRAIT_UNDERWATER_BASKETWEAVING_KNOWLEDGE,
-<<<<<<< HEAD
- //"TRAIT_UNHUSKABLE" = TRAIT_UNHUSKABLE, SKYRAT EDIT - TODO - These require transformation sting pr
-=======
"TRAIT_UNHUSKABLE" = TRAIT_UNHUSKABLE,
->>>>>>> def96ed8d ([MIRROR] Golems cannot turn into Plasmamen [MDB IGNORE] (#24809))
"TRAIT_UNINTELLIGIBLE_SPEECH" = TRAIT_UNINTELLIGIBLE_SPEECH,
"TRAIT_UNKNOWN" = TRAIT_UNKNOWN,
"TRAIT_UNNATURAL_RED_GLOWY_EYES" = TRAIT_UNNATURAL_RED_GLOWY_EYES,
diff --git a/code/_onclick/hud/rendering/plane_masters/camera_static.dm b/code/_onclick/hud/rendering/plane_masters/camera_static.dm
deleted file mode 100644
index 4cb3436a7a4..00000000000
--- a/code/_onclick/hud/rendering/plane_masters/camera_static.dm
+++ /dev/null
@@ -1,30 +0,0 @@
-/atom/movable/screen/plane_master/camera_static
- name = "Camera static"
- documentation = "Holds camera static images. Usually only visible to people who can well, see static.\
-
We use images rather then vis contents because they're lighter on maptick, and maptick sucks butt."
- plane = CAMERA_STATIC_PLANE
-
-/atom/movable/screen/plane_master/camera_static/show_to(mob/mymob)
- . = ..()
- if(!.)
- return
- var/datum/hud/our_hud = home.our_hud
- if(isnull(our_hud))
- return
-
- // We'll hide the slate if we're not seeing through a camera eye
- // This can call on a cycle cause we don't clear in hide_from
- // Yes this is the best way of hooking into the hud, I hate myself too
- RegisterSignal(our_hud, COMSIG_HUD_EYE_CHANGED, PROC_REF(eye_changed), override = TRUE)
- eye_changed(our_hud, null, our_hud.mymob?.canon_client?.eye)
-
-/atom/movable/screen/plane_master/camera_static/proc/eye_changed(datum/hud/source, atom/old_eye, atom/new_eye)
- SIGNAL_HANDLER
-
- if(!isaicamera(new_eye))
- if(!force_hidden)
- hide_plane(source.mymob)
- return
-
- if(force_hidden)
- unhide_plane(source.mymob)
diff --git a/code/_onclick/hud/rendering/plane_masters/clickcatcher.dm b/code/_onclick/hud/rendering/plane_masters/clickcatcher.dm
deleted file mode 100644
index 8e581dc081c..00000000000
--- a/code/_onclick/hud/rendering/plane_masters/clickcatcher.dm
+++ /dev/null
@@ -1,20 +0,0 @@
-/atom/movable/screen/plane_master/clickcatcher
- name = "Click Catcher"
- documentation = "Contains the screen object we use as a backdrop to catch clicks on portions of the screen that would otherwise contain nothing else. \
-
Will always be below almost everything else"
- plane = CLICKCATCHER_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- multiz_scaled = FALSE
- critical = PLANE_CRITICAL_DISPLAY
-
-/atom/movable/screen/plane_master/clickcatcher/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
- . = ..()
- RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(offset_increased))
- offset_increased(SSmapping, 0, SSmapping.max_plane_offset)
-
-/atom/movable/screen/plane_master/clickcatcher/proc/offset_increased(datum/source, old_off, new_off)
- SIGNAL_HANDLER
- // We only want need the lowest level
- // If my system better supported changing PM plane values mid op I'd do that, but I do NOT so
- if(new_off > offset)
- hide_plane(home?.our_hud?.mymob)
diff --git a/code/_onclick/hud/rendering/plane_masters/core_game_planes.dm b/code/_onclick/hud/rendering/plane_masters/core_game_planes.dm
deleted file mode 100644
index 89379070b35..00000000000
--- a/code/_onclick/hud/rendering/plane_masters/core_game_planes.dm
+++ /dev/null
@@ -1,122 +0,0 @@
-//-------------------- FLOOR PLANE --------------------
-
-///Contains just the floor
-/atom/movable/screen/plane_master/floor
- name = "Floor"
- documentation = "The well, floor. This is mostly used as a sorting mechanism, but it also lets us create a \"border\" around the game world plane, so its drop shadow will actually work."
- plane = FLOOR_PLANE
- render_relay_planes = list(RENDER_PLANE_GAME, LIGHT_MASK_PLANE)
-
-/atom/movable/screen/plane_master/transparent_floor
- name = "Transparent Floor"
- documentation = "Really just openspace, stuff that is a turf but has no color or alpha whatsoever.\
-
We use this to draw to just the light mask plane, cause if it's not there we get holes of blackness over openspace"
- plane = TRANSPARENT_FLOOR_PLANE
- render_relay_planes = list(LIGHT_MASK_PLANE)
- // Needs to be critical or it uh, it'll look white
- critical = PLANE_CRITICAL_DISPLAY|PLANE_CRITICAL_NO_RELAY
-
-/atom/movable/screen/plane_master/floor/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
- . = ..()
- add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_FLOOR_LAYER, relay_color = GLOB.em_block_color)
-
-//-------------------- WALL PLANE --------------------
-
-/atom/movable/screen/plane_master/wall
- name = "Wall"
- documentation = "Holds all walls. We render this onto the game world. Separate so we can use this + space and floor planes as a guide for where byond blackness is NOT."
- plane = WALL_PLANE
- render_relay_planes = list(RENDER_PLANE_GAME_WORLD, LIGHT_MASK_PLANE)
-
-/atom/movable/screen/plane_master/wall/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
- . = ..()
- add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_WALL_LAYER, relay_color = GLOB.em_block_color)
-
-/atom/movable/screen/plane_master/wall_upper
- name = "Upper wall"
- documentation = "There are some walls that want to render above most things (mostly minerals since they shift over.\
-
We draw them to their own plane so we can hijack them for our emissive mask stuff"
- plane = WALL_PLANE_UPPER
- render_relay_planes = list(RENDER_PLANE_GAME_WORLD, LIGHT_MASK_PLANE)
-
-/atom/movable/screen/plane_master/wall_upper/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
- . = ..()
- add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_WALL_LAYER, relay_color = GLOB.em_block_color)
-
-//-------------------- AREA PLANE --------------------
-
-/atom/movable/screen/plane_master/area
- name = "Area"
- documentation = "Holds the areas themselves, which ends up meaning it holds any overlays/effects we apply to areas. NOT snow or rad storms, those go on above lighting"
- plane = AREA_PLANE
-
-//-------------------- GAME PLANES --------------------
-
-/atom/movable/screen/plane_master/game
- name = "Lower game world"
- documentation = "Exists mostly because of FOV shit. Basically, if you've just got a normal not ABOVE fov thing, and you don't want it masked, stick it here yeah?"
- plane = GAME_PLANE
- render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
-
-/atom/movable/screen/plane_master/high_game
- name = "High Game"
- documentation = "Holds anything that wants to be displayed above the rest of the game plane, and doesn't want to be clickable. \
-
This includes atmos debug overlays, blind sound images, and mining scanners. \
-
Really only exists for its layering potential, we don't use this for any vfx"
- plane = HIGH_GAME_PLANE
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
-
-//-------------------- FOV PLANES --------------------
-
-/atom/movable/screen/plane_master/game_world_fov_hidden
- name = "lower game world fov hidden"
- documentation = "If you want something to be hidden by fov, stick it on this plane. We're masked by the fov blocker plane, so the items on us can actually well, disappear."
- plane = GAME_PLANE_FOV_HIDDEN
- render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
-
-/atom/movable/screen/plane_master/game_world_fov_hidden/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- add_filter("vision_cone", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(FIELD_OF_VISION_BLOCKER_RENDER_TARGET, offset), flags = MASK_INVERSE))
-
-/atom/movable/screen/plane_master/field_of_vision_blocker
- name = "Field of vision blocker"
- documentation = "This is one of those planes that's only used as a filter. It masks out things that want to be hidden by fov.\
-
Literally just contains FOV images, or masks."
- plane = FIELD_OF_VISION_BLOCKER_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- render_target = FIELD_OF_VISION_BLOCKER_RENDER_TARGET
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- render_relay_planes = list()
- // We do NOT allow offsetting, because there's no case where you would want to block only one layer, at least currently
- allows_offsetting = FALSE
- start_hidden = TRUE
- // We mark as multiz_scaled FALSE so transforms don't effect us, and we draw to the planes below us as if they were us.
- // This is safe because we will ALWAYS be on the top z layer, so it DON'T MATTER
- multiz_scaled = FALSE
-
-/atom/movable/screen/plane_master/field_of_vision_blocker/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
- . = ..()
- mirror_parent_hidden()
-
-/atom/movable/screen/plane_master/game_world_above
- name = "Above game world"
- documentation = "We need a place that's unmasked by fov that also draws above the upper game world fov hidden plane. I told you fov was hacky man."
- plane = ABOVE_GAME_PLANE
- render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
-
-/atom/movable/screen/plane_master/game_world_upper
- name = "Upper game world"
- documentation = "Ok so fov is kinda fucky, because planes in byond serve both as effect groupings and as rendering orderers. Since that's true, we need a plane that we can stick stuff that draws above fov blocked stuff on."
- plane = GAME_PLANE_UPPER
- render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
-
-/atom/movable/screen/plane_master/game_world_upper_fov_hidden
- name = "Upper game world fov hidden"
- documentation = "Just as we need a place to draw things \"above\" the hidden fov plane, we also need to be able to hide stuff that draws over the upper game plane."
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
- render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
-
-/atom/movable/screen/plane_master/game_world_upper_fov_hidden/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- // Dupe of the other hidden plane
- add_filter("vision_cone", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(FIELD_OF_VISION_BLOCKER_RENDER_TARGET, offset), flags = MASK_INVERSE))
diff --git a/code/_onclick/hud/rendering/plane_masters/default.dm b/code/_onclick/hud/rendering/plane_masters/default.dm
deleted file mode 100644
index f4f49f5bdd8..00000000000
--- a/code/_onclick/hud/rendering/plane_masters/default.dm
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * Plane master that byond will by default draw to
- * Shouldn't be used, exists to prevent people using plane 0
- * NOTE: If we used SEE_BLACKNESS on a map format that wasn't SIDE_MAP, this is where its darkness would land
- * This would allow us to control it and do fun things. But we can't because side map doesn't support it, so this is just a stub
- */
-/atom/movable/screen/plane_master/default
- name = "Default"
- documentation = "This is quite fiddly, so bear with me. By default (in byond) everything in the game is rendered onto plane 0. It's the default plane. \
-
But, because we've moved everything we control off plane 0, all that's left is stuff byond internally renders. \
-
What I'd like to do with this is capture byond blackness by giving mobs the SEE_BLACKNESS sight flag. \
-
But we CAN'T because SEE_BLACKNESS does not work with our rendering format. So I just eat it I guess"
- plane = DEFAULT_PLANE
- multiz_scaled = FALSE
- start_hidden = TRUE // Doesn't DO anything, exists to hold this place
diff --git a/code/_onclick/hud/rendering/plane_masters/in_world_chat.dm b/code/_onclick/hud/rendering/plane_masters/in_world_chat.dm
deleted file mode 100644
index bc7d327b8fb..00000000000
--- a/code/_onclick/hud/rendering/plane_masters/in_world_chat.dm
+++ /dev/null
@@ -1,20 +0,0 @@
-/atom/movable/screen/plane_master/runechat
- name = "Runechat"
- documentation = "Holds runechat images, that text that pops up when someone say something. Uses a dropshadow to well, look nice."
- plane = RUNECHAT_PLANE
- render_relay_planes = list(RENDER_PLANE_NON_GAME)
-
-/atom/movable/screen/plane_master/runechat/show_to(mob/mymob)
- . = ..()
- if(!.)
- return
- remove_filter("AO")
- if(istype(mymob) && mymob.canon_client?.prefs?.read_preference(/datum/preference/toggle/ambient_occlusion))
- add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 4, color = "#04080FAA"))
-
-/atom/movable/screen/plane_master/balloon_chat
- name = "Balloon chat"
- documentation = "Holds ballon chat images, those little text bars that pop up for a second when you do some things. NOT runechat."
- plane = BALLOON_CHAT_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- render_relay_planes = list(RENDER_PLANE_NON_GAME)
diff --git a/code/_onclick/hud/rendering/plane_masters/lighting.dm b/code/_onclick/hud/rendering/plane_masters/lighting.dm
deleted file mode 100644
index 2ac4139b78f..00000000000
--- a/code/_onclick/hud/rendering/plane_masters/lighting.dm
+++ /dev/null
@@ -1,44 +0,0 @@
-///Contains all turf lighting
-/atom/movable/screen/plane_master/turf_lighting
- name = "Turf Lighting"
- documentation = "Contains all lighting drawn to turfs. Not so complex, draws directly onto the lighting plate."
- plane = LIGHTING_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- render_relay_planes = list(RENDER_PLANE_LIGHTING)
- blend_mode_override = BLEND_ADD
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- critical = PLANE_CRITICAL_DISPLAY
-
-/// This will not work through multiz, because of a byond bug with BLEND_MULTIPLY
-/// Bug report is up, waiting on a fix
-/atom/movable/screen/plane_master/o_light_visual
- name = "Overlight light visual"
- documentation = "Holds overlay lighting objects, or the sort of lighting that's a well, overlay stuck to something.\
-
Exists because lighting updating is really slow, and movement needs to feel smooth.\
-
We draw to the game plane, and mask out space for ourselves on the lighting plane so any color we have has the chance to display."
- plane = O_LIGHTING_VISUAL_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- render_target = O_LIGHTING_VISUAL_RENDER_TARGET
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- blend_mode = BLEND_MULTIPLY
- critical = PLANE_CRITICAL_DISPLAY
-
-/atom/movable/screen/plane_master/above_lighting
- name = "Above lighting"
- plane = ABOVE_LIGHTING_PLANE
- documentation = "Anything on the game plane that needs a space to draw on that will be above the lighting plane.\
-
Mostly little alerts and effects, also sometimes contains things that are meant to look as if they glow."
-
-/**
- * Handles emissive overlays and emissive blockers.
- */
-/atom/movable/screen/plane_master/emissive
- name = "Emissive"
- documentation = "Holds things that will be used to mask the lighting plane later on. Masked by the Emissive Mask plane to ensure we don't emiss out under a wall.\
-
Relayed onto the Emissive render plane to do the actual masking of lighting, since we need to be transformed and other emissive stuff needs to be transformed too.\
-
Don't want to double scale now."
- plane = EMISSIVE_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- render_relay_planes = list(EMISSIVE_RENDER_PLATE)
- critical = PLANE_CRITICAL_DISPLAY
diff --git a/code/_onclick/hud/rendering/plane_masters/parallax.dm b/code/_onclick/hud/rendering/plane_masters/parallax.dm
deleted file mode 100644
index ee49ab17700..00000000000
--- a/code/_onclick/hud/rendering/plane_masters/parallax.dm
+++ /dev/null
@@ -1,105 +0,0 @@
-/atom/movable/screen/plane_master/parallax_white
- name = "Parallax whitifier"
- documentation = "Essentially a backdrop for the parallax plane. We're rendered just below it, so we'll be multiplied by its well, parallax.\
-
If you want something to look as if it has parallax on it, draw it to this plane."
- plane = PLANE_SPACE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- render_relay_planes = list(RENDER_PLANE_GAME, LIGHT_MASK_PLANE)
- critical = PLANE_CRITICAL_FUCKO_PARALLAX // goes funny when touched. no idea why I don't trust byond
-
-/atom/movable/screen/plane_master/parallax_white/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
- . = ..()
- add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_SPACE_LAYER)
-
-///Contains space parallax
-/atom/movable/screen/plane_master/parallax
- name = "Parallax"
- documentation = "Contains parallax, or to be more exact the screen objects that hold parallax.\
-
Note the BLEND_MULTIPLY. The trick here is how low our plane value is. Because of that, we draw below almost everything in the game.\
-
We abuse this to ensure we multiply against the Parallax whitifier plane, or space's plane. It's set to full white, so when you do the multiply you just get parallax out where it well, makes sense to be.\
-
Also notice that the parent parallax plane is mirrored down to all children. We want to support viewing parallax across all z levels at once."
- plane = PLANE_SPACE_PARALLAX
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- blend_mode = BLEND_MULTIPLY
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- multiz_scaled = FALSE
-
-/atom/movable/screen/plane_master/parallax/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
- . = ..()
- if(offset != 0)
- // You aren't the source? don't change yourself
- return
- RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(on_offset_increase))
- RegisterSignal(SSdcs, COMSIG_NARSIE_SUMMON_UPDATE, PROC_REF(narsie_modified))
- if(GLOB.narsie_summon_count >= 1)
- narsie_start_midway(GLOB.narsie_effect_last_modified) // We assume we're on the start, so we can use this number
- offset_increase(0, SSmapping.max_plane_offset)
-
-/atom/movable/screen/plane_master/parallax/proc/on_offset_increase(datum/source, old_offset, new_offset)
- SIGNAL_HANDLER
- offset_increase(old_offset, new_offset)
-
-/atom/movable/screen/plane_master/parallax/proc/offset_increase(old_offset, new_offset)
- // Parallax will be mirrored down to any new planes that are added, so it will properly render across mirage borders
- for(var/offset in old_offset to new_offset)
- if(offset != 0)
- // Overlay so we don't multiply twice, and thus fuck up our rendering
- add_relay_to(GET_NEW_PLANE(plane, offset), BLEND_OVERLAY)
-
-// Hacky shit to ensure parallax works in perf mode
-/atom/movable/screen/plane_master/parallax/outside_bounds(mob/relevant)
- if(offset == 0)
- remove_relay_from(GET_NEW_PLANE(RENDER_PLANE_GAME, 0))
- is_outside_bounds = TRUE // I'm sorry :(
- return
- // If we can't render, and we aren't the bottom layer, don't render us
- // This way we only multiply against stuff that's not fullwhite space
- var/atom/movable/screen/plane_master/parent_parallax = home.our_hud.get_plane_master(PLANE_SPACE_PARALLAX)
- var/turf/viewing_turf = get_turf(relevant)
- if(!viewing_turf || offset != GET_LOWEST_STACK_OFFSET(viewing_turf.z))
- parent_parallax.remove_relay_from(plane)
- else
- parent_parallax.add_relay_to(plane, BLEND_OVERLAY)
- return ..()
-
-/atom/movable/screen/plane_master/parallax/inside_bounds(mob/relevant)
- if(offset == 0)
- add_relay_to(GET_NEW_PLANE(RENDER_PLANE_GAME, 0))
- is_outside_bounds = FALSE
- return
- // Always readd, just in case we lost it
- var/atom/movable/screen/plane_master/parent_parallax = home.our_hud.get_plane_master(PLANE_SPACE_PARALLAX)
- parent_parallax.add_relay_to(plane, BLEND_OVERLAY)
- return ..()
-
-// Needs to handle rejoining on a lower z level, so we NEED to readd old planes
-/atom/movable/screen/plane_master/parallax/check_outside_bounds()
- // If we're outside bounds AND we're the 0th plane, we need to show cause parallax is hacked to hell
- return offset != 0 && is_outside_bounds
-
-/// Starts the narsie animation midway, so we can catch up to everyone else quickly
-/atom/movable/screen/plane_master/parallax/proc/narsie_start_midway(start_time)
- var/time_elapsed = world.time - start_time
- narsie_summoned_effect(max(16 SECONDS - time_elapsed, 0))
-
-/// Starts the narsie animation, make us grey, then red
-/atom/movable/screen/plane_master/parallax/proc/narsie_modified(datum/source, new_count)
- SIGNAL_HANDLER
- if(new_count >= 1)
- narsie_summoned_effect(16 SECONDS)
- else
- narsie_unsummoned()
-
-/atom/movable/screen/plane_master/parallax/proc/narsie_summoned_effect(animate_time)
- if(GLOB.narsie_summon_count >= 2)
- var/static/list/nightmare_parallax = list(255,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, -130,0,0,0)
- animate(src, color = nightmare_parallax, time = animate_time)
- return
-
- var/static/list/grey_parallax = list(0.4,0.4,0.4,0, 0.4,0.4,0.4,0, 0.4,0.4,0.4,0, 0,0,0,1, -0.1,-0.1,-0.1,0)
- // We're gonna animate ourselves grey
- // Then, once it's done, about 40 seconds into the event itself, we're gonna start doin some shit. see below
- animate(src, color = grey_parallax, time = animate_time)
-
-/atom/movable/screen/plane_master/parallax/proc/narsie_unsummoned()
- animate(src, color = null, time = 8 SECONDS)
diff --git a/code/_onclick/hud/rendering/plane_masters/pipecrawl.dm b/code/_onclick/hud/rendering/plane_masters/pipecrawl.dm
deleted file mode 100644
index cb75135e1a4..00000000000
--- a/code/_onclick/hud/rendering/plane_masters/pipecrawl.dm
+++ /dev/null
@@ -1,15 +0,0 @@
-/atom/movable/screen/plane_master/pipecrawl
- name = "Pipecrawl"
- documentation = "Holds pipecrawl images generated during well, pipecrawling.\
-
Has a few effects and a funky color matrix designed to make things a bit more visually readable."
- plane = PIPECRAWL_IMAGES_PLANE
- start_hidden = TRUE
-
-/atom/movable/screen/plane_master/pipecrawl/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- // Makes everything on this plane slightly brighter
- // Has a nice effect, makes thing stand out
- color = list(1.2,0,0,0, 0,1.2,0,0, 0,0,1.2,0, 0,0,0,1, 0,0,0,0)
- // This serves a similar purpose, I want the pipes to pop
- add_filter("pipe_dropshadow", 1, drop_shadow_filter(x = -1, y= -1, size = 1, color = "#0000007A"))
- mirror_parent_hidden()
diff --git a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
new file mode 100644
index 00000000000..8988b2f7f33
--- /dev/null
+++ b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
@@ -0,0 +1,447 @@
+/atom/movable/screen/plane_master/clickcatcher
+ name = "Click Catcher"
+ documentation = "Contains the screen object we use as a backdrop to catch clicks on portions of the screen that would otherwise contain nothing else. \
+
Will always be below almost everything else"
+ plane = CLICKCATCHER_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ multiz_scaled = FALSE
+ critical = PLANE_CRITICAL_DISPLAY
+
+/atom/movable/screen/plane_master/clickcatcher/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
+ . = ..()
+ RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(offset_increased))
+ offset_increased(SSmapping, 0, SSmapping.max_plane_offset)
+
+/atom/movable/screen/plane_master/clickcatcher/proc/offset_increased(datum/source, old_off, new_off)
+ SIGNAL_HANDLER
+ // We only want need the lowest level
+ // If my system better supported changing PM plane values mid op I'd do that, but I do NOT so
+ if(new_off > offset)
+ hide_plane(home?.our_hud?.mymob)
+
+/atom/movable/screen/plane_master/parallax_white
+ name = "Parallax whitifier"
+ documentation = "Essentially a backdrop for the parallax plane. We're rendered just below it, so we'll be multiplied by its well, parallax.\
+
If you want something to look as if it has parallax on it, draw it to this plane."
+ plane = PLANE_SPACE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ render_relay_planes = list(RENDER_PLANE_GAME, LIGHT_MASK_PLANE)
+ critical = PLANE_CRITICAL_FUCKO_PARALLAX // goes funny when touched. no idea why I don't trust byond
+
+/atom/movable/screen/plane_master/parallax_white/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
+ . = ..()
+ add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_SPACE_LAYER)
+
+///Contains space parallax
+/atom/movable/screen/plane_master/parallax
+ name = "Parallax"
+ documentation = "Contains parallax, or to be more exact the screen objects that hold parallax.\
+
Note the BLEND_MULTIPLY. The trick here is how low our plane value is. Because of that, we draw below almost everything in the game.\
+
We abuse this to ensure we multiply against the Parallax whitifier plane, or space's plane. It's set to full white, so when you do the multiply you just get parallax out where it well, makes sense to be.\
+
Also notice that the parent parallax plane is mirrored down to all children. We want to support viewing parallax across all z levels at once."
+ plane = PLANE_SPACE_PARALLAX
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ blend_mode = BLEND_MULTIPLY
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ multiz_scaled = FALSE
+
+/atom/movable/screen/plane_master/parallax/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
+ . = ..()
+ if(offset != 0)
+ // You aren't the source? don't change yourself
+ return
+ RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(on_offset_increase))
+ RegisterSignal(SSdcs, COMSIG_NARSIE_SUMMON_UPDATE, PROC_REF(narsie_modified))
+ if(GLOB.narsie_summon_count >= 1)
+ narsie_start_midway(GLOB.narsie_effect_last_modified) // We assume we're on the start, so we can use this number
+ offset_increase(0, SSmapping.max_plane_offset)
+
+/atom/movable/screen/plane_master/parallax/proc/on_offset_increase(datum/source, old_offset, new_offset)
+ SIGNAL_HANDLER
+ offset_increase(old_offset, new_offset)
+
+/atom/movable/screen/plane_master/parallax/proc/offset_increase(old_offset, new_offset)
+ // Parallax will be mirrored down to any new planes that are added, so it will properly render across mirage borders
+ for(var/offset in old_offset to new_offset)
+ if(offset != 0)
+ // Overlay so we don't multiply twice, and thus fuck up our rendering
+ add_relay_to(GET_NEW_PLANE(plane, offset), BLEND_OVERLAY)
+
+// Hacky shit to ensure parallax works in perf mode
+/atom/movable/screen/plane_master/parallax/outside_bounds(mob/relevant)
+ if(offset == 0)
+ remove_relay_from(GET_NEW_PLANE(RENDER_PLANE_GAME, 0))
+ is_outside_bounds = TRUE // I'm sorry :(
+ return
+ // If we can't render, and we aren't the bottom layer, don't render us
+ // This way we only multiply against stuff that's not fullwhite space
+ var/atom/movable/screen/plane_master/parent_parallax = home.our_hud.get_plane_master(PLANE_SPACE_PARALLAX)
+ var/turf/viewing_turf = get_turf(relevant)
+ if(!viewing_turf || offset != GET_LOWEST_STACK_OFFSET(viewing_turf.z))
+ parent_parallax.remove_relay_from(plane)
+ else
+ parent_parallax.add_relay_to(plane, BLEND_OVERLAY)
+ return ..()
+
+/atom/movable/screen/plane_master/parallax/inside_bounds(mob/relevant)
+ if(offset == 0)
+ add_relay_to(GET_NEW_PLANE(RENDER_PLANE_GAME, 0))
+ is_outside_bounds = FALSE
+ return
+ // Always readd, just in case we lost it
+ var/atom/movable/screen/plane_master/parent_parallax = home.our_hud.get_plane_master(PLANE_SPACE_PARALLAX)
+ parent_parallax.add_relay_to(plane, BLEND_OVERLAY)
+ return ..()
+
+// Needs to handle rejoining on a lower z level, so we NEED to readd old planes
+/atom/movable/screen/plane_master/parallax/check_outside_bounds()
+ // If we're outside bounds AND we're the 0th plane, we need to show cause parallax is hacked to hell
+ return offset != 0 && is_outside_bounds
+
+/// Starts the narsie animation midway, so we can catch up to everyone else quickly
+/atom/movable/screen/plane_master/parallax/proc/narsie_start_midway(start_time)
+ var/time_elapsed = world.time - start_time
+ narsie_summoned_effect(max(16 SECONDS - time_elapsed, 0))
+
+/// Starts the narsie animation, make us grey, then red
+/atom/movable/screen/plane_master/parallax/proc/narsie_modified(datum/source, new_count)
+ SIGNAL_HANDLER
+ if(new_count >= 1)
+ narsie_summoned_effect(16 SECONDS)
+ else
+ narsie_unsummoned()
+
+/atom/movable/screen/plane_master/parallax/proc/narsie_summoned_effect(animate_time)
+ if(GLOB.narsie_summon_count >= 2)
+ var/static/list/nightmare_parallax = list(255,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, -130,0,0,0)
+ animate(src, color = nightmare_parallax, time = animate_time)
+ return
+
+ var/static/list/grey_parallax = list(0.4,0.4,0.4,0, 0.4,0.4,0.4,0, 0.4,0.4,0.4,0, 0,0,0,1, -0.1,-0.1,-0.1,0)
+ // We're gonna animate ourselves grey
+ // Then, once it's done, about 40 seconds into the event itself, we're gonna start doin some shit. see below
+ animate(src, color = grey_parallax, time = animate_time)
+
+/atom/movable/screen/plane_master/parallax/proc/narsie_unsummoned()
+ animate(src, color = null, time = 8 SECONDS)
+
+/atom/movable/screen/plane_master/gravpulse
+ name = "Gravpulse"
+ documentation = "Ok so this one's fun. Basically, we want to be able to distort the game plane when a grav annom is around.\
+
So we draw the pattern we want to use to this plane, and it's then used as a render target by a distortion filter on the game plane.\
+
Note the blend mode and lack of relay targets. This plane exists only to distort, it's never rendered anywhere."
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ plane = GRAVITY_PULSE_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ blend_mode = BLEND_ADD
+ render_target = GRAVITY_PULSE_RENDER_TARGET
+ render_relay_planes = list()
+
+///Contains just the floor
+/atom/movable/screen/plane_master/floor
+ name = "Floor"
+ documentation = "The well, floor. This is mostly used as a sorting mechanism, but it also lets us create a \"border\" around the game world plane, so its drop shadow will actually work."
+ plane = FLOOR_PLANE
+ render_relay_planes = list(RENDER_PLANE_GAME, LIGHT_MASK_PLANE)
+
+/atom/movable/screen/plane_master/transparent_floor
+ name = "Transparent Floor"
+ documentation = "Really just openspace, stuff that is a turf but has no color or alpha whatsoever.\
+
We use this to draw to just the light mask plane, cause if it's not there we get holes of blackness over openspace"
+ plane = TRANSPARENT_FLOOR_PLANE
+ render_relay_planes = list(LIGHT_MASK_PLANE)
+ // Needs to be critical or it uh, it'll look white
+ critical = PLANE_CRITICAL_DISPLAY|PLANE_CRITICAL_NO_RELAY
+
+/atom/movable/screen/plane_master/floor/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
+ . = ..()
+ add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_FLOOR_LAYER, relay_color = GLOB.em_block_color)
+
+/atom/movable/screen/plane_master/wall
+ name = "Wall"
+ documentation = "Holds all walls. We render this onto the game world. Separate so we can use this + space and floor planes as a guide for where byond blackness is NOT."
+ plane = WALL_PLANE
+ render_relay_planes = list(RENDER_PLANE_GAME_WORLD, LIGHT_MASK_PLANE)
+
+/atom/movable/screen/plane_master/wall/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
+ . = ..()
+ add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_WALL_LAYER, relay_color = GLOB.em_block_color)
+
+/atom/movable/screen/plane_master/game
+ name = "Lower game world"
+ documentation = "Exists mostly because of FOV shit. Basically, if you've just got a normal not ABOVE fov thing, and you don't want it masked, stick it here yeah?"
+ plane = GAME_PLANE
+ render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
+
+/atom/movable/screen/plane_master/game_world_fov_hidden
+ name = "lower game world fov hidden"
+ documentation = "If you want something to be hidden by fov, stick it on this plane. We're masked by the fov blocker plane, so the items on us can actually well, disappear."
+ plane = GAME_PLANE_FOV_HIDDEN
+ render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
+
+/atom/movable/screen/plane_master/game_world_fov_hidden/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ add_filter("vision_cone", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(FIELD_OF_VISION_BLOCKER_RENDER_TARGET, offset), flags = MASK_INVERSE))
+
+/atom/movable/screen/plane_master/field_of_vision_blocker
+ name = "Field of vision blocker"
+ documentation = "This is one of those planes that's only used as a filter. It masks out things that want to be hidden by fov.\
+
Literally just contains FOV images, or masks."
+ plane = FIELD_OF_VISION_BLOCKER_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ render_target = FIELD_OF_VISION_BLOCKER_RENDER_TARGET
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ render_relay_planes = list()
+ // We do NOT allow offsetting, because there's no case where you would want to block only one layer, at least currently
+ allows_offsetting = FALSE
+ start_hidden = TRUE
+ // We mark as multiz_scaled FALSE so transforms don't effect us, and we draw to the planes below us as if they were us.
+ // This is safe because we will ALWAYS be on the top z layer, so it DON'T MATTER
+ multiz_scaled = FALSE
+
+/atom/movable/screen/plane_master/field_of_vision_blocker/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
+ . = ..()
+ mirror_parent_hidden()
+
+/atom/movable/screen/plane_master/game_world_upper
+ name = "Upper game world"
+ documentation = "Ok so fov is kinda fucky, because planes in byond serve both as effect groupings and as rendering orderers. Since that's true, we need a plane that we can stick stuff that draws above fov blocked stuff on."
+ plane = GAME_PLANE_UPPER
+ render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
+
+/atom/movable/screen/plane_master/wall_upper
+ name = "Upper wall"
+ documentation = "There are some walls that want to render above most things (mostly minerals since they shift over.\
+
We draw them to their own plane so we can hijack them for our emissive mask stuff"
+ plane = WALL_PLANE_UPPER
+ render_relay_planes = list(RENDER_PLANE_GAME_WORLD, LIGHT_MASK_PLANE)
+
+/atom/movable/screen/plane_master/wall_upper/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset)
+ . = ..()
+ add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_WALL_LAYER, relay_color = GLOB.em_block_color)
+
+/atom/movable/screen/plane_master/game_world_upper_fov_hidden
+ name = "Upper game world fov hidden"
+ documentation = "Just as we need a place to draw things \"above\" the hidden fov plane, we also need to be able to hide stuff that draws over the upper game plane."
+ plane = GAME_PLANE_UPPER_FOV_HIDDEN
+ render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
+
+/atom/movable/screen/plane_master/game_world_upper_fov_hidden/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ // Dupe of the other hidden plane
+ add_filter("vision_cone", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(FIELD_OF_VISION_BLOCKER_RENDER_TARGET, offset), flags = MASK_INVERSE))
+
+/atom/movable/screen/plane_master/seethrough
+ name = "Seethrough"
+ documentation = "Holds the seethrough versions (done using image overrides) of large objects. Mouse transparent, so you can click through them."
+ plane = SEETHROUGH_PLANE
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
+ start_hidden = TRUE
+
+/atom/movable/screen/plane_master/game_world_above
+ name = "Above game world"
+ documentation = "We need a place that's unmasked by fov that also draws above the upper game world fov hidden plane. I told you fov was hacky man."
+ plane = ABOVE_GAME_PLANE
+ render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
+
+/**
+ * Plane master that byond will by default draw to
+ * Shouldn't be used, exists to prevent people using plane 0
+ * NOTE: If we used SEE_BLACKNESS on a map format that wasn't SIDE_MAP, this is where its darkness would land
+ * This would allow us to control it and do fun things. But we can't because side map doesn't support it, so this is just a stub
+ */
+/atom/movable/screen/plane_master/default
+ name = "Default"
+ documentation = "This is quite fiddly, so bear with me. By default (in byond) everything in the game is rendered onto plane 0. It's the default plane. \
+
But, because we've moved everything we control off plane 0, all that's left is stuff byond internally renders. \
+
What I'd like to do with this is capture byond blackness by giving mobs the SEE_BLACKNESS sight flag. \
+
But we CAN'T because SEE_BLACKNESS does not work with our rendering format. So I just eat it I guess"
+ plane = DEFAULT_PLANE
+ multiz_scaled = FALSE
+ start_hidden = TRUE // Doesn't DO anything, exists to hold this place
+
+/atom/movable/screen/plane_master/area
+ name = "Area"
+ documentation = "Holds the areas themselves, which ends up meaning it holds any overlays/effects we apply to areas. NOT snow or rad storms, those go on above lighting"
+ plane = AREA_PLANE
+
+/atom/movable/screen/plane_master/massive_obj
+ name = "Massive object"
+ documentation = "Huge objects need to render above everything else on the game plane, otherwise they'd well, get clipped and look not that huge. This does that."
+ plane = MASSIVE_OBJ_PLANE
+
+/atom/movable/screen/plane_master/point
+ name = "Point"
+ documentation = "I mean like, what do you want me to say? Points draw over pretty much everything else, so they get their own plane. Remember we layer render relays to draw planes in their proper order on render plates."
+ plane = POINT_PLANE
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+
+///Contains all turf lighting
+/atom/movable/screen/plane_master/turf_lighting
+ name = "Turf Lighting"
+ documentation = "Contains all lighting drawn to turfs. Not so complex, draws directly onto the lighting plate."
+ plane = LIGHTING_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ render_relay_planes = list(RENDER_PLANE_LIGHTING)
+ blend_mode_override = BLEND_ADD
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ critical = PLANE_CRITICAL_DISPLAY
+
+/// This will not work through multiz, because of a byond bug with BLEND_MULTIPLY
+/// Bug report is up, waiting on a fix
+/atom/movable/screen/plane_master/o_light_visual
+ name = "Overlight light visual"
+ documentation = "Holds overlay lighting objects, or the sort of lighting that's a well, overlay stuck to something.\
+
Exists because lighting updating is really slow, and movement needs to feel smooth.\
+
We draw to the game plane, and mask out space for ourselves on the lighting plane so any color we have has the chance to display."
+ plane = O_LIGHTING_VISUAL_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ render_target = O_LIGHTING_VISUAL_RENDER_TARGET
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ blend_mode = BLEND_MULTIPLY
+ critical = PLANE_CRITICAL_DISPLAY
+
+/atom/movable/screen/plane_master/above_lighting
+ name = "Above lighting"
+ plane = ABOVE_LIGHTING_PLANE
+ documentation = "Anything on the game plane that needs a space to draw on that will be above the lighting plane.\
+
Mostly little alerts and effects, also sometimes contains things that are meant to look as if they glow."
+
+/**
+ * Handles emissive overlays and emissive blockers.
+ */
+/atom/movable/screen/plane_master/emissive
+ name = "Emissive"
+ documentation = "Holds things that will be used to mask the lighting plane later on. Masked by the Emissive Mask plane to ensure we don't emiss out under a wall.\
+
Relayed onto the Emissive render plane to do the actual masking of lighting, since we need to be transformed and other emissive stuff needs to be transformed too.\
+
Don't want to double scale now."
+ plane = EMISSIVE_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ render_relay_planes = list(EMISSIVE_RENDER_PLATE)
+ critical = PLANE_CRITICAL_DISPLAY
+
+/atom/movable/screen/plane_master/pipecrawl
+ name = "Pipecrawl"
+ documentation = "Holds pipecrawl images generated during well, pipecrawling.\
+
Has a few effects and a funky color matrix designed to make things a bit more visually readable."
+ plane = PIPECRAWL_IMAGES_PLANE
+ start_hidden = TRUE
+
+/atom/movable/screen/plane_master/pipecrawl/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ // Makes everything on this plane slightly brighter
+ // Has a nice effect, makes thing stand out
+ color = list(1.2,0,0,0, 0,1.2,0,0, 0,0,1.2,0, 0,0,0,1, 0,0,0,0)
+ // This serves a similar purpose, I want the pipes to pop
+ add_filter("pipe_dropshadow", 1, drop_shadow_filter(x = -1, y= -1, size = 1, color = "#0000007A"))
+ mirror_parent_hidden()
+
+/atom/movable/screen/plane_master/camera_static
+ name = "Camera static"
+ documentation = "Holds camera static images. Usually only visible to people who can well, see static.\
+
We use images rather then vis contents because they're lighter on maptick, and maptick sucks butt."
+ plane = CAMERA_STATIC_PLANE
+
+/atom/movable/screen/plane_master/camera_static/show_to(mob/mymob)
+ . = ..()
+ if(!.)
+ return
+ var/datum/hud/our_hud = home.our_hud
+ if(isnull(our_hud))
+ return
+
+ // We'll hide the slate if we're not seeing through a camera eye
+ // This can call on a cycle cause we don't clear in hide_from
+ // Yes this is the best way of hooking into the hud, I hate myself too
+ RegisterSignal(our_hud, COMSIG_HUD_EYE_CHANGED, PROC_REF(eye_changed), override = TRUE)
+ eye_changed(our_hud, null, our_hud.mymob?.canon_client?.eye)
+
+/atom/movable/screen/plane_master/camera_static/proc/eye_changed(datum/hud/source, atom/old_eye, atom/new_eye)
+ SIGNAL_HANDLER
+
+ if(!isaicamera(new_eye))
+ if(!force_hidden)
+ hide_plane(source.mymob)
+ return
+
+ if(force_hidden)
+ unhide_plane(source.mymob)
+
+/atom/movable/screen/plane_master/high_game
+ name = "High Game"
+ documentation = "Holds anything that wants to be displayed above the rest of the game plane, and doesn't want to be clickable. \
+
This includes atmos debug overlays, blind sound images, and mining scanners. \
+
Really only exists for its layering potential, we don't use this for any vfx"
+ plane = HIGH_GAME_PLANE
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+
+/atom/movable/screen/plane_master/ghost
+ name = "Ghost"
+ documentation = "Ghosts draw here, so they don't get mixed up in the visuals of the game world. Note, this is not not how we HIDE ghosts from people, that's done with invisible and see_invisible."
+ plane = GHOST_PLANE
+ render_relay_planes = list(RENDER_PLANE_NON_GAME)
+
+/atom/movable/screen/plane_master/fullscreen
+ name = "Fullscreen"
+ documentation = "Holds anything that applies to or above the full screen. \
+
Note, it's still rendered underneath hud objects, but this lets us control the order that things like death/damage effects render in."
+ plane = FULLSCREEN_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ render_relay_planes = list(RENDER_PLANE_NON_GAME)
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ allows_offsetting = FALSE
+
+/atom/movable/screen/plane_master/runechat
+ name = "Runechat"
+ documentation = "Holds runechat images, that text that pops up when someone say something. Uses a dropshadow to well, look nice."
+ plane = RUNECHAT_PLANE
+ render_relay_planes = list(RENDER_PLANE_NON_GAME)
+
+/atom/movable/screen/plane_master/runechat/show_to(mob/mymob)
+ . = ..()
+ if(!.)
+ return
+ remove_filter("AO")
+ if(istype(mymob) && mymob.canon_client?.prefs?.read_preference(/datum/preference/toggle/ambient_occlusion))
+ add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 4, color = "#04080FAA"))
+
+/atom/movable/screen/plane_master/balloon_chat
+ name = "Balloon chat"
+ documentation = "Holds ballon chat images, those little text bars that pop up for a second when you do some things. NOT runechat."
+ plane = BALLOON_CHAT_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ render_relay_planes = list(RENDER_PLANE_NON_GAME)
+
+/atom/movable/screen/plane_master/hud
+ name = "HUD"
+ documentation = "Contains anything that want to be rendered on the hud. Typically is just screen elements."
+ plane = HUD_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ render_relay_planes = list(RENDER_PLANE_NON_GAME)
+ allows_offsetting = FALSE
+
+/atom/movable/screen/plane_master/above_hud
+ name = "Above HUD"
+ documentation = "Anything that wants to be drawn ABOVE the rest of the hud. Typically close buttons and other elements that need to be always visible. Think preventing draggable action button memes."
+ plane = ABOVE_HUD_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ render_relay_planes = list(RENDER_PLANE_NON_GAME)
+ allows_offsetting = FALSE
+
+/atom/movable/screen/plane_master/splashscreen
+ name = "Splashscreen"
+ documentation = "Cinematics and the splash screen."
+ plane = SPLASHSCREEN_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ render_relay_planes = list(RENDER_PLANE_NON_GAME)
+ allows_offsetting = FALSE
+
+/atom/movable/screen/plane_master/escape_menu
+ name = "Escape Menu"
+ documentation = "Anything relating to the escape menu."
+ plane = ESCAPE_MENU_PLANE
+ appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
+ render_relay_planes = list(RENDER_PLANE_MASTER)
+ allows_offsetting = FALSE
diff --git a/code/_onclick/hud/rendering/plane_masters/simple_plane_masters.dm b/code/_onclick/hud/rendering/plane_masters/simple_plane_masters.dm
deleted file mode 100644
index ea0ad21a773..00000000000
--- a/code/_onclick/hud/rendering/plane_masters/simple_plane_masters.dm
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Put plane masters that are just a simple type def in here, anything more complex should get its own file
- */
-
-/atom/movable/screen/plane_master/gravpulse
- name = "Gravpulse"
- documentation = "Ok so this one's fun. Basically, we want to be able to distort the game plane when a grav annom is around.\
-
So we draw the pattern we want to use to this plane, and it's then used as a render target by a distortion filter on the game plane.\
-
Note the blend mode and lack of relay targets. This plane exists only to distort, it's never rendered anywhere."
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- plane = GRAVITY_PULSE_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- blend_mode = BLEND_ADD
- render_target = GRAVITY_PULSE_RENDER_TARGET
- render_relay_planes = list()
-
-/atom/movable/screen/plane_master/seethrough
- name = "Seethrough"
- documentation = "Holds the seethrough versions (done using image overrides) of large objects. Mouse transparent, so you can click through them."
- plane = SEETHROUGH_PLANE
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- render_relay_planes = list(RENDER_PLANE_GAME_WORLD)
- start_hidden = TRUE
-
-/atom/movable/screen/plane_master/massive_obj
- name = "Massive object"
- documentation = "Huge objects need to render above everything else on the game plane, otherwise they'd well, get clipped and look not that huge. This does that."
- plane = MASSIVE_OBJ_PLANE
-
-/atom/movable/screen/plane_master/point
- name = "Point"
- documentation = "I mean like, what do you want me to say? Points draw over pretty much everything else, so they get their own plane. Remember we layer render relays to draw planes in their proper order on render plates."
- plane = POINT_PLANE
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
-
-/atom/movable/screen/plane_master/ghost
- name = "Ghost"
- documentation = "Ghosts draw here, so they don't get mixed up in the visuals of the game world. Note, this is not not how we HIDE ghosts from people, that's done with invisible and see_invisible."
- plane = GHOST_PLANE
- render_relay_planes = list(RENDER_PLANE_NON_GAME)
-
-/atom/movable/screen/plane_master/fullscreen
- name = "Fullscreen"
- documentation = "Holds anything that applies to or above the full screen. \
-
Note, it's still rendered underneath hud objects, but this lets us control the order that things like death/damage effects render in."
- plane = FULLSCREEN_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- render_relay_planes = list(RENDER_PLANE_NON_GAME)
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- allows_offsetting = FALSE
-
-/atom/movable/screen/plane_master/hud
- name = "HUD"
- documentation = "Contains anything that want to be rendered on the hud. Typically is just screen elements."
- plane = HUD_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- render_relay_planes = list(RENDER_PLANE_NON_GAME)
- allows_offsetting = FALSE
-
-/atom/movable/screen/plane_master/above_hud
- name = "Above HUD"
- documentation = "Anything that wants to be drawn ABOVE the rest of the hud. Typically close buttons and other elements that need to be always visible. Think preventing draggable action button memes."
- plane = ABOVE_HUD_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- render_relay_planes = list(RENDER_PLANE_NON_GAME)
- allows_offsetting = FALSE
-
-/atom/movable/screen/plane_master/splashscreen
- name = "Splashscreen"
- documentation = "Cinematics and the splash screen."
- plane = SPLASHSCREEN_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- render_relay_planes = list(RENDER_PLANE_NON_GAME)
- allows_offsetting = FALSE
-
-/atom/movable/screen/plane_master/escape_menu
- name = "Escape Menu"
- documentation = "Anything relating to the escape menu."
- plane = ESCAPE_MENU_PLANE
- appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
- render_relay_planes = list(RENDER_PLANE_MASTER)
- allows_offsetting = FALSE
diff --git a/code/controllers/subsystem/persistence/_persistence.dm b/code/controllers/subsystem/persistence/_persistence.dm
index c98be5d1c5a..565cf519d4b 100644
--- a/code/controllers/subsystem/persistence/_persistence.dm
+++ b/code/controllers/subsystem/persistence/_persistence.dm
@@ -62,8 +62,9 @@ SUBSYSTEM_DEF(persistence)
///Loads up Poly's speech buffer.
/datum/controller/subsystem/persistence/proc/load_poly()
- for(var/mob/living/simple_animal/parrot/poly/P in GLOB.alive_mob_list)
- twitterize(P.speech_buffer, "polytalk")
+ for(var/mob/living/basic/parrot/poly/bird in GLOB.alive_mob_list)
+ var/list/list_to_read = bird.get_static_list_of_phrases()
+ twitterize(list_to_read, "polytalk")
break //Who's been duping the bird?!
/// Loads up the amount of times maps appeared to alter their appearance in voting and rotation.
diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm
index 21b141feff8..0f7fe6ef142 100644
--- a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm
+++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm
@@ -1,6 +1,8 @@
/datum/ai_behavior/basic_melee_attack
action_cooldown = 0.2 SECONDS // We gotta check unfortunately often because we're in a race condition with nextmove
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
+ ///do we finish this action after hitting once?
+ var/terminate_after_action = FALSE
/datum/ai_behavior/basic_melee_attack/setup(datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key)
. = ..()
@@ -39,12 +41,21 @@
else
basic_mob.melee_attack(target)
+ if(terminate_after_action)
+ finish_action(controller, TRUE, target_key)
/datum/ai_behavior/basic_melee_attack/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key)
. = ..()
if(!succeeded)
controller.clear_blackboard_key(target_key)
+/datum/ai_behavior/basic_melee_attack/interact_once
+ terminate_after_action = TRUE
+
+/datum/ai_behavior/basic_melee_attack/interact_once/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key)
+ . = ..()
+ controller.clear_blackboard_key(target_key)
+
/datum/ai_behavior/basic_ranged_attack
action_cooldown = 0.6 SECONDS
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM
diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm
new file mode 100644
index 00000000000..d0fb7e3e8c7
--- /dev/null
+++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm
@@ -0,0 +1,14 @@
+/datum/ai_behavior/unbuckle_mob
+
+/datum/ai_behavior/unbuckle_mob/perform(seconds_per_tick, datum/ai_controller/controller)
+ . = ..()
+
+ var/mob/living/living_pawn = controller.pawn
+ var/atom/movable/buckled_too = living_pawn.buckled
+
+ if(isnull(buckled_too))
+ finish_action(controller, FALSE)
+ return
+
+ buckled_too.unbuckle_mob(living_pawn)
+ finish_action(controller, TRUE)
diff --git a/code/datums/ai/generic/find_and_set.dm b/code/datums/ai/generic/find_and_set.dm
index f54ba33e6e3..d368641ce0c 100644
--- a/code/datums/ai/generic/find_and_set.dm
+++ b/code/datums/ai/generic/find_and_set.dm
@@ -72,6 +72,16 @@
if(length(found))
return pick(found)
+/// Like find_and_set/in_list, but we return the turf location of the item instead of the item itself.
+/datum/ai_behavior/find_and_set/in_list/turf_location
+
+/datum/ai_behavior/find_and_set/in_list/turf_location/search_tactic(datum/ai_controller/controller, locate_paths, search_range)
+ . = ..()
+ if(isnull(.))
+ return null
+
+ return get_turf(.)
+
/**
* Variant of find and set which returns an object which can be animated with a staff of change
*/
@@ -148,3 +158,16 @@
return pick(customers)
return null
+
+/datum/ai_behavior/find_and_set/nearby_friends
+ action_cooldown = 2 SECONDS
+
+/datum/ai_behavior/find_and_set/nearby_friends/search_tactic(datum/ai_controller/controller, locate_path, search_range)
+ var/atom/friend = locate(/mob/living/carbon/human) in oview(search_range, controller.pawn)
+
+ if(isnull(friend))
+ return null
+
+ var/mob/living/living_pawn = controller.pawn
+ var/potential_friend = living_pawn.faction.Find(REF(friend)) ? friend : null
+ return potential_friend
diff --git a/code/datums/ai/generic/generic_behaviors.dm b/code/datums/ai/generic/generic_behaviors.dm
index 4f816de4be3..962c8d141cd 100644
--- a/code/datums/ai/generic/generic_behaviors.dm
+++ b/code/datums/ai/generic/generic_behaviors.dm
@@ -299,6 +299,8 @@
/datum/ai_behavior/perform_speech
/datum/ai_behavior/perform_speech/perform(seconds_per_tick, datum/ai_controller/controller, speech, speech_sound)
+ . = ..()
+
var/mob/living/living_pawn = controller.pawn
if(!istype(living_pawn))
return
diff --git a/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm b/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm
index 486f9411934..7dd453163c5 100644
--- a/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm
+++ b/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm
@@ -123,12 +123,18 @@
///Sprite accessories are singletons, stored list("Big Snout" = instance of /datum/sprite_accessory/snout/big), so here we get that singleton
/datum/bodypart_overlay/mutant/proc/fetch_sprite_datum(datum/sprite_accessory/accessory_path)
- var/list/feature_list = get_global_feature_list()
-
- return feature_list[initial(accessory_path.name)]
+ return fetch_sprite_datum_from_name(initial(accessory_path.name))
///Get the singleton from the sprite name
/datum/bodypart_overlay/mutant/proc/fetch_sprite_datum_from_name(accessory_name)
var/list/feature_list = get_global_feature_list()
-
- return feature_list[accessory_name]
+ var/found = feature_list[accessory_name]
+ if(found)
+ return found
+
+ if(!length(feature_list))
+ CRASH("External organ [type] returned no sprite datums from get_global_feature_list(), so no accessories could be found!")
+ else if(accessory_name)
+ CRASH("External organ [type] couldn't find sprite accessory [accessory_name]!")
+ else
+ CRASH("External organ [type] had fetch_sprite_datum called with a null accessory name!")
diff --git a/code/datums/components/acid.dm b/code/datums/components/acid.dm
index 1d9cf9e87d6..67be754c504 100644
--- a/code/datums/components/acid.dm
+++ b/code/datums/components/acid.dm
@@ -254,7 +254,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
if(!isliving(arrived))
return
var/mob/living/crosser = arrived
- if(crosser.movement_type & FLYING)
+ if(crosser.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
return
if(crosser.move_intent == MOVE_INTENT_WALK)
return
diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm
index 45a9d1976c5..0ec866960e3 100644
--- a/code/datums/components/caltrop.dm
+++ b/code/datums/components/caltrop.dm
@@ -78,7 +78,7 @@
if((flags & CALTROP_IGNORE_WALKERS) && H.move_intent == MOVE_INTENT_WALK)
return
- if(H.movement_type & (FLOATING|FLYING)) //check if they are able to pass over us
+ if(H.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) //check if they are able to pass over us
//gravity checking only our parent would prevent us from triggering they're using magboots / other gravity assisting items that would cause them to still touch us.
return
diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm
index 18420016c54..f85ca347aa0 100644
--- a/code/datums/components/chasm.dm
+++ b/code/datums/components/chasm.dm
@@ -101,7 +101,7 @@
return CHASM_NOT_DROPPING
if(is_type_in_typecache(dropped_thing, forbidden_types) || (!isliving(dropped_thing) && !isobj(dropped_thing)))
return CHASM_NOT_DROPPING
- if(dropped_thing.throwing || (dropped_thing.movement_type & (FLOATING|FLYING)))
+ if(dropped_thing.throwing || (dropped_thing.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
return CHASM_REGISTER_SIGNALS
//Flies right over the chasm
diff --git a/code/datums/components/conveyor_movement.dm b/code/datums/components/conveyor_movement.dm
index 4439b08e0a7..d8affca3649 100644
--- a/code/datums/components/conveyor_movement.dm
+++ b/code/datums/components/conveyor_movement.dm
@@ -24,7 +24,7 @@
source.delay = speed //We use the default delay
if(living_parent)
var/mob/living/moving_mob = parent
- if((moving_mob.movement_type & FLYING) && !moving_mob.stat)
+ if((moving_mob.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) && !moving_mob.stat)
return MOVELOOP_SKIP_STEP
var/atom/movable/moving_parent = parent
if(moving_parent.anchored || !moving_parent.has_gravity())
diff --git a/code/datums/components/irradiated.dm b/code/datums/components/irradiated.dm
index c8a57f3761a..bffd56459bf 100644
--- a/code/datums/components/irradiated.dm
+++ b/code/datums/components/irradiated.dm
@@ -96,7 +96,7 @@
process_tox_damage(human_parent, seconds_per_tick)
/datum/component/irradiated/proc/should_halt_effects(mob/living/carbon/human/target)
- if (IS_IN_STASIS(target))
+ if (HAS_TRAIT(target, TRAIT_STASIS))
return TRUE
if (HAS_TRAIT(target, TRAIT_HALT_RADIATION_EFFECTS))
diff --git a/code/datums/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm
new file mode 100644
index 00000000000..88a8644d262
--- /dev/null
+++ b/code/datums/components/listen_and_repeat.dm
@@ -0,0 +1,88 @@
+/// The maximum number of phrases we can store in our speech buffer
+#define MAX_SPEECH_BUFFER_SIZE 500
+/// Tendency we have to ignore radio chatter
+#define RADIO_IGNORE_CHANCE 10
+
+/// Simple element that will deterministically set a value based on stuff that the source has heard and will then compel the source to repeat it.
+/// Requires a valid AI Blackboard.
+/datum/component/listen_and_repeat
+ /// List of things that we start out having in our speech buffer
+ var/list/desired_phrases = null
+ /// The AI Blackboard Key we assign the value to.
+ var/blackboard_key = null
+ /// Probability we speak
+ var/speech_probability = null
+ /// Probabiliy we switch our phrase
+ var/switch_phrase_probability = null
+ /// List of things that we've heard and will repeat.
+ var/list/speech_buffer = null
+
+/datum/component/listen_and_repeat/Initialize(list/desired_phrases, blackboard_key)
+ . = ..()
+ if(!ismovable(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ if(!isnull(desired_phrases))
+ LAZYADD(speech_buffer, desired_phrases)
+
+ src.blackboard_key = blackboard_key
+
+ RegisterSignal(parent, COMSIG_MOVABLE_PRE_HEAR, PROC_REF(on_hear))
+ RegisterSignal(parent, COMSIG_NEEDS_NEW_PHRASE, PROC_REF(set_new_blackboard_phrase))
+ RegisterSignal(parent, COMSIG_LIVING_WRITE_MEMORY, PROC_REF(on_write_memory))
+
+ ADD_TRAIT(parent, TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, type)
+
+/datum/component/listen_and_repeat/Destroy(force, silent)
+ REMOVE_TRAIT(parent, TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, type)
+ return ..()
+
+/// Called when we hear something.
+/datum/component/listen_and_repeat/proc/on_hear(datum/source, list/passed_arguments)
+ SIGNAL_HANDLER
+
+ var/message = passed_arguments[HEARING_RAW_MESSAGE]
+ var/speaker = passed_arguments[HEARING_SPEAKER]
+ var/over_radio = !!passed_arguments[HEARING_RADIO_FREQ]
+ if(speaker == source) // don't parrot ourselves
+ return
+
+ if(over_radio && prob(RADIO_IGNORE_CHANCE))
+ return
+ //SKYRAT EDIT ADDITION START - parrot commands
+ var/mob/living/basic/parrot/maybe_parrot = parent
+ if(!over_radio && istype(maybe_parrot))
+ maybe_parrot.check_command(message, speaker)
+ // SKYRAT EDIT ADDITION END
+
+ var/number_of_excess_strings = LAZYLEN(speech_buffer) - MAX_SPEECH_BUFFER_SIZE
+ if(number_of_excess_strings > 0) // only remove if we're overfull
+ for(var/i in 1 to number_of_excess_strings)
+ LAZYREMOVE(speech_buffer, pick(speech_buffer))
+
+ LAZYOR(speech_buffer, html_decode(message))
+
+/// Called to set a new value for the blackboard key.
+/datum/component/listen_and_repeat/proc/set_new_blackboard_phrase(datum/source)
+ SIGNAL_HANDLER
+ var/atom/movable/atom_source = source
+ var/datum/ai_controller/controller = atom_source.ai_controller
+ if(!LAZYLEN(speech_buffer))
+ controller.clear_blackboard_key(blackboard_key)
+ return NO_NEW_PHRASE_AVAILABLE
+
+ var/selected_phrase = pick(speech_buffer)
+ controller.set_blackboard_key(blackboard_key, selected_phrase)
+
+/// Exports all the speech buffer data to a dedicated blackboard key on the source.
+/datum/component/listen_and_repeat/proc/on_write_memory(datum/source, dead, gibbed)
+ SIGNAL_HANDLER
+ var/atom/movable/atom_source = source
+ var/datum/ai_controller/controller = atom_source.ai_controller
+ if(LAZYLEN(speech_buffer)) // what? well whatever let's just move on
+ return
+
+ controller.set_blackboard_key(BB_EXPORTABLE_STRING_BUFFER_LIST, speech_buffer.Copy())
+
+#undef MAX_SPEECH_BUFFER_SIZE
+#undef RADIO_IGNORE_CHANCE
diff --git a/code/datums/components/omen.dm b/code/datums/components/omen.dm
index f03f28b8d6a..56af8e6a9db 100644
--- a/code/datums/components/omen.dm
+++ b/code/datums/components/omen.dm
@@ -17,7 +17,7 @@
/// Base damage from negative events. Cursed take 25% of this damage.
var/damage_mod = 1
-/datum/component/omen/Initialize(obj/vessel, incidents_left = 1, luck_mod, damage_mod)
+/datum/component/omen/Initialize(obj/vessel, incidents_left, luck_mod, damage_mod)
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
diff --git a/code/datums/components/slippery.dm b/code/datums/components/slippery.dm
index 2119c8ad7e1..620f6baa816 100644
--- a/code/datums/components/slippery.dm
+++ b/code/datums/components/slippery.dm
@@ -161,7 +161,7 @@
if(HAS_TRAIT(turf, TRAIT_TURF_IGNORE_SLIPPERY))
return
var/mob/living/victim = arrived
- if((victim.movement_type & (FLYING | FLOATING)))
+ if(victim.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
return
if(can_slip_callback && !can_slip_callback.Invoke(holder, victim))
return
diff --git a/code/datums/components/squashable.dm b/code/datums/components/squashable.dm
index 8174127aeb3..86135faa7bb 100644
--- a/code/datums/components/squashable.dm
+++ b/code/datums/components/squashable.dm
@@ -54,7 +54,7 @@
return //Everything worked, we're done!
if(isliving(crossing_movable))
var/mob/living/crossing_mob = crossing_movable
- if(crossing_mob.mob_size > MOB_SIZE_SMALL && !(crossing_mob.movement_type & FLYING))
+ if(crossing_mob.mob_size > MOB_SIZE_SMALL && !(crossing_mob.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
if(HAS_TRAIT(crossing_mob, TRAIT_PACIFISM))
crossing_mob.visible_message(span_notice("[crossing_mob] carefully steps over [parent_as_living]."), span_notice("You carefully step over [parent_as_living] to avoid hurting it."))
return
diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm
index af0686fd1eb..c5d42797ab4 100644
--- a/code/datums/components/squeak.dm
+++ b/code/datums/components/squeak.dm
@@ -98,7 +98,7 @@
var/obj/item/I = arrived
if(I.item_flags & ABSTRACT)
return
- if(arrived.movement_type & (FLYING|FLOATING) || !arrived.has_gravity())
+ if((arrived.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || !arrived.has_gravity())
return
if(ismob(arrived) && !arrived.density) // Prevents 10 overlapping mice from making an unholy sound while moving
return
diff --git a/code/datums/components/supermatter_crystal.dm b/code/datums/components/supermatter_crystal.dm
index 3138f07c5ec..15048ffff93 100644
--- a/code/datums/components/supermatter_crystal.dm
+++ b/code/datums/components/supermatter_crystal.dm
@@ -300,7 +300,7 @@
message_admins("[atom_source] has consumed [key_name_admin(consumed_mob)] [ADMIN_JMP(atom_source)].")
atom_source.investigate_log("has consumed [key_name(consumed_mob)].", INVESTIGATE_ENGINE)
consumed_mob.investigate_log("has been dusted by [atom_source].", INVESTIGATE_DEATHS)
- if(istype(consumed_mob, /mob/living/simple_animal/parrot/poly)) // Dusting Poly creates a power surge
+ if(istype(consumed_mob, /mob/living/basic/parrot/poly)) // Dusting Poly creates a power surge
force_event(/datum/round_event_control/supermatter_surge/poly, "Poly's revenge")
notify_ghosts(
"[consumed_mob] has been dusted by [atom_source]!",
diff --git a/code/datums/diseases/dna_spread.dm b/code/datums/diseases/dna_spread.dm
index 7783465aabd..48ca9506e2e 100644
--- a/code/datums/diseases/dna_spread.dm
+++ b/code/datums/diseases/dna_spread.dm
@@ -24,7 +24,7 @@
return FALSE
//Only species that can be spread by transformation sting can be spread by the retrovirus
- if(HAS_TRAIT(affected_mob, TRAIT_NO_TRANSFORMATION_STING) || HAS_TRAIT(affected_mob, TRAIT_NO_DNA_COPY))
+ if(HAS_TRAIT(affected_mob, TRAIT_NO_DNA_COPY))
cure()
return FALSE
diff --git a/code/datums/diseases/parrotpossession.dm b/code/datums/diseases/parrotpossession.dm
index 23f68e1a42f..bbe587b2a8d 100644
--- a/code/datums/diseases/parrotpossession.dm
+++ b/code/datums/diseases/parrotpossession.dm
@@ -13,24 +13,41 @@
severity = DISEASE_SEVERITY_MEDIUM
infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD|MOB_ROBOTIC|MOB_MINERAL
bypasses_immunity = TRUE //2spook
- var/mob/living/simple_animal/parrot/poly/ghost/parrot
+ ///chance we speak
+ var/speak_chance = 5
+ ///controller we speak from
+ var/datum/ai_controller/basic_controller/parrot_controller
/datum/disease/parrot_possession/stage_act(seconds_per_tick, times_fired)
. = ..()
- if(!.)
+
+ if(!. || isnull(parrot_controller))
return
- if(QDELETED(parrot) || parrot.loc != affected_mob)
- cure()
- return FALSE
+ var/potential_phrase = parrot_controller.blackboard[BB_PARROT_REPEAT_STRING]
- if(length(parrot.speech_buffer) && SPT_PROB(parrot.speak_chance, seconds_per_tick)) // I'm not going to dive into polycode trying to adjust that probability. Enjoy doubled ghost parrot speach
- affected_mob.say(pick(parrot.speech_buffer), forced = "parrot possession")
+ if(SPT_PROB(speak_chance, seconds_per_tick) && !isnull(potential_phrase))
+ affected_mob.say(potential_phrase, forced = "parrot possession")
/datum/disease/parrot_possession/cure()
- if(parrot && parrot.loc == affected_mob)
- parrot.forceMove(affected_mob.drop_location())
- affected_mob.visible_message(span_danger("[parrot] is violently driven out of [affected_mob]!"), span_userdanger("[parrot] bursts out of your chest!"))
- ..()
+ var/atom/movable/inside_parrot = locate(/mob/living/basic/parrot/poly/ghost) in affected_mob
+ if(inside_parrot)
+ UnregisterSignal(inside_parrot, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_MOVED))
+ inside_parrot.forceMove(affected_mob.drop_location())
+ affected_mob.visible_message(
+ span_danger("[inside_parrot] is violently driven out of [affected_mob]!"),
+ span_userdanger("[inside_parrot] bursts out of your chest!"),
+ )
+ parrot_controller = null
+ return ..()
+
+/datum/disease/parrot_possession/proc/set_parrot(mob/living/parrot)
+ parrot_controller = parrot.ai_controller
+ RegisterSignals(parrot, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_MOVED), PROC_REF(on_parrot_exit))
+
+/datum/disease/parrot_possession/proc/on_parrot_exit(datum/source)
+ SIGNAL_HANDLER
+ UnregisterSignal(source, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_MOVED))
+ cure()
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index 2d745cb6290..d27779387ed 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -84,8 +84,9 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
var/blood_type
///The type of mutant race the player is if applicable (i.e. potato-man)
var/datum/species/species = new /datum/species/human
- ///first value is mutant color
- var/list/features = list("FFF")
+ /// Assoc list of feature keys to their value
+ /// Note if you set these manually, and do not update [unique_features] afterwards, it will likely be reset.
+ var/list/features = list("mcolor" = "#FFFFFF")
///Stores the hashed values of the person's non-human features
var/unique_features
///Stores the real name of the person who originally got this dna datum. Used primarely for changelings,
@@ -135,12 +136,17 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
destination.dna.features = features.Copy()
destination.dna.real_name = real_name
destination.dna.temporary_mutations = temporary_mutations.Copy()
+ //SKYRAT EDIT ADDITION BEGIN - CUSTOMIZATION
+ destination.dna.mutant_bodyparts = mutant_bodyparts.Copy()
+ destination.dna.body_markings = body_markings.Copy()
+ destination.dna.update_body_size()
+ //SKYRAT EDIT ADDITION END
if(transfer_SE)
destination.dna.mutation_index = mutation_index
destination.dna.default_mutation_genes = default_mutation_genes
if(transfer_species)
//destination.set_species(species.type, icon_update=0) - ORIGINAL
- destination.set_species(species.type, TRUE, null, features.Copy(), mutant_bodyparts.Copy(), body_markings.Copy()) //SKYRAT EDIT CHANGE - CUSTOMIZATION
+ destination.set_species(species.type, TRUE, FALSE, features.Copy(), mutant_bodyparts.Copy(), body_markings.Copy()) //SKYRAT EDIT CHANGE - CUSTOMIZATION
/datum/dna/proc/copy_dna(datum/dna/new_dna)
new_dna.unique_enzymes = unique_enzymes
@@ -381,7 +387,9 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
if(DNA_LIZARD_MARKINGS_BLOCK)
set_uni_feature_block(blocknumber, construct_block(GLOB.body_markings_list.Find(features["body_markings"]), GLOB.body_markings_list.len))
if(DNA_TAIL_BLOCK)
- set_uni_feature_block(blocknumber, construct_block(GLOB.tails_list.Find(features["tail_lizard"]), GLOB.tails_list.len))
+ set_uni_feature_block(blocknumber, construct_block(GLOB.tails_list_human.Find(features["tail_cat"]), GLOB.tails_list_human.len))
+ if(DNA_LIZARD_TAIL_BLOCK)
+ set_uni_feature_block(blocknumber, construct_block(GLOB.tails_list_lizard.Find(features["tail_lizard"]), GLOB.tails_list_lizard.len))
if(DNA_SNOUT_BLOCK)
set_uni_feature_block(blocknumber, construct_block(GLOB.snouts_list.Find(features["snout"]), GLOB.snouts_list.len))
if(DNA_HORNS_BLOCK)
@@ -468,25 +476,41 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
if(message)
to_chat(holder, message)
-//used to update dna UI, UE, and dna.real_name.
+/// Updates the UI, UE, and UF of the DNA according to the features, appearance, name, etc. of the DNA / holder.
/datum/dna/proc/update_dna_identity()
unique_identity = generate_unique_identity()
unique_enzymes = generate_unique_enzymes()
unique_features = generate_unique_features()
-//SKYRAT EDIT REMOVAL BEGIN - CUSTOMIZATION (moved to modular)
-/*
-/datum/dna/proc/initialize_dna(newblood_type, skip_index = FALSE)
+/**
+ * Sets up DNA codes and initializes some features.
+ *
+ * * newblood_type - Optional, the blood type to set the DNA to
+ * * create_mutation_blocks - If true, generate_dna_blocks is called, which is used to set up mutation blocks (what a mob can naturally mutate).
+ * * randomize_features - If true, all entries in the features list will be randomized.
+ */
+/datum/dna/proc/initialize_dna(newblood_type, create_mutation_blocks = TRUE, randomize_features = TRUE)
if(newblood_type)
blood_type = newblood_type
- unique_enzymes = generate_unique_enzymes()
- unique_identity = generate_unique_identity()
- if(!skip_index) //I hate this
+ if(create_mutation_blocks) //I hate this
generate_dna_blocks()
- features = random_features()
- unique_features = generate_unique_features()
-*/
-//SKYRAT EDIT REMOVAL END
+ mutant_bodyparts = species.get_mutant_bodyparts(features, existing_mutant_bodyparts = randomize_features ? list() : mutant_bodyparts) // SKYRAT EDIT ADDITION
+ if(randomize_features)
+ /* SKYRAT EDIT REMOVAL - We don't really want this, do we? We get the same effect from get_mutant_bodyparts() on our end, but without mixing up weird species features.
+ var/static/list/all_species_protoypes
+ if(isnull(all_species_protoypes))
+ all_species_protoypes = list()
+ for(var/species_path in subtypesof(/datum/species))
+ all_species_protoypes += new species_path()
+
+ for(var/datum/species/random_species as anything in all_species_protoypes)
+ features |= random_species.randomize_features()
+ SKYRAT EDIT REMOVAL END */
+ body_markings = species.get_random_body_markings(features) // SKYRAT EDIT ADDITION
+
+ features["mcolor"] = "#[random_color()]"
+
+ update_dna_identity()
/datum/dna/stored //subtype used by brain mob's stored_dna
@@ -519,9 +543,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
stored_dna.species = mrace //not calling any species update procs since we're a brain, not a monkey/human
-//SKYRAT EDIT REMOVAL BEGIN - CUSTOMIZATION (moved to modular_skyrat/modules/customization/code/datums/dna.dm)
-/*
-/mob/living/carbon/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE)
+/mob/living/carbon/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE, list/override_features, list/override_mutantparts, list/override_markings) // SKYRAT EDIT CHANGE - ORIGINAL: /mob/living/carbon/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE)
if(QDELETED(src))
CRASH("You're trying to change your species post deletion, this is a recipe for madness")
if(isnull(mrace))
@@ -547,10 +569,32 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
if (old_species.properly_gained)
old_species.on_species_loss(src, new_race, pref_load)
+ // SKYRAT EDIT ADDITION START - BODYPARTS AND FEATURES
+ // We need to instantiate the list with compatible mutant parts so we don't break things
+
+ if(override_mutantparts && override_mutantparts.len)
+ for(var/feature in dna.mutant_bodyparts)
+ override_mutantparts[feature] = dna.mutant_bodyparts[feature]
+ dna.mutant_bodyparts = override_mutantparts
+
+ if(override_markings && override_markings.len)
+ for(var/feature in dna.body_markings)
+ override_markings[feature] = dna.body_markings[feature]
+ dna.body_markings = override_markings
+
+ if(override_features && override_features.len)
+ for(var/feature in dna.features)
+ override_features[feature] = dna.features[feature]
+ dna.features = override_features
+
+ apply_customizable_dna_features_to_species()
+ dna.unique_features = dna.generate_unique_features()
+
+ dna.update_body_size()
+ // SKYRAT EDIT ADDITION END
+
dna.species.on_species_gain(src, old_species, pref_load)
log_mob_tag("TAG: [tag] SPECIES: [key_name(src)] \[[mrace]\]")
-*/
-//SKYRAT EDIT REMOVAL END
/mob/living/carbon/human/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE)
..()
diff --git a/code/datums/elements/immerse.dm b/code/datums/elements/immerse.dm
index 6449d8de1ad..1a3b37d6530 100644
--- a/code/datums/elements/immerse.dm
+++ b/code/datums/elements/immerse.dm
@@ -229,7 +229,7 @@
*/
/datum/element/immerse/proc/try_immerse(atom/movable/movable, atom/movable/buckled)
var/atom/movable/to_check = buckled || movable
- if(!(to_check.movement_type & (FLYING|FLOATING)) && !movable.throwing)
+ if(!(to_check.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) && !movable.throwing)
add_immerse_overlay(movable)
if(!buckled)
RegisterSignal(movable, COMSIG_MOVETYPE_FLAG_ENABLED, PROC_REF(on_move_flag_enabled))
@@ -243,7 +243,7 @@
*/
/datum/element/immerse/proc/try_unimmerse(atom/movable/movable, atom/movable/buckled)
var/atom/movable/to_check = buckled || movable
- if(!(to_check.movement_type & (FLYING|FLOATING)) && !movable.throwing)
+ if(!(to_check.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) && !movable.throwing)
remove_immerse_overlay(movable)
if(!buckled)
UnregisterSignal(movable, list(COMSIG_MOVETYPE_FLAG_ENABLED, COMSIG_MOVETYPE_FLAG_DISABLED, COMSIG_MOVABLE_POST_THROW, COMSIG_MOVABLE_THROW_LANDED))
@@ -256,7 +256,7 @@
///Removes the overlay from mob and bucklees is flying.
/datum/element/immerse/proc/on_move_flag_enabled(atom/movable/source, flag, old_movement_type)
SIGNAL_HANDLER
- if(!(flag & (FLYING|FLOATING)) || old_movement_type & (FLYING|FLOATING) || source.throwing)
+ if(!(flag & MOVETYPES_NOT_TOUCHING_GROUND) || (old_movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || source.throwing)
return
remove_immerse_overlay(source)
for(var/mob/living/buckled_mob as anything in source.buckled_mobs)
@@ -265,7 +265,7 @@
///Works just like on_move_flag_enabled, except it only has to check that movable isn't flying
/datum/element/immerse/proc/on_throw(atom/movable/source)
SIGNAL_HANDLER
- if(source.movement_type & (FLYING|FLOATING))
+ if(source.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
return
remove_immerse_overlay(source)
for(var/mob/living/buckled_mob as anything in source.buckled_mobs)
@@ -274,7 +274,7 @@
///Readds the overlay to the mob and bucklees if no longer flying.
/datum/element/immerse/proc/on_move_flag_disabled(atom/movable/source, flag, old_movement_type)
SIGNAL_HANDLER
- if(!(flag & (FLYING|FLOATING)) || source.movement_type & (FLYING|FLOATING) || source.throwing)
+ if(!(flag & MOVETYPES_NOT_TOUCHING_GROUND) || (source.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || source.throwing)
return
add_immerse_overlay(source)
for(var/mob/living/buckled_mob as anything in source.buckled_mobs)
@@ -283,7 +283,7 @@
///Works just like on_move_flag_disabled, except it only has to check that movable isn't flying
/datum/element/immerse/proc/on_throw_landed(atom/movable/source)
SIGNAL_HANDLER
- if(source.movement_type & (FLYING|FLOATING))
+ if(source.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
return
add_immerse_overlay(source)
for(var/mob/living/buckled_mob as anything in source.buckled_mobs)
diff --git a/code/datums/elements/pet_bonus.dm b/code/datums/elements/pet_bonus.dm
index 41977da31c0..5ef8b515077 100644
--- a/code/datums/elements/pet_bonus.dm
+++ b/code/datums/elements/pet_bonus.dm
@@ -33,6 +33,7 @@
return
new /obj/effect/temp_visual/heart(pet.loc)
+ SEND_SIGNAL(pet, COMSIG_ANIMAL_PET, petter, modifiers)
if(emote_message && prob(33))
pet.manual_emote(emote_message)
petter.add_mood_event("petting_bonus", moodlet, pet)
diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm
index 06e2c1e18d6..c3562aa5987 100644
--- a/code/datums/helper_datums/getrev.dm
+++ b/code/datums/helper_datums/getrev.dm
@@ -37,6 +37,8 @@
else if(!originmastercommit)
msg += "No commit information"
+ msg += "Running rust-g version [rustg_get_version()]"
+
return msg.Join("\n")
/datum/getrev/proc/GetTestMergeInfo(header = TRUE)
diff --git a/code/datums/memory/_memory.dm b/code/datums/memory/_memory.dm
index 2b3e250a3fb..dd571c85746 100644
--- a/code/datums/memory/_memory.dm
+++ b/code/datums/memory/_memory.dm
@@ -266,6 +266,7 @@
/mob/living/basic/morph,
/mob/living/basic/mouse,
/mob/living/basic/mushroom,
+ /mob/living/basic/parrot,
/mob/living/basic/pet/dog/breaddog,
/mob/living/basic/pet/dog/corgi,
/mob/living/basic/pet/dog/pug,
@@ -276,7 +277,6 @@
/mob/living/basic/stickman,
/mob/living/basic/stickman/dog,
/mob/living/simple_animal/hostile/megafauna/dragon/lesser,
- /mob/living/simple_animal/parrot,
/mob/living/simple_animal/pet/cat,
/mob/living/simple_animal/pet/cat/cak,
/obj/item/food/sausage/american,
diff --git a/code/datums/mutations/void_magnet.dm b/code/datums/mutations/void_magnet.dm
index d6636b0b630..48f04eda636 100644
--- a/code/datums/mutations/void_magnet.dm
+++ b/code/datums/mutations/void_magnet.dm
@@ -60,7 +60,7 @@
/datum/action/cooldown/spell/void/cursed/proc/on_life(mob/living/source, seconds_per_tick, times_fired)
SIGNAL_HANDLER
- if(!isliving(source) || IS_IN_STASIS(source) || source.stat == DEAD || HAS_TRAIT(source, TRAIT_NO_TRANSFORM))
+ if(!isliving(source) || HAS_TRAIT(source, TRAIT_STASIS) || source.stat == DEAD || HAS_TRAIT(source, TRAIT_NO_TRANSFORM))
return
if(!is_valid_target(source))
diff --git a/code/datums/quirks/negative_quirks/allergic.dm b/code/datums/quirks/negative_quirks/allergic.dm
index d6a510f62b6..64b4c560bde 100644
--- a/code/datums/quirks/negative_quirks/allergic.dm
+++ b/code/datums/quirks/negative_quirks/allergic.dm
@@ -48,7 +48,7 @@
if(!iscarbon(quirk_holder))
return
- if(IS_IN_STASIS(quirk_holder))
+ if(HAS_TRAIT(quirk_holder, TRAIT_STASIS))
return
if(quirk_holder.stat == DEAD)
diff --git a/code/datums/sprite_accessories.dm b/code/datums/sprite_accessories.dm
index 44f77fc1d76..c0c6578437e 100644
--- a/code/datums/sprite_accessories.dm
+++ b/code/datums/sprite_accessories.dm
@@ -1783,11 +1783,13 @@
color_src = HAIR_COLOR
/datum/sprite_accessory/tails/monkey
- name = "Monkey"
icon = 'icons/mob/human/species/monkey/monkey_tail.dmi'
- icon_state = "monkey"
color_src = FALSE
+/datum/sprite_accessory/tails/monkey/standard
+ name = "Monkey"
+ icon_state = "monkey"
+
/datum/sprite_accessory/pod_hair
icon = 'icons/mob/human/species/podperson_hair.dmi'
em_block = TRUE
diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm
index fccb6a5d8f5..2865a230f43 100644
--- a/code/datums/status_effects/debuffs/debuffs.dm
+++ b/code/datums/status_effects/debuffs/debuffs.dm
@@ -278,8 +278,8 @@
. = ..()
if(!.)
return
- owner.add_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED, TRAIT_NUMBED), TRAIT_STATUS_EFFECT(id))//SKYRAT EDIT START - STASIS APPLIES NUMBING
- owner.throw_alert("stasis numbed", /atom/movable/screen/alert/numbed) //SKYRAT EDIT END
+ owner.add_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED, TRAIT_STASIS, TRAIT_NUMBED), TRAIT_STATUS_EFFECT(id)) // SKYRAT EDIT CHANGE - ORIGINAL: owner.add_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED, TRAIT_STASIS), TRAIT_STATUS_EFFECT(id))
+ owner.throw_alert("stasis numbed", /atom/movable/screen/alert/numbed) //SKYRAT EDIT ADDITION - STASIS APPLIES NUMBED
owner.add_filter("stasis_status_ripple", 2, list("type" = "ripple", "flags" = WAVE_BOUNDED, "radius" = 0, "size" = 2))
var/filter = owner.get_filter("stasis_status_ripple")
animate(filter, radius = 0, time = 0.2 SECONDS, size = 2, easing = JUMP_EASING, loop = -1, flags = ANIMATION_PARALLEL)
@@ -294,8 +294,8 @@
owner.Sleeping(15 SECONDS) //SKYRAT EDIT END
/datum/status_effect/grouped/stasis/on_remove()
- owner.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED, TRAIT_NUMBED), TRAIT_STATUS_EFFECT(id)) //SKYRAT EDIT START - STASIS END REMOVES NUMBING
- owner.clear_alert("stasis numbed") //SKYRAT EDIT END
+ owner.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED, TRAIT_STASIS, TRAIT_NUMBED), TRAIT_STATUS_EFFECT(id)) // SKYRAT EDIT CHANGE - ORIGINAL: owner.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED, TRAIT_STASIS), TRAIT_STATUS_EFFECT(id))
+ owner.clear_alert("stasis numbed") //SKYRAT EDIT ADDITION - STASIS APPLIED NUMBED
owner.remove_filter("stasis_status_ripple")
update_time_of_death()
if(iscarbon(owner))
diff --git a/code/datums/status_effects/debuffs/dna_transformation.dm b/code/datums/status_effects/debuffs/dna_transformation.dm
new file mode 100644
index 00000000000..33b6eb1d913
--- /dev/null
+++ b/code/datums/status_effects/debuffs/dna_transformation.dm
@@ -0,0 +1,91 @@
+/// Transforms a carbon mob into a new DNA for a set amount of time,
+/// then turns them back to how they were before transformation.
+/datum/status_effect/temporary_transformation
+ id = "temp_dna_transformation"
+ tick_interval = -1
+ duration = 1 MINUTES // set in on creation, this just needs to be any value to process
+ alert_type = null
+ /// A reference to a COPY of the DNA that the mob will be transformed into.
+ var/datum/dna/new_dna
+ /// A reference to a COPY of the DNA of the mob prior to transformation.
+ var/datum/dna/old_dna
+
+/datum/status_effect/temporary_transformation/Destroy()
+ . = ..() // parent must be called first, so we clear DNA refs AFTER transforming back... yeah i know
+ QDEL_NULL(new_dna)
+ QDEL_NULL(old_dna)
+
+/datum/status_effect/temporary_transformation/on_creation(mob/living/new_owner, new_duration = 1 MINUTES, datum/dna/dna_to_copy)
+ src.duration = (new_duration == INFINITY) ? -1 : new_duration
+ src.new_dna = new()
+ src.old_dna = new()
+ dna_to_copy.copy_dna(new_dna)
+ return ..()
+
+/datum/status_effect/temporary_transformation/on_apply()
+ if(!iscarbon(owner))
+ return FALSE
+
+ var/mob/living/carbon/transforming = owner
+ if(!transforming.has_dna())
+ return FALSE
+
+ // Save the old DNA
+ transforming.dna.copy_dna(old_dna)
+ // Makes them into the new DNA
+ new_dna.transfer_identity(transforming)
+ transforming.real_name = new_dna.real_name
+ transforming.name = transforming.get_visible_name()
+ transforming.updateappearance(mutcolor_update = TRUE)
+ transforming.domutcheck()
+ return TRUE
+
+/datum/status_effect/temporary_transformation/on_remove()
+ var/mob/living/carbon/transforming = owner
+
+ if(!QDELING(owner)) // Don't really need to do appearance stuff if we're being deleted
+ old_dna.transfer_identity(transforming)
+ transforming.updateappearance(mutcolor_update = TRUE)
+ transforming.domutcheck()
+
+ transforming.real_name = old_dna.real_name // Name is fine though
+ transforming.name = transforming.get_visible_name()
+
+/datum/status_effect/temporary_transformation/trans_sting
+ /// Tracks the time left on the effect when the owner last died. Used to pause the effect.
+ var/time_before_pause = -1
+ /// Signals which we react to to determine if we should pause the effect.
+ var/static/list/update_on_signals = list(
+ COMSIG_MOB_STATCHANGE,
+ SIGNAL_ADDTRAIT(TRAIT_STASIS),
+ SIGNAL_REMOVETRAIT(TRAIT_STASIS),
+ SIGNAL_ADDTRAIT(TRAIT_DEATHCOMA),
+ SIGNAL_REMOVETRAIT(TRAIT_DEATHCOMA),
+ )
+
+/datum/status_effect/temporary_transformation/trans_sting/on_apply()
+ . = ..()
+ if(!.)
+ return
+ RegisterSignals(owner, update_on_signals, PROC_REF(pause_effect))
+ pause_effect(owner) // for if we sting a dead guy
+
+/datum/status_effect/temporary_transformation/trans_sting/on_remove()
+ . = ..()
+ UnregisterSignal(owner, update_on_signals)
+
+/datum/status_effect/temporary_transformation/trans_sting/proc/pause_effect(mob/living/source)
+ SIGNAL_HANDLER
+
+ // Pause if we're dead, appear dead, or in stasis
+ if(source.stat == DEAD || HAS_TRAIT(source, TRAIT_DEATHCOMA) || HAS_TRAIT(source, TRAIT_STASIS))
+ if(duration == -1)
+ return // Already paused
+
+ time_before_pause = duration - world.time
+ duration = -1
+
+ // Resume if we're none of the above and also were paused
+ else if(time_before_pause != -1)
+ duration = time_before_pause + world.time
+ time_before_pause = -1
diff --git a/code/datums/status_effects/debuffs/drowsiness.dm b/code/datums/status_effects/debuffs/drowsiness.dm
index ebf1f43796c..cd99e879066 100644
--- a/code/datums/status_effects/debuffs/drowsiness.dm
+++ b/code/datums/status_effects/debuffs/drowsiness.dm
@@ -29,7 +29,7 @@
/datum/status_effect/drowsiness/tick(seconds_between_ticks)
// You do not feel drowsy while unconscious or in stasis
- if(owner.stat >= UNCONSCIOUS || IS_IN_STASIS(owner))
+ if(owner.stat >= UNCONSCIOUS || HAS_TRAIT(owner, TRAIT_STASIS))
return
// Resting helps against drowsiness
diff --git a/code/datums/status_effects/debuffs/drunk.dm b/code/datums/status_effects/debuffs/drunk.dm
index e46915922e5..061c008def8 100644
--- a/code/datums/status_effects/debuffs/drunk.dm
+++ b/code/datums/status_effects/debuffs/drunk.dm
@@ -65,7 +65,7 @@
/datum/status_effect/inebriated/tick(seconds_between_ticks)
// Drunk value does not decrease while dead or in stasis
- if(owner.stat == DEAD || IS_IN_STASIS(owner))
+ if(owner.stat == DEAD || HAS_TRAIT(owner, TRAIT_STASIS))
return
// Every tick, the drunk value decrases by
diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm
index 48b14f36cd0..416fe031341 100644
--- a/code/datums/status_effects/neutral.dm
+++ b/code/datums/status_effects/neutral.dm
@@ -526,7 +526,7 @@
monkey_tail.Insert(human_mob, drop_if_replaced = FALSE)
var/datum/species/human_species = human_mob.dna?.species
if(human_species)
- human_species.randomize_features(human_mob)
+ human_species.randomize_active_features(human_mob)
human_species.randomize_active_underwear(human_mob)
owner.remove_status_effect(/datum/status_effect/eigenstasium)
diff --git a/code/datums/status_effects/wound_effects.dm b/code/datums/status_effects/wound_effects.dm
index 6ec793c5672..f7d640a6d1c 100644
--- a/code/datums/status_effects/wound_effects.dm
+++ b/code/datums/status_effects/wound_effects.dm
@@ -67,7 +67,7 @@
/datum/status_effect/limp/proc/check_step(mob/whocares, OldLoc, Dir, forced)
SIGNAL_HANDLER
- if(!owner.client || owner.body_position == LYING_DOWN || !owner.has_gravity() || (owner.movement_type & FLYING) || forced || owner.buckled)
+ if(!owner.client || owner.body_position == LYING_DOWN || !owner.has_gravity() || (owner.movement_type & (FLYING|FLOATING)) || forced || owner.buckled)
return
// less limping while we have determination still
diff --git a/code/datums/weather/weather_types/floor_is_lava.dm b/code/datums/weather/weather_types/floor_is_lava.dm
index df4e9eec0f0..03ed0c68c31 100644
--- a/code/datums/weather/weather_types/floor_is_lava.dm
+++ b/code/datums/weather/weather_types/floor_is_lava.dm
@@ -39,7 +39,7 @@
for(var/obj/structure/structure_to_check in mob_turf)
if(structure_to_check.density)
return FALSE
- if(mob_to_check.movement_type & (FLYING|FLOATING))
+ if(mob_to_check.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
return FALSE
/datum/weather/floor_is_lava/weather_act(mob/living/victim)
diff --git a/code/datums/wounds/bones.dm b/code/datums/wounds/bones.dm
index d4624acbb92..1df5ff689ff 100644
--- a/code/datums/wounds/bones.dm
+++ b/code/datums/wounds/bones.dm
@@ -75,7 +75,7 @@
/datum/wound/blunt/bone/handle_process(seconds_per_tick, times_fired)
. = ..()
- if (!victim || IS_IN_STASIS(victim))
+ if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
return
if(limb.body_zone == BODY_ZONE_HEAD && brain_trauma_group && world.time > next_trauma_cycle)
diff --git a/code/datums/wounds/burns.dm b/code/datums/wounds/burns.dm
index 39e91d06fb6..c041c4fd003 100644
--- a/code/datums/wounds/burns.dm
+++ b/code/datums/wounds/burns.dm
@@ -36,7 +36,7 @@
/datum/wound/burn/flesh/handle_process(seconds_per_tick, times_fired)
- if (!victim || IS_IN_STASIS(victim))
+ if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
return
. = ..()
diff --git a/code/datums/wounds/pierce.dm b/code/datums/wounds/pierce.dm
index ec166584632..7304d21365c 100644
--- a/code/datums/wounds/pierce.dm
+++ b/code/datums/wounds/pierce.dm
@@ -64,7 +64,7 @@
return BLOOD_FLOW_STEADY
/datum/wound/pierce/bleed/handle_process(seconds_per_tick, times_fired)
- if (!victim || IS_IN_STASIS(victim))
+ if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
return
set_blood_flow(min(blood_flow, WOUND_SLASH_MAX_BLOODFLOW))
diff --git a/code/datums/wounds/slash.dm b/code/datums/wounds/slash.dm
index 4475d95f508..d17b59cf80b 100644
--- a/code/datums/wounds/slash.dm
+++ b/code/datums/wounds/slash.dm
@@ -134,7 +134,7 @@
/datum/wound/slash/flesh/handle_process(seconds_per_tick, times_fired)
- if (!victim || IS_IN_STASIS(victim))
+ if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
return
// in case the victim has the NOBLOOD trait, the wound will simply not clot on it's own
diff --git a/code/game/machinery/barsigns.dm b/code/game/machinery/barsigns.dm
index 64b104b4007..cc90f3346f6 100644
--- a/code/game/machinery/barsigns.dm
+++ b/code/game/machinery/barsigns.dm
@@ -424,6 +424,12 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/barsign, 32)
desc = "Where every drink is masterfully crafted with industrial efficiency!"
neon_color = "#ffffff"
+/datum/barsign/bargonia
+ name = "Bargonia"
+ icon_state = "bargonia"
+ desc = "The warehouse yearns for a higher calling... so Supply has declared BARGONIA!"
+ neon_color = COLOR_WHITE
+
// Hidden signs list below this point
/datum/barsign/hiddensigns
diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm
index 415dacc9f17..f875617bd40 100644
--- a/code/game/machinery/rechargestation.dm
+++ b/code/game/machinery/rechargestation.dm
@@ -156,4 +156,7 @@
/obj/machinery/recharge_station/proc/process_occupant(seconds_per_tick)
if(!occupant)
return
- SEND_SIGNAL(occupant, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, recharge_speed * seconds_per_tick / 2, repairs, sendmats)
+ var/main_draw = use_power_from_net(recharge_speed * seconds_per_tick, take_any = TRUE) //Pulls directly from the Powernet to dump into the cell
+ if(!main_draw)
+ return
+ SEND_SIGNAL(occupant, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, main_draw, repairs, sendmats)
diff --git a/code/game/machinery/stasis.dm b/code/game/machinery/stasis.dm
index 8bcdba43d05..b3020c8d749 100644
--- a/code/game/machinery/stasis.dm
+++ b/code/game/machinery/stasis.dm
@@ -52,7 +52,7 @@
/obj/machinery/stasis/Exited(atom/movable/gone, direction)
if(gone == occupant)
var/mob/living/L = gone
- if(IS_IN_STASIS(L))
+ if(HAS_TRAIT(L, TRAIT_STASIS))
thaw_them(L)
return ..()
@@ -139,9 +139,9 @@
return
var/mob/living/L_occupant = occupant
if(stasis_running())
- if(!IS_IN_STASIS(L_occupant))
+ if(!HAS_TRAIT(L_occupant, TRAIT_STASIS))
chill_out(L_occupant)
- else if(IS_IN_STASIS(L_occupant))
+ else if(HAS_TRAIT(L_occupant, TRAIT_STASIS))
thaw_them(L_occupant)
/obj/machinery/stasis/screwdriver_act(mob/living/user, obj/item/I)
diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm
index 1ea301a1427..0731cf8c0c6 100644
--- a/code/game/objects/effects/mines.dm
+++ b/code/game/objects/effects/mines.dm
@@ -71,7 +71,7 @@
return FALSE
living_mob = on_who
- if(living_mob?.incorporeal_move || on_who.movement_type & FLYING)
+ if(living_mob?.incorporeal_move || (on_who.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
return foot_on_mine ? IS_WEAKREF_OF(on_who, foot_on_mine) : FALSE //Only go boom if their foot was on the mine PRIOR to flying/phasing. You fucked up, you live with the consequences.
return TRUE
diff --git a/code/game/objects/items/granters/martial_arts/sleeping_carp.dm b/code/game/objects/items/granters/martial_arts/sleeping_carp.dm
index ab3e343b28d..a6b0ac58225 100644
--- a/code/game/objects/items/granters/martial_arts/sleeping_carp.dm
+++ b/code/game/objects/items/granters/martial_arts/sleeping_carp.dm
@@ -5,7 +5,7 @@
desc = "A scroll filled with strange markings. It seems to be drawings of some sort of martial art."
greet = "You have learned the ancient martial art of the Sleeping Carp! Your hand-to-hand combat has become much more effective, and you are now able to deflect any projectiles \
directed toward you while in Throw Mode. Your body has also hardened itself, granting extra protection against lasting wounds that would otherwise mount during extended combat. \
- However, you are also unable to use any ranged weaponry. You can learn more about your newfound art by using the Recall Teachings verb in the Sleeping Carp tab."
+ However, you are also unable to use any ranged weaponry. You can learn more about your newfound art by using the Recall Teachings verb in the Sleeping Carp tab." // FF edit. Original: ... while in Combat Mode. ...
icon = 'icons/obj/scrolls.dmi'
icon_state = "sleepingcarp"
worn_icon_state = "scroll"
diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm
index 5472f880755..45aed58c91c 100644
--- a/code/game/objects/items/handcuffs.dm
+++ b/code/game/objects/items/handcuffs.dm
@@ -380,7 +380,7 @@
. = ..()
update_appearance()
var/static/list/loc_connections = list(
- COMSIG_ATOM_ENTERED = PROC_REF(spring_trap),
+ COMSIG_ATOM_ENTERED = PROC_REF(trap_stepped_on),
)
AddElement(/datum/element/connect_loc, loc_connections)
@@ -412,10 +412,23 @@
update_appearance()
playsound(src, 'sound/effects/snap.ogg', 50, TRUE)
-/obj/item/restraints/legcuffs/beartrap/proc/spring_trap(datum/source, atom/movable/target, thrown_at = FALSE)
+/obj/item/restraints/legcuffs/beartrap/proc/trap_stepped_on(datum/source, atom/movable/entering, ...)
SIGNAL_HANDLER
+
+ spring_trap(entering)
+
+/**
+ * Tries to spring the trap on the target movable.
+ *
+ * This proc is safe to call without knowing if the target is valid or if the trap is armed.
+ *
+ * Does not trigger on tiny mobs.
+ * If ignore_movetypes is FALSE, does not trigger on floating / flying / etc. mobs.
+ */
+/obj/item/restraints/legcuffs/beartrap/proc/spring_trap(atom/movable/target, ignore_movetypes = FALSE)
if(!armed || !isturf(loc) || !isliving(target))
return
+
var/mob/living/victim = target
if(istype(victim.buckled, /obj/vehicle))
var/obj/vehicle/ridden_vehicle = victim.buckled
@@ -424,12 +437,14 @@
ridden_vehicle.visible_message(span_danger("[ridden_vehicle] triggers \the [src]."))
return
- //don't close the trap if they're as small as a mouse, or not touching the ground
- if(victim.mob_size <= MOB_SIZE_TINY || (!thrown_at && victim.movement_type & (FLYING|FLOATING)))
+ //don't close the trap if they're as small as a mouse
+ if(victim.mob_size <= MOB_SIZE_TINY)
+ return
+ if(!ignore_movetypes && (victim.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
return
close_trap()
- if(thrown_at)
+ if(ignore_movetypes)
victim.visible_message(span_danger("\The [src] ensnares [victim]!"), \
span_userdanger("\The [src] ensnares you!"))
else
@@ -477,7 +492,7 @@
qdel(src)
/obj/item/restraints/legcuffs/beartrap/energy/attack_hand(mob/user, list/modifiers)
- spring_trap(null, user)
+ spring_trap(user)
return ..()
/obj/item/restraints/legcuffs/beartrap/energy/cyborg
@@ -554,7 +569,7 @@
/obj/item/restraints/legcuffs/bola/energy/ensnare(atom/hit_atom)
var/obj/item/restraints/legcuffs/beartrap/energy/cyborg/B = new (get_turf(hit_atom))
- B.spring_trap(null, hit_atom, TRUE)
+ B.spring_trap(hit_atom, ignore_movetypes = TRUE)
qdel(src)
/**
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 37c95a418db..b029a099875 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -598,7 +598,6 @@
/obj/item/borg/upgrade/rped/action(mob/living/silicon/robot/R, user = usr)
. = ..()
if(.)
-
var/obj/item/storage/part_replacer/cyborg/RPED = locate() in R
if(RPED)
to_chat(user, span_warning("This unit is already equipped with a RPED module!"))
@@ -615,6 +614,38 @@
if (RPED)
R.model.remove_module(RPED, TRUE)
+/obj/item/borg/upgrade/inducer
+ name = "engineering integrated power inducer"
+ desc = "An integrated inducer that can charge a device's internal cell from power provided by the cyborg."
+ require_model = TRUE
+ model_type = list(/obj/item/robot_model/engineering, /obj/item/robot_model/saboteur)
+ model_flags = BORG_MODEL_ENGINEERING
+
+/obj/item/borg/upgrade/inducer/action(mob/living/silicon/robot/R, user = usr)
+ . = ..()
+ if(.)
+ var/obj/item/inducer/cyborg/inter_inducer = locate() in R
+ if(inter_inducer)
+ return FALSE
+ inter_inducer = new(R.model)
+ R.model.basic_modules += inter_inducer
+ R.model.add_module(inter_inducer, FALSE, TRUE)
+ inter_inducer.cell = R.cell
+
+/obj/item/borg/upgrade/inducer/deactivate(mob/living/silicon/robot/R, user = usr)
+ . = ..()
+ if (.)
+ var/obj/item/inducer/cyborg/inter_inducer = locate() in R.model
+ if (inter_inducer)
+ R.model.remove_module(inter_inducer, TRUE)
+ inter_inducer.cell = null
+
+/obj/item/inducer/cyborg
+ name = "Internal inducer"
+ powertransfer = 1500
+ icon = 'icons/obj/tools.dmi'
+ icon_state = "inducer-engi"
+
/obj/item/borg/upgrade/pinpointer
name = "medical cyborg crew pinpointer"
desc = "A crew pinpointer module for the medical cyborg. Permits remote access to the crew monitor."
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index e198ff6121d..7a180bc5dbd 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -388,7 +388,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
SIGNAL_HANDLER
if(isliving(AM))
var/mob/living/L = AM
- if(!(L.movement_type & (FLYING|FLOATING)) || L.buckled)
+ if(!(L.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || L.buckled)
playsound(src, 'sound/effects/footstep/glass_step.ogg', HAS_TRAIT(L, TRAIT_LIGHT_STEP) ? 30 : 50, TRUE)
/obj/item/shard/plasma
diff --git a/code/game/objects/items/storage/wallets.dm b/code/game/objects/items/storage/wallets.dm
index 7a75ba88674..7cb0afc2079 100644
--- a/code/game/objects/items/storage/wallets.dm
+++ b/code/game/objects/items/storage/wallets.dm
@@ -28,10 +28,14 @@
/obj/item/seeds,
/obj/item/stack/medical,
/obj/item/toy/crayon,
+ /obj/item/clothing/accessory/dogtag,
/obj/item/coin,
- /obj/item/food/chococoin,
+ /obj/item/coupon,
/obj/item/dice,
/obj/item/disk,
+ /obj/item/flashlight/pen,
+ /obj/item/folder/biscuit,
+ /obj/item/food/chococoin,
/obj/item/implanter,
/obj/item/laser_pointer,
/obj/item/lighter,
@@ -44,8 +48,11 @@
/obj/item/reagent_containers/syringe,
/obj/item/reagent_containers/pill,
/obj/item/screwdriver,
+ /obj/item/seeds,
/obj/item/spess_knife,
- /obj/item/stamp),
+ /obj/item/stack/medical,
+ /obj/item/stamp,
+ /obj/item/toy/crayon),
list(/obj/item/screwdriver/power))
/obj/item/storage/wallet/Exited(atom/movable/gone, direction)
diff --git a/code/game/objects/items/syndie_spraycan.dm b/code/game/objects/items/syndie_spraycan.dm
index 78ffb6a4772..deab6229a28 100644
--- a/code/game/objects/items/syndie_spraycan.dm
+++ b/code/game/objects/items/syndie_spraycan.dm
@@ -181,7 +181,7 @@
* * victim - whoever just slipped, point and laugh at them
*/
/obj/effect/decal/cleanable/traitor_rune/proc/slip(mob/living/victim)
- if(victim.movement_type & FLYING)
+ if(victim.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
return
if (!victim.slip(slip_time, src, slip_flags))
return
diff --git a/code/game/objects/items/tanks/watertank.dm b/code/game/objects/items/tanks/watertank.dm
index 558c287e1e5..29dcdbcf702 100644
--- a/code/game/objects/items/tanks/watertank.dm
+++ b/code/game/objects/items/tanks/watertank.dm
@@ -205,11 +205,6 @@
to_chat(user, span_notice("You [amount_per_transfer_from_this == 10 ? "remove" : "affix"] the nozzle. You'll now use [amount_per_transfer_from_this] units per spray."))
//ATMOS FIRE FIGHTING BACKPACK
-
-#define EXTINGUISHER 0
-#define RESIN_LAUNCHER 1
-#define RESIN_FOAM 2
-
/obj/item/watertank/atmos
name = "backpack firefighter tank"
desc = "A refrigerated and pressurized backpack tank with extinguisher nozzle, intended to fight fires. Swaps between extinguisher, resin launcher and a smaller scale resin foamer."
diff --git a/code/game/objects/structures/headpike.dm b/code/game/objects/structures/headpike.dm
index 7731af947f0..b48cafc85d9 100644
--- a/code/game/objects/structures/headpike.dm
+++ b/code/game/objects/structures/headpike.dm
@@ -47,11 +47,11 @@
. = ..()
if(!victim)
return
- var/mutable_appearance/MA = new()
- MA.copy_overlays(victim)
- MA.pixel_y = 12
- MA.pixel_x = pixel_x
- . += victim
+ var/mutable_appearance/appearance = new()
+ appearance.copy_overlays(victim)
+ appearance.pixel_y = 12
+ appearance.layer = layer + 0.1
+ . += appearance
/obj/structure/headpike/Exited(atom/movable/gone, direction)
. = ..()
diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm
index 1b2816d73db..fd4daaeaa18 100644
--- a/code/game/objects/structures/kitchen_spike.dm
+++ b/code/game/objects/structures/kitchen_spike.dm
@@ -125,6 +125,7 @@
m180.Turn(180)
animate(target, transform = m180, time = 3)
target.pixel_y = target.base_pixel_y + PIXEL_Y_OFFSET_LYING
+ ADD_TRAIT(target, TRAIT_MOVE_UPSIDE_DOWN, REF(src))
/obj/structure/kitchenspike/user_unbuckle_mob(mob/living/buckled_mob, mob/user)
if(buckled_mob != user)
@@ -156,6 +157,7 @@
m180.Turn(180)
animate(buckled_mob, transform = m180, time = 3)
buckled_mob.pixel_y = buckled_mob.base_pixel_y + PIXEL_Y_OFFSET_LYING
+ REMOVE_TRAIT(buckled_mob, TRAIT_MOVE_UPSIDE_DOWN, REF(src))
/obj/structure/kitchenspike/deconstruct(disassembled = TRUE)
if(disassembled)
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
index fa12148d330..5d3c4aee07c 100644
--- a/code/game/objects/structures/railings.dm
+++ b/code/game/objects/structures/railings.dm
@@ -121,7 +121,7 @@
/obj/structure/railing/CanPass(atom/movable/mover, border_dir)
. = ..()
if(border_dir & dir)
- return . || mover.throwing || mover.movement_type & (FLYING | FLOATING)
+ return . || mover.throwing || (mover.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
return TRUE
/obj/structure/railing/CanAStarPass(to_dir, datum/can_pass_info/pass_info)
@@ -144,7 +144,7 @@
if (leaving.throwing)
return
- if (leaving.movement_type & (PHASING | FLYING | FLOATING))
+ if (leaving.movement_type & (PHASING|MOVETYPES_NOT_TOUCHING_GROUND))
return
if (leaving.move_force >= MOVE_FORCE_EXTREMELY_STRONG)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index c80e2da85d6..78357a5dd4a 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -450,7 +450,7 @@
check_break(M)
/obj/structure/table/glass/proc/check_break(mob/living/M)
- if(M.has_gravity() && M.mob_size > MOB_SIZE_SMALL && !(M.movement_type & FLYING))
+ if(M.has_gravity() && M.mob_size > MOB_SIZE_SMALL && !(M.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
table_shatter(M)
/obj/structure/table/glass/proc/table_shatter(mob/living/victim)
diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm
index 657d94d5c5f..6b83427a7b2 100644
--- a/code/game/turfs/open/_open.dm
+++ b/code/game/turfs/open/_open.dm
@@ -285,7 +285,7 @@
return TRUE
/turf/open/handle_slip(mob/living/carbon/slipper, knockdown_amount, obj/slippable, lube, paralyze_amount, force_drop)
- if(slipper.movement_type & (FLYING | FLOATING))
+ if(slipper.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
return FALSE
if(!has_gravity(src))
return FALSE
@@ -446,4 +446,3 @@
playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE)
new /obj/structure/girder/tram(src)
-
diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm
index be6faca6ecd..cce5a49e993 100644
--- a/code/game/turfs/open/lava.dm
+++ b/code/game/turfs/open/lava.dm
@@ -246,7 +246,7 @@
. = TRUE
/turf/open/lava/proc/can_burn_stuff(atom/movable/burn_target)
- if(burn_target.movement_type & (FLYING|FLOATING)) //you're flying over it.
+ if(burn_target.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) //you're flying over it.
return LAVA_BE_IGNORING
if(isobj(burn_target))
@@ -265,7 +265,7 @@
var/mob/living/burn_living = burn_target
var/atom/movable/burn_buckled = burn_living.buckled
if(burn_buckled)
- if(burn_buckled.movement_type & (FLYING|FLOATING))
+ if(burn_buckled.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
return LAVA_BE_PROCESSING
if(isobj(burn_buckled))
var/obj/burn_buckled_obj = burn_buckled
diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm
index 36dd0416bde..8c574d207b2 100644
--- a/code/modules/admin/create_mob.dm
+++ b/code/modules/admin/create_mob.dm
@@ -10,36 +10,28 @@
user << browse(create_panel_helper(create_mob_html), "window=create_mob;size=425x475")
-/proc/randomize_human(mob/living/carbon/human/human)
- if(human.dna.species.sexes)
- human.gender = pick(MALE, FEMALE, PLURAL, NEUTER)
- else
- human.gender = PLURAL
+/**
+ * Randomizes everything about a human, including DNA and name
+ */
+/proc/randomize_human(mob/living/carbon/human/human, randomize_mutations = FALSE)
+ human.gender = human.dna.species.sexes ? pick(MALE, FEMALE, PLURAL, NEUTER) : PLURAL
human.physique = human.gender
human.real_name = human.dna?.species.random_name(human.gender) || random_unique_name(human.gender)
- human.name = human.real_name
- human.hairstyle = random_hairstyle(human.gender)
- human.facial_hairstyle = random_facial_hairstyle(human.gender)
- human.hair_color = "#[random_color()]"
- human.facial_hair_color = human.hair_color
- var/random_eye_color = random_eye_color()
- human.eye_color_left = random_eye_color
- human.eye_color_right = random_eye_color
- human.dna.blood_type = random_blood_type()
- human.dna.features["mcolor"] = "#[random_color()]"
+ human.name = human.get_visible_name()
+ human.set_hairstyle(random_hairstyle(human.gender), update = FALSE)
+ human.set_facial_hairstyle(random_facial_hairstyle(human.gender), update = FALSE)
+ human.set_haircolor("#[random_color()]", update = FALSE)
+ human.set_facial_haircolor(human.hair_color, update = FALSE)
+ human.eye_color_left = random_eye_color()
+ human.eye_color_right = human.eye_color_left
+ human.skin_tone = random_skin_tone()
human.dna.species.randomize_active_underwear_only(human)
- /*SKYRAT EDIT OLD
- for(var/datum/species/species_path as anything in subtypesof(/datum/species))
- var/datum/species/new_species = new species_path
- new_species.randomize_features(human)
- SKYRAT EDIT ADDITION BEGIN - CUSTOMIZATION*/
- human.dna.species.randomize_features(human)
- human.dna.mutant_bodyparts = human.dna.species.get_random_mutant_bodyparts(human.dna.features)
- human.dna.body_markings = human.dna.species.get_random_body_markings(human.dna.features)
+ // Needs to be called towards the end to update all the UIs just set above
+ human.dna.initialize_dna(newblood_type = random_blood_type(), create_mutation_blocks = randomize_mutations, randomize_features = TRUE)
+ // SKYRAT EDIT ADDITION BEGIN - CUSTOMIZATION
human.dna.species.mutant_bodyparts = human.dna.mutant_bodyparts.Copy()
human.dna.species.body_markings = human.dna.body_markings.Copy()
- //SKYRAT EDIT ADDITION END
+ // SKYRAT EDIT ADDITION END
+ // Snowflake stuff (ethereals)
human.dna.species.spec_updatehealth(human)
- human.dna.update_dna_identity()
- human.updateappearance()
- human.update_body(is_creating = TRUE)
+ human.updateappearance(mutcolor_update = TRUE)
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index 808117057fd..e909fdfc0ad 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -940,11 +940,13 @@
attempted_fake_scar.fake = TRUE
user.regenerate_icons()
-
+ user.name = user.get_visible_name()
+ current_profile = chosen_profile
// SKYRAT EDIT START
chosen_dna.transfer_identity(user, TRUE)
user.updateappearance(mutcolor_update = TRUE, eyeorgancolor_update = TRUE)
user.regenerate_icons()
+ user.name = user.get_visible_name()
current_profile = chosen_profile
// SKYRAT EDIT END
//THE FLUFFY FRONTIER EDIT ADDITION BEGIN - Blooper
diff --git a/code/modules/antagonists/changeling/powers/tiny_prick.dm b/code/modules/antagonists/changeling/powers/tiny_prick.dm
index eb6ccccd8b5..f8c1bfc5160 100644
--- a/code/modules/antagonists/changeling/powers/tiny_prick.dm
+++ b/code/modules/antagonists/changeling/powers/tiny_prick.dm
@@ -66,38 +66,61 @@
to_chat(target, span_warning("You feel a tiny prick."))
return 1
-//SKYRAT EDIT REMOVAL BEGIN - CHANGELING_TRANSFORMATION_REMOVAL
-/*
/datum/action/changeling/sting/transformation
name = "Transformation Sting"
- desc = "We silently sting a human, injecting a retrovirus that forces them to transform. Costs 50 chemicals."
- helptext = "The victim will transform much like a changeling would. Does not provide a warning to others. Mutations will not be transferred, and monkeys will become human."
+ desc = "We silently sting an organism, injecting a retrovirus that forces them to transform."
+ helptext = "The victim will transform much like a changeling would. \
+ For complex humanoids, the transformation is temporarily, but the duration is paused while the victim is dead or in stasis. \
+ For more simple humanoids, such as monkeys, the transformation is permanent. \
+ Does not provide a warning to others. Mutations will not be transferred."
button_icon_state = "sting_transform"
- chemical_cost = 50
- dna_cost = 3
- var/datum/changeling_profile/selected_dna = null
+ chemical_cost = 33 // Low enough that you can sting only two people in quick succession
+ dna_cost = 2
+ /// A reference to our active profile, which we grab DNA from
+ VAR_FINAL/datum/changeling_profile/selected_dna
+ /// Duration of the sting
+ var/sting_duration = 8 MINUTES
+
+/datum/action/changeling/sting/transformation/Grant(mob/grant_to)
+ . = ..()
+ build_all_button_icons(UPDATE_BUTTON_NAME)
-/datum/action/changeling/sting/transformation/Trigger(trigger_flags)
- var/mob/user = usr
+/datum/action/changeling/sting/transformation/update_button_name(atom/movable/screen/movable/action_button/button, force)
+ . = ..()
+ button.desc += " Lasts [DisplayTimeText(sting_duration)] for humans, but duration is paused while dead or in stasis."
+ button.desc += " Costs [chemical_cost] chemicals."
+
+/datum/action/changeling/sting/transformation/Destroy()
+ selected_dna = null
+ return ..()
+
+/datum/action/changeling/sting/transformation/set_sting(mob/user)
+ selected_dna = null
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
- if(changeling.chosen_sting)
- unset_sting(user)
+ var/datum/changeling_profile/new_selected_dna = changeling.select_dna()
+ if(QDELETED(src) || QDELETED(changeling) || QDELETED(user))
return
- selected_dna = changeling.select_dna()
- if(!selected_dna)
- return
- if(HAS_TRAIT(user, TRAIT_NO_TRANSFORMATION_STING))
- user.balloon_alert(user, "incompatible DNA!")
+ if(!new_selected_dna || changeling.chosen_sting || selected_dna) // selected other sting or other DNA while sleeping
return
+ selected_dna = new_selected_dna
return ..()
/datum/action/changeling/sting/transformation/can_sting(mob/user, mob/living/carbon/target)
. = ..()
if(!.)
return
- if(!iscarbon(target) || HAS_TRAIT(target, TRAIT_HUSK) || HAS_TRAIT(target, TRAIT_NO_TRANSFORMATION_STING))
+ // Similar checks here are ran to that of changeling can_absorb_dna -
+ // Logic being that if their DNA is incompatible with us, it's also bad for transforming
+ if(!iscarbon(target) \
+ || !target.has_dna() \
+ || HAS_TRAIT(target, TRAIT_HUSK) \
+ || HAS_TRAIT(target, TRAIT_BADDNA) \
+ || (HAS_TRAIT(target, TRAIT_NO_DNA_COPY) && !ismonkey(target))) // sure, go ahead, make a monk-clone
user.balloon_alert(user, "incompatible DNA!")
return FALSE
+ if(target.has_status_effect(/datum/status_effect/temporary_transformation/trans_sting))
+ user.balloon_alert(user, "already transformed!")
+ return FALSE
return TRUE
/datum/action/changeling/sting/transformation/sting_action(mob/living/user, mob/living/target)
@@ -113,8 +136,6 @@
to_chat(user, final_message)
return TRUE
return FALSE
-*/
-//SKYRAT EDIT REMOVAL END
/datum/action/changeling/sting/false_armblade
name = "False Armblade Sting"
diff --git a/code/modules/antagonists/nightmare/nightmare_species.dm b/code/modules/antagonists/nightmare/nightmare_species.dm
index 2db4ca2bb2c..068bb2b6c5c 100644
--- a/code/modules/antagonists/nightmare/nightmare_species.dm
+++ b/code/modules/antagonists/nightmare/nightmare_species.dm
@@ -20,7 +20,6 @@
TRAIT_NOHUNGER,
TRAIT_NOBLOOD,
TRAIT_NO_DNA_COPY,
- TRAIT_NO_TRANSFORMATION_STING,
TRAIT_NODISMEMBER,
TRAIT_NEVER_WOUNDED,
)
diff --git a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
index 3ea6488b2d4..7521ff15549 100644
--- a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
+++ b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
@@ -404,10 +404,10 @@
/datum/export/pirate/parrot
cost = 2000
unit_name = "alive parrot"
- export_types = list(/mob/living/simple_animal/parrot)
+ export_types = list(/mob/living/basic/parrot)
/datum/export/pirate/parrot/find_loot()
- for(var/mob/living/simple_animal/parrot/current_parrot in GLOB.alive_mob_list)
+ for(var/mob/living/basic/parrot/current_parrot in GLOB.alive_mob_list)
var/turf/parrot_turf = get_turf(current_parrot)
if(parrot_turf && is_station_level(parrot_turf.z))
return current_parrot
diff --git a/code/modules/antagonists/traitor/objectives/kill_pet.dm b/code/modules/antagonists/traitor/objectives/kill_pet.dm
index 8ea89cb44d0..01ab042f11b 100644
--- a/code/modules/antagonists/traitor/objectives/kill_pet.dm
+++ b/code/modules/antagonists/traitor/objectives/kill_pet.dm
@@ -24,7 +24,7 @@
),
JOB_CAPTAIN = /mob/living/basic/pet/fox/renault,
JOB_CHIEF_MEDICAL_OFFICER = /mob/living/simple_animal/pet/cat/runtime,
- JOB_CHIEF_ENGINEER = /mob/living/simple_animal/parrot/poly,
+ JOB_CHIEF_ENGINEER = /mob/living/basic/parrot/poly,
JOB_QUARTERMASTER = list(
/mob/living/basic/gorilla/cargorilla,
/mob/living/basic/sloth/citrus,
diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm
index e706c6f7598..1f760e29b89 100644
--- a/code/modules/assembly/mousetrap.dm
+++ b/code/modules/assembly/mousetrap.dm
@@ -189,7 +189,7 @@
if(armed)
if(ismob(AM))
var/mob/MM = AM
- if(!(MM.movement_type & FLYING))
+ if(!(MM.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
if(ishuman(AM))
var/mob/living/carbon/H = AM
if(H.move_intent == MOVE_INTENT_RUN)
diff --git a/code/modules/bitrunning/components/netpod_healing.dm b/code/modules/bitrunning/components/netpod_healing.dm
index 8cb363517de..98fdb5ba021 100644
--- a/code/modules/bitrunning/components/netpod_healing.dm
+++ b/code/modules/bitrunning/components/netpod_healing.dm
@@ -49,6 +49,13 @@
id = "embryonic"
alert_type = /atom/movable/screen/alert/status_effect/embryonic
+/datum/status_effect/embryonic/on_apply()
+ ADD_TRAIT(owner, TRAIT_STASIS, TRAIT_STATUS_EFFECT(id))
+ return TRUE
+
+/datum/status_effect/embryonic/on_remove()
+ REMOVE_TRAIT(owner, TRAIT_STASIS, TRAIT_STATUS_EFFECT(id))
+
/atom/movable/screen/alert/status_effect/embryonic
name = "Embryonic Stasis"
icon_state = "netpod_stasis"
diff --git a/code/modules/cargo/markets/market_items/weapons.dm b/code/modules/cargo/markets/market_items/weapons.dm
index 6115b1e1557..ee16daae7d5 100644
--- a/code/modules/cargo/markets/market_items/weapons.dm
+++ b/code/modules/cargo/markets/market_items/weapons.dm
@@ -72,4 +72,4 @@
price_min = CARGO_CRATE_VALUE * 2
price_max = CARGO_CRATE_VALUE * 4
stock_max = 1
- availability_prob = 50
+ availability_prob = 75
diff --git a/code/modules/cargo/packs/livestock.dm b/code/modules/cargo/packs/livestock.dm
index ef9fb961823..942b1414cf9 100644
--- a/code/modules/cargo/packs/livestock.dm
+++ b/code/modules/cargo/packs/livestock.dm
@@ -6,13 +6,13 @@
name = "Bird Crate"
desc = "Contains five expert telecommunication birds."
cost = CARGO_CRATE_VALUE * 8
- contains = list(/mob/living/simple_animal/parrot)
+ contains = list(/mob/living/basic/parrot)
crate_name = "parrot crate"
/datum/supply_pack/critter/parrot/generate()
. = ..()
for(var/i in 1 to 4)
- new /mob/living/simple_animal/parrot(.)
+ new /mob/living/basic/parrot(.)
/datum/supply_pack/critter/butterfly
name = "Butterflies Crate"
diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm
index 32e2a567f63..a176a84dfa4 100644
--- a/code/modules/clothing/glasses/_glasses.dm
+++ b/code/modules/clothing/glasses/_glasses.dm
@@ -296,7 +296,7 @@
return
if(isliving(movable))
var/mob/living/crusher = movable
- if(crusher.move_intent != MOVE_INTENT_WALK && (!(crusher.movement_type & (FLYING|FLOATING)) || crusher.buckled))
+ if(crusher.move_intent != MOVE_INTENT_WALK && (!(crusher.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || crusher.buckled))
playsound(src, 'sound/effects/footstep/glass_step.ogg', 30, TRUE)
visible_message(span_warning("[crusher] steps on [src], damaging it!"))
take_damage(100, sound_effect = FALSE)
diff --git a/code/modules/events/ghost_role/sentience.dm b/code/modules/events/ghost_role/sentience.dm
index abc57d33a07..f33333ddb1b 100644
--- a/code/modules/events/ghost_role/sentience.dm
+++ b/code/modules/events/ghost_role/sentience.dm
@@ -7,6 +7,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list(
/mob/living/basic/goat,
/mob/living/basic/lizard,
/mob/living/basic/mouse/brown/tom,
+ /mob/living/basic/parrot,
/mob/living/basic/pet,
/mob/living/basic/pig,
/mob/living/basic/rabbit,
@@ -16,7 +17,6 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list(
/mob/living/basic/spider/giant/sgt_araneus,
/mob/living/simple_animal/bot/secbot/beepsky,
/mob/living/simple_animal/hostile/retaliate/goose/vomit,
- /mob/living/simple_animal/parrot,
/mob/living/simple_animal/pet,
)))
diff --git a/code/modules/events/holiday/halloween.dm b/code/modules/events/holiday/halloween.dm
index d52dd1b9c50..8eb29fa706c 100644
--- a/code/modules/events/holiday/halloween.dm
+++ b/code/modules/events/holiday/halloween.dm
@@ -18,9 +18,9 @@
for(var/mob/living/basic/pet/dog/corgi/ian/Ian in GLOB.mob_living_list)
Ian.place_on_head(new /obj/item/bedsheet(Ian))
- for(var/mob/living/simple_animal/parrot/poly/Poly in GLOB.mob_living_list)
- new /mob/living/simple_animal/parrot/poly/ghost(Poly.loc)
- qdel(Poly)
+ for(var/mob/living/basic/parrot/poly/bird in GLOB.mob_living_list)
+ new /mob/living/basic/parrot/poly/ghost(bird.loc)
+ qdel(bird)
/datum/round_event/spooky/announce(fake)
priority_announce(pick("RATTLE ME BONES!","THE RIDE NEVER ENDS!", "A SKELETON POPS OUT!", "SPOOKY SCARY SKELETONS!", "CREWMEMBERS BEWARE, YOU'RE IN FOR A SCARE!") , "THE CALL IS COMING FROM INSIDE THE HOUSE")
diff --git a/code/modules/events/wizard/petsplosion.dm b/code/modules/events/wizard/petsplosion.dm
index a9664d8dd47..282dae5d33c 100644
--- a/code/modules/events/wizard/petsplosion.dm
+++ b/code/modules/events/wizard/petsplosion.dm
@@ -9,6 +9,7 @@ GLOBAL_LIST_INIT(petsplosion_candidates, typecacheof(list(
/mob/living/basic/lizard,
/mob/living/basic/mothroach,
/mob/living/basic/mouse/brown/tom,
+ /mob/living/basic/parrot,
/mob/living/basic/pet,
/mob/living/basic/pig,
/mob/living/basic/rabbit,
@@ -17,7 +18,6 @@ GLOBAL_LIST_INIT(petsplosion_candidates, typecacheof(list(
/mob/living/basic/snake,
/mob/living/basic/spider/giant/sgt_araneus,
/mob/living/simple_animal/hostile/retaliate/goose/vomit,
- /mob/living/simple_animal/parrot,
/mob/living/simple_animal/pet,
)))
diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm
index 359c3edf4ea..6494f4252e5 100644
--- a/code/modules/mapping/mapping_helpers.dm
+++ b/code/modules/mapping/mapping_helpers.dm
@@ -917,7 +917,6 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
if(new_human_species)
new_human.set_species(new_human_species)
new_human_species = new_human.dna.species
- new_human_species.randomize_features(new_human)
new_human.fully_replace_character_name(new_human.real_name, new_human_species.random_name(new_human.gender, TRUE, TRUE))
else
stack_trace("failed to spawn cadaver with species ID [species_to_pick]") //if it's invalid they'll just be a human, so no need to worry too much aside from yelling at the server owner lol.
diff --git a/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_abilities.dm b/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_abilities.dm
index 6e8ff992891..d82812b62a3 100644
--- a/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_abilities.dm
+++ b/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_abilities.dm
@@ -22,7 +22,7 @@
if(!iscarbon(target) || blocked >= 100)
return
var/obj/item/restraints/legcuffs/beartrap/mega_arachnid/restraint = new(get_turf(target))
- restraint.spring_trap(null, target)
+ restraint.spring_trap(target)
/obj/item/restraints/legcuffs/beartrap/mega_arachnid
name = "fleshy restraints"
diff --git a/code/modules/mob/living/basic/pets/parrot/_parrot.dm b/code/modules/mob/living/basic/pets/parrot/_parrot.dm
new file mode 100644
index 00000000000..42aa0884816
--- /dev/null
+++ b/code/modules/mob/living/basic/pets/parrot/_parrot.dm
@@ -0,0 +1,458 @@
+#define FORCED_SPEECH_COOLDOWN_DURATION 5 SECONDS
+
+GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
+ /datum/strippable_item/parrot_headset,
+)))
+
+
+/// Parrots! Klepto bastards that imitate your speech and hoard your shit.
+/mob/living/basic/parrot
+ name = "parrot"
+ desc = "The parrot squawks, \"They're a Parrot! BAWWK!\""
+ icon = 'icons/mob/simple/animal.dmi'
+ icon_state = "parrot_fly"
+ icon_living = "parrot_fly"
+ icon_dead = "parrot_dead"
+ density = FALSE
+ health = 80
+ maxHealth = 80
+ pass_flags = PASSTABLE | PASSMOB
+
+ guaranteed_butcher_results = list(/obj/item/food/cracker = 1)
+ melee_damage_upper = 10
+ melee_damage_lower = 5
+
+ response_help_continuous = "pets"
+ response_help_simple = "pet"
+ response_disarm_continuous = "gently moves aside"
+ response_disarm_simple = "gently move aside"
+ response_harm_continuous = "swats"
+ response_harm_simple = "swat"
+ combat_mode = TRUE //parrots now start "aggressive" since only player parrots will nuzzle.
+ attack_verb_continuous = "chomps"
+ attack_verb_simple = "chomp"
+ attack_vis_effect = ATTACK_EFFECT_BITE
+ friendly_verb_continuous = "grooms"
+ friendly_verb_simple = "groom"
+ mob_size = MOB_SIZE_SMALL
+ gold_core_spawnable = FRIENDLY_SPAWN
+
+ ai_controller = /datum/ai_controller/basic_controller/parrot
+
+ /// Icon we use while sitting
+ var/icon_sit = "parrot_sit"
+
+ ///Headset for Poly to yell at engineers :)
+ var/obj/item/radio/headset/ears = null
+
+ ///Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding.
+ var/obj/item/held_item = null
+
+ /// The blackboard key we use to store the string we're repeating
+ var/speech_blackboard_key = BB_PARROT_REPEAT_STRING
+ /// The generic probability odds we have to do a speech-related action
+ var/speech_probability_rate = 5
+ /// The generic probability odds we have to switch out our speech string
+ var/speech_shuffle_rate = 30
+
+ /// Contains all of the perches that parrots will generally sit on until something catches their eye.
+ var/static/list/desired_perches = typecacheof(list(
+ /obj/machinery/computer,
+ /obj/machinery/dna_scannernew,
+ /obj/machinery/nuclearbomb,
+ /obj/machinery/recharge_station,
+ /obj/machinery/smartfridge,
+ /obj/machinery/suit_storage_unit,
+ /obj/machinery/telecomms,
+ /obj/machinery/teleport,
+ /obj/structure/displaycase,
+ /obj/structure/filingcabinet,
+ /obj/structure/frame/computer,
+ ))
+ ///items we wont pick up
+ var/static/list/ignore_items = typecacheof(list(/obj/item/radio))
+
+ /// Food that Poly loves to eat (spoiler alert it's just crackers)
+ var/static/list/edibles = list(
+ /obj/item/food/cracker,
+ )
+
+ /// Tracks the times when we send a phrase through either being pet or attack to ensure it's not abused to flood
+ COOLDOWN_DECLARE(forced_speech_cooldown)
+
+/mob/living/basic/parrot/Initialize(mapload)
+ . = ..()
+ setup_headset()
+ update_speech_blackboards()
+ ai_controller.set_blackboard_key(BB_PARROT_PERCH_TYPES, desired_perches)
+ ai_controller.set_blackboard_key(BB_IGNORE_ITEMS, ignore_items)
+ AddElement(/datum/element/pet_bonus) // parrots will listen for the signal this element sends in order to say something in response to the pat
+ AddElement(/datum/element/ai_retaliate)
+ AddElement(/datum/element/strippable, GLOB.strippable_parrot_items)
+ AddElement(/datum/element/simple_flying)
+ AddComponent(/datum/component/listen_and_repeat, desired_phrases = get_static_list_of_phrases(), blackboard_key = BB_PARROT_REPEAT_STRING)
+ AddComponent(\
+ /datum/component/tameable,\
+ food_types = edibles,\
+ tame_chance = 100,\
+ bonus_tame_chance = 0,\
+ after_tame = CALLBACK(src, PROC_REF(tamed)),\
+ )
+
+ RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attacking))
+ RegisterSignal(src, COMSIG_MOB_CLICKON, PROC_REF(on_click))
+ RegisterSignal(src, COMSIG_ATOM_ATTACKBY_SECONDARY, PROC_REF(on_attacked)) // this means we could have a peaceful interaction, like getting a cracker
+ RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_injured)) // this means we got hurt and it's go time
+ RegisterSignal(src, COMSIG_ANIMAL_PET, PROC_REF(on_pet))
+ RegisterSignal(src, COMSIG_KB_MOB_DROPITEM_DOWN, PROC_REF(drop_item_on_signal))
+
+/mob/living/basic/parrot/Destroy()
+ // should have cleaned these up on death, but let's be super safe in case that didn't happen
+ if(!QDELETED(ears))
+ QDEL_NULL(ears)
+ if(!QDELETED(held_item))
+ QDEL_NULL(held_item)
+ return ..()
+
+/mob/living/basic/parrot/death(gibbed)
+ if(held_item)
+ held_item.forceMove(drop_location())
+ held_item = null
+
+ if(ears)
+ ears.forceMove(drop_location())
+ ears = null
+
+ if(!isnull(buckled))
+ buckled.unbuckle_mob(src, force = TRUE)
+ buckled = null
+ pixel_x = base_pixel_x
+ pixel_y = base_pixel_y
+
+ return ..()
+
+/mob/living/basic/parrot/examine(mob/user)
+ . = ..()
+ . += "It appears to [isnull(held_item) ? "not be holding anything." : "be holding \a [held_item]."]"
+
+ if(stat != DEAD)
+ return
+
+ if(HAS_MIND_TRAIT(user, TRAIT_NAIVE))
+ . += pick(
+ "It seems tired and shagged out after a long squawk.",
+ "It seems to be pining for the fjords.",
+ "It's resting. It's a beautiful bird. Lovely plumage.",
+ )
+ else
+ . += pick(
+ "This is a late parrot.",
+ "This is an ex-parrot.",
+ "This parrot is no more.",
+ )
+
+/mob/living/basic/parrot/say_dead(message)
+ return // this is so flarped
+
+/mob/living/basic/parrot/get_status_tab_items()
+ . = ..()
+ . += "Held Item: [held_item]"
+
+/mob/living/basic/parrot/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
+ if(stat != DEAD) // parrots have evolved to let them fly in space because fucking uhhhhhhhhhh
+ return TRUE
+ return ..()
+
+/mob/living/basic/parrot/radio(message, list/message_mods = list(), list/spans, language) //literally copied from human/radio(), but there's no other way to do this. at least it's better than it used to be.
+ . = ..()
+ if(. != NONE)
+ return
+
+ if(message_mods[MODE_HEADSET])
+ if(ears)
+ ears.talk_into(src, message, , spans, language, message_mods)
+ return ITALICS | REDUCE_RANGE
+ else if(message_mods[RADIO_EXTENSION] == MODE_DEPARTMENT)
+ if(ears)
+ ears.talk_into(src, message, message_mods[RADIO_EXTENSION], spans, language, message_mods)
+ return ITALICS | REDUCE_RANGE
+ else if(message_mods[RADIO_EXTENSION] in GLOB.radiochannels)
+ if(ears)
+ ears.talk_into(src, message, message_mods[RADIO_EXTENSION], spans, language, message_mods)
+ return ITALICS | REDUCE_RANGE
+
+ return NONE
+
+/mob/living/basic/parrot/update_icon_state()
+ . = ..()
+ if(HAS_TRAIT(src, TRAIT_PARROT_PERCHED))
+ icon_state = icon_sit
+ else
+ icon_state = icon_living
+
+/// Proc that we just use to see if we're rightclicking something for perch behavior or dropping the item we currently ahve
+/mob/living/basic/parrot/proc/on_click(mob/living/basic/source, atom/target, params)
+ SIGNAL_HANDLER
+ if(!LAZYACCESS(params, RIGHT_CLICK) || !CanReach(target))
+ return
+ if(start_perching(target) && !isnull(held_item))
+ drop_held_item(gently = TRUE)
+
+/// Proc that handles sending the signal and returning a valid phrase to say. Will not do anything if we don't have a stat or if we're cliented.
+/// Will return either a string or null.
+/mob/living/basic/parrot/proc/get_phrase()
+ if(!isnull(client) || stat != CONSCIOUS)
+ return null
+
+ if(!COOLDOWN_FINISHED(src, forced_speech_cooldown))
+ return null
+
+ var/return_value = SEND_SIGNAL(src, COMSIG_NEEDS_NEW_PHRASE)
+ if(return_value & NO_NEW_PHRASE_AVAILABLE)
+ return null
+
+ COOLDOWN_START(src, forced_speech_cooldown, FORCED_SPEECH_COOLDOWN_DURATION)
+ return ai_controller.blackboard[BB_PARROT_REPEAT_STRING]
+
+/// Proc that listens for when a parrot is pet so we can dispatch a voice line.
+/mob/living/basic/parrot/proc/on_pet(mob/living/basic/source, mob/living/petter, modifiers)
+ SIGNAL_HANDLER
+ var/return_value = get_phrase()
+ if(isnull(return_value))
+ return
+
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/atom/movable, say), message = return_value, forced = "parrot oneliner on pet")
+
+/// Proc that ascertains the type of perch we're dealing with and starts the perching process.
+/// Returns TRUE if we started perching, FALSE otherwise.
+/mob/living/basic/parrot/proc/start_perching(atom/target)
+ if(HAS_TRAIT(src, TRAIT_PARROT_PERCHED))
+ balloon_alert(src, "already perched!")
+ return FALSE
+
+ if(ishuman(target))
+ return perch_on_human(target)
+
+ if(!is_type_in_typecache(target, desired_perches))
+ return FALSE
+
+ forceMove(get_turf(target))
+ drop_held_item(gently = TRUE) // comfy :)
+ toggle_perched(perched = TRUE)
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(after_move))
+ return TRUE
+
+/mob/living/basic/parrot/proc/after_move(atom/source)
+ SIGNAL_HANDLER
+
+ UnregisterSignal(src, COMSIG_MOVABLE_MOVED)
+ toggle_perched(perched = FALSE)
+
+/// Proc that will perch us on a human. Returns TRUE if we perched, FALSE otherwise.
+/mob/living/basic/parrot/proc/perch_on_human(mob/living/carbon/human/target)
+ if(LAZYLEN(target.buckled_mobs) >= target.max_buckled_mobs)
+ balloon_alert(src, "can't perch on them!")
+ return FALSE
+
+ forceMove(get_turf(target))
+ if(!target.buckle_mob(src, TRUE))
+ return FALSE
+
+ to_chat(src, span_notice("You sit on [target]'s shoulder."))
+ toggle_perched(perched = TRUE)
+ RegisterSignal(src, COMSIG_LIVING_SET_BUCKLED, PROC_REF(on_unbuckle))
+ return TRUE
+
+/mob/living/basic/parrot/proc/on_unbuckle(mob/living/source, atom/movable/new_buckled)
+ SIGNAL_HANDLER
+
+ if(new_buckled)
+ return
+ UnregisterSignal(src, COMSIG_LIVING_SET_BUCKLED)
+ toggle_perched(perched = FALSE)
+
+/mob/living/basic/parrot/proc/toggle_perched(perched)
+ if(!perched)
+ REMOVE_TRAIT(src, TRAIT_PARROT_PERCHED, TRAIT_GENERIC)
+ else
+ ADD_TRAIT(src, TRAIT_PARROT_PERCHED, TRAIT_GENERIC)
+ update_appearance(UPDATE_ICON_STATE)
+
+/// Master proc which will determine the intent of OUR attacks on an object and summon the relevant procs accordingly.
+/// This is pretty much meant for players, AI will use the task-specific procs instead.
+/mob/living/basic/parrot/proc/pre_attacking(mob/living/basic/source, atom/target)
+ SIGNAL_HANDLER
+ if(stat != CONSCIOUS)
+ return
+
+ if(isitem(target) && steal_from_ground(target))
+ return COMPONENT_HOSTILE_NO_ATTACK
+
+ if(iscarbon(target) && steal_from_mob(target))
+ return COMPONENT_HOSTILE_NO_ATTACK
+
+/// Picks up an item from the ground and puts it in our claws. Returns TRUE if we picked it up, FALSE otherwise.
+/mob/living/basic/parrot/proc/steal_from_ground(obj/item/target)
+ if(!isnull(held_item))
+ balloon_alert(src, "already holding something!")
+ return FALSE
+
+ if(target.w_class > WEIGHT_CLASS_SMALL)
+ balloon_alert(src, "too big to pick up!")
+ return FALSE
+
+ pick_up_item(target)
+ visible_message(
+ span_notice("[src] grabs [held_item]!"),
+ span_notice("You grab [held_item]!"),
+ span_hear("You hear the sounds of wings flapping furiously."),
+ )
+ return TRUE
+
+/// Looks for an item that we can snatch and puts it in our claws. Returns TRUE if we picked it up, FALSE otherwise.
+/mob/living/basic/parrot/proc/steal_from_mob(mob/living/carbon/victim)
+ if(!isnull(held_item))
+ balloon_alert(src, "already holding something!")
+ return FALSE
+
+ for(var/obj/item/stealable in victim.held_items)
+ if(stealable.w_class > WEIGHT_CLASS_SMALL)
+ continue
+
+ if(!victim.temporarilyRemoveItemFromInventory(stealable))
+ continue
+
+ visible_message(
+ span_notice("[src] grabs [held_item] out of [victim]'s hand!"),
+ span_notice("You snag [held_item] out of [victim]'s hand!"),
+ span_hear("You hear the sounds of wings flapping furiously."),
+ )
+ pick_up_item(stealable)
+ return TRUE
+
+ return FALSE
+
+/// If we're right-clicked on with a cracker, we eat the cracker.
+/mob/living/basic/parrot/proc/on_attacked(mob/living/basic/source, obj/item/thing, mob/living/attacker, params)
+ SIGNAL_HANDLER
+ if(!istype(thing, /obj/item/food/cracker)) // Poly wants a cracker
+ return
+
+ consume_cracker(thing) // potential clash with the tameable element so we'll leave it to that to handle qdeling the cracker.
+ return COMPONENT_NO_AFTERATTACK
+
+/// Eats a cracker (or anything i guess). This would be nice to eventually fold into the basic_eating element but we do too much snowflake inventory code stuff for this to be reliable presently.
+/// We don't qdel the item here, we assume the invoking proc will have handled that somehow.
+/// Returns TRUE if we ate the thing.
+/mob/living/basic/parrot/proc/consume_cracker(obj/item/thing)
+ to_chat(src, span_notice("[src] eagerly devours \the [thing]."))
+ if(!istype(thing, /obj/item/food/cracker))
+ return TRUE // we still ate it
+
+ if(health < maxHealth)
+ adjustBruteLoss(-10)
+ speech_probability_rate *= 1.27
+ speech_shuffle_rate += 10
+ update_speech_blackboards()
+ return TRUE
+
+/// Handles special behavior whenever we are injured.
+/mob/living/basic/parrot/proc/on_injured(mob/living/basic/source, mob/living/attacker, attack_flags)
+ SIGNAL_HANDLER
+ if(!isnull(client) || stat == CONSCIOUS)
+ return
+
+ drop_held_item(gently = FALSE)
+
+ var/return_value = get_phrase()
+ if(isnull(return_value))
+ return
+
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/atom/movable, say), message = return_value, forced = "parrot oneliner on attack")
+
+/// Handles picking up the item we're holding, done in its own proc because of a snowflake edge case we need to account for. No additional logic beyond that.
+/// Returns TRUE if we picked it up, FALSE otherwise.
+/mob/living/basic/parrot/proc/pick_up_item(obj/item/target)
+ if(istype(target, /obj/item/food/cracker))
+ consume_cracker(target)
+ qdel(target)
+ return
+
+ target.forceMove(src)
+ held_item = target
+
+/// Handles dropping items we're holding. Gently is a special modifier we can use for special interactions.
+/mob/living/basic/parrot/proc/drop_held_item(gently = TRUE)
+ if(isnull(held_item))
+ balloon_alert(src, "nothing to drop!")
+ return
+
+ if(stat != CONSCIOUS) // don't gotta do shit
+ return
+
+ if(!gently && isgrenade(held_item))
+ var/obj/item/grenade/bomb = held_item
+ balloon_alert(src, "bombs away!") // you'll likely die too so we can get away with the `!` here
+ bomb.forceMove(drop_location())
+ bomb.detonate()
+ return
+
+ balloon_alert(src, "dropped item")
+ held_item.forceMove(drop_location())
+
+/mob/living/basic/parrot/Exited(atom/movable/gone, direction)
+ . = ..()
+ if(gone != held_item)
+ return
+ held_item = null
+
+/mob/living/basic/parrot/vv_edit_var(var_name, vval)
+ . = ..() // give admins an easier time when it comes to fucking with poly
+ switch(var_name)
+ if(NAMEOF(src, speech_probability_rate))
+ update_speech_blackboards()
+ if(NAMEOF(src, speech_shuffle_rate))
+ update_speech_blackboards()
+
+/// Updates our speech blackboards mob-side to reflect the current speech on the controller to ensure everything is synchronized.
+/mob/living/basic/parrot/proc/update_speech_blackboards()
+ ai_controller.set_blackboard_key(BB_PARROT_REPEAT_PROBABILITY, speech_probability_rate)
+ ai_controller.set_blackboard_key(BB_PARROT_PHRASE_CHANGE_PROBABILITY, speech_shuffle_rate)
+
+/// Will simply set up the headset for the parrot to use. Stub, implemented on subtypes.
+/mob/living/basic/parrot/proc/setup_headset()
+ return
+
+/// Gets a static list of phrases we wish to pass to the element.
+/mob/living/basic/parrot/proc/get_static_list_of_phrases()
+ var/static/list/default_phrases = list(
+ "BAWWWWK george mellons griffing me!",
+ "Cracker?",
+ "Hello!",
+ "Hi!",
+ )
+
+ return default_phrases
+
+/// Gets the available channels that this parrot has access to. Returns a list of the channels we can use.
+/mob/living/basic/parrot/proc/get_available_channels()
+ var/list/returnable_list = list()
+ if(isnull(ears))
+ return returnable_list
+
+ var/list/headset_channels = ears.channels
+ for(var/channel in headset_channels)
+ returnable_list += GLOB.channel_tokens[channel] // will return something like ":e" or ":c" y'know
+
+ return returnable_list
+
+/mob/living/basic/parrot/proc/tamed()
+ new /obj/effect/temp_visual/heart(drop_location())
+
+/mob/living/basic/parrot/proc/drop_item_on_signal(mob/living/user)
+ SIGNAL_HANDLER
+
+ drop_held_item()
+ return COMSIG_KB_ACTIVATED
+
+#undef FORCED_SPEECH_COOLDOWN_DURATION
diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm
new file mode 100644
index 00000000000..9b7ecd4af1d
--- /dev/null
+++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm
@@ -0,0 +1,40 @@
+/datum/ai_controller/basic_controller/parrot
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items,
+ BB_HOARD_LOCATION_RANGE = 9,
+ )
+
+ ai_traits = STOP_MOVING_WHEN_PULLED
+ ai_movement = /datum/ai_movement/basic_avoidance
+ idle_behavior = /datum/idle_behavior/idle_random_walk/parrot
+
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/target_retaliate,
+ /datum/ai_planning_subtree/perch_on_target,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree,
+ /datum/ai_planning_subtree/hoard_items,
+ /datum/ai_planning_subtree/parrot_as_in_repeat, // always get a witty oneliner in when you can
+ )
+
+/datum/idle_behavior/idle_random_walk/parrot
+ ///chance of us moving while perched
+ var/walk_chance_when_perched = 1 // SKYRAT EDIT CHANGE - More obedient poly ORIGINAL : var/walk_chance_when_perched = 5
+
+/datum/idle_behavior/idle_random_walk/parrot/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller)
+ var/mob/living/living_pawn = controller.pawn
+ walk_chance = HAS_TRAIT(living_pawn, TRAIT_PARROT_PERCHED) ? walk_chance_when_perched : initial(walk_chance)
+ return ..()
+
+/datum/ai_behavior/travel_towards/and_drop
+
+/datum/ai_behavior/travel_towards/and_drop/finish_action(datum/ai_controller/controller, succeeded, target_key)
+ . = ..()
+ var/mob/living/living_mob = controller.pawn
+ var/obj/drop_item = locate(/obj/item) in (living_mob.contents - typecache_filter_list(living_mob.contents, controller.blackboard[BB_IGNORE_ITEMS]))
+ drop_item?.forceMove(get_turf(living_mob))
+
+/datum/ai_behavior/basic_melee_attack/interact_once/parrot
+
+/datum/ai_behavior/basic_melee_attack/interact_once/parrot/finish_action(datum/ai_controller/controller, succeeded, target_key)
+ . = ..()
+ controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, FALSE)
diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm
new file mode 100644
index 00000000000..58bdef408a5
--- /dev/null
+++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm
@@ -0,0 +1,37 @@
+/// Used for ghost poly.
+/datum/ai_controller/basic_controller/parrot/ghost
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/parrot_as_in_repeat,
+ /datum/ai_planning_subtree/possess_humans,
+ /datum/ai_planning_subtree/hoard_items,
+ )
+
+///subtree to possess humans
+/datum/ai_planning_subtree/possess_humans
+ ///chance we go possess humans
+ var/possess_chance = 2
+
+/datum/ai_planning_subtree/possess_humans/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ var/mob/living/living_pawn = controller.pawn
+
+ if(controller.blackboard_key_exists(BB_PERCH_TARGET))
+ controller.queue_behavior(/datum/ai_behavior/perch_on_target/haunt, BB_PERCH_TARGET)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+
+ if(!SPT_PROB(possess_chance, seconds_per_tick))
+ if(ishuman(living_pawn.loc))
+ return SUBTREE_RETURN_FINISH_PLANNING
+ return
+
+ if(ishuman(living_pawn.loc))
+ controller.set_blackboard_key(living_pawn.loc)
+ return
+
+ controller.queue_behavior(/datum/ai_behavior/find_and_set/conscious_person, BB_PERCH_TARGET)
+
+
+/datum/ai_behavior/perch_on_target/haunt
+
+/datum/ai_behavior/perch_on_target/haunt/check_human_conditions(mob/living/living_human)
+ return (living_human.stat != DEAD)
diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm
new file mode 100644
index 00000000000..7484cbfe675
--- /dev/null
+++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm
@@ -0,0 +1,74 @@
+///subtree to steal items
+/datum/ai_planning_subtree/hoard_items
+ var/theft_chance = 5
+
+/datum/ai_planning_subtree/hoard_items/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ var/mob/living/living_pawn = controller.pawn
+
+ var/turf/myspace = controller.blackboard[BB_HOARD_LOCATION]
+
+ if(isnull(myspace) || myspace.is_blocked_turf(source_atom = controller.pawn) || get_dist(myspace, controller.pawn) > controller.blackboard[BB_HOARD_LOCATION_RANGE])
+ controller.queue_behavior(/datum/ai_behavior/find_and_set/hoard_location, BB_HOARD_LOCATION, /turf/open)
+ return
+
+ //we have an item, go drop!
+ var/list/our_contents = living_pawn.contents - typecache_filter_list(living_pawn.contents, controller.blackboard[BB_IGNORE_ITEMS])
+ if(length(our_contents))
+ controller.queue_behavior(/datum/ai_behavior/travel_towards/and_drop, BB_HOARD_LOCATION)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+ if(controller.blackboard_key_exists(BB_HOARD_ITEM_TARGET))
+ controller.queue_behavior(/datum/ai_behavior/basic_melee_attack/interact_once, BB_HOARD_ITEM_TARGET, BB_TARGETING_STRATEGY)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+ if(!SPT_PROB(theft_chance, seconds_per_tick))
+ return
+ controller.queue_behavior(/datum/ai_behavior/find_and_set/hoard_item, BB_HOARD_ITEM_TARGET)
+
+/datum/ai_behavior/find_and_set/hoard_location
+
+/datum/ai_behavior/find_and_set/hoard_location/search_tactic(datum/ai_controller/controller, locate_path, search_range)
+ for(var/turf/open/candidate in oview(search_range, controller.pawn))
+ if(isspaceturf(candidate) || isopenspaceturf(candidate))
+ continue
+ if(candidate.is_blocked_turf(source_atom = controller.pawn))
+ continue
+ return candidate
+
+ return null
+
+/datum/ai_behavior/find_and_set/hoard_item
+ action_cooldown = 5 SECONDS
+ behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
+
+/datum/ai_behavior/find_and_set/hoard_item/search_tactic(datum/ai_controller/controller, locate_path, search_range)
+ if(!controller.blackboard_key_exists(BB_HOARD_LOCATION))
+ return null
+ var/turf/nest_turf = controller.blackboard[BB_HOARD_LOCATION]
+ var/mob/living/living_pawn = controller.pawn
+ for(var/atom/potential_victim in oview(search_range, controller.pawn))
+ if(is_type_in_typecache(potential_victim, controller.blackboard[BB_IGNORE_ITEMS]))
+ continue
+ if(potential_victim.loc == nest_turf)
+ continue
+ if(isitem(potential_victim))
+ var/obj/item/item_steal = potential_victim
+ if(item_steal.w_class <= WEIGHT_CLASS_SMALL)
+ return potential_victim
+ if(!ishuman(potential_victim))
+ continue
+ if(living_pawn.faction.Find(REF(potential_victim)))
+ continue //dont steal from friends
+ if(holding_valuable(controller, potential_victim))
+ controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, TRUE)
+ return potential_victim
+
+ return null
+
+/datum/ai_behavior/find_and_set/hoard_item/proc/holding_valuable(datum/ai_controller/controller, mob/living/human_target)
+ for(var/obj/item/potential_item in human_target.held_items)
+ if(is_type_in_typecache(potential_item, controller.blackboard[BB_IGNORE_ITEMS]))
+ continue
+ if(potential_item.w_class <= WEIGHT_CLASS_SMALL)
+ return TRUE
+ return FALSE
diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm
new file mode 100644
index 00000000000..1bb5c6fc960
--- /dev/null
+++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm
@@ -0,0 +1,78 @@
+///subtree to perch on targets
+/datum/ai_planning_subtree/perch_on_target
+ ///perchance...
+ var/perch_chance = 5
+ ///chance we unbuckle
+ var/unperch_chance = 15
+
+
+/datum/ai_planning_subtree/perch_on_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ var/mob/living/living_pawn = controller.pawn
+ var/atom/buckled_too = living_pawn.buckled
+
+ //do we have a current target or is chance to unbuckle has passed? then unbuckle!
+ if(buckled_too)
+ if((SPT_PROB(unperch_chance, seconds_per_tick) || controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)))
+ controller.queue_behavior(/datum/ai_behavior/unbuckle_mob)
+ return
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+ //if we are perched, we can go find something else to perch too
+ var/final_chance = HAS_TRAIT(living_pawn, TRAIT_PARROT_PERCHED) ? unperch_chance : perch_chance
+
+ if(!SPT_PROB(final_chance, seconds_per_tick) || controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET))
+ return
+
+ if(controller.blackboard_key_exists(BB_PERCH_TARGET))
+ controller.queue_behavior(/datum/ai_behavior/perch_on_target, BB_PERCH_TARGET)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+ //50 50 chance to look for an object, or a friend
+ if(prob(50))
+ controller.queue_behavior(/datum/ai_behavior/find_and_set/nearby_friends, BB_PERCH_TARGET)
+ return
+
+ controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list, BB_PERCH_TARGET, controller.blackboard[BB_PARROT_PERCH_TYPES])
+
+/// Parrot behavior that allows them to perch on a target.
+/datum/ai_behavior/perch_on_target
+ behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
+
+/datum/ai_behavior/perch_on_target/setup(datum/ai_controller/controller, target_key)
+ . = ..()
+ var/atom/target = controller.blackboard[target_key]
+ if(QDELETED(target))
+ return FALSE
+
+ set_movement_target(controller, target)
+
+/datum/ai_behavior/perch_on_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
+ . = ..()
+ var/atom/target = controller.blackboard[target_key]
+ if(QDELETED(target))
+ finish_action(controller, FALSE, target_key)
+ return
+
+ var/mob/living/basic/parrot/living_pawn = controller.pawn
+
+ if(!ishuman(target))
+ living_pawn.start_perching(target)
+ finish_action(controller, TRUE, target_key)
+ return
+
+ if(!check_human_conditions(target))
+ finish_action(controller, FALSE, target_key)
+ return
+
+ living_pawn.start_perching(target)
+ finish_action(controller, TRUE, target_key)
+
+/datum/ai_behavior/perch_on_target/proc/check_human_conditions(mob/living/living_human)
+ if(living_human.stat == DEAD || LAZYLEN(living_human.buckled_mobs) >= living_human.max_buckled_mobs)
+ return FALSE
+
+ return TRUE
+
+/datum/ai_behavior/perch_on_target/finish_action(datum/ai_controller/controller, succeeded, target_key)
+ . = ..()
+ controller.clear_blackboard_key(target_key)
diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm
new file mode 100644
index 00000000000..d1488a60b3b
--- /dev/null
+++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm
@@ -0,0 +1,50 @@
+/// When a parrot... parrots...
+/datum/ai_planning_subtree/parrot_as_in_repeat
+ operational_datums = list(/datum/component/listen_and_repeat)
+
+/datum/ai_planning_subtree/parrot_as_in_repeat/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ var/atom/speaking_pawn = controller.pawn
+
+ var/switch_up_probability = controller.blackboard[BB_PARROT_PHRASE_CHANGE_PROBABILITY]
+ if(SPT_PROB(switch_up_probability, seconds_per_tick) || isnull(controller.blackboard[BB_PARROT_REPEAT_STRING]))
+ if(SEND_SIGNAL(speaking_pawn, COMSIG_NEEDS_NEW_PHRASE) & NO_NEW_PHRASE_AVAILABLE)
+ return
+
+ if(!SPT_PROB(controller.blackboard[BB_PARROT_REPEAT_PROBABILITY], seconds_per_tick))
+ return
+
+ var/potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING]
+ if(isnull(potential_string))
+ stack_trace("Parrot As In Repeat Subtree somehow is getting a null potential string while not getting `NO_NEW_PHRASE_AVAILABLE`!")
+ return
+
+ controller.queue_behavior(/datum/ai_behavior/perform_speech/parrot, potential_string)
+
+/datum/ai_behavior/perform_speech/parrot
+ action_cooldown = 7.5 SECONDS // gets really annoying (moreso than usual) really fast otherwise
+
+/datum/ai_behavior/perform_speech/parrot/perform(seconds_per_tick, datum/ai_controller/controller, speech, speech_sound)
+ var/mob/living/basic/parrot/speaking_pawn = controller.pawn
+ var/list/available_channels = speaking_pawn.get_available_channels()
+ var/modified_speech = speech
+ var/use_radio = prob(50) // we might not even use the radio if we even have a channel
+
+#define HAS_CHANNEL_PREFIX (speech[1] in GLOB.department_radio_prefixes) && (copytext_char(speech, 2, 3) in GLOB.department_radio_keys) // determine if we need to crop the channel prefix
+
+ if(!length(available_channels)) // might not even use the radio at all
+ if(HAS_CHANNEL_PREFIX)
+ modified_speech = copytext_char(speech, 3)
+
+ else
+ if(HAS_CHANNEL_PREFIX)
+ modified_speech = "[use_radio ? pick(available_channels) : ""][copytext_char(speech, 3)]"
+ else
+ modified_speech = "[use_radio ? pick(available_channels) : ""][speech]"
+
+
+ speaking_pawn.say(modified_speech, forced = "AI Controller")
+ if(speech_sound)
+ playsound(speaking_pawn, speech_sound, 80, vary = TRUE)
+ finish_action(controller, TRUE)
+
+#undef HAS_CHANNEL_PREFIX
diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_items.dm b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm
new file mode 100644
index 00000000000..c071bf7fdbe
--- /dev/null
+++ b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm
@@ -0,0 +1,60 @@
+/datum/strippable_item/parrot_headset
+ key = STRIPPABLE_ITEM_PARROT_HEADSET
+
+/datum/strippable_item/parrot_headset/get_item(atom/source)
+ var/mob/living/basic/parrot/poly/parrot_source = source
+ return istype(parrot_source) ? parrot_source.ears : null
+
+/datum/strippable_item/parrot_headset/try_equip(atom/source, obj/item/equipping, mob/user)
+ . = ..()
+ if (!.)
+ return FALSE
+
+ if (!istype(equipping, /obj/item/radio/headset))
+ to_chat(user, span_warning("[equipping] won't fit!"))
+ return FALSE
+
+ return TRUE
+
+// There is no delay for putting a headset on a parrot.
+/datum/strippable_item/parrot_headset/start_equip(atom/source, obj/item/equipping, mob/user)
+ return TRUE
+
+/datum/strippable_item/parrot_headset/finish_equip(atom/source, obj/item/equipping, mob/user)
+ var/obj/item/radio/headset/radio = equipping
+ if (!istype(radio))
+ return
+
+ var/mob/living/basic/parrot/parrot_source = source
+ if (!istype(parrot_source))
+ return
+
+ if (!user.transferItemToLoc(radio, source))
+ return
+
+ parrot_source.ears = radio
+
+ to_chat(user, span_notice("You fit [radio] onto [source]."))
+
+/datum/strippable_item/parrot_headset/start_unequip(atom/source, mob/user)
+ . = ..()
+ if (!.)
+ return FALSE
+
+ var/mob/living/basic/parrot/parrot_source = source
+ if (!istype(parrot_source))
+ return
+
+ if (parrot_source.stat == CONSCIOUS)
+ var/list/list_of_channels = parrot_source.get_available_channels()
+ parrot_source.say("[list_of_channels ? "[pick(list_of_channels)] " : null]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!", forced = "attempted headset removal")
+
+ return TRUE
+
+/datum/strippable_item/parrot_headset/finish_unequip(atom/source, mob/user)
+ var/mob/living/basic/parrot/parrot_source = source
+ if (!istype(parrot_source))
+ return
+
+ parrot_source.ears.forceMove(parrot_source.drop_location())
+ parrot_source.ears = null
diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm
new file mode 100644
index 00000000000..a0e8a4b5294
--- /dev/null
+++ b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm
@@ -0,0 +1,14 @@
+// this file is for parrots that aren't poly
+
+/// Parrot that will just randomly spawn with a headset. Nothing too special beyond that.
+/mob/living/basic/parrot/headsetted
+
+/mob/living/basic/parrot/headsetted/setup_headset()
+ var/headset = pick(
+ /obj/item/radio/headset/headset_cargo,
+ /obj/item/radio/headset/headset_eng,
+ /obj/item/radio/headset/headset_med,
+ /obj/item/radio/headset/headset_sci,
+ /obj/item/radio/headset/headset_sec,
+ )
+ ears = new headset(src)
diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm
new file mode 100644
index 00000000000..42c3061bdd6
--- /dev/null
+++ b/code/modules/mob/living/basic/pets/parrot/poly.dm
@@ -0,0 +1,254 @@
+/// Default poly, presumably died the last shift and has no special traits.
+#define POLY_DEFAULT "default"
+/// Poly has survived a number of rounds equivalent to the longest survival of his being.
+#define POLY_LONGEST_SURVIVAL "longest_survival"
+/// Poly has survived a number of rounds equivalent to the longest deathstreak of his being.
+#define POLY_BEATING_DEATHSTREAK "longest_deathstreak"
+/// Poly has only just survived a round, and is doing a consecutive one.
+#define POLY_CONSECUTIVE_ROUND "consecutive_round"
+/// haunt filter we apply to who we possess
+#define POLY_POSSESS_FILTER
+/// haunt filter color we apply to who we possess
+#define POLY_POSSESS_GLOW "#522059"
+
+/// The classically famous compadre to the Chief Engineer, Poly.
+/mob/living/basic/parrot/poly
+ name = "Poly"
+ desc = "Poly the Parrot. An expert on quantum cracker theory."
+ gold_core_spawnable = NO_SPAWN
+ speech_probability_rate = 13
+
+ /// Callback to save our memory at the end of the round.
+ var/datum/callback/roundend_callback = null
+ /// Did we write the memory to disk?
+ var/memory_saved = FALSE
+ /// How long has this bird been alive for?
+ var/rounds_survived = 0
+ /// How long have we survived for at max?
+ var/longest_survival = 0
+ /// How many rounds in a row have we been dead for?
+ var/longest_deathstreak = 0
+
+/mob/living/basic/parrot/poly/Initialize(mapload)
+ . = ..()
+
+ if(!memory_saved)
+ roundend_callback = CALLBACK(src, PROC_REF(Write_Memory))
+ SSticker.OnRoundend(roundend_callback)
+
+ REGISTER_REQUIRED_MAP_ITEM(1, 1) // every map needs a poly!
+ update_appearance()
+
+ if(!SStts.tts_enabled)
+ return
+
+ voice = pick(SStts.available_speakers)
+ if(SStts.pitch_enabled)
+ if(findtext(voice, "Woman"))
+ pitch = 12 // up-pitch by one octave
+ else
+ pitch = 24 // up-pitch by 2 octaves
+ else
+ voice_filter = "rubberband=pitch=1.5" // Use the filter to pitch up if we can't naturally pitch up.
+
+/mob/living/basic/parrot/poly/Destroy()
+ LAZYREMOVE(SSticker.round_end_events, roundend_callback) // we do the memory writing stuff on death, but this is important to yeet as fast as we can if we need to destroy
+ roundend_callback = null
+ return ..()
+
+/mob/living/basic/parrot/poly/death(gibbed)
+ if(HAS_TRAIT(src, TRAIT_DONT_WRITE_MEMORY))
+ return ..() // Don't read memory either.
+ if(!memory_saved)
+ Write_Memory(TRUE)
+ var/special_status = determine_special_poly()
+ if(special_status == POLY_LONGEST_SURVIVAL || special_status == POLY_BEATING_DEATHSTREAK || prob(0.666))
+ var/mob/living/basic/parrot/poly/ghost/specter = new(loc)
+ if(mind)
+ mind.transfer_to(specter)
+ else
+ specter.key = key
+ return ..()
+
+/mob/living/basic/parrot/poly/get_static_list_of_phrases() // there's only one poly, so there should only be one ongoing list of phrases. i guess
+ var/static/list/phrases_to_return = list()
+ if(length(phrases_to_return))
+ return phrases_to_return
+
+ phrases_to_return += read_memory() // must come first!!!
+ // now add some valuable lines every poly should have
+ phrases_to_return += list(
+ ":e Check the crystal, you chucklefucks!",
+ ":e OH GOD ITS ABOUT TO DELAMINATE CALL THE SHUTTLE",
+ ":e WHO TOOK THE DAMN MODSUITS?",
+ ":e Wire the solars, you lazy bums!",
+ "Poly wanna cracker!",
+ )
+ switch(determine_special_poly())
+ if(POLY_DEFAULT)
+ phrases_to_return += pick("...alive?", "This isn't parrot heaven!", "I live, I die, I live again!", "The void fades!")
+ if(POLY_LONGEST_SURVIVAL)
+ phrases_to_return += pick("...[longest_survival].", "The things I've seen!", "I have lived many lives!", "What are you before me?")
+ if(POLY_BEATING_DEATHSTREAK)
+ phrases_to_return += pick("What are you waiting for!", "Violence breeds violence!", "Blood! Blood!", "Strike me down if you dare!")
+ if(POLY_CONSECUTIVE_ROUND)
+ phrases_to_return += pick("...again?", "No, It was over!", "Let me out!", "It never ends!")
+
+ return phrases_to_return
+
+/mob/living/basic/parrot/poly/update_desc()
+ . = ..()
+ switch(determine_special_poly())
+ if(POLY_LONGEST_SURVIVAL)
+ desc += " Old as sin, and just as loud. Claimed to be [rounds_survived]."
+ if(POLY_BEATING_DEATHSTREAK)
+ desc += " The squawks of [-rounds_survived] dead parrots ring out in your ears..."
+ if(POLY_CONSECUTIVE_ROUND)
+ desc += " Over [rounds_survived] shifts without a \"terrible\" \"accident\"!"
+
+/mob/living/basic/parrot/poly/update_icon()
+ . = ..()
+ switch(determine_special_poly())
+ if(POLY_LONGEST_SURVIVAL)
+ add_atom_colour("#EEEE22", FIXED_COLOUR_PRIORITY)
+ if(POLY_BEATING_DEATHSTREAK)
+ add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY)
+
+/// Reads the memory of the parrot, and updates the necessary variables. Returns a list of phrases to add to the parrot's speech buffer.
+/mob/living/basic/parrot/poly/proc/read_memory()
+ RETURN_TYPE(/list)
+ var/list/returnable_list = list()
+ if(fexists("data/npc_saves/Poly.sav")) //legacy compatability to convert old format to new
+ var/savefile/legacy = new /savefile("data/npc_saves/Poly.sav")
+ legacy["phrases"] >> returnable_list
+ legacy["roundssurvived"] >> rounds_survived
+ legacy["longestsurvival"] >> longest_survival
+ legacy["longestdeathstreak"] >> longest_deathstreak
+ fdel("data/npc_saves/Poly.sav")
+
+ else
+ var/json_file = file("data/npc_saves/Poly.json")
+ if(!fexists(json_file))
+ return list()
+ var/list/json = json_decode(file2text(json_file))
+ returnable_list = json["phrases"]
+ rounds_survived = json["roundssurvived"]
+ longest_survival = json["longestsurvival"]
+ longest_deathstreak = json["longestdeathstreak"]
+
+ return returnable_list
+
+/// Determines the type of Poly we might have here based on the statistics we got from the memory.
+/mob/living/basic/parrot/poly/proc/determine_special_poly()
+ if(rounds_survived == longest_survival)
+ return POLY_LONGEST_SURVIVAL
+ else if(rounds_survived == longest_deathstreak)
+ return POLY_BEATING_DEATHSTREAK
+ else if(rounds_survived > 0)
+ return POLY_CONSECUTIVE_ROUND
+ else
+ return POLY_DEFAULT
+
+/mob/living/basic/parrot/poly/Write_Memory(dead, gibbed)
+ . = ..()
+ if(!. || memory_saved) // if we die, no more memory
+ return FALSE
+
+ if(!dead && (stat != DEAD))
+ dead = FALSE
+
+ var/file_path = "data/npc_saves/Poly.json"
+ var/list/file_data = list()
+
+ var/list/exportable_speech_buffer = ai_controller.blackboard[BB_EXPORTABLE_STRING_BUFFER_LIST] // should have been populated when we sent the signal out on parent
+ if(!!length(exportable_speech_buffer))
+ file_data["phrases"] = exportable_speech_buffer
+
+ if(dead)
+ file_data["roundssurvived"] = min(rounds_survived - 1, 0)
+ file_data["longestsurvival"] = longest_survival
+ if(rounds_survived - 1 < longest_deathstreak)
+ file_data["longestdeathstreak"] = rounds_survived - 1
+ else
+ file_data["longestdeathstreak"] = longest_deathstreak
+ else
+
+ file_data["roundssurvived"] = max(rounds_survived, 0) + 1
+ if(rounds_survived + 1 > longest_survival)
+ file_data["longestsurvival"] = rounds_survived + 1
+ else
+ file_data["longestsurvival"] = longest_survival
+ file_data["longestdeathstreak"] = longest_deathstreak
+
+ var/formatted_data
+#if DM_VERSION >= 515
+ formatted_data = json_encode(file_data, JSON_PRETTY_PRINT)
+#else
+ formatted_data = json_encode(file_data)
+#endif
+
+ rustg_file_write(formatted_data, file_path)
+ memory_saved = TRUE
+ return TRUE
+
+/mob/living/basic/parrot/poly/setup_headset()
+ ears = new /obj/item/radio/headset/headset_eng(src)
+
+/mob/living/basic/parrot/poly/ghost
+ name = "The Ghost of Poly"
+ desc = "Doomed to squawk the Earth."
+ color = "#FFFFFF77"
+ status_flags = GODMODE
+ sentience_type = SENTIENCE_BOSS //This is so players can't mindswap into ghost poly to become a literal god
+ incorporeal_move = INCORPOREAL_MOVE_BASIC
+ butcher_results = list(/obj/item/ectoplasm = 1)
+ ai_controller = /datum/ai_controller/basic_controller/parrot/ghost
+ speech_probability_rate = 1
+
+/mob/living/basic/parrot/poly/ghost/Initialize(mapload)
+ // block anything and everything that could possibly happen with writing memory for ghosts
+ memory_saved = TRUE
+ ADD_TRAIT(src, TRAIT_DONT_WRITE_MEMORY, INNATE_TRAIT)
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
+ return ..()
+
+//we perch on human souls
+/mob/living/basic/parrot/poly/ghost/perch_on_human(mob/living/carbon/human/target)
+ if(loc == target) //dismount
+ forceMove(get_turf(target))
+ return FALSE
+ if(ishuman(loc))
+ balloon_alert(src, "already possessing!")
+ return FALSE
+ forceMove(target)
+ return TRUE
+
+/mob/living/basic/parrot/poly/ghost/proc/on_moved(atom/movable/movable, atom/old_loc)
+ SIGNAL_HANDLER
+
+ if(ishuman(old_loc))
+ var/mob/living/unpossessed_human = old_loc
+ unpossessed_human.remove_filter(POLY_POSSESS_FILTER)
+ return
+
+ if(!ishuman(loc))
+ return
+
+ var/mob/living/possessed_human = loc
+ possessed_human.add_filter(POLY_POSSESS_FILTER, 2, list("type" = "outline", "color" = POLY_POSSESS_GLOW, "size" = 2))
+ var/filter = possessed_human.get_filter(POLY_POSSESS_FILTER)
+
+ if(filter)
+ animate(filter, alpha = 200, time = 2 SECONDS, loop = -1)
+ animate(alpha = 60, time = 2 SECONDS)
+
+ var/datum/disease/parrot_possession/on_possession = new /datum/disease/parrot_possession
+ on_possession.set_parrot(src)
+ possessed_human.ForceContractDisease(on_possession, make_copy = FALSE, del_on_fail = TRUE)
+
+#undef POLY_DEFAULT
+#undef POLY_LONGEST_SURVIVAL
+#undef POLY_BEATING_DEATHSTREAK
+#undef POLY_CONSECUTIVE_ROUND
+#undef POLY_POSSESS_FILTER
+#undef POLY_POSSESS_GLOW
diff --git a/code/modules/mob/living/basic/space_fauna/meteor_heart/spine_traps.dm b/code/modules/mob/living/basic/space_fauna/meteor_heart/spine_traps.dm
index 4f7135b1deb..8ddcc1ed9e4 100644
--- a/code/modules/mob/living/basic/space_fauna/meteor_heart/spine_traps.dm
+++ b/code/modules/mob/living/basic/space_fauna/meteor_heart/spine_traps.dm
@@ -86,7 +86,7 @@
/// Called when something enters our turf, if it is a non-flying mob then give it a stab
/obj/effect/temp_visual/thrusting_spines/proc/on_entered(datum/source, atom/movable/arrived)
- if (!active || !isliving(arrived) || (arrived.movement_type & (FLYING | FLOATING)))
+ if (!active || !isliving(arrived) || (arrived.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
return
if (!COOLDOWN_FINISHED(src, thrust_delay))
return
diff --git a/code/modules/mob/living/carbon/alien/larva/life.dm b/code/modules/mob/living/carbon/alien/larva/life.dm
index 2e2674e14e7..147079ae720 100644
--- a/code/modules/mob/living/carbon/alien/larva/life.dm
+++ b/code/modules/mob/living/carbon/alien/larva/life.dm
@@ -3,7 +3,7 @@
/mob/living/carbon/alien/larva/Life(seconds_per_tick = SSMOBS_DT, times_fired)
if(HAS_TRAIT(src, TRAIT_NO_TRANSFORM))
return
- if(!..() || IS_IN_STASIS(src) || (amount_grown >= max_grown))
+ if(!..() || HAS_TRAIT(src, TRAIT_STASIS) || (amount_grown >= max_grown))
return // We're dead, in stasis, or already grown.
// GROW!
amount_grown = min(amount_grown + (0.5 * seconds_per_tick), max_grown)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 786b663758f..6a34c7fd005 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -52,6 +52,7 @@
. = ..()
var/hurt = TRUE
var/extra_speed = 0
+ var/oof_noise = FALSE //We smacked something with denisty, so play a noise
if(throwingdatum.thrower != src)
extra_speed = min(max(0, throwingdatum.speed - initial(throw_speed)), CARBON_MAX_IMPACT_SPEED_BONUS)
@@ -66,19 +67,37 @@
take_bodypart_damage(5 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
else if(!iscarbon(hit_atom) && extra_speed)
take_bodypart_damage(5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
+ oof_noise = TRUE
+
if(iscarbon(hit_atom) && hit_atom != src)
var/mob/living/carbon/victim = hit_atom
+ var/blocked = FALSE
if(victim.movement_type & FLYING)
return
- if(hurt)
- victim.take_bodypart_damage(10 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
- take_bodypart_damage(10 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
+ if(!hurt)
+ return
+
+ if(victim.check_block(src, 0, "[name]", LEAP_ATTACK))
+ blocked = TRUE
+
+ take_bodypart_damage(10 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
+ Paralyze(2 SECONDS)
+ oof_noise = TRUE
+
+ if(blocked)
+ visible_message(span_danger("[src] crashes into [victim][extra_speed ? " really hard" : ""], but [victim] blocked the worst of it!"),\
+ span_userdanger("You violently crash into [victim][extra_speed ? " extra hard" : ""], but [victim] managed to block the worst of it!"))
+ log_combat(src, victim, "crashed into and was blocked by")
+ return
+ else
victim.Paralyze(2 SECONDS)
- Paralyze(2 SECONDS)
+ victim.take_bodypart_damage(10 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
visible_message(span_danger("[src] crashes into [victim][extra_speed ? " really hard" : ""], knocking them both over!"),\
span_userdanger("You violently crash into [victim][extra_speed ? " extra hard" : ""]!"))
+ log_combat(src, victim, "crashed into")
+
+ if(oof_noise)
playsound(src,'sound/weapons/punch1.ogg',50,TRUE)
- log_combat(src, victim, "crashed into")
//Throwing stuff
/mob/living/carbon/proc/toggle_throw_mode()
diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm
index 82bda7c7cd4..d900706f102 100644
--- a/code/modules/mob/living/carbon/carbon_movement.dm
+++ b/code/modules/mob/living/carbon/carbon_movement.dm
@@ -1,5 +1,5 @@
/mob/living/carbon/slip(knockdown_amount, obj/slipped_on, lube_flags, paralyze, force_drop = FALSE)
- if(movement_type & (FLYING | FLOATING))
+ if(movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
return FALSE
if(!(lube_flags & SLIDE_ICE))
log_combat(src, (slipped_on || get_turf(src)), "slipped on the", null, ((lube_flags & SLIDE) ? "(SLIDING)" : null))
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index b847b7a73da..10c3288e0cd 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -858,19 +858,30 @@ GLOBAL_LIST_EMPTY(features_by_species)
randomize_active_underwear_only(human_mob)
human_mob.update_body()
-///Proc that will randomize all the external organs (i.e. horns, frills, tails etc.) of a species' associated mob
-/datum/species/proc/randomize_external_organs(mob/living/carbon/human/human_mob)
+/datum/species/proc/randomize_active_features(mob/living/carbon/human/human_mob)
+ var/list/new_features = randomize_features()
+ for(var/feature_key in new_features)
+ human_mob.dna.features[feature_key] = new_features[feature_key]
+ human_mob.updateappearance(mutcolor_update = TRUE)
+
+/**
+ * Returns a list of features, randomized, to be used by DNA
+ */
+/datum/species/proc/randomize_features()
+ SHOULD_CALL_PARENT(TRUE)
+
+ var/list/new_features = list()
+ var/static/list/organs_to_randomize = list()
for(var/obj/item/organ/external/organ_path as anything in external_organs)
- var/obj/item/organ/external/randomized_organ = human_mob.get_organ_by_type(organ_path)
- if(randomized_organ)
- var/datum/bodypart_overlay/mutant/overlay = randomized_organ.bodypart_overlay
- var/new_look = pick(overlay.get_global_feature_list())
- human_mob.dna.features["[overlay.feature_key]"] = new_look
- mutant_bodyparts["[overlay.feature_key]"] = new_look
-
-///Proc that randomizes all the appearance elements (external organs, markings, hair etc.) of a species' associated mob. Function set by child procs
-/datum/species/proc/randomize_features(mob/living/carbon/human/human_mob)
- return
+ var/overlay_path = initial(organ_path.bodypart_overlay)
+ var/datum/bodypart_overlay/mutant/sample_overlay = organs_to_randomize[overlay_path]
+ if(isnull(sample_overlay))
+ sample_overlay = new overlay_path()
+ organs_to_randomize[overlay_path] = sample_overlay
+
+ new_features["[sample_overlay.feature_key]"] = pick(sample_overlay.get_global_feature_list())
+
+ return new_features
/datum/species/proc/spec_life(mob/living/carbon/human/H, seconds_per_tick, times_fired)
SHOULD_CALL_PARENT(TRUE)
@@ -1307,7 +1318,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
return
//Only stabilise core temp when alive and not in statis
- if(humi.stat < DEAD && !IS_IN_STASIS(humi))
+ if(humi.stat < DEAD && !HAS_TRAIT(humi, TRAIT_STASIS))
body_temperature_core(humi, seconds_per_tick, times_fired)
//These do run in statis
@@ -1315,7 +1326,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
body_temperature_alerts(humi, seconds_per_tick, times_fired)
//Do not cause more damage in statis
- if(!IS_IN_STASIS(humi))
+ if(!HAS_TRAIT(humi, TRAIT_STASIS))
body_temperature_damage(humi, seconds_per_tick, times_fired)
/**
diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm
index 3c29bf93847..44fe6d1a1c0 100644
--- a/code/modules/mob/living/carbon/human/dummy.dm
+++ b/code/modules/mob/living/carbon/human/dummy.dm
@@ -75,9 +75,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
cut_overlays(TRUE)
/mob/living/carbon/human/dummy/setup_human_dna()
- create_dna()
- randomize_human(src)
- dna.initialize_dna(skip_index = TRUE) //Skip stuff that requires full round init.
+ randomize_human(src, randomize_mutations = FALSE)
/mob/living/carbon/human/dummy/log_mob_tag(text)
return
@@ -89,7 +87,6 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
return consistent_entry
/proc/create_consistent_human_dna(mob/living/carbon/human/target)
- target.dna.initialize_dna(skip_index = TRUE)
/* SKYRAT EDIT START - Customization - ORIGINAL:
target.dna.features["mcolor"] = COLOR_VIBRANT_LIME
target.dna.features["ethcolor"] = COLOR_WHITE
@@ -109,6 +106,17 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
target.dna.features["mcolor"] = COLOR_VIBRANT_LIME
target.dna.features["ethcolor"] = COLOR_WHITE
// SKYRAT EDIT END
+ target.dna.initialize_dna(create_mutation_blocks = FALSE, randomize_features = FALSE)
+ // UF and UI are nondeterministic, even though the features are the same some blocks will randomize slightly
+ // In practice this doesn't matter, but this is for the sake of 100%(ish) consistency
+ var/static/consistent_UF
+ var/static/consistent_UI
+ if(isnull(consistent_UF) || isnull(consistent_UI))
+ consistent_UF = target.dna.unique_features
+ consistent_UI = target.dna.unique_identity
+ else
+ target.dna.unique_features = consistent_UF
+ target.dna.unique_identity = consistent_UI
/// Provides a dummy that is consistently bald, white, naked, etc.
/mob/living/carbon/human/dummy/consistent
@@ -122,11 +130,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
/mob/living/carbon/human/consistent/setup_human_dna()
create_consistent_human_dna(src)
-
-/mob/living/carbon/human/consistent/update_body(is_creating)
- ..()
- if(is_creating)
- fully_replace_character_name(real_name, "John Doe")
+ fully_replace_character_name(real_name, "John Doe")
/mob/living/carbon/human/consistent/domutcheck()
return // We skipped adding any mutations so this runtimes
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 3de1c08d49c..94c7143060c 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -464,7 +464,8 @@
. += "\[View general records\]"
//SKYRAT EDIT ADDITION END
else if(isobserver(user))
- . += span_info("Traits: [get_quirk_string(FALSE, CAT_QUIRK_ALL)]")
+ . += span_info("Quirks: [get_quirk_string(FALSE, CAT_QUIRK_ALL)]")
+ . += ""
//SKYRAT EDIT ADDITION BEGIN - EXAMINE RECORDS
if(isobserver(user) || user.mind?.can_see_exploitables || user.mind?.has_exploitables_override)
@@ -486,7 +487,7 @@
//SKYRAT EDIT ADDITION END
//SKYRAT EDIT ADDITION BEGIN - CUSTOMIZATION
- for(var/genital in possible_genitals)
+ for(var/genital in GLOB.possible_genitals)
if(dna.species.mutant_bodyparts[genital])
var/datum/sprite_accessory/genital/G = GLOB.sprite_accessories[genital][dna.species.mutant_bodyparts[genital][MUTANT_INDEX_NAME]]
if(G)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 0905a4ac40f..a75253e1517 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -54,9 +54,7 @@
ADD_TRAIT(src, TRAIT_AGEUSIA, NO_TONGUE_TRAIT)
/mob/living/carbon/human/proc/setup_human_dna()
- //initialize dna. for spawned humans; overwritten by other code
- randomize_human(src)
- dna.initialize_dna()
+ randomize_human(src, randomize_mutations = TRUE)
/mob/living/carbon/human/Destroy()
QDEL_NULL(physiology)
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index cfda7721da0..fcdc37fdbaa 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -73,7 +73,11 @@
var/list/datum/bioware/biowares
/// What types of mobs are allowed to ride/buckle to this mob
- var/static/list/can_ride_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/simple_animal/parrot))
+ var/static/list/can_ride_typecache = typecacheof(list(
+ /mob/living/basic/parrot,
+ /mob/living/carbon/human,
+ /mob/living/simple_animal/slime,
+ ))
var/lastpuke = 0
var/account_id
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 2e061d32a6b..be355bddbea 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -28,7 +28,7 @@
//Body temperature stability and damage
dna.species.handle_body_temperature(src, seconds_per_tick, times_fired)
- if(!IS_IN_STASIS(src))
+ if(!HAS_TRAIT(src, TRAIT_STASIS))
if(stat != DEAD)
//handle active mutations
for(var/datum/mutation/human/human_mutation as anything in dna.mutations)
diff --git a/code/modules/mob/living/carbon/human/species_types/android.dm b/code/modules/mob/living/carbon/human/species_types/android.dm
index 3a17a0bb6ba..dec4fd9e306 100644
--- a/code/modules/mob/living/carbon/human/species_types/android.dm
+++ b/code/modules/mob/living/carbon/human/species_types/android.dm
@@ -3,35 +3,25 @@
id = SPECIES_ANDROID
examine_limb_id = SPECIES_HUMAN
inherent_traits = list(
- TRAIT_NO_UNDERWEAR,
TRAIT_GENELESS,
TRAIT_LIMBATTACHMENT,
+ TRAIT_LIVERLESS_METABOLISM,
+ TRAIT_NOBLOOD,
TRAIT_NOBREATH,
TRAIT_NOCLONELOSS,
TRAIT_NOCRITDAMAGE,
TRAIT_NOFIRE,
TRAIT_NOHUNGER,
-<<<<<<< HEAD
- TRAIT_LIVERLESS_METABOLISM,
-=======
TRAIT_NO_DNA_COPY,
TRAIT_NO_PLASMA_TRANSFORM,
TRAIT_NO_UNDERWEAR,
->>>>>>> def96ed8d ([MIRROR] Golems cannot turn into Plasmamen [MDB IGNORE] (#24809))
TRAIT_PIERCEIMMUNE,
TRAIT_RADIMMUNE,
TRAIT_RESISTCOLD,
TRAIT_RESISTHEAT,
- TRAIT_RESISTLOWPRESSURE,
TRAIT_RESISTHIGHPRESSURE,
+ TRAIT_RESISTLOWPRESSURE,
TRAIT_TOXIMMUNE,
-<<<<<<< HEAD
- TRAIT_NOBLOOD,
- TRAIT_NO_DNA_COPY,
- TRAIT_NO_TRANSFORMATION_STING,
- TRAIT_NOCRITDAMAGE,
-=======
->>>>>>> def96ed8d ([MIRROR] Golems cannot turn into Plasmamen [MDB IGNORE] (#24809))
)
inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID
diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
index 4c28d190eff..abe7d058181 100644
--- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm
+++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
@@ -104,8 +104,10 @@
return randname
-/datum/species/ethereal/randomize_features(mob/living/carbon/human/human_mob)
- human_mob.dna.features["ethcolor"] = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)]
+/datum/species/ethereal/randomize_features()
+ var/list/features = ..()
+ features["ethcolor"] = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)]
+ return features
/datum/species/ethereal/spec_updatehealth(mob/living/carbon/human/ethereal)
. = ..()
diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm
index 4773cf769ad..e22940db2ad 100644
--- a/code/modules/mob/living/carbon/human/species_types/felinid.dm
+++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm
@@ -48,8 +48,9 @@
return ..()
/datum/species/human/felinid/randomize_features(mob/living/carbon/human/human_mob)
- randomize_external_organs(human_mob)
- return ..()
+ var/list/features = ..()
+ features["ears"] = pick("None", "Cat")
+ return features
/proc/mass_purrbation()
for(var/mob in GLOB.human_list)
diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm
index f39d5ef390a..01451a8db36 100644
--- a/code/modules/mob/living/carbon/human/species_types/golems.dm
+++ b/code/modules/mob/living/carbon/human/species_types/golems.dm
@@ -3,21 +3,13 @@
name = "Golem"
id = SPECIES_GOLEM
inherent_traits = list(
- TRAIT_NO_UNDERWEAR,
TRAIT_GENELESS,
TRAIT_LAVA_IMMUNE,
- TRAIT_NOBREATH,
+ TRAIT_NEVER_WOUNDED,
TRAIT_NOBLOOD,
- TRAIT_NOFIRE,
-<<<<<<< HEAD
- TRAIT_PIERCEIMMUNE,
- TRAIT_RADIMMUNE,
- TRAIT_NO_DNA_COPY,
- TRAIT_NO_TRANSFORMATION_STING,
- TRAIT_NO_AUGMENTS,
+ TRAIT_NOBREATH,
TRAIT_NODISMEMBER,
- TRAIT_NEVER_WOUNDED
-=======
+ TRAIT_NOFIRE,
TRAIT_NO_AUGMENTS,
TRAIT_NO_DNA_COPY,
TRAIT_NO_PLASMA_TRANSFORM,
@@ -26,7 +18,6 @@
TRAIT_RADIMMUNE,
TRAIT_SNOWSTORM_IMMUNE, // Shared with plasma river... but I guess if you can survive a plasma river a blizzard isn't a big deal
TRAIT_UNHUSKABLE,
->>>>>>> def96ed8d ([MIRROR] Golems cannot turn into Plasmamen [MDB IGNORE] (#24809))
)
mutantheart = null
mutantlungs = null
diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm
index b169beba940..c7a181027e6 100644
--- a/code/modules/mob/living/carbon/human/species_types/humans.dm
+++ b/code/modules/mob/living/carbon/human/species_types/humans.dm
@@ -13,9 +13,6 @@
human.set_haircolor("#bb9966", update = FALSE) // brown
human.set_hairstyle("Business Hair", update = TRUE)
-/datum/species/human/randomize_features(mob/living/carbon/human/human_mob)
- human_mob.skin_tone = random_skin_tone()
-
/datum/species/human/get_scream_sound(mob/living/carbon/human/human)
if(human.physique == MALE)
if(prob(1))
@@ -82,4 +79,3 @@
))
return to_add
-
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index 5f91cbb2830..45e34d96bfd 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -75,8 +75,9 @@
//SKYRAT EDIT REMOVAL BEGIN
/*
/datum/species/lizard/randomize_features(mob/living/carbon/human/human_mob)
- human_mob.dna.features["body_markings"] = pick(GLOB.body_markings_list)
- randomize_external_organs(human_mob)
+ var/list/features = ..()
+ features["body_markings"] = pick(GLOB.body_markings_list)
+ return features
*/
//SKYRAT EDIT REMOVAL END
diff --git a/code/modules/mob/living/carbon/human/species_types/monkeys.dm b/code/modules/mob/living/carbon/human/species_types/monkeys.dm
index df289c514dc..8fb36e7c4e2 100644
--- a/code/modules/mob/living/carbon/human/species_types/monkeys.dm
+++ b/code/modules/mob/living/carbon/human/species_types/monkeys.dm
@@ -12,13 +12,13 @@
meat = /obj/item/food/meat/slab/monkey
knife_butcher_results = list(/obj/item/food/meat/slab/monkey = 5, /obj/item/stack/sheet/animalhide/monkey = 1)
inherent_traits = list(
- TRAIT_NO_UNDERWEAR,
- TRAIT_NO_BLOOD_OVERLAY,
TRAIT_GUN_NATURAL,
+ TRAIT_NO_AUGMENTS,
+ TRAIT_NO_BLOOD_OVERLAY,
+ TRAIT_NO_DNA_COPY,
+ TRAIT_NO_UNDERWEAR,
TRAIT_VENTCRAWLER_NUDE,
TRAIT_WEAK_SOUL,
- TRAIT_NO_TRANSFORMATION_STING,
- TRAIT_NO_AUGMENTS,
)
no_equip_flags = ITEM_SLOT_OCLOTHING | ITEM_SLOT_GLOVES | ITEM_SLOT_FEET | ITEM_SLOT_SUITSTORE
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | ERT_SPAWN | SLIME_EXTRACT
diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
index df816fbca35..8f3cd84e1e5 100644
--- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
@@ -59,9 +59,10 @@
if(istype(attacking_item, /obj/item/melee/flyswatter))
damage_mods += 10 // Yes, a 10x damage modifier
-/datum/species/moth/randomize_features(mob/living/carbon/human/human_mob)
- human_mob.dna.features["moth_markings"] = pick(GLOB.moth_wings_list)
- randomize_external_organs(human_mob)
+/datum/species/moth/randomize_features()
+ var/list/features = ..()
+ features["moth_markings"] = pick(GLOB.moth_wings_list) // SKYRAT EDIT CHANGE - ORIGINAL: features["moth_markings"] = pick(GLOB.moth_markings_list)
+ return features
/datum/species/moth/get_scream_sound(mob/living/carbon/human/human)
return 'sound/voice/moth/scream_moth.ogg'
diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
index 64dc5bb5192..eaabee63485 100644
--- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
@@ -8,20 +8,12 @@
inherent_traits = list(
TRAIT_GENELESS,
TRAIT_HARDLY_WOUNDED,
-<<<<<<< HEAD
- TRAIT_RADIMMUNE,
- TRAIT_RESISTCOLD,
- TRAIT_NOBLOOD,
- TRAIT_NO_DNA_COPY,
- TRAIT_NO_TRANSFORMATION_STING,
-=======
TRAIT_NOBLOOD,
TRAIT_NO_DNA_COPY,
TRAIT_NO_PLASMA_TRANSFORM,
TRAIT_RADIMMUNE,
TRAIT_RESISTCOLD,
TRAIT_UNHUSKABLE,
->>>>>>> def96ed8d ([MIRROR] Golems cannot turn into Plasmamen [MDB IGNORE] (#24809))
)
diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
index 0b645fd120a..5cf2355c86c 100644
--- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
@@ -105,6 +105,3 @@
))
return to_add
-
-/datum/species/pod/randomize_features(mob/living/carbon/human_mob)
- randomize_external_organs(human_mob)
diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm
index 654ccfa3118..4120f939d51 100644
--- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm
+++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm
@@ -10,10 +10,13 @@
TRAIT_FAKEDEATH,
TRAIT_GENELESS,
TRAIT_LIMBATTACHMENT,
+ TRAIT_NOBLOOD,
TRAIT_NOBREATH,
TRAIT_NOCLONELOSS,
- TRAIT_RADIMMUNE,
+ TRAIT_NO_DNA_COPY,
+ TRAIT_NO_UNDERWEAR,
TRAIT_PIERCEIMMUNE,
+ TRAIT_RADIMMUNE,
TRAIT_RESISTCOLD,
TRAIT_RESISTHEAT,
TRAIT_RESISTHIGHPRESSURE,
@@ -21,9 +24,6 @@
TRAIT_TOXIMMUNE,
TRAIT_UNHUSKABLE,
TRAIT_XENO_IMMUNE,
- TRAIT_NOBLOOD,
- TRAIT_NO_DNA_COPY,
- TRAIT_NO_TRANSFORMATION_STING,
)
inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID
mutanttongue = /obj/item/organ/internal/tongue/bone
diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm
index 14c4c4d1c15..e567b1a292b 100644
--- a/code/modules/mob/living/carbon/human/species_types/zombies.dm
+++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm
@@ -7,22 +7,22 @@
mutanttongue = /obj/item/organ/internal/tongue/zombie
inherent_traits = list(
// SHARED WITH ALL ZOMBIES
- TRAIT_NO_ZOMBIFY,
TRAIT_EASILY_WOUNDED,
TRAIT_EASYDISMEMBER,
TRAIT_FAKEDEATH,
TRAIT_LIMBATTACHMENT,
+ TRAIT_LIVERLESS_METABOLISM,
TRAIT_NOBREATH,
TRAIT_NOCLONELOSS,
TRAIT_NODEATH,
TRAIT_NOHUNGER,
- TRAIT_LIVERLESS_METABOLISM,
+ TRAIT_NO_DNA_COPY,
+ TRAIT_NO_ZOMBIFY,
TRAIT_RADIMMUNE,
TRAIT_RESISTCOLD,
TRAIT_RESISTHIGHPRESSURE,
TRAIT_RESISTLOWPRESSURE,
TRAIT_TOXIMMUNE,
- TRAIT_NO_TRANSFORMATION_STING,
// HIGH FUNCTIONING UNIQUE
TRAIT_NOBLOOD,
TRAIT_SUCCUMB_OVERRIDE,
@@ -105,11 +105,12 @@
TRAIT_EASYDISMEMBER,
TRAIT_FAKEDEATH,
TRAIT_LIMBATTACHMENT,
+ TRAIT_LIVERLESS_METABOLISM,
TRAIT_NOBREATH,
TRAIT_NOCLONELOSS,
TRAIT_NODEATH,
TRAIT_NOHUNGER,
- TRAIT_LIVERLESS_METABOLISM,
+ TRAIT_NO_DNA_COPY,
TRAIT_RADIMMUNE,
TRAIT_RESISTCOLD,
TRAIT_RESISTHIGHPRESSURE,
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index a6b766289c0..182eba52b71 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -13,7 +13,7 @@
damageoverlaytemp = 0
update_damage_hud()
- if(IS_IN_STASIS(src))
+ if(HAS_TRAIT(src, TRAIT_STASIS))
. = ..()
reagents.handle_stasis_chems(src, seconds_per_tick, times_fired)
else
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index 446e92f09f0..356653745e8 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -40,7 +40,7 @@
if(isnull(loc) || HAS_TRAIT(src, TRAIT_NO_TRANSFORM))
return
- if(!IS_IN_STASIS(src))
+ if(!HAS_TRAIT(src, TRAIT_STASIS))
if(stat != DEAD)
//Mutations and radiation
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index a42a0365bc5..5e4a564125c 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -509,7 +509,7 @@
return TRUE
if(!(flags & IGNORE_GRAB) && pulledby && pulledby.grab_state >= GRAB_AGGRESSIVE)
return TRUE
- if(!(flags & IGNORE_STASIS) && IS_IN_STASIS(src))
+ if(!(flags & IGNORE_STASIS) && HAS_TRAIT(src, TRAIT_STASIS))
return TRUE
return FALSE
@@ -1241,6 +1241,7 @@
if(-INFINITY to NEGATIVE_GRAVITY)
if(!istype(gravity_alert, /atom/movable/screen/alert/negative))
throw_alert(ALERT_GRAVITY, /atom/movable/screen/alert/negative)
+ ADD_TRAIT(src, TRAIT_MOVE_UPSIDE_DOWN, NEGATIVE_GRAVITY_TRAIT)
var/matrix/flipped_matrix = transform
flipped_matrix.b = -flipped_matrix.b
flipped_matrix.e = -flipped_matrix.e
@@ -1265,6 +1266,7 @@
if(istype(gravity_alert, /atom/movable/screen/alert/weightless))
REMOVE_TRAIT(src, TRAIT_MOVE_FLOATING, NO_GRAVITY_TRAIT)
if(istype(gravity_alert, /atom/movable/screen/alert/negative))
+ REMOVE_TRAIT(src, TRAIT_MOVE_UPSIDE_DOWN, NEGATIVE_GRAVITY_TRAIT)
var/matrix/flipped_matrix = transform
flipped_matrix.b = -flipped_matrix.b
flipped_matrix.e = -flipped_matrix.e
@@ -1513,6 +1515,7 @@
/mob/living/basic/morph,
/mob/living/basic/mouse,
/mob/living/basic/mushroom,
+ /mob/living/basic/parrot,
/mob/living/basic/pet/dog/breaddog,
/mob/living/basic/pet/dog/corgi,
/mob/living/basic/pet/dog/pug,
@@ -1523,7 +1526,6 @@
/mob/living/basic/stickman,
/mob/living/basic/stickman/dog,
/mob/living/simple_animal/hostile/megafauna/dragon/lesser,
- /mob/living/simple_animal/parrot,
/mob/living/simple_animal/pet/cat,
/mob/living/simple_animal/pet/cat/cak,
)
diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm
index 56d066cdcc0..50b4abf8035 100644
--- a/code/modules/mob/living/living_say.dm
+++ b/code/modules/mob/living/living_say.dm
@@ -255,7 +255,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list(
/mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range=0)
- if(!GET_CLIENT(src))
+ if((SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_HEAR, args) & COMSIG_MOVABLE_CANCEL_HEARING) || !GET_CLIENT(src))
return FALSE
var/deaf_message
@@ -536,7 +536,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list(
I.talk_into(src, message, , spans, language, message_mods)
return ITALICS | REDUCE_RANGE
- return 0
+ return NONE
/mob/living/say_mod(input, list/message_mods = list())
if(message_mods[WHISPER_MODE] == MODE_WHISPER)
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
deleted file mode 100644
index b02e8354da8..00000000000
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ /dev/null
@@ -1,1070 +0,0 @@
-/* Parrots!
- * Contains
- * Defines
- * Inventory (headset stuff)
- * Attack responces
- * AI
- * Procs / Verbs (usable by players)
- * Sub-types
- * Hear & say (the things we do for gimmicks)
- */
-
-/*
- * Defines
- */
-
-//Only a maximum of one action and one intent should be active at any given time.
-//Actions
-#define PARROT_PERCH (1<<0) //Sitting/sleeping, not moving
-#define PARROT_SWOOP (1<<1) //Moving towards or away from a target
-#define PARROT_WANDER (1<<2) //Moving without a specific target in mind
-
-//Intents
-#define PARROT_STEAL (1<<3) //Flying towards a target to steal it/from it
-#define PARROT_ATTACK (1<<4) //Flying towards a target to attack it
-#define PARROT_RETURN (1<<5) //Flying towards its perch
-#define PARROT_FLEE (1<<6) //Flying away from its attacker
-
-
-/mob/living/simple_animal/parrot
- name = "parrot"
- desc = "The parrot squawks, \"They're a Parrot! BAWWK!\"" //'
- icon = 'icons/mob/simple/animal.dmi'
- icon_state = "parrot_fly"
- icon_living = "parrot_fly"
- icon_dead = "parrot_dead"
- var/icon_sit = "parrot_sit"
- density = FALSE
- health = 80
- maxHealth = 80
- pass_flags = PASSTABLE | PASSMOB
-
- speak = list("Hi!","Hello!","Cracker?","BAWWWWK george mellons griffing me!")
- speak_emote = list("squawks","says","yells")
- emote_hear = list("squawks.","bawks!")
- emote_see = list("flutters their wings.")
-
- speak_chance = 1 //1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s
- turns_per_move = 5
- butcher_results = list(/obj/item/food/cracker = 1)
- melee_damage_upper = 10
- melee_damage_lower = 5
-
- response_help_continuous = "pets"
- response_help_simple = "pet"
- response_disarm_continuous = "gently moves aside"
- response_disarm_simple = "gently move aside"
- response_harm_continuous = "swats"
- response_harm_simple = "swat"
- stop_automated_movement = 1
- combat_mode = TRUE //parrots now start "aggressive" since only player parrots will nuzzle.
- attack_verb_continuous = "chomps"
- attack_verb_simple = "chomp"
- attack_vis_effect = ATTACK_EFFECT_BITE
- friendly_verb_continuous = "grooms"
- friendly_verb_simple = "groom"
- mob_size = MOB_SIZE_SMALL
- gold_core_spawnable = FRIENDLY_SPAWN
-
- var/parrot_damage_upper = 10
- var/parrot_state = PARROT_WANDER //Hunt for a perch when created
- var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in life() being run every single tick.
- var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down
- var/parrot_dam_zone = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_ARM, BODY_ZONE_R_LEG) //For humans, select a bodypart to attack
-
- var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower.
- var/parrot_lastmove = null //Updates/Stores position of the parrot while it's moving
- var/parrot_stuck = 0 //If parrot_lastmove hasn't changed, this will increment until it reaches parrot_stuck_threshold
- var/parrot_stuck_threshold = 10 //if this == parrot_stuck, it'll force the parrot back to wandering
-
- var/list/speech_buffer = list()
- var/speech_shuffle_rate = 20
- var/list/available_channels = list()
-
- //Headset for Poly to yell at engineers :)
- var/obj/item/radio/headset/ears = null
-
- //Wheter the Parrot should come with a headset
- var/spawn_headset = TRUE
-
- //The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from,
- //mobs it wants to attack or mobs that have attacked it
- var/atom/movable/parrot_interest = null
-
- //Parrots will generally sit on their perch unless something catches their eye.
- //These vars store their preffered perch and if they dont have one, what they can use as a perch
- var/obj/parrot_perch = null
- var/obj/desired_perches = list(/obj/structure/frame/computer,
- /obj/structure/displaycase,
- /obj/structure/filingcabinet,
- /obj/machinery/teleport,
- /obj/machinery/dna_scannernew,
- /obj/machinery/telecomms,
- /obj/machinery/nuclearbomb,
- /obj/machinery/recharge_station,
- /obj/machinery/smartfridge,
- /obj/machinery/computer,
- /obj/machinery/suit_storage_unit,
- )
-
- //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding.
- var/obj/item/held_item = null
-
-
-/mob/living/simple_animal/parrot/Initialize(mapload)
- . = ..()
- parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var
-
- add_verb(src, list(/mob/living/simple_animal/parrot/proc/steal_from_ground, \
- /mob/living/simple_animal/parrot/proc/steal_from_mob, \
- /mob/living/simple_animal/parrot/verb/drop_held_item_player, \
- /mob/living/simple_animal/parrot/proc/perch_player, \
- /mob/living/simple_animal/parrot/proc/toggle_mode,
- /mob/living/simple_animal/parrot/proc/perch_mob_player))
-
- AddElement(/datum/element/strippable, GLOB.strippable_parrot_items)
- AddElement(/datum/element/simple_flying)
- if(!spawn_headset)
- return
- if(!ears)
- var/headset = pick(/obj/item/radio/headset/headset_sec, \
- /obj/item/radio/headset/headset_eng, \
- /obj/item/radio/headset/headset_med, \
- /obj/item/radio/headset/headset_sci, \
- /obj/item/radio/headset/headset_cargo)
- ears = new headset(src)
-
-/mob/living/simple_animal/parrot/Destroy()
- QDEL_NULL(ears)
- return ..()
-
-/mob/living/simple_animal/parrot/examine(mob/user)
- . = ..()
- if(stat != DEAD)
- return
-
- if(HAS_MIND_TRAIT(user, TRAIT_NAIVE))
- . += pick(
- "It seems tired and shagged out after a long squawk.",
- "It seems to be pining for the fjords.",
- "It's resting. It's a beautiful bird. Lovely plumage.",
- )
- else
- . += pick(
- "This parrot is no more.",
- "This is a late parrot.",
- "This is an ex-parrot.",
- )
-
-/mob/living/simple_animal/parrot/death(gibbed)
- if(held_item)
- held_item.forceMove(drop_location())
- held_item = null
- SSmove_manager.stop_looping(src)
-
- if(buckled)
- buckled.unbuckle_mob(src,force=1)
- buckled = null
- pixel_x = base_pixel_x
- pixel_y = base_pixel_y
-
- return ..()
-
-
-/mob/living/simple_animal/parrot/get_status_tab_items()
- . = ..()
- . += "Held Item: [held_item]"
-
-// SKYRAT EDIT REMOVAL BEGIN - MOVED TO modular_skyrat/modules/poly_commands
-/*
-/mob/living/simple_animal/parrot/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range)
- . = ..()
- if(speaker != src && prob(50)) //Dont imitate ourselves
- if(!radio_freq || prob(10))
- if(speech_buffer.len >= 500)
- speech_buffer -= pick(speech_buffer)
- speech_buffer |= html_decode(raw_message)
- if(speaker == src && !client) //If a parrot squawks in the woods and no one is around to hear it, does it make a sound? This code says yes!
- return message
-*/
-// SKYRAT EDIT REMOVAL END
-
-/mob/living/simple_animal/parrot/radio(message, list/message_mods = list(), list/spans, language) //literally copied from human/radio(), but there's no other way to do this. at least it's better than it used to be.
- . = ..()
- if(.)
- return
-
- if(message_mods[MODE_HEADSET])
- if(ears)
- ears.talk_into(src, message, , spans, language, message_mods)
- return ITALICS | REDUCE_RANGE
- else if(message_mods[RADIO_EXTENSION] == MODE_DEPARTMENT)
- if(ears)
- ears.talk_into(src, message, message_mods[RADIO_EXTENSION], spans, language, message_mods)
- return ITALICS | REDUCE_RANGE
- else if(message_mods[RADIO_EXTENSION] in GLOB.radiochannels)
- if(ears)
- ears.talk_into(src, message, message_mods[RADIO_EXTENSION], spans, language, message_mods)
- return ITALICS | REDUCE_RANGE
-
- return FALSE
-
-GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
- /datum/strippable_item/parrot_headset,
-)))
-
-/datum/strippable_item/parrot_headset
- key = STRIPPABLE_ITEM_PARROT_HEADSET
-
-/datum/strippable_item/parrot_headset/get_item(atom/source)
- var/mob/living/simple_animal/parrot/parrot_source = source
- return istype(parrot_source) ? parrot_source.ears : null
-
-/datum/strippable_item/parrot_headset/try_equip(atom/source, obj/item/equipping, mob/user)
- . = ..()
- if (!.)
- return FALSE
-
- if (!istype(equipping, /obj/item/radio/headset))
- to_chat(user, span_warning("[equipping] won't fit!"))
- return FALSE
-
- return TRUE
-
-// There is no delay for putting a headset on a parrot.
-/datum/strippable_item/parrot_headset/start_equip(atom/source, obj/item/equipping, mob/user)
- return TRUE
-
-/datum/strippable_item/parrot_headset/finish_equip(atom/source, obj/item/equipping, mob/user)
- var/obj/item/radio/headset/radio = equipping
- if (!istype(radio))
- return
-
- var/mob/living/simple_animal/parrot/parrot_source = source
- if (!istype(parrot_source))
- return
-
- if (!user.transferItemToLoc(radio, source))
- return
-
- parrot_source.ears = radio
-
- to_chat(user, span_notice("You fit [radio] onto [source]."))
-
- parrot_source.available_channels.Cut()
-
- for (var/channel in radio.channels)
- var/channel_to_add
-
- switch (channel)
- if (RADIO_CHANNEL_ENGINEERING)
- channel_to_add = RADIO_TOKEN_ENGINEERING
- if (RADIO_CHANNEL_COMMAND)
- channel_to_add = RADIO_TOKEN_COMMAND
- if (RADIO_CHANNEL_SECURITY)
- channel_to_add = RADIO_TOKEN_SECURITY
- if (RADIO_CHANNEL_SCIENCE)
- channel_to_add = RADIO_TOKEN_SCIENCE
- if (RADIO_CHANNEL_MEDICAL)
- channel_to_add = RADIO_TOKEN_MEDICAL
- if (RADIO_CHANNEL_SUPPLY)
- channel_to_add = RADIO_TOKEN_SUPPLY
- if (RADIO_CHANNEL_SERVICE)
- channel_to_add = RADIO_TOKEN_SERVICE
-
- if (channel_to_add)
- parrot_source.available_channels += channel_to_add
-
- if (radio.translate_binary)
- parrot_source.available_channels.Add(MODE_TOKEN_BINARY)
-
-/datum/strippable_item/parrot_headset/start_unequip(atom/source, mob/user)
- . = ..()
- if (!.)
- return FALSE
-
- var/mob/living/simple_animal/parrot/parrot_source = source
- if (!istype(parrot_source))
- return
-
- if (!parrot_source.stat)
- parrot_source.say("[parrot_source.available_channels.len ? "[pick(parrot_source.available_channels)] " : null]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!")
-
- return TRUE
-
-/datum/strippable_item/parrot_headset/finish_unequip(atom/source, mob/user)
- var/mob/living/simple_animal/parrot/parrot_source = source
- if (!istype(parrot_source))
- return
-
- parrot_source.ears.forceMove(parrot_source.drop_location())
- parrot_source.ears = null
-
-/*
- * Attack responces
- */
-//Humans, monkeys, aliens
-/mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/user, list/modifiers)
- ..()
- if(client)
- return
- if(!stat && user.combat_mode)
-
- icon_state = icon_living //It is going to be flying regardless of whether it flees or attacks
-
- if(parrot_state == PARROT_PERCH)
- parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
-
- set_parrot_interest(user)
- parrot_state = PARROT_SWOOP //The parrot just got hit, it WILL move, now to pick a direction..
-
- if(health > 30) //Let's get in there and squawk it up!
- parrot_state |= PARROT_ATTACK
- else
- parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell!
- drop_held_item(0)
- if(stat != DEAD && !user.combat_mode)
- handle_automated_speech(1) //assured speak/emote
- return
-
-/mob/living/simple_animal/parrot/attack_paw(mob/living/carbon/human/user, list/modifiers)
- return attack_hand(user, modifiers)
-
-//Simple animals
-/mob/living/simple_animal/parrot/attack_animal(mob/living/simple_animal/user, list/modifiers)
- . = ..() //goodbye immortal parrots
- if(client)
- return
-
- if(parrot_state == PARROT_PERCH)
- parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
-
- if(. > 0 && stat == CONSCIOUS)
- set_parrot_interest(user)
- parrot_state = PARROT_SWOOP | PARROT_ATTACK //Attack other animals regardless
- icon_state = icon_living
-
-//Mobs with objects
-/mob/living/simple_animal/parrot/attackby(obj/item/O, mob/living/user, params)
- if(!stat && !client && !istype(O, /obj/item/stack/medical) && !istype(O, /obj/item/food/cracker))
- if(O.force)
- if(parrot_state == PARROT_PERCH)
- parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
-
- set_parrot_interest(user)
- parrot_state = PARROT_SWOOP
- if(health > 30) //Let's get in there and squawk it up!
- parrot_state |= PARROT_ATTACK
- else
- parrot_state |= PARROT_FLEE
- icon_state = icon_living
- drop_held_item(0)
- else if(istype(O, /obj/item/food/cracker)) //Poly wants a cracker.
- qdel(O)
- if(health < maxHealth)
- adjustBruteLoss(-10)
- speak_chance *= 1.27 // 20 crackers to go from 1% to 100%
- speech_shuffle_rate += 10
- to_chat(user, span_notice("[src] eagerly devours the cracker."))
- ..()
- return
-
-//Bullets
-/mob/living/simple_animal/parrot/bullet_act(obj/projectile/Proj)
- . = ..()
- if(!stat && !client)
- if(parrot_state == PARROT_PERCH)
- parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
-
- set_parrot_interest(null)
- parrot_state = PARROT_WANDER | PARROT_FLEE //Been shot and survived! RUN LIKE HELL!
- //parrot_been_shot += 5
- icon_state = icon_living
- drop_held_item(0)
-
-/mob/living/simple_animal/parrot/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
- if(!stat) //Birds can fly, fun fact. No I don't care that space doesn't have air. Space parrots bitch
- return TRUE
- return ..()
-/*
- * AI - Not really intelligent, but I'm calling it AI anyway.
- */
-/mob/living/simple_animal/parrot/Life(seconds_per_tick = SSMOBS_DT, times_fired)
- ..()
-
- //Sprite update for when a parrot gets pulled
- if(pulledby && !stat && parrot_state != PARROT_WANDER)
- if(buckled)
- buckled.unbuckle_mob(src, TRUE)
- buckled = null
- icon_state = icon_living
- parrot_state = PARROT_WANDER
- pixel_x = initial(pixel_x)
- pixel_y = initial(pixel_y)
- return
-
-
-//-----SPEECH
- /* Parrot speech mimickry!
- Phrases that the parrot Hear()s get added to speach_buffer.
- Every once in a while, the parrot picks one of the lines from the buffer and replaces an element of the 'speech' list. */
-/mob/living/simple_animal/parrot/handle_automated_speech()
- ..()
- if(speech_buffer.len && prob(speech_shuffle_rate)) //shuffle out a phrase and add in a new one
- if(speak.len)
- speak.Remove(pick(speak))
-
- speak.Add(pick(speech_buffer))
-
-
-/mob/living/simple_animal/parrot/handle_automated_movement()
- if(!isturf(src.loc) || !(mobility_flags & MOBILITY_MOVE) || buckled)
- return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove)
-
- if(client && stat == CONSCIOUS && parrot_state != icon_living)
- icon_state = icon_living
-
-//-----SLEEPING
- if(parrot_state == PARROT_PERCH)
- if(parrot_perch && parrot_perch.loc != src.loc) //Make sure someone hasn't moved our perch on us
- if(parrot_perch in view(src))
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- icon_state = icon_living
- return
- else
- parrot_state = PARROT_WANDER
- icon_state = icon_living
- return
-
- parrot_sleep_dur--
- if(parrot_sleep_dur) //Zzz
- return
-
- else
- //This way we only call the stuff below once every [sleep_max] ticks.
- parrot_sleep_dur = parrot_sleep_max
-
- //Cycle through message modes for the headset
- if(speak.len)
- var/list/newspeak = list()
-
- if(available_channels.len && src.ears)
- for(var/possible_phrase in speak)
-
- //50/50 chance to not use the radio at all
- var/useradio = 0
- if(prob(50))
- useradio = 1
-
- if((possible_phrase[1] in GLOB.department_radio_prefixes) && (copytext_char(possible_phrase, 2, 3) in GLOB.department_radio_keys))
- possible_phrase = "[useradio?pick(available_channels):""][copytext_char(possible_phrase, 3)]" //crop out the channel prefix
- else
- possible_phrase = "[useradio?pick(available_channels):""][possible_phrase]"
-
- newspeak.Add(possible_phrase)
-
- else //If we have no headset or channels to use, dont try to use any!
- for(var/possible_phrase in speak)
- if((possible_phrase[1] in GLOB.department_radio_prefixes) && (copytext_char(possible_phrase, 2, 3) in GLOB.department_radio_keys))
- possible_phrase = copytext_char(possible_phrase, 3) //crop out the channel prefix
- newspeak.Add(possible_phrase)
- speak = newspeak
-
- //Search for item to steal
- set_parrot_interest(search_for_item())
- if(parrot_interest)
- manual_emote("looks in [parrot_interest]'s direction and takes flight.")
- parrot_state = PARROT_SWOOP | PARROT_STEAL
- icon_state = icon_living
- return
-
-//-----WANDERING - This is basically a 'I dont know what to do yet' state
- else if(parrot_state == PARROT_WANDER)
- //Stop movement, we'll set it later
- SSmove_manager.stop_looping(src)
- set_parrot_interest(null)
-
- //Wander around aimlessly. This will help keep the loops from searches down
- //and possibly move the mob into a new are in view of something they can use
- if(prob(90))
- step(src, pick(GLOB.cardinals))
- return
-
- if(!held_item && !parrot_perch) //If we've got nothing to do.. look for something to do.
- var/atom/movable/AM = search_for_perch_and_item() //This handles checking through lists so we know it's either a perch or stealable item
- if(AM)
- if(isitem(AM) || isliving(AM)) //If stealable item
- set_parrot_interest(AM)
- manual_emote("turns and flies towards [parrot_interest].")
- parrot_state = PARROT_SWOOP | PARROT_STEAL
- return
- else //Else it's a perch
- parrot_perch = AM
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- return
- return
-
- if(parrot_interest && (parrot_interest in view(src)))
- parrot_state = PARROT_SWOOP | PARROT_STEAL
- return
-
- if(parrot_perch && (parrot_perch in view(src)))
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- return
-
- else //Have an item but no perch? Find one!
- parrot_perch = search_for_perch()
- if(parrot_perch)
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- return
-//-----STEALING
- else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL))
- SSmove_manager.stop_looping(src)
- if(!parrot_interest || held_item)
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- return
-
- if(!(parrot_interest in view(src)))
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- return
-
- if(Adjacent(parrot_interest))
-
- if(isliving(parrot_interest))
- steal_from_mob()
-
- else //This should ensure that we only grab the item we want, and make sure it's not already collected on our perch
- if(!parrot_perch || parrot_interest.loc != parrot_perch.loc)
- held_item = parrot_interest
- parrot_interest.forceMove(src)
- visible_message(span_notice("[src] grabs [held_item]!"), span_notice("You grab [held_item]!"), span_hear("You hear the sounds of wings flapping furiously."))
-
- set_parrot_interest(null)
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- return
-
- SSmove_manager.move_to(src, parrot_interest, 1, parrot_speed)
- if(isStuck())
- return
-
- return
-
-//-----RETURNING TO PERCH
- else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN))
- SSmove_manager.stop_looping(src)
- if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isn't inside of something else.
- parrot_perch = null
- parrot_state = PARROT_WANDER
- return
-
- if(Adjacent(parrot_perch))
- forceMove(parrot_perch.loc)
- drop_held_item()
- parrot_state = PARROT_PERCH
- icon_state = icon_sit
- return
-
- SSmove_manager.move_to(src, parrot_perch, 1, parrot_speed)
- if(isStuck())
- return
-
- return
-
-//-----FLEEING
- else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE))
- SSmove_manager.stop_looping(src)
- if(!parrot_interest || !isliving(parrot_interest)) //Sanity
- parrot_state = PARROT_WANDER
-
- SSmove_manager.move_away(src, parrot_interest, 1, parrot_speed)
- if(isStuck())
- return
-
- return
-
-//-----ATTACKING
- else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK))
-
- //If we're attacking a nothing, an object, a turf or a ghost for some stupid reason, switch to wander
- if(!parrot_interest || !isliving(parrot_interest))
- set_parrot_interest(null)
- parrot_state = PARROT_WANDER
- return
-
- var/mob/living/L = parrot_interest
- if(melee_damage_upper == 0)
- melee_damage_upper = parrot_damage_upper
- set_combat_mode(TRUE)
-
- //If the mob is close enough to interact with
- if(Adjacent(parrot_interest))
-
- //If the mob we've been chasing/attacking dies or falls into crit, check for loot!
- if(L.stat)
- set_parrot_interest(null)
- if(!held_item)
- held_item = steal_from_ground()
- if(!held_item)
- held_item = steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances.
- if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- else
- parrot_state = PARROT_WANDER
- return
-
- attack_verb_continuous = pick("claws at", "chomps")
- attack_verb_simple = pick("claw at", "chomp")
- L.attack_animal(src)//Time for the hurt to begin!
- //Otherwise, fly towards the mob!
- else
- SSmove_manager.move_to(src, parrot_interest, 1, parrot_speed)
- if(isStuck())
- return
-
- return
-//-----STATE MISHAP
- else //This should not happen. If it does lets reset everything and try again
- SSmove_manager.stop_looping(src)
- set_parrot_interest(null)
- parrot_perch = null
- drop_held_item()
- parrot_state = PARROT_WANDER
- return
-
-/*
- * Procs
- */
-
-/mob/living/simple_animal/parrot/proc/set_parrot_interest(atom/movable/shiny)
- if(parrot_interest)
- UnregisterSignal(parrot_interest, COMSIG_QDELETING)
- parrot_interest = shiny
- if(parrot_interest)
- RegisterSignal(parrot_interest, COMSIG_QDELETING, PROC_REF(shiny_deleted))
-
-/mob/living/simple_animal/parrot/proc/shiny_deleted(datum/source)
- SIGNAL_HANDLER
- set_parrot_interest(null)
-
-/mob/living/simple_animal/parrot/proc/isStuck()
- //Check to see if the parrot is stuck due to things like windows or doors or windowdoors
- if(parrot_lastmove)
- if(parrot_lastmove == src.loc)
- if(parrot_stuck_threshold >= ++parrot_stuck) //If it has been stuck for a while, go back to wander.
- parrot_state = PARROT_WANDER
- parrot_stuck = 0
- parrot_lastmove = null
- return TRUE
- else
- parrot_lastmove = null
- else
- parrot_lastmove = src.loc
- return FALSE
-
-/mob/living/simple_animal/parrot/proc/search_for_item()
- var/item
- for(var/atom/movable/AM in view(src))
- //Skip items we already stole or are wearing or are too big
- if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src)
- continue
- if(isitem(AM))
- var/obj/item/I = AM
- if(I.w_class < WEIGHT_CLASS_SMALL)
- item = I
- else if(iscarbon(AM))
- var/mob/living/carbon/C = AM
- for(var/obj/item/I in C.held_items)
- if(I.w_class <= WEIGHT_CLASS_SMALL)
- item = I
- break
- if(item)
- if(!length(get_path_to(src, item))) // WHY DO WE DISREGARD THE PATH AHHHHHH
- item = null
- continue
- return item
-
-/mob/living/simple_animal/parrot/proc/search_for_perch()
- for(var/obj/O in view(src))
- for(var/path in desired_perches)
- if(istype(O, path))
- return O
- return null
-
-//This proc was made to save on doing two 'in view' loops seperatly
-/mob/living/simple_animal/parrot/proc/search_for_perch_and_item()
- for(var/atom/movable/AM in view(src))
- for(var/perch_path in desired_perches)
- if(istype(AM, perch_path))
- return AM
-
- //Skip items we already stole or are wearing or are too big
- if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src)
- continue
-
- if(isitem(AM))
- var/obj/item/I = AM
- if(I.w_class <= WEIGHT_CLASS_SMALL)
- return I
-
- if(iscarbon(AM))
- var/mob/living/carbon/C = AM
- for(var/obj/item/I in C.held_items)
- if(I.w_class <= WEIGHT_CLASS_SMALL)
- return C
- return null
-
-
-/*
- * Verbs - These are actually procs, but can be used as verbs by player-controlled parrots.
- */
-/mob/living/simple_animal/parrot/proc/steal_from_ground()
- set name = "Steal from ground"
- set category = "Parrot"
- set desc = "Grabs a nearby item."
-
- if(stat)
- return -1
-
- if(held_item)
- to_chat(src, span_warning("You are already holding [held_item]!"))
- return 1
-
- for(var/obj/item/I in view(1,src))
- //Make sure we're not already holding it and it's small enough
- if(I.loc != src && I.w_class <= WEIGHT_CLASS_SMALL)
-
- //If we have a perch and the item is sitting on it, continue
- if(!client && parrot_perch && I.loc == parrot_perch.loc)
- continue
-
- held_item = I
- I.forceMove(src)
- visible_message(span_notice("[src] grabs [held_item]!"), span_notice("You grab [held_item]!"), span_hear("You hear the sounds of wings flapping furiously."))
- return held_item
-
- to_chat(src, span_warning("There is nothing of interest to take!"))
- return 0
-
-/mob/living/simple_animal/parrot/proc/steal_from_mob()
- set name = "Steal from mob"
- set category = "Parrot"
- set desc = "Steals an item right out of a person's hand!"
-
- if(stat)
- return -1
-
- if(held_item)
- to_chat(src, span_warning("You are already holding [held_item]!"))
- return 1
-
- var/obj/item/stolen_item = null
-
- for(var/mob/living/carbon/C in view(1,src))
- for(var/obj/item/I in C.held_items)
- if(I.w_class <= WEIGHT_CLASS_SMALL)
- stolen_item = I
- break
-
- if(stolen_item)
- C.transferItemToLoc(stolen_item, src, TRUE)
- held_item = stolen_item
- visible_message(span_notice("[src] grabs [held_item] out of [C]'s hand!"), span_notice("You snag [held_item] out of [C]'s hand!"), span_hear("You hear the sounds of wings flapping furiously."))
- return held_item
-
- to_chat(src, span_warning("There is nothing of interest to take!"))
- return 0
-
-/mob/living/simple_animal/parrot/verb/drop_held_item_player()
- set name = "Drop held item"
- set category = "Parrot"
- set desc = "Drop the item you're holding."
-
- if(stat)
- return
-
- src.drop_held_item()
-
- return
-
-/mob/living/simple_animal/parrot/proc/drop_held_item(drop_gently = 1)
- set name = "Drop held item"
- set category = "Parrot"
- set desc = "Drop the item you're holding."
-
- if(stat)
- return -1
-
- if(!held_item)
- if(src == usr) //So that other mobs won't make this message appear when they're bludgeoning you.
- to_chat(src, span_warning("You have nothing to drop!"))
- return 0
-
-
-//parrots will eat crackers instead of dropping them
- if(istype(held_item, /obj/item/food/cracker) && (drop_gently))
- qdel(held_item)
- held_item = null
- if(health < maxHealth)
- adjustBruteLoss(-10)
- manual_emote("[src] eagerly downs the cracker.")
- return 1
-
-
- if(!drop_gently)
- if(isgrenade(held_item))
- var/obj/item/grenade/G = held_item
- G.forceMove(drop_location())
- G.detonate()
- to_chat(src, span_danger("You let go of [held_item]!"))
- held_item = null
- return 1
-
- to_chat(src, span_notice("You drop [held_item]."))
-
- held_item.forceMove(drop_location())
- held_item = null
- return 1
-
-/mob/living/simple_animal/parrot/proc/perch_player()
- set name = "Sit"
- set category = "Parrot"
- set desc = "Sit on a nice comfy perch."
-
- if(stat || !client)
- return
-
- if(icon_state == icon_living)
- for(var/atom/movable/AM in view(src,1))
- for(var/perch_path in desired_perches)
- if(istype(AM, perch_path))
- src.forceMove(AM.loc)
- icon_state = icon_sit
- parrot_state = PARROT_PERCH
- return
- to_chat(src, span_warning("There is no perch nearby to sit on!"))
- return
-
-/mob/living/simple_animal/parrot/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
- . = ..()
- if(. && !stat && client && parrot_state == PARROT_PERCH)
- parrot_state = PARROT_WANDER
- icon_state = icon_living
- pixel_x = initial(pixel_x)
- pixel_y = initial(pixel_y)
-
-/mob/living/simple_animal/parrot/proc/perch_mob_player()
- set name = "Sit on Human's Shoulder"
- set category = "Parrot"
- set desc = "Sit on a nice comfy human being!"
-
- if(stat || !client)
- return
-
- if(!buckled)
- for(var/mob/living/carbon/human/H in view(src,1))
- if(H.has_buckled_mobs() && H.buckled_mobs.len >= H.max_buckled_mobs) //Already has a parrot, or is being eaten by a slime
- continue
- perch_on_human(H)
- return
- to_chat(src, span_warning("There is nobody nearby that you can sit on!"))
- else
- icon_state = icon_living
- parrot_state = PARROT_WANDER
- if(buckled)
- to_chat(src, span_notice("You are no longer sitting on [buckled]'s shoulder."))
- buckled.unbuckle_mob(src, TRUE)
- buckled = null
- pixel_x = initial(pixel_x)
- pixel_y = initial(pixel_y)
-
-/* SKYRAT EDIT - MOVED TO modular_skyrat/modules/poly_commands/parrot.dm
-/mob/living/simple_animal/parrot/proc/perch_on_human(mob/living/carbon/human/H)
- if(!H)
- return
- forceMove(get_turf(H))
- if(H.buckle_mob(src, TRUE))
- pixel_y = 9
- pixel_x = pick(-8,8) //pick left or right shoulder
- icon_state = icon_sit
- parrot_state = PARROT_PERCH
- to_chat(src, span_notice("You sit on [H]'s shoulder."))
-*/
-
-/mob/living/simple_animal/parrot/proc/toggle_mode()
- set name = "Toggle mode"
- set category = "Parrot"
- set desc = "Time to bear those claws!"
-
- if(stat || !client)
- return
-
- if(combat_mode)
- melee_damage_upper = 0
- set_combat_mode(FALSE)
- else
- melee_damage_upper = parrot_damage_upper
- set_combat_mode(TRUE)
- to_chat(src, span_notice("You will now [combat_mode ? "Harm" : "Help"] others."))
- return
-
-/mob/living/simple_animal/parrot/natural
- spawn_headset = FALSE
-/*
- * Sub-types
- */
-/mob/living/simple_animal/parrot/poly
- name = "Poly"
- desc = "Poly the Parrot. An expert on quantum cracker theory."
- speak = list("Poly wanna cracker!", ":e Check the crystal, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN MODSUITS?",":e OH GOD ITS ABOUT TO DELAMINATE CALL THE SHUTTLE")
- gold_core_spawnable = NO_SPAWN
- speak_chance = 3
-
- var/memory_saved = FALSE
- var/rounds_survived = 0
- var/longest_survival = 0
- var/longest_deathstreak = 0
-
-
-/mob/living/simple_animal/parrot/poly/Initialize(mapload)
- ears = new /obj/item/radio/headset/headset_eng(src)
- if(SStts.tts_enabled)
- voice = pick(SStts.available_speakers)
- if(SStts.pitch_enabled)
- if(findtext(voice, "Woman"))
- pitch = 12 // up-pitch by one octave
- else
- pitch = 24 // up-pitch by 2 octaves
- else
- voice_filter = "rubberband=pitch=1.5" // Use the filter to pitch up if we can't naturally pitch up.
-
- available_channels = list(":e")
- Read_Memory()
- if(rounds_survived == longest_survival)
- speak += pick("...[longest_survival].", "The things I've seen!", "I have lived many lives!", "What are you before me?")
- desc += " Old as sin, and just as loud. Claimed to be [rounds_survived]."
- speak_chance = 20 //His hubris has made him more annoying/easier to justify killing
- add_atom_colour("#EEEE22", FIXED_COLOUR_PRIORITY)
- else if(rounds_survived == longest_deathstreak)
- speak += pick("What are you waiting for!", "Violence breeds violence!", "Blood! Blood!", "Strike me down if you dare!")
- desc += " The squawks of [-rounds_survived] dead parrots ring out in your ears..."
- add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY)
- else if(rounds_survived > 0)
- speak += pick("...again?", "No, It was over!", "Let me out!", "It never ends!")
- desc += " Over [rounds_survived] shifts without a \"terrible\" \"accident\"!"
- else
- speak += pick("...alive?", "This isn't parrot heaven!", "I live, I die, I live again!", "The void fades!")
-
- . = ..()
-
- // Ensure 1 Poly exists
- REGISTER_REQUIRED_MAP_ITEM(1, 1)
-
-/mob/living/simple_animal/parrot/poly/Life(seconds_per_tick = SSMOBS_DT, times_fired)
- if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved)
- Write_Memory(FALSE)
- memory_saved = TRUE
- ..()
-
-/mob/living/simple_animal/parrot/poly/death(gibbed)
- if(HAS_TRAIT(src, TRAIT_DONT_WRITE_MEMORY))
- return ..() // Don't read memory either.
- if(!memory_saved)
- Write_Memory(TRUE)
- if(rounds_survived == longest_survival || rounds_survived == longest_deathstreak || prob(0.666))
- var/mob/living/simple_animal/parrot/poly/ghost/G = new(loc)
- if(mind)
- mind.transfer_to(G)
- else
- G.key = key
- return ..()
-
-/mob/living/simple_animal/parrot/poly/proc/Read_Memory()
- if(fexists("data/npc_saves/Poly.sav")) //legacy compatability to convert old format to new
- var/savefile/S = new /savefile("data/npc_saves/Poly.sav")
- S["phrases"] >> speech_buffer
- S["roundssurvived"] >> rounds_survived
- S["longestsurvival"] >> longest_survival
- S["longestdeathstreak"] >> longest_deathstreak
- fdel("data/npc_saves/Poly.sav")
- else
- var/json_file = file("data/npc_saves/Poly.json")
- if(!fexists(json_file))
- return
- var/list/json = json_decode(file2text(json_file))
- speech_buffer = json["phrases"]
- rounds_survived = json["roundssurvived"]
- longest_survival = json["longestsurvival"]
- longest_deathstreak = json["longestdeathstreak"]
- if(!islist(speech_buffer))
- speech_buffer = list()
-
-/mob/living/simple_animal/parrot/poly/Write_Memory(dead, gibbed)
- . = ..()
- if(!.)
- return
- var/json_file = file("data/npc_saves/Poly.json")
- var/list/file_data = list()
- if(islist(speech_buffer))
- file_data["phrases"] = speech_buffer
- if(dead)
- file_data["roundssurvived"] = min(rounds_survived - 1, 0)
- file_data["longestsurvival"] = longest_survival
- if(rounds_survived - 1 < longest_deathstreak)
- file_data["longestdeathstreak"] = rounds_survived - 1
- else
- file_data["longestdeathstreak"] = longest_deathstreak
- else
- file_data["roundssurvived"] = max(rounds_survived, 0) + 1
- if(rounds_survived + 1 > longest_survival)
- file_data["longestsurvival"] = rounds_survived + 1
- else
- file_data["longestsurvival"] = longest_survival
- file_data["longestdeathstreak"] = longest_deathstreak
- fdel(json_file)
- WRITE_FILE(json_file, json_encode(file_data))
-
-/mob/living/simple_animal/parrot/poly/ghost
- name = "The Ghost of Poly"
- desc = "Doomed to squawk the Earth."
- color = "#FFFFFF77"
- speak_chance = 20
- status_flags = GODMODE
- sentience_type = SENTIENCE_BOSS //This is so players can't mindswap into ghost poly to become a literal god
- incorporeal_move = INCORPOREAL_MOVE_BASIC
- butcher_results = list(/obj/item/ectoplasm = 1)
-
-/mob/living/simple_animal/parrot/poly/ghost/Initialize(mapload)
- memory_saved = TRUE //At this point nothing is saved
- . = ..()
-
-/mob/living/simple_animal/parrot/poly/ghost/handle_automated_speech()
- if(ismob(loc))
- return
- ..()
-
-/mob/living/simple_animal/parrot/poly/ghost/handle_automated_movement()
- if(isliving(parrot_interest))
- if(!ishuman(parrot_interest))
- set_parrot_interest(null)
- else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK) && Adjacent(parrot_interest))
- SSmove_manager.move_to(src, parrot_interest, 0, parrot_speed)
- Possess(parrot_interest)
- ..()
-
-/mob/living/simple_animal/parrot/poly/ghost/proc/Possess(mob/living/carbon/human/H)
- if(!ishuman(H))
- return
- var/datum/disease/parrot_possession/P = new
- P.parrot = src
- forceMove(H)
- H.ForceContractDisease(P, FALSE)
- set_parrot_interest(null)
- H.visible_message(span_danger("[src] dive bombs into [H]'s chest and vanishes!"), span_userdanger("[src] dive bombs into your chest, vanishing! This can't be good!"))
-
-#undef PARROT_PERCH
-#undef PARROT_SWOOP
-#undef PARROT_WANDER
-#undef PARROT_STEAL
-#undef PARROT_ATTACK
-#undef PARROT_RETURN
-#undef PARROT_FLEE
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 790f9312ea1..6136f8c8189 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -398,7 +398,7 @@
return TRUE
if(ispath(MP, /mob/living/basic/bear))
return TRUE
- if(ispath(MP, /mob/living/simple_animal/parrot))
+ if(ispath(MP, /mob/living/basic/parrot))
return TRUE //Parrots are no longer unfinished! -Nodrak
//Not in here? Must be untested!
diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm
index f7b53eaa9f0..30889e4f982 100644
--- a/code/modules/mod/modules/modules_maint.dm
+++ b/code/modules/mod/modules/modules_maint.dm
@@ -288,6 +288,7 @@
RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(check_upstairs))
RegisterSignal(mod.wearer, COMSIG_MOB_SAY, PROC_REF(on_talk))
ADD_TRAIT(mod.wearer, TRAIT_SILENT_FOOTSTEPS, MOD_TRAIT)
+ passtable_on(mod.wearer, MOD_TRAIT)
check_upstairs() //todo at some point flip your screen around
/obj/item/mod/module/atrocinator/on_deactivation(display_message = TRUE, deleting = FALSE)
@@ -304,6 +305,7 @@
UnregisterSignal(mod.wearer, COMSIG_MOB_SAY)
step_count = 0
REMOVE_TRAIT(mod.wearer, TRAIT_SILENT_FOOTSTEPS, MOD_TRAIT)
+ passtable_off(mod.wearer, MOD_TRAIT)
var/turf/open/openspace/current_turf = get_turf(mod.wearer)
if(istype(current_turf))
current_turf.zFall(mod.wearer, falling_from_move = TRUE)
diff --git a/code/modules/modular_computers/computers/item/pda.dm b/code/modules/modular_computers/computers/item/pda.dm
index a4b08f2d90f..0c027b5f30b 100644
--- a/code/modules/modular_computers/computers/item/pda.dm
+++ b/code/modules/modular_computers/computers/item/pda.dm
@@ -38,6 +38,7 @@
/datum/computer_file/program/notepad,
// SKYRAT EDIT ADDITION START
/datum/computer_file/program/crew_manifest, // Adds crew manifest to all base tablets
+ /datum/computer_file/program/maintenance/camera // Adds camera to all base tablets
// SKRAT EDIT ADDITION END
)
///List of items that can be stored in a PDA
diff --git a/code/modules/power/lighting/light_items.dm b/code/modules/power/lighting/light_items.dm
index 9f2bff9cdca..5e9df6ee432 100644
--- a/code/modules/power/lighting/light_items.dm
+++ b/code/modules/power/lighting/light_items.dm
@@ -116,7 +116,7 @@
if(!isliving(moving_atom))
return
var/mob/living/moving_mob = moving_atom
- if(!(moving_mob.movement_type & (FLYING|FLOATING)) || moving_mob.buckled)
+ if(!(moving_mob.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || moving_mob.buckled)
playsound(src, 'sound/effects/footstep/glass_step.ogg', HAS_TRAIT(moving_mob, TRAIT_LIGHT_STEP) ? 30 : 50, TRUE)
if(status == LIGHT_BURNED || status == LIGHT_OK)
shatter(moving_mob)
diff --git a/code/modules/projectiles/ammunition/energy/special.dm b/code/modules/projectiles/ammunition/energy/special.dm
index 9a733dbc47a..5a83a944e41 100644
--- a/code/modules/projectiles/ammunition/energy/special.dm
+++ b/code/modules/projectiles/ammunition/energy/special.dm
@@ -81,6 +81,7 @@
/obj/item/ammo_casing/energy/fisher
projectile_type = /obj/projectile/energy/fisher
- select_name = "light-buster"
+ select_name = "light disruptor"
+ harmful = FALSE
e_cost = LASER_SHOTS(2, STANDARD_CELL_CHARGE * 0.5)
fire_sound = 'sound/weapons/gun/general/heavy_shot_suppressed.ogg' // fwip fwip fwip fwip
diff --git a/code/modules/projectiles/projectile/energy/net_snare.dm b/code/modules/projectiles/projectile/energy/net_snare.dm
index c60c0c35d17..925096f6351 100644
--- a/code/modules/projectiles/projectile/energy/net_snare.dm
+++ b/code/modules/projectiles/projectile/energy/net_snare.dm
@@ -69,7 +69,7 @@
new/obj/item/restraints/legcuffs/beartrap/energy(get_turf(loc))
else if(iscarbon(target))
var/obj/item/restraints/legcuffs/beartrap/B = new /obj/item/restraints/legcuffs/beartrap/energy(get_turf(target))
- B.spring_trap(null, target)
+ B.spring_trap(target)
. = ..()
/obj/projectile/energy/trap/on_range()
@@ -88,7 +88,7 @@
qdel(src)
if(iscarbon(target))
var/obj/item/restraints/legcuffs/beartrap/B = new /obj/item/restraints/legcuffs/beartrap/energy/cyborg(get_turf(target))
- B.spring_trap(null, target)
+ B.spring_trap(target)
QDEL_IN(src, 10)
. = ..()
diff --git a/code/modules/projectiles/projectile/special/lightbreaker.dm b/code/modules/projectiles/projectile/special/lightbreaker.dm
index fd7d3d89e7a..2be6d9e4470 100644
--- a/code/modules/projectiles/projectile/special/lightbreaker.dm
+++ b/code/modules/projectiles/projectile/special/lightbreaker.dm
@@ -4,7 +4,7 @@
damage = 0
damage_type = BRUTE
armor_flag = BOMB
- range = 14
+ range = 21
projectile_phasing = PASSTABLE | PASSMOB | PASSMACHINE | PASSSTRUCTURE
hitscan = TRUE
var/disrupt_duration = 10 SECONDS
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index 63648375045..0c9107ab19a 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -61,6 +61,8 @@
/obj/item/reagent_containers/hypospray/cmo
+ volume = 60
+ possible_transfer_amounts = list(1,3,5)
list_reagents = list(/datum/reagent/medicine/omnizine = 30)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index 6a86bcbaae1..8c1bfa246a7 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -1581,6 +1581,17 @@
RND_CATEGORY_MECHFAB_CYBORG_MODULES + RND_SUBCATEGORY_MECHFAB_CYBORG_MODULES_ENGINEERING
)
+/datum/design/borg_upgrade_inducer
+ name = "Cyborg inducer"
+ id = "borg_upgrade_inducer"
+ build_type = MECHFAB
+ build_path = /obj/item/borg/upgrade/inducer
+ materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 2)
+ construction_time = 120
+ category = list(
+ RND_CATEGORY_MECHFAB_CYBORG_MODULES + RND_SUBCATEGORY_MECHFAB_CYBORG_MODULES_ENGINEERING
+ )
+
/datum/design/borg_upgrade_circuit_app
name = "Circuit Manipulator"
id = "borg_upgrade_circuitapp"
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index 3acfe8f5294..17c425c4e2f 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -641,10 +641,10 @@
/mob/living/basic/crab,
/mob/living/basic/lizard,
/mob/living/basic/mouse,
+ /mob/living/basic/parrot,
/mob/living/basic/pet/dog/corgi,
/mob/living/basic/pet/dog/pug,
/mob/living/basic/pet/fox,
- /mob/living/simple_animal/parrot/natural,
/mob/living/simple_animal/pet/cat,
)
for(var/counter in 1 to rand(1, 25))
diff --git a/code/modules/research/stock_parts.dm b/code/modules/research/stock_parts.dm
index 86b9f2dfafe..6638f56c1b4 100644
--- a/code/modules/research/stock_parts.dm
+++ b/code/modules/research/stock_parts.dm
@@ -197,12 +197,18 @@ If you create T5+ please take a pass at mech_fabricator.dm. The parts being good
/obj/item/storage/part_replacer/cyborg //SKYRAT EDIT - ICON OVERRIDEN BY AESTHETICS - SEE MODULE
name = "rapid part exchange device"
- desc = "Special mechanical module made to store, sort, and apply standard machine parts."
+ desc = "Special mechanical module made to store, sort, and apply standard machine parts. This one has an extra large compartment for more parts."
icon_state = "borgrped"
inhand_icon_state = "RPED"
lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
+/obj/item/storage/part_replacer/cyborg/Initialize(mapload)
+ . = ..()
+ atom_storage.max_slots = 400
+ atom_storage.max_total_storage = 800
+ atom_storage.max_specific_storage = WEIGHT_CLASS_GIGANTIC
+
/obj/item/storage/part_replacer/proc/get_sorted_parts(ignore_stacks = FALSE)
var/list/part_list = list()
//Assemble a list of current parts, then sort them by their rating!
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index d46bb35d8c3..28710bbfb8d 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -1017,6 +1017,7 @@
"borg_upgrade_lavaproof",
"borg_upgrade_rped",
"borg_upgrade_hypermod",
+ "borg_upgrade_inducer",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index d05588f6294..a9dab01c918 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -1267,12 +1267,12 @@
if(BLEED_OVERLAY_LOW to BLEED_OVERLAY_MED)
new_bleed_icon = "[body_zone]_1"
if(BLEED_OVERLAY_MED to BLEED_OVERLAY_GUSH)
- if(owner.body_position == LYING_DOWN || IS_IN_STASIS(owner) || owner.stat == DEAD)
+ if(owner.body_position == LYING_DOWN || HAS_TRAIT(owner, TRAIT_STASIS) || owner.stat == DEAD)
new_bleed_icon = "[body_zone]_2s"
else
new_bleed_icon = "[body_zone]_2"
if(BLEED_OVERLAY_GUSH to INFINITY)
- if(IS_IN_STASIS(owner) || owner.stat == DEAD)
+ if(HAS_TRAIT(owner, TRAIT_STASIS) || owner.stat == DEAD)
new_bleed_icon = "[body_zone]_2s"
else
new_bleed_icon = "[body_zone]_3"
diff --git a/code/modules/surgery/organs/external/tails.dm b/code/modules/surgery/organs/external/tails.dm
index e21f277ecf6..bfeb128eb7a 100644
--- a/code/modules/surgery/organs/external/tails.dm
+++ b/code/modules/surgery/organs/external/tails.dm
@@ -7,11 +7,12 @@
zone = BODY_ZONE_PRECISE_GROIN
slot = ORGAN_SLOT_EXTERNAL_TAIL
- bodypart_overlay = /datum/bodypart_overlay/mutant/tail
-
//dna_block = DNA_TAIL_BLOCK // SKYRAT EDIT REMOVAL - Customization - We have our own system to handle DNA.
restyle_flags = EXTERNAL_RESTYLE_FLESH
+ // defaults to cat, but the parent type shouldn't be created regardless
+ bodypart_overlay = /datum/bodypart_overlay/mutant/tail/cat
+
///Does this tail have a wagging sprite, and is it currently wagging?
var/wag_flags = NONE
///The original owner of this tail
@@ -83,7 +84,7 @@
organ_owner.update_body_parts()
UnregisterSignal(organ_owner, COMSIG_LIVING_DEATH)
-///Tail parent type (which is MONKEEEEEEEEEEE by default), with wagging functionality
+///Tail parent type, with wagging functionality
/datum/bodypart_overlay/mutant/tail
layers = EXTERNAL_FRONT|EXTERNAL_BEHIND
feature_key = "tail" // SKYRAT EDIT - Customization - ORIGINAL: feature_key = "tail_monkey"
@@ -92,8 +93,10 @@
/datum/bodypart_overlay/mutant/tail/get_base_icon_state()
return (wagging ? "wagging_" : "") + sprite_datum.icon_state //add the wagging tag if we be wagging
+// SKYRAT EDIT ADDITION - CUSTOMIZATION
/datum/bodypart_overlay/mutant/tail/get_global_feature_list()
- return GLOB.sprite_accessories["tail"] // SKYRAT EDIT - Customization - ORIGINAL: return GLOB.tails_list
+ return GLOB.sprite_accessories["tail"]
+// SKYRAT EDIT ADDITION END
/datum/bodypart_overlay/mutant/tail/can_draw_on_bodypart(mob/living/carbon/human/human)
if(human.wear_suit && (human.wear_suit.flags_inv & HIDEJUMPSUIT))
@@ -124,6 +127,9 @@
color_source = NONE
feature_key = "tail" // SKYRAT EDIT - Customization - ORIGINAL: feature_key = "tail_monkey"
+/datum/bodypart_overlay/mutant/tail/monkey/get_global_feature_list()
+ return GLOB.sprite_accessories["tail"] // SKYRAT EDIT CHANGE - ORIGINAL: return GLOB.tails_list_monkey
+
/obj/item/organ/external/tail/lizard
name = "lizard tail"
desc = "A severed lizard tail. Somewhere, no doubt, a lizard hater is very pleased with themselves."
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index d9d163b8702..56c10327441 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -109,6 +109,7 @@
#include "card_mismatch.dm"
#include "cardboard_cutouts.dm"
#include "chain_pull_through_space.dm"
+#include "changeling.dm"
#include "chat_filter.dm"
#include "circuit_component_category.dm"
#include "client_colours.dm"
diff --git a/code/modules/unit_tests/changeling.dm b/code/modules/unit_tests/changeling.dm
new file mode 100644
index 00000000000..449188a75fd
--- /dev/null
+++ b/code/modules/unit_tests/changeling.dm
@@ -0,0 +1,99 @@
+/// Tests transformation sting goes back and forth correctly
+/datum/unit_test/transformation_sting
+ var/ling_name = "Is-A-Changeling"
+ var/base_victim_name
+ var/last_frame = 1
+ var/icon/final_icon
+
+/datum/unit_test/transformation_sting/Run()
+ var/mob/living/carbon/human/ling = setup_ling()
+ var/mob/living/carbon/human/victim = setup_victim()
+ var/datum/antagonist/changeling/ling_datum = ling.mind.has_antag_datum(/datum/antagonist/changeling)
+
+ // Get the ability we're testing
+ ling_datum.give_power(/datum/action/changeling/sting/transformation) // SKYRAT EDIT CHANGE - Transformation sting not purchasable here - ORIGINAL : ling_datum.purchase_power(/datum/action/changeling/sting/transformation)
+ var/datum/action/changeling/sting/transformation/sting_action = locate() in ling.actions
+ sting_action.selected_dna = ling_datum.current_profile
+ sting_action.sting_duration = 0.5 SECONDS // just makes sure everything settles.
+
+ // Check that they look different before stinging
+ add_to_screenshot(ling, victim, both_species = TRUE)
+
+ // Do the sting, make the transformation
+ sting_action.sting_action(ling, victim)
+ // Check their name and species align
+ TEST_ASSERT(victim.has_status_effect(/datum/status_effect/temporary_transformation), "Victim did not get temporary transformation status effect on being transformation stung.")
+ TEST_ASSERT_EQUAL(victim.real_name, ling_name, "Victim real name did not change on being transformation stung.")
+ TEST_ASSERT_EQUAL(victim.name, ling_name, "Victim name did not change on being transformation stung.")
+ TEST_ASSERT_EQUAL(victim.dna.species.type, ling.dna.species.type, "Victim species did not change on being transformation stung.")
+ TEST_ASSERT_EQUAL(victim.dna.features["mcolor"], ling.dna.features["mcolor"], "Victim mcolor did not change on being transformation stung.")
+ // Check they actually look the same
+ add_to_screenshot(ling, victim)
+
+ // Make sure we give it enough time such that the status effect process ticks over and finishes
+ sleep(sting_action.sting_duration + 0.5 SECONDS)
+
+ // Check their name and species reset correctly
+ TEST_ASSERT_EQUAL(victim.name, base_victim_name, "Victim name did not change back after transformation sting expired.")
+ TEST_ASSERT_EQUAL(victim.real_name, base_victim_name, "Victim real name did not change back after transformation sting expired.")
+ TEST_ASSERT_NOTEQUAL(victim.dna.species.type, ling.dna.species.type, "Victim species did not change back after transformation sting expired.")
+ TEST_ASSERT_NOTEQUAL(victim.dna.features["mcolor"], ling.dna.features["mcolor"], "Victim mcolor did not reset after transformation sting expired.")
+ // Check they actually look different again
+ add_to_screenshot(ling, victim, both_species = TRUE)
+
+ test_screenshot("appearances", final_icon)
+
+/// Adds both mobs to the screenshot test, if both_species is TRUE, it also adds the victim in lizard form
+/datum/unit_test/transformation_sting/proc/add_to_screenshot(mob/living/carbon/human/ling, mob/living/carbon/human/victim, both_species = FALSE)
+ if(isnull(final_icon))
+ final_icon = icon('icons/effects/effects.dmi', "nothing")
+
+ // If we have a lot of dna features with a lot of parts (icons)
+ // This'll eventually runtime into a bad icon operation
+ // So we're recaching the icons here to prevent it from failing
+ final_icon = icon(final_icon)
+ final_icon.Insert(getFlatIcon(ling, no_anim = TRUE), dir = SOUTH, frame = last_frame)
+ final_icon.Insert(getFlatIcon(victim, no_anim = TRUE), dir = NORTH, frame = last_frame)
+
+ if(both_species)
+ var/prior_species = victim.dna.species.type
+ victim.set_species(/datum/species/lizard)
+ final_icon.Insert(getFlatIcon(victim, no_anim = TRUE), dir = EAST, frame = last_frame)
+ victim.set_species(prior_species)
+
+ last_frame += 1
+
+/datum/unit_test/transformation_sting/proc/setup_victim()
+ var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human/consistent)
+ base_victim_name = victim.real_name
+ victim.mind_initialize()
+ return victim
+
+/datum/unit_test/transformation_sting/proc/setup_ling()
+ var/mob/living/carbon/human/ling = allocate(/mob/living/carbon/human/consistent)
+ // Because we use two consistent humans, we need to change some of the features to know they're actually updating to new values.
+ // The more DNA features and random things we change, the more likely we are to catch something not updating correctly.
+ // Yeah guess who/what this is, I dare you.
+ ling.dna.features["mcolor"] = "#886600"
+ ling.dna.mutant_bodyparts["tail"] = list(MUTANT_INDEX_NAME = "Smooth", MUTANT_INDEX_COLOR_LIST = list("#886600", "#886600", "#886600")) // SKYRAT EDIT CHANGE - ORIGINAL: ling.dna.features["tail_lizard"] = "Smooth"
+ ling.dna.mutant_bodyparts["snout"] = list(MUTANT_INDEX_NAME = "Sharp + Light", MUTANT_INDEX_COLOR_LIST = list("#886600", "#886600", "#886600")) // SKYRAT EDIT CHANGE - ORIGINAL: ling.dna.features["snout"] = "Sharp + Light"
+ ling.dna.mutant_bodyparts["horns"] = list(MUTANT_INDEX_NAME = "Curled", MUTANT_INDEX_COLOR_LIST = list("#292826", "#292826", "#8292826")) // SKYRAT EDIT CHANGE - ORIGINAL: ling.dna.features["horns"] = "Curved"
+ ling.dna.mutant_bodyparts["frills"] = list(MUTANT_INDEX_NAME = "Short", MUTANT_INDEX_COLOR_LIST = list("#886600", "#886600", "#886600")) // SKYRAT EDIT CHANGE - ORIGINAL: ling.dna.features["frills"] = "Sort"
+ ling.dna.mutant_bodyparts["spines"] = list(MUTANT_INDEX_NAME = "Long + Membrane", MUTANT_INDEX_COLOR_LIST = list("#886600", "#886600", "#886600")) // SKYRAT EDIT CHANGE - ORIGINAL: ling.dna.features["spines"] = "Long + Membrane"
+ ling.dna.body_markings["chest"] = list("Light Belly" = list("#886600", 0)) // SKYRAT EDIT CHANGE - ORIGINAL : ling.dna.features[body_markings] = list("Light Belly")
+ ling.dna.features["legs"] = DIGITIGRADE_LEGS
+ ling.eye_color_left = "#FFFFFF"
+ ling.eye_color_right = "#FFFFFF"
+ ling.dna.update_ui_block(DNA_EYE_COLOR_LEFT_BLOCK)
+ ling.dna.update_ui_block(DNA_EYE_COLOR_RIGHT_BLOCK)
+ ling.set_species(/datum/species/lizard)
+
+ ling.real_name = ling_name
+ ling.dna.real_name = ling_name
+ ling.name = ling_name
+ ling.dna.initialize_dna(create_mutation_blocks = FALSE, randomize_features = FALSE)
+
+ ling.mind_initialize()
+ ling.mind.add_antag_datum(/datum/antagonist/changeling)
+
+ return ling
diff --git a/code/modules/unit_tests/required_map_items.dm b/code/modules/unit_tests/required_map_items.dm
index 39930afd822..5cbef645391 100644
--- a/code/modules/unit_tests/required_map_items.dm
+++ b/code/modules/unit_tests/required_map_items.dm
@@ -15,10 +15,11 @@
/datum/unit_test/required_map_items/proc/setup_expected_types()
expected_types += subtypesof(/obj/item/stamp/head)
expected_types += subtypesof(/obj/machinery/computer/department_orders)
- expected_types += /obj/machinery/computer/communications
- expected_types += /mob/living/carbon/human/species/monkey/punpun
+
+ expected_types += /mob/living/basic/parrot/poly
expected_types += /mob/living/basic/pet/dog/corgi/ian
- expected_types += /mob/living/simple_animal/parrot/poly
+ expected_types += /mob/living/carbon/human/species/monkey/punpun
+ expected_types += /obj/machinery/computer/communications
expected_types += /obj/machinery/drone_dispenser
/datum/unit_test/required_map_items/Run()
diff --git a/code/modules/unit_tests/screenshots/transformation_sting_appearances.png b/code/modules/unit_tests/screenshots/transformation_sting_appearances.png
new file mode 100644
index 00000000000..ce0545d1b56
Binary files /dev/null and b/code/modules/unit_tests/screenshots/transformation_sting_appearances.png differ
diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm
index dcba0cd69db..1a8b4561140 100644
--- a/code/modules/unit_tests/simple_animal_freeze.dm
+++ b/code/modules/unit_tests/simple_animal_freeze.dm
@@ -92,10 +92,6 @@
/mob/living/simple_animal/hostile/retaliate/goose/vomit,
/mob/living/simple_animal/hostile/vatbeast,
/mob/living/simple_animal/hostile/zombie,
- /mob/living/simple_animal/parrot,
- /mob/living/simple_animal/parrot/natural,
- /mob/living/simple_animal/parrot/poly,
- /mob/living/simple_animal/parrot/poly/ghost,
/mob/living/simple_animal/pet,
/mob/living/simple_animal/pet/cat,
/mob/living/simple_animal/pet/cat/_proc,
diff --git a/dependencies.sh b/dependencies.sh
index b24b85d55b2..6162b349b51 100644
--- a/dependencies.sh
+++ b/dependencies.sh
@@ -8,7 +8,7 @@ export BYOND_MAJOR=514
export BYOND_MINOR=1588
#rust_g git tag
-export RUST_G_VERSION=1.2.0
+export RUST_G_VERSION=3.0.0
#node version
export NODE_VERSION=14
diff --git a/html/changelogs/archive/2023-11.yml b/html/changelogs/archive/2023-11.yml
index b41d68bd4b5..9764f72e362 100644
--- a/html/changelogs/archive/2023-11.yml
+++ b/html/changelogs/archive/2023-11.yml
@@ -1080,3 +1080,87 @@
once friendly. Being rather large dogs, they also have access to most of the
pet commands you'd expect, such as fetching things, and violently mauling people
their owners point at.
+2023-11-22:
+ 1393F:
+ - bugfix: ~~The Sleeping Carp scroll no longer says deflect using throw mode.~~
+ Nevermind.
+ Bumtickley00:
+ - balance: The CMO's hypospray now holds 60u, and can be set to inject smaller amounts
+ of reagents
+ DaCoolBoss:
+ - bugfix: Removed a duplicate grille from the abandoned mime outpost space ruin,
+ and replaced the 'energy weapon lenses' with spent revolver rounds.
+ EuSouAFazer:
+ - bugfix: Meta's recycler works again
+ - qol: Slightly moved the universal enzyme on meta's kitchen to a prettier spot.
+ - bugfix: The universal enzyme on meta's kitchen is no longer unecessarely varedited.
+ Ghommie:
+ - spellcheck: Examining a human mob as an observer displays "Quirks", not "Traits"
+ Hatterhat:
+ - balance: The SC/FISHER disruptor pistol is now more likely to show up in black
+ market uplinks.
+ - balance: The SC/FISHER now has more range (21 tiles up from 14), and is usable
+ by pacifists.
+ Jane:
+ - rscadd: Bargonia Bar Sign for Cargo Bar Enjoyers
+ JohnFulpWillard:
+ - bugfix: '[Icebox] Atmos techs have access to the Engineering front desk windoor.'
+ - qol: '[Icebox] Security''s lower floor is not as easily cut off from the powernet
+ anymore.'
+ LT3:
+ - bugfix: Cursed/bad luck omen will now stick with the player for more than 1 incident
+ Melbert:
+ - bugfix: Atrocinating mobs will now behave more as you'd expect. Meaning they don't
+ slip on wet patches, can't trigger bear traps / landmines / mouse traps, ignore
+ conveyors, and can walk over tables and railings.
+ - bugfix: Floating mobs are unaffected by conveyor belts, acid (on the ground),
+ glass tables
+ - bugfix: Floating mobs won't squish stuff like roaches anymore
+ - bugfix: Fixes bear traps triggering on floating / flying mobs
+ OrionTheFox:
+ - bugfix: fixed a few missing/outdated HUD icons, including the Bitrunner's SecHUD
+ icon, and switching the "Suspected" tag to match the color used on consoles
+ dieamond13:
+ - bugfix: removes a duplicate bookcase in icebox permabrig library
+ - bugfix: adds back one way exits to Tramstation science's entrance
+ lizardqueenlexi:
+ - bugfix: Heads impaled on spears now render in the correct place on the tip, instead
+ of halfway down the shaft.
+ - bugfix: Blind personnel are no longer able to magically see heads impaled on spears
+ from a distance.
+ necromanceranne:
+ - bugfix: When you successfully block a body collision, it does something rather
+ than nothing at all.
+ san7890 and Ben10Omintrix/Kobsamobsa:
+ - refactor: Parrots (including Poly) have undergone a massive refactor, please report
+ any bugs or unexpected behavior that you may encounter.
+ - qol: Left-clicking a parrot with a cracker will tame it, right-clicking a parrot
+ with a cracker will now feed it the cracker.
+ yooriss:
+ - rscadd: Examining tamed companions of any type with shift double-click now gives
+ a brief indication as to their overall health. This also includes a handy reminder
+ on how to heal pets if they get injured.
+ - rscadd: A new recipe for anointing bloodresin is now available exclusively for
+ Primitive Demihumans, made from 80u liquid gibs and 20u of blood. Bloodresin
+ invokes traditional rites of the Hearth to give names to worthy creatures. Using
+ this in poor ways may displease the Gods. Woe upon your lineage.
+ - rscadd: A single pack of glowshroom mycelium has been added to the Hearth's starting
+ seed stores for the benefit of creative healers, shamans, and radiation enthusiasts.
+2023-11-23:
+ Autisem:
+ - rscadd: Cyborg inducer for engineering borgs
+ - balance: The borg RPED can hold as many part as the BSRPED now
+ - balance: Cyborg chargers now draw from the power net as cell chargers do
+ Iajret:
+ - bugfix: borers can once again fill their hosts with various wonders of chemistry
+ Nerev4r:
+ - rscadd: NanoTrasen, after extensive testing in a variety of stars' light wavelengths,
+ have deemed their PDAs ready enough to actually use the onboard cameras they
+ already had.
+ OrionTheFox:
+ - qol: Allergy Dogtags (and any other dogtags, really) are now actually whitelisted
+ to fit into wallets.
+ san7890:
+ - code_imp: 'The currently operating rust-g version on a live server is posted to
+ places like the runtime.log, in the same place where the revision information
+ and any applicable test merges already were. /:cl:'
diff --git a/icons/obj/machines/barsigns.dmi b/icons/obj/machines/barsigns.dmi
index f3cfe8eaed9..de55f769981 100644
Binary files a/icons/obj/machines/barsigns.dmi and b/icons/obj/machines/barsigns.dmi differ
diff --git a/modular_skyrat/master_files/code/modules/antagonists/changeling/powers/tiny_prick.dm b/modular_skyrat/master_files/code/modules/antagonists/changeling/powers/tiny_prick.dm
new file mode 100644
index 00000000000..0eaf7f50f9f
--- /dev/null
+++ b/modular_skyrat/master_files/code/modules/antagonists/changeling/powers/tiny_prick.dm
@@ -0,0 +1,3 @@
+// Transformation sting is not able to be purchased
+/datum/action/changeling/sting/transformation
+ dna_cost = CHANGELING_POWER_UNOBTAINABLE
diff --git a/modular_skyrat/master_files/code/modules/antagonists/traitor/objectives/kill_pet.dm b/modular_skyrat/master_files/code/modules/antagonists/traitor/objectives/kill_pet.dm
index b3486f40ba7..c6bcd6ed585 100644
--- a/modular_skyrat/master_files/code/modules/antagonists/traitor/objectives/kill_pet.dm
+++ b/modular_skyrat/master_files/code/modules/antagonists/traitor/objectives/kill_pet.dm
@@ -7,7 +7,7 @@
),
JOB_CAPTAIN = /mob/living/basic/pet/fox/renault,
JOB_CHIEF_MEDICAL_OFFICER = /mob/living/simple_animal/pet/cat/runtime,
- JOB_CHIEF_ENGINEER = /mob/living/simple_animal/parrot/poly,
+ JOB_CHIEF_ENGINEER = /mob/living/basic/parrot/poly,
JOB_QUARTERMASTER = list(
/mob/living/basic/sloth/citrus,
/mob/living/basic/sloth/paperwork,
diff --git a/modular_skyrat/master_files/code/modules/client/preferences.dm b/modular_skyrat/master_files/code/modules/client/preferences.dm
index bc97b467cd9..2532bc4c308 100644
--- a/modular_skyrat/master_files/code/modules/client/preferences.dm
+++ b/modular_skyrat/master_files/code/modules/client/preferences.dm
@@ -111,28 +111,29 @@
return language
/datum/preferences/proc/validate_species_parts()
- var/list/target_bodyparts = pref_species.default_mutant_bodyparts.Copy()
+ var/list/default_bodyparts = GLOB.default_mutant_bodyparts[pref_species.name]
+ var/list/target_bodyparts = default_bodyparts.Copy()
// Remove all "extra" accessories
for(var/key in mutant_bodyparts)
if(!GLOB.sprite_accessories[key]) // That accessory no longer exists, remove it
mutant_bodyparts -= key
continue
- if(!pref_species.default_mutant_bodyparts[key])
+ if(!GLOB.default_mutant_bodyparts[pref_species.name][key])
mutant_bodyparts -= key
continue
if(!GLOB.sprite_accessories[key][mutant_bodyparts[key][MUTANT_INDEX_NAME]]) // The individual accessory no longer exists
- mutant_bodyparts[key][MUTANT_INDEX_NAME] = pref_species.default_mutant_bodyparts[key]
+ mutant_bodyparts[key][MUTANT_INDEX_NAME] = GLOB.default_mutant_bodyparts[pref_species.name[key][MUTANT_INDEX_NAME]]
validate_color_keys_for_part(key) // Validate the color count of each accessory that wasnt removed
// Add any missing accessories
for(var/key in target_bodyparts)
if(!mutant_bodyparts[key])
var/datum/sprite_accessory/SA
- if(target_bodyparts[key] == ACC_RANDOM)
+ if(target_bodyparts[key][MUTANT_INDEX_CAN_RANDOMIZE])
SA = random_accessory_of_key_for_species(key, pref_species)
else
- SA = GLOB.sprite_accessories[key][target_bodyparts[key]]
+ SA = GLOB.sprite_accessories[key][target_bodyparts[key][MUTANT_INDEX_NAME]]
var/final_list = list()
final_list[MUTANT_INDEX_NAME] = SA.name
final_list[MUTANT_INDEX_COLOR_LIST] = SA.get_default_color(features, pref_species)
diff --git a/modular_skyrat/master_files/code/modules/client/preferences/mutant_parts.dm b/modular_skyrat/master_files/code/modules/client/preferences/mutant_parts.dm
index fd11a6bd3b5..57c26e4dd37 100644
--- a/modular_skyrat/master_files/code/modules/client/preferences/mutant_parts.dm
+++ b/modular_skyrat/master_files/code/modules/client/preferences/mutant_parts.dm
@@ -164,6 +164,9 @@
. = ..()
var/obj/item/bodypart/head/our_head = target.get_bodypart(BODY_ZONE_HEAD)
+ if(isnull(our_head)) // dullahans.
+ return
+
if(.)
our_head.bodytype |= BODYTYPE_SNOUTED
else
diff --git a/modular_skyrat/master_files/code/modules/mob/living/human/species.dm b/modular_skyrat/master_files/code/modules/mob/living/human/species.dm
index 30df82a7f11..1bb68a1f10f 100644
--- a/modular_skyrat/master_files/code/modules/mob/living/human/species.dm
+++ b/modular_skyrat/master_files/code/modules/mob/living/human/species.dm
@@ -14,7 +14,7 @@
var/datum/preference/preference = GLOB.preference_entries[preference_type]
if ( \
- (preference.relevant_mutant_bodypart in default_mutant_bodyparts) \
+ (preference.relevant_mutant_bodypart in GLOB.default_mutant_bodyparts[name]) \
|| (preference.relevant_inherent_trait in inherent_traits) \
|| (preference.relevant_head_flag && check_head_flags(preference.relevant_head_flag)) \
)
diff --git a/modular_skyrat/master_files/code/modules/modular_computers/file_system/programs/maintenance/camera.dm b/modular_skyrat/master_files/code/modules/modular_computers/file_system/programs/maintenance/camera.dm
new file mode 100644
index 00000000000..1fbfdedb519
--- /dev/null
+++ b/modular_skyrat/master_files/code/modules/modular_computers/file_system/programs/maintenance/camera.dm
@@ -0,0 +1,4 @@
+// Makes camera app readily available to crew
+/datum/computer_file/program/maintenance/camera
+ available_on_ntnet = TRUE
+ unique_copy = FALSE
diff --git a/modular_skyrat/master_files/icons/mob/huds/hud.dmi b/modular_skyrat/master_files/icons/mob/huds/hud.dmi
index 3d81edc5f20..56163691900 100644
Binary files a/modular_skyrat/master_files/icons/mob/huds/hud.dmi and b/modular_skyrat/master_files/icons/mob/huds/hud.dmi differ
diff --git a/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm b/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm
index 7b38a565698..a4ca86c4f18 100644
--- a/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm
+++ b/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm
@@ -202,7 +202,7 @@
/mob/living/basic/mining/ice_whelp,
/mob/living/basic/mining/lobstrosity,
/mob/living/simple_animal/hostile/asteroid/polarbear,
- /mob/living/simple_animal/hostile/asteroid/wolf,
+ /mob/living/basic/mining/wolf,
)
new mob_type(success_rune.loc)
diff --git a/modular_skyrat/modules/better_vox/code/vox_species.dm b/modular_skyrat/modules/better_vox/code/vox_species.dm
index 4d3f1b237c9..8fdd3506cc0 100644
--- a/modular_skyrat/modules/better_vox/code/vox_species.dm
+++ b/modular_skyrat/modules/better_vox/code/vox_species.dm
@@ -17,9 +17,6 @@
breathid = "n2"
mutant_bodyparts = list()
mutanttongue = /obj/item/organ/internal/tongue/vox
- default_mutant_bodyparts = list(
- "tail" = "Vox Primalis Tail",
- )
payday_modifier = 1.0
outfit_important_for_life = /datum/outfit/vox
species_language_holder = /datum/language_holder/vox
@@ -50,6 +47,10 @@
LOADOUT_ITEM_EARS = VOX_PRIMALIS_EARS_ICON,
)
+/datum/species/vox_primalis/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Vox Primalis Tail", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
/datum/species/vox_primalis/pre_equip_species_outfit(datum/job/job, mob/living/carbon/human/equipping, visuals_only)
. = ..()
diff --git a/modular_skyrat/modules/customization/__DEFINES/lists.dm b/modular_skyrat/modules/customization/__DEFINES/lists.dm
index b0877b91b76..6c5d6bf496b 100644
--- a/modular_skyrat/modules/customization/__DEFINES/lists.dm
+++ b/modular_skyrat/modules/customization/__DEFINES/lists.dm
@@ -2,6 +2,17 @@ GLOBAL_LIST_EMPTY(sprite_accessories)
GLOBAL_LIST_EMPTY(generic_accessories)
GLOBAL_LIST_EMPTY(genetic_accessories)
+/// What accessories can a species have as well as their default accessory of such type e.g. "frills" = "Aquatic". Default accessory colors is dictated by the accessory properties and mutcolors of the specie
+GLOBAL_LIST_EMPTY(default_mutant_bodyparts)
+GLOBAL_LIST_INIT(possible_genitals, list(
+ ORGAN_SLOT_VAGINA,
+ ORGAN_SLOT_WOMB,
+ ORGAN_SLOT_TESTICLES,
+ ORGAN_SLOT_BREASTS,
+ ORGAN_SLOT_ANUS,
+ ORGAN_SLOT_PENIS
+))
+
GLOBAL_LIST_EMPTY(body_markings)
GLOBAL_LIST_EMPTY_TYPED(body_markings_per_limb, /list)
GLOBAL_LIST_EMPTY(body_marking_sets)
diff --git a/modular_skyrat/modules/customization/__HELPERS/global_lists.dm b/modular_skyrat/modules/customization/__HELPERS/global_lists.dm
index a2dca2e39be..cc2c6c9e3e8 100644
--- a/modular_skyrat/modules/customization/__HELPERS/global_lists.dm
+++ b/modular_skyrat/modules/customization/__HELPERS/global_lists.dm
@@ -1,5 +1,6 @@
/proc/make_skyrat_datum_references()
make_sprite_accessory_references()
+ make_default_mutant_bodypart_references()
make_body_marking_references()
make_body_marking_set_references()
make_body_marking_dna_block_references()
@@ -30,6 +31,18 @@
if(P.generic && !GLOB.generic_accessories[P.key])
GLOB.generic_accessories[P.key] = P.generic
+/proc/make_default_mutant_bodypart_references()
+ // Build the global list for default species' mutant_bodyparts
+ for(var/path in subtypesof(/datum/species))
+ var/datum/species/species_type = path
+ var/datum/species/species_instance = new species_type
+ if(!isnull(species_instance.name))
+ GLOB.default_mutant_bodyparts[species_instance.name] = species_instance.get_default_mutant_bodyparts()
+ if(species_instance.can_have_genitals)
+ for(var/genital in GLOB.possible_genitals)
+ GLOB.default_mutant_bodyparts[species_instance.name] += list((genital) = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE))
+ qdel(species_instance)
+
/proc/make_body_marking_references()
// Here we build the global list for all body markings
for(var/path in subtypesof(/datum/body_marking))
diff --git a/modular_skyrat/modules/customization/datums/dna.dm b/modular_skyrat/modules/customization/datums/dna.dm
index 7e0fb14838c..e29e403e97b 100644
--- a/modular_skyrat/modules/customization/datums/dna.dm
+++ b/modular_skyrat/modules/customization/datums/dna.dm
@@ -57,16 +57,6 @@ GLOBAL_LIST_EMPTY(total_uf_len_by_block)
///Current body size, used for proper re-sizing and keeping track of that
var/current_body_size = BODY_SIZE_NORMAL
-/datum/dna/proc/initialize_dna(newblood_type, skip_index = FALSE)
- if(newblood_type)
- blood_type = newblood_type
- unique_enzymes = generate_unique_enzymes()
- unique_identity = generate_unique_identity()
- if(!skip_index) //I hate this
- generate_dna_blocks()
- mutant_bodyparts = species.get_random_mutant_bodyparts(features)
- unique_features = generate_unique_features()
-
/datum/dna/proc/generate_unique_features()
var/list/data = list()
@@ -186,59 +176,6 @@ GLOBAL_LIST_EMPTY(total_uf_len_by_block)
holder.maptext_height = 32 * features["body_size"] // Adjust runechat height
current_body_size = features["body_size"]
-/mob/living/carbon/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE, list/override_features, list/override_mutantparts, list/override_markings, retain_features = FALSE, retain_mutantparts = FALSE)
- if(QDELETED(src))
- CRASH("You're trying to change your species post deletion, this is a recipe for madness")
- if(isnull(mrace))
- CRASH("set_species called without a species to set to")
- if(!has_dna())
- return
-
- var/datum/species/new_race
- if(ispath(mrace))
- new_race = new mrace
- else if(istype(mrace))
- if(QDELING(mrace))
- CRASH("someone is calling set_species() and is passing it a qdeling species datum, this is VERY bad, stop it")
- new_race = mrace
- else
- CRASH("set_species called with an invalid mrace [mrace]")
-
- death_sound = new_race.death_sound
-
- var/datum/species/old_species = dna.species
- dna.species = new_race
-
- if (old_species.properly_gained)
- old_species.on_species_loss(src, new_race, pref_load)
-
- //BODYPARTS AND FEATURES - We need to instantiate the list with compatible mutant parts so we don't break things
-
- if(override_mutantparts && override_mutantparts.len)
- for(var/feature in dna.mutant_bodyparts)
- override_mutantparts[feature] = dna.mutant_bodyparts[feature]
- dna.mutant_bodyparts = override_mutantparts
-
- if(override_markings && override_markings.len)
- for(var/feature in dna.body_markings)
- override_markings[feature] = dna.body_markings[feature]
- dna.body_markings = override_markings
-
- if(override_features && override_features.len)
- for(var/feature in dna.features)
- override_features[feature] = dna.features[feature]
- dna.features = override_features
- //END OF BODYPARTS AND FEATURES
-
- apply_customizable_dna_features_to_species()
- dna.unique_features = dna.generate_unique_features()
-
- dna.update_body_size()
-
- dna.species.on_species_gain(src, old_species, pref_load)
- log_mob_tag("TAG: [tag] SPECIES: [key_name(src)] \[[mrace]\]")
-
-
/mob/living/carbon/proc/apply_customizable_dna_features_to_species()
if(!has_dna())
CRASH("[src] does not have DNA")
diff --git a/modular_skyrat/modules/customization/modules/mob/dead/new_player/preferences_setup.dm b/modular_skyrat/modules/customization/modules/mob/dead/new_player/preferences_setup.dm
index 543a0c15cc1..c2bfd6c7d49 100644
--- a/modular_skyrat/modules/customization/modules/mob/dead/new_player/preferences_setup.dm
+++ b/modular_skyrat/modules/customization/modules/mob/dead/new_player/preferences_setup.dm
@@ -162,7 +162,7 @@
var/list/new_features = pref_species.get_random_features() //We do this to keep flavor text, genital sizes etc.
for(var/key in new_features)
features[key] = new_features[key]
- mutant_bodyparts = pref_species.get_random_mutant_bodyparts(features)
+ mutant_bodyparts = pref_species.get_mutant_bodyparts(features)
body_markings = pref_species.get_random_body_markings(features)
if(pref_species.use_skintones)
features["uses_skintones"] = TRUE
diff --git a/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/ears.dm b/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/ears.dm
index b27d27bae0a..e4f05a3b205 100644
--- a/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/ears.dm
+++ b/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/ears.dm
@@ -34,7 +34,6 @@
/datum/sprite_accessory/ears/fox
color_src = USE_ONE_COLOR
-
/datum/sprite_accessory/ears/mutant
icon = 'modular_skyrat/master_files/icons/mob/sprite_accessory/ears.dmi'
organ_type = /obj/item/organ/external/ears // SET BACK TO THIS AS SOON AS WE GET EARS AS EXTERNAL ORGANS: organ_type = /obj/item/organ/internal/ears/mutant
@@ -289,7 +288,7 @@
icon_state = "deerear"
/datum/sprite_accessory/ears/mutant/teshari
- recommended_species = list(SPECIES_TESHARI)
+ recommended_species = list(SPECIES_TESHARI, SPECIES_TESHARI_ALT) //FLUFFY FRONTIER EDIT - TESHARI_REBORN
/datum/sprite_accessory/ears/mutant/teshari/regular
name = "Teshari Regular"
diff --git a/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/tails.dm b/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/tails.dm
index 4831414ca85..ef9212cb509 100644
--- a/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/tails.dm
+++ b/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/tails.dm
@@ -78,7 +78,7 @@
recommended_species = list(SPECIES_MAMMAL, SPECIES_HUMAN, SPECIES_SYNTH, SPECIES_TAJARAN, SPECIES_HUMANOID, SPECIES_GHOUL)
/datum/sprite_accessory/tails/mammal/teshari
- recommended_species = list(SPECIES_TESHARI)
+ recommended_species = list(SPECIES_TESHARI, SPECIES_TESHARI_ALT) //FLUFFY FRONTIER EDIT - TESHARI_REBORN
/datum/sprite_accessory/tails/mammal/wagging/vulpkanin
recommended_species = list(SPECIES_MAMMAL, SPECIES_HUMAN, SPECIES_SYNTH, SPECIES_VULP, SPECIES_HUMANOID, SPECIES_GHOUL)
diff --git a/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/wings.dm b/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/wings.dm
index c21b8859d21..03a68cb6925 100644
--- a/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/wings.dm
+++ b/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/wings.dm
@@ -313,7 +313,7 @@
icon_state = "brown"
/datum/sprite_accessory/wings/moth/burnt
- name = "Moth (Burnt)"
+ name = "Burnt Off"
icon_state = "burnt_off"
locked = TRUE
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/human.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/human.dm
index 78c597e7864..a8499e2fcdb 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/human.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/human.dm
@@ -1,9 +1,6 @@
// This DMI holds our radial icons for the 'hide mutant parts' verb
#define HIDING_RADIAL_DMI 'modular_skyrat/modules/customization/modules/mob/living/carbon/human/MOD_sprite_accessories/icons/radial.dmi'
-/mob/living/carbon/human
- var/static/list/possible_genitals = list(ORGAN_SLOT_PENIS, ORGAN_SLOT_TESTICLES, ORGAN_SLOT_VAGINA, ORGAN_SLOT_BREASTS, ORGAN_SLOT_ANUS)
-
/mob/living/carbon/human/Topic(href, href_list)
. = ..()
@@ -11,7 +8,7 @@
switch(href_list["lookup_info"])
if("genitals")
var/list/line = list()
- for(var/genital in possible_genitals)
+ for(var/genital in GLOB.possible_genitals)
if(!dna.species.mutant_bodyparts[genital])
continue
var/datum/sprite_accessory/genital/G = GLOB.sprite_accessories[genital][dna.species.mutant_bodyparts[genital][MUTANT_INDEX_NAME]]
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm
index 357e827e744..08d83bd7313 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm
@@ -27,32 +27,37 @@ GLOBAL_LIST_EMPTY(customizable_races)
var/list/custom_worn_icons = list()
///Is this species restricted from changing their body_size in character creation?
var/body_size_restricted = FALSE
- ///What accessories can a species have aswell as their default accessory of such type e.g. "frills" = "Aquatic". Default accessory colors is dictated by the accessory properties and mutcolors of the specie
- var/list/default_mutant_bodyparts = list()
- /// A static list of all genital slot possibilities.
- var/static/list/genitals_list = list(ORGAN_SLOT_VAGINA, ORGAN_SLOT_WOMB, ORGAN_SLOT_TESTICLES, ORGAN_SLOT_BREASTS, ORGAN_SLOT_ANUS, ORGAN_SLOT_PENIS)
/// Are we lore protected? This prevents people from changing the species lore or species name.
var/lore_protected = FALSE
+/// Returns a list of the default mutant bodyparts, and whether or not they can be randomized or not
+/datum/species/proc/get_default_mutant_bodyparts()
+ return list()
+
/datum/species/proc/handle_mutant_bodyparts(mob/living/carbon/human/owner, forced_colour, force_update = FALSE)
return
-/datum/species/New()
- . = ..()
- if(can_have_genitals)
- for(var/genital in genitals_list)
- default_mutant_bodyparts[genital] = "None"
-
/datum/species/dullahan
mutant_bodyparts = list()
/datum/species/human/felinid
mutant_bodyparts = list()
- default_mutant_bodyparts = list("tail" = "Cat", "ears" = "Cat")
+
+/datum/species/human/felinid/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Cat", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "ears" = list(MUTANT_INDEX_NAME = "Cat", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
/datum/species/human
mutant_bodyparts = list()
- default_mutant_bodyparts = list("ears" = "None", "tail" = "None", "wings" = "None")
+
+/datum/species/human/get_default_mutant_bodyparts()
+ return list(
+ "ears" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "tail" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "wings" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
/datum/species/mush
mutant_bodyparts = list()
@@ -75,20 +80,35 @@ GLOBAL_LIST_EMPTY(customizable_races)
always_customizable = TRUE
/datum/species/randomize_features(mob/living/carbon/human/human_mob)
- return
+ var/list/features = ..()
+ return features
-/datum/species/proc/get_random_mutant_bodyparts(list/features) //Needs features to base the colour off of
+/**
+ * Returns a list of mutant_bodyparts
+ *
+ * Gets the default species mutant_bodyparts list for the given species datum and sets up its sprite accessories.
+ *
+ * Arguments:
+ * * features - Features are needed for the part color
+ * * existing_mutant_bodyparts - When passed a list of existing mutant bodyparts, the existing ones will not get overwritten
+ */
+/datum/species/proc/get_mutant_bodyparts(list/features, list/existing_mutant_bodyparts) //Needs features to base the colour off of
var/list/mutantpart_list = list()
- var/list/bodyparts_to_add = default_mutant_bodyparts.Copy()
+ if(LAZYLEN(existing_mutant_bodyparts))
+ mutantpart_list = existing_mutant_bodyparts.Copy()
+ var/list/default_bodypart_data = GLOB.default_mutant_bodyparts[name]
+ var/list/bodyparts_to_add = default_bodypart_data.Copy()
if(CONFIG_GET(flag/disable_erp_preferences))
- for(var/genital in genitals_list)
+ for(var/genital in GLOB.possible_genitals)
bodyparts_to_add.Remove(genital)
for(var/key in bodyparts_to_add)
+ if(LAZYLEN(existing_mutant_bodyparts) && existing_mutant_bodyparts[key])
+ continue
var/datum/sprite_accessory/SP
- if(bodyparts_to_add[key] == ACC_RANDOM)
+ if(default_bodypart_data[key][MUTANT_INDEX_CAN_RANDOMIZE])
SP = random_accessory_of_key_for_species(key, src)
else
- SP = GLOB.sprite_accessories[key][bodyparts_to_add[key]]
+ SP = GLOB.sprite_accessories[key][bodyparts_to_add[key][MUTANT_INDEX_NAME]]
if(!SP)
CRASH("Cant find accessory of [key] key, [bodyparts_to_add[key]] name, for species [id]")
var/list/color_list = SP.get_default_color(features, src)
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/akula.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/akula.dm
index 19492ef4456..95733d3df2b 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/akula.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/akula.dm
@@ -29,10 +29,6 @@
)
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID
mutant_bodyparts = list()
- default_mutant_bodyparts = list(
- "tail" = ACC_RANDOM,
- "legs" = "Normal Legs"
- )
outfit_important_for_life = /datum/outfit/akula
payday_modifier = 1.0
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
@@ -47,6 +43,12 @@
/// This variable stores the timer datum which appears if the mob becomes wet
var/dry_up_timer = TIMER_ID_NULL
+/datum/species/akula/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Akula", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
+
/datum/species/akula/get_species_description()
return placeholder_description
@@ -60,7 +62,8 @@
"Yet, despite their differences, all Agurkrral citizens swim freely in their kingdom's waters. Even the most controlling border princes, even those in the Old Principalities working the slave trade, know better than to openly erode a citizen's right to life, property, and speech. Any alien species can become an Agurkrral citizen, and even non-citizens enjoy the right to life, with executions outright banned. The aristocracy remains well-educated, even the edgerunner warlords of the New Principalities, and the Kingdom as a whole enjoys its status as a nation that's now a true rival to Sol. Larger, more populated, and better developed; though, having to 'integrate' Solarian technologies, goods, and peoples to fully succeed. The Azuleans are even known as an environmentally-focused people; although they hold no care for lands they cannot make use of, modern nobles are still in charge of maintaining the biosphere of lands they control, to allow their strangely engineered flora and fauna to thrive, and for the people to have healthy and clean waters to live in.",
)
-/datum/species/akula/randomize_features(mob/living/carbon/human/human_mob)
+/datum/species/akula/randomize_features()
+ var/list/features = ..()
var/main_color
var/secondary_color
var/tertiary_color
@@ -82,9 +85,10 @@
main_color = "#DB35DE"
secondary_color = "#BE3AFE"
tertiary_color = "#F5E2EE"
- human_mob.dna.features["mcolor"] = main_color
- human_mob.dna.features["mcolor2"] = secondary_color
- human_mob.dna.features["mcolor3"] = tertiary_color
+ features["mcolor"] = main_color
+ features["mcolor2"] = secondary_color
+ features["mcolor3"] = tertiary_color
+ return features
/datum/species/akula/prepare_human_for_preview(mob/living/carbon/human/akula)
var/main_color = "#1CD3E5"
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/aquatic.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/aquatic.dm
index 80aa57ca8ca..2fa8525ff48 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/aquatic.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/aquatic.dm
@@ -11,14 +11,6 @@
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID
mutant_bodyparts = list()
mutanttongue = /obj/item/organ/internal/tongue/aquatic
- default_mutant_bodyparts = list(
- "tail" = ACC_RANDOM,
- "snout" = ACC_RANDOM,
- "horns" = "None",
- "ears" = ACC_RANDOM,
- "legs" = "Normal Legs",
- "wings" = "None"
- )
payday_modifier = 1.0
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
examine_limb_id = SPECIES_AKULA
@@ -31,6 +23,16 @@
BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/mutant/aquatic,
)
+/datum/species/aquatic/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Shark", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "snout" = list(MUTANT_INDEX_NAME = "Shark", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "horns" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "ears" = list(MUTANT_INDEX_NAME = "Hammerhead", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "wings" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
+
/obj/item/organ/internal/tongue/aquatic
liked_foodtypes = SEAFOOD | MEAT | FRUIT | GORE
disliked_foodtypes = CLOTH | GROSS
@@ -38,6 +40,7 @@
/datum/species/aquatic/randomize_features(mob/living/carbon/human/human_mob)
+ var/list/features = ..()
var/main_color
var/second_color
var/random = rand(1,5)
@@ -58,9 +61,10 @@
if(5)
main_color = "#444444"
second_color = "#DDDDEE"
- human_mob.dna.features["mcolor"] = main_color
- human_mob.dna.features["mcolor2"] = second_color
- human_mob.dna.features["mcolor3"] = second_color
+ features["mcolor"] = main_color
+ features["mcolor2"] = second_color
+ features["mcolor3"] = second_color
+ return features
/datum/species/aquatic/get_random_body_markings(list/passed_features)
var/name = "Shark"
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm
index 0b615d6bfc1..f0b2c18d297 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm
@@ -4,11 +4,6 @@
examine_limb_id = SPECIES_GHOUL
can_have_genitals = FALSE //WHY WOULD YOU WANT TO FUCK ONE OF THESE THINGS?
mutant_bodyparts = list("ghoulcolor" = "Tan Necrotic")
- default_mutant_bodyparts = list(
- "tail" = "None",
- "ears" = "None",
- "legs" = "Normal Legs"
- )
mutanttongue = /obj/item/organ/internal/tongue/ghoul
inherent_traits = list(
TRAIT_ADVANCEDTOOLUSER,
@@ -36,6 +31,13 @@
//i dont have to worry about sprites due to limbs_icon, thank god
//also the head needs to be normal for hair to work
+/datum/species/ghoul/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "ears" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
+
/proc/proof_ghoul_features(list/inFeatures)
// Missing Defaults in DNA? Randomize!
if(inFeatures["ghoulcolor"] == null || inFeatures["ghoulcolor"] == "")
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_species.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_species.dm
index 6f721354cd5..a74eb02b7fd 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_species.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_species.dm
@@ -17,9 +17,6 @@
TRAIT_USES_SKINTONES,
)
inherent_biotypes = MOB_HUMANOID | MOB_ORGANIC
- default_mutant_bodyparts = list(
- "legs" = "Normal Legs"
- )
exotic_bloodtype = "U"
mutantheart = /obj/item/organ/internal/heart/hemophage
mutantliver = /obj/item/organ/internal/liver/hemophage
@@ -31,6 +28,10 @@
skinned_type = /obj/item/stack/sheet/animalhide/human
veteran_only = TRUE
+/datum/species/hemophage/get_default_mutant_bodyparts()
+ return list(
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
/datum/species/hemophage/check_roundstart_eligible()
if(check_holidays(HALLOWEEN))
@@ -38,7 +39,6 @@
return ..()
-
/datum/species/hemophage/on_species_gain(mob/living/carbon/human/new_hemophage, datum/species/old_species, pref_load)
. = ..()
to_chat(new_hemophage, HEMOPHAGE_SPAWN_TEXT)
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/humanoid.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/humanoid.dm
index 41f3715e8ca..33d15682332 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/humanoid.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/humanoid.dm
@@ -9,19 +9,21 @@
)
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID
mutant_bodyparts = list()
- default_mutant_bodyparts = list(
- "tail" = "None",
- "snout" = "None",
- "ears" = "None",
- "legs" = "Normal Legs",
- "wings" = "None",
- "taur" = "None",
- "horns" = "None"
- )
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
payday_modifier = 1.0
examine_limb_id = SPECIES_HUMAN
+/datum/species/humanoid/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "snout" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "ears" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "wings" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "taur" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "horns" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
+
/datum/species/humanoid/get_species_description()
return "This is a template species for your own creations!"
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/insect.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/insect.dm
index 50f2cbec142..880ecec4ca4 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/insect.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/insect.dm
@@ -9,17 +9,6 @@
)
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BUG
mutant_bodyparts = list()
- default_mutant_bodyparts = list(
- "tail" = "None",
- "snout" = "None",
- "horns" = "None",
- "ears" = "None",
- "legs" = "Normal Legs",
- "taur" = "None",
- "fluff" = "None",
- "wings" = "Bee",
- "moth_antennae" = "None"
- )
mutanttongue = /obj/item/organ/internal/tongue/insect
payday_modifier = 1.0
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
@@ -34,6 +23,19 @@
)
eyes_icon = 'modular_skyrat/modules/organs/icons/insect_eyes.dmi'
+/datum/species/insect/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "snout" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "horns" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "ears" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "taur" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "fluff" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "wings" = list(MUTANT_INDEX_NAME = "Bee", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "moth_antennae" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
+
/datum/species/insect/get_species_description()
return placeholder_description
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/lizard.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/lizard.dm
index dd938a43e21..82ea0cf9c31 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/lizard.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/lizard.dm
@@ -1,20 +1,23 @@
/datum/species/lizard
mutant_bodyparts = list()
external_organs = list()
- default_mutant_bodyparts = list(
- "tail" = ACC_RANDOM,
- "snout" = ACC_RANDOM,
- "spines" = ACC_RANDOM,
- "frills" = ACC_RANDOM,
- "horns" = ACC_RANDOM,
- "body_markings" = ACC_RANDOM,
- "legs" = DIGITIGRADE_LEGS,
- "taur" = "None",
- "wings" = "None",
- )
payday_modifier = 1.0
-/datum/species/lizard/randomize_features(mob/living/carbon/human/human_mob)
+/datum/species/lizard/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Smooth", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "snout" = list(MUTANT_INDEX_NAME = "Sharp + Light", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "spines" = list(MUTANT_INDEX_NAME = "Long + Membrane", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "frills" = list(MUTANT_INDEX_NAME = "Short", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "horns" = list(MUTANT_INDEX_NAME = "Curled", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "body_markings" = list(MUTANT_INDEX_NAME = "Light Belly", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "legs" = list(MUTANT_INDEX_NAME = DIGITIGRADE_LEGS, MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "taur" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "wings" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
+
+/datum/species/lizard/randomize_features()
+ var/list/features = ..()
var/main_color = "#[random_color()]"
var/second_color
var/third_color
@@ -29,9 +32,10 @@
if(3) //Third case, more randomisation
second_color = "#[random_color()]"
third_color = "#[random_color()]"
- human_mob.dna.features["mcolor"] = main_color
- human_mob.dna.features["mcolor2"] = second_color
- human_mob.dna.features["mcolor3"] = third_color
+ features["mcolor"] = main_color
+ features["mcolor2"] = second_color
+ features["mcolor3"] = third_color
+ return features
/datum/species/lizard/prepare_human_for_preview(mob/living/carbon/human/lizard, lizard_color = "#009999")
lizard.dna.features["mcolor"] = lizard_color
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/mammal.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/mammal.dm
index 801695b4274..7bf7d0014d6 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/mammal.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/mammal.dm
@@ -10,18 +10,6 @@
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID
mutant_bodyparts = list()
mutanttongue = /obj/item/organ/internal/tongue/mammal
- default_mutant_bodyparts = list(
- "tail" = ACC_RANDOM,
- "snout" = ACC_RANDOM,
- "horns" = "None",
- "ears" = ACC_RANDOM,
- "legs" = ACC_RANDOM,
- "taur" = "None",
- "fluff" = "None",
- "wings" = "None",
- "head_acc" = "None",
- "neck_acc" = "None"
- )
payday_modifier = 1.0
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
bodypart_overrides = list(
@@ -33,6 +21,19 @@
BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/mutant,
)
+/datum/species/mammal/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Husky", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "snout" = list(MUTANT_INDEX_NAME = "Husky", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "horns" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "ears" = list(MUTANT_INDEX_NAME = "Husky", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "taur" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "fluff" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "wings" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "head_acc" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "neck_acc" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
/obj/item/organ/internal/tongue/mammal
liked_foodtypes = GRAIN | MEAT
@@ -40,7 +41,8 @@
toxic_foodtypes = TOXIC
-/datum/species/mammal/randomize_features(mob/living/carbon/human/human_mob)
+/datum/species/mammal/randomize_features()
+ var/list/features = ..()
var/main_color
var/second_color
var/third_color
@@ -74,9 +76,10 @@
main_color = "#[random_color()]"
second_color = "#[random_color()]"
third_color = "#[random_color()]"
- human_mob.dna.features["mcolor"] = main_color
- human_mob.dna.features["mcolor2"] = second_color
- human_mob.dna.features["mcolor3"] = third_color
+ features["mcolor"] = main_color
+ features["mcolor2"] = second_color
+ features["mcolor3"] = third_color
+ return features
/datum/species/mammal/get_random_body_markings(list/passed_features)
var/name = "None"
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/monkey.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/monkey.dm
index 2b46cb02c21..fdcb5630d31 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/monkey.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/monkey.dm
@@ -1,5 +1,7 @@
-/datum/species/monkey
- default_mutant_bodyparts = list("tail" = "Monkey")
+/datum/species/monkey/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Monkey", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
/datum/species/monkey/prepare_human_for_preview(mob/living/carbon/human/monke)
regenerate_organs(monke, src, visual_only = TRUE)
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/moth.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/moth.dm
index 6628060ee92..04a452712a4 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/moth.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/moth.dm
@@ -1,10 +1,5 @@
/datum/species/moth
mutant_bodyparts = list()
- default_mutant_bodyparts = list(
- "fluff" = "None",
- "wings" = ACC_RANDOM,
- "moth_antennae" = ACC_RANDOM,
- )
inherent_traits = list(
TRAIT_HAS_MARKINGS,
TRAIT_TACKLING_WINGED_ATTACKER,
@@ -12,8 +7,17 @@
TRAIT_MUTANT_COLORS,
)
-/datum/species/moth/randomize_features(mob/living/carbon/human/human_mob)
- human_mob.dna.features["mcolor"] = "#E5CD99"
+/datum/species/moth/get_default_mutant_bodyparts()
+ return list(
+ "fluff" = list(MUTANT_INDEX_NAME = "Plain", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "wings" = list(MUTANT_INDEX_NAME = "Moth (Plain)", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "moth_antennae" = list(MUTANT_INDEX_NAME = "Plain", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ )
+
+/datum/species/moth/randomize_features()
+ var/list/features = ..()
+ features["mcolor"] = "#E5CD99"
+ return features
/datum/species/moth/get_random_body_markings(list/passed_features)
var/name = "None"
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/podweak.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/podweak.dm
index 90a4e4298a7..73b66a22c57 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/podweak.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/podweak.dm
@@ -7,12 +7,14 @@
TRAIT_LITERATE,
)
mutant_bodyparts = list()
- default_mutant_bodyparts = list(
- "pod_hair" = ACC_RANDOM,
- "legs" = "Normal Legs"
- )
payday_modifier = 1.0
+/datum/species/pod/get_default_mutant_bodyparts()
+ return list(
+ "pod_hair" = list(MUTANT_INDEX_NAME = "Ivy", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
+
/datum/species/pod/podweak
name = "Podperson"
id = SPECIES_PODPERSON_WEAK
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/roundstartslime.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/roundstartslime.dm
index 422c1fd12cd..eec5fb6e5c7 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/roundstartslime.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/roundstartslime.dm
@@ -1,20 +1,22 @@
/datum/species/jelly
- default_mutant_bodyparts = list(
- "tail" = "None",
- "snout" = "None",
- "ears" = "None",
- "taur" = "None",
- "wings" = "None",
- "legs" = "Normal Legs",
- "horns" = "None",
- "spines" = "None",
- "frills" = "None",
- )
mutant_bodyparts = list()
hair_color = "mutcolor"
hair_alpha = 160 //a notch brighter so it blends better.
facial_hair_alpha = 160
+/datum/species/jelly/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "snout" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "ears" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "taur" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "wings" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "horns" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "spines" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "frills" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
+
/datum/species/jelly/get_species_description()
return placeholder_description
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/skrell.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/skrell.dm
index 47cd5be08c0..b489e6a1c74 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/skrell.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/skrell.dm
@@ -16,7 +16,6 @@
mutant_bodyparts = list()
mutanttongue = /obj/item/organ/internal/tongue/skrell
payday_modifier = 1.0
- default_mutant_bodyparts = list("skrell_hair" = ACC_RANDOM)
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
eyes_icon = 'modular_skyrat/modules/organs/icons/skrell_eyes.dmi'
mutantbrain = /obj/item/organ/internal/brain/skrell
@@ -34,13 +33,19 @@
BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/mutant/skrell,
)
+/datum/species/skrell/get_default_mutant_bodyparts()
+ return list(
+ "skrell_hair" = list(MUTANT_INDEX_NAME = "Male", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ )
+
/datum/species/skrell/get_species_description()
return placeholder_description
/datum/species/skrell/get_species_lore()
return list(placeholder_lore)
-/datum/species/skrell/randomize_features(mob/living/carbon/human/human_mob)
+/datum/species/skrell/randomize_features()
+ var/list/features = ..()
var/main_color
var/random = rand(1,6)
//Choose from a range of green-blue colors
@@ -57,9 +62,10 @@
main_color = "#22BBFF"
if(6)
main_color = "#2266FF"
- human_mob.dna.features["mcolor"] = main_color
- human_mob.dna.features["mcolor2"] = main_color
- human_mob.dna.features["mcolor3"] = main_color
+ features["mcolor"] = main_color
+ features["mcolor2"] = main_color
+ features["mcolor3"] = main_color
+ return features
/datum/species/skrell/prepare_human_for_preview(mob/living/carbon/human/skrell)
var/skrell_color = "#22BBFF"
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/tajaran.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/tajaran.dm
index e5739b23624..e26208ec567 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/tajaran.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/tajaran.dm
@@ -11,12 +11,6 @@
mutanttongue = /obj/item/organ/internal/tongue/cat/tajaran
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID
mutant_bodyparts = list()
- default_mutant_bodyparts = list(
- "tail" = ACC_RANDOM,
- "snout" = ACC_RANDOM,
- "ears" = ACC_RANDOM,
- "legs" = "Normal Legs"
- )
payday_modifier = 1.0
species_language_holder = /datum/language_holder/tajaran
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
@@ -30,13 +24,21 @@
BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/mutant,
)
+/datum/species/tajaran/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Cat (Big)", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "snout" = list(MUTANT_INDEX_NAME = "Cat, normal", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "ears" = list(MUTANT_INDEX_NAME = "Cat, normal", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
/obj/item/organ/internal/tongue/cat/tajaran
liked_foodtypes = GRAIN | MEAT
disliked_foodtypes = CLOTH
-/datum/species/tajaran/randomize_features(mob/living/carbon/human/human_mob)
+/datum/species/tajaran/randomize_features()
+ var/list/features = ..()
var/main_color
var/second_color
var/random = rand(1,5)
@@ -57,9 +59,10 @@
if(5)
main_color = "#DDCC99"
second_color = "#DDCCAA"
- human_mob.dna.features["mcolor"] = main_color
- human_mob.dna.features["mcolor2"] = second_color
- human_mob.dna.features["mcolor3"] = second_color
+ features["mcolor"] = main_color
+ features["mcolor2"] = second_color
+ features["mcolor3"] = second_color
+ return features
/datum/species/tajaran/get_random_body_markings(list/passed_features)
var/name = pick("Tajaran", "Floof", "Floofer")
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/unathi.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/unathi.dm
index c744097639a..e2638e06e3e 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/unathi.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/unathi.dm
@@ -10,15 +10,6 @@
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID
mutant_bodyparts = list()
mutanttongue = /obj/item/organ/internal/tongue/unathi
- default_mutant_bodyparts = list(
- "tail" = ACC_RANDOM,
- "snout" = ACC_RANDOM,
- "spines" = "None",
- "frills" = "None",
- "horns" = ACC_RANDOM,
- "body_markings" = ACC_RANDOM,
- "legs" = "Normal Legs"
- )
payday_modifier = 1.0
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
examine_limb_id = SPECIES_LIZARD
@@ -33,6 +24,16 @@
BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/lizard,
)
+/datum/species/unathi/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Smooth", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "snout" = list(MUTANT_INDEX_NAME = "Sharp + Light", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "spines" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "frills" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "horns" = list(MUTANT_INDEX_NAME = "Curled", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "body_markings" = list(MUTANT_INDEX_NAME = "Smooth Belly", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
/obj/item/organ/internal/tongue/unathi
liked_foodtypes = GORE | MEAT | SEAFOOD | NUTS
@@ -40,7 +41,8 @@
toxic_foodtypes = TOXIC
-/datum/species/unathi/randomize_features(mob/living/carbon/human/human_mob)
+/datum/species/unathi/randomize_features()
+ var/list/features = ..()
var/main_color
var/second_color
var/random = rand(1,5)
@@ -61,9 +63,10 @@
if(5)
main_color = "#33BB11"
second_color = "#339911"
- human_mob.dna.features["mcolor"] = main_color
- human_mob.dna.features["mcolor2"] = second_color
- human_mob.dna.features["mcolor3"] = second_color
+ features["mcolor"] = main_color
+ features["mcolor2"] = second_color
+ features["mcolor3"] = second_color
+ return features
/datum/species/unathi/get_species_description()
return placeholder_description
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/vox.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/vox.dm
index 059019f249e..ecf2d84f623 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/vox.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/vox.dm
@@ -16,12 +16,6 @@
mutantbrain = /obj/item/organ/internal/brain/vox
breathid = "n2"
mutant_bodyparts = list()
- default_mutant_bodyparts = list(
- "tail" = "Vox Tail",
- "legs" = DIGITIGRADE_LEGS,
- "snout" = "Vox Snout",
- "spines" = ACC_RANDOM
- )
payday_modifier = 1.0
outfit_important_for_life = /datum/outfit/vox
species_language_holder = /datum/language_holder/vox
@@ -51,6 +45,14 @@
LOADOUT_ITEM_EARS = VOX_EARS_ICON
)
+/datum/species/vox/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Vox Tail", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "legs" = list(MUTANT_INDEX_NAME = DIGITIGRADE_LEGS, MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "snout" = list(MUTANT_INDEX_NAME = "Vox Snout", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "spines" = list(MUTANT_INDEX_NAME = "Vox Bands", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ )
+
/datum/species/vox/pre_equip_species_outfit(datum/job/job, mob/living/carbon/human/equipping, visuals_only)
. = ..()
if(job?.vox_outfit)
@@ -69,10 +71,12 @@
return randname
-/datum/species/vox/randomize_features(mob/living/carbon/human/human_mob)
- human_mob.dna.features["mcolor"] = pick("#77DD88", "#77DDAA", "#77CCDD", "#77DDCC")
- human_mob.dna.features["mcolor2"] = pick("#EEDD88", "#EECC88")
- human_mob.dna.features["mcolor3"] = pick("#222222", "#44EEFF", "#44FFBB", "#8844FF", "#332233")
+/datum/species/vox/randomize_features()
+ var/list/features = ..()
+ features["mcolor"] = pick("#77DD88", "#77DDAA", "#77CCDD", "#77DDCC")
+ features["mcolor2"] = pick("#EEDD88", "#EECC88")
+ features["mcolor3"] = pick("#222222", "#44EEFF", "#44FFBB", "#8844FF", "#332233")
+ return features
/datum/species/vox/get_random_body_markings(list/passed_features)
var/name = pick(list("Vox", "Vox Hive", "Vox Nightling", "Vox Heart", "Vox Tiger"))
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/vulpkanin.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/vulpkanin.dm
index 62e001c5f74..410d5200eed 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/vulpkanin.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/vulpkanin.dm
@@ -10,12 +10,6 @@
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID
mutant_bodyparts = list()
mutanttongue = /obj/item/organ/internal/tongue/vulpkanin
- default_mutant_bodyparts = list(
- "tail" = ACC_RANDOM,
- "snout" = ACC_RANDOM,
- "ears" = ACC_RANDOM,
- "legs" = "Normal Legs"
- )
species_language_holder = /datum/language_holder/vulpkanin
payday_modifier = 1.0
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
@@ -29,6 +23,13 @@
BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/mutant,
)
+/datum/species/vulpkanin/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Fox", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "snout" = list(MUTANT_INDEX_NAME = "Mammal, Long", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "ears" = list(MUTANT_INDEX_NAME = "Fox", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
/obj/item/organ/internal/tongue/vulpkanin
liked_foodtypes = RAW | MEAT
@@ -36,7 +37,8 @@
toxic_foodtypes = TOXIC
-/datum/species/vulpkanin/randomize_features(mob/living/carbon/human/human_mob)
+/datum/species/vulpkanin/randomize_features()
+ var/list/features = ..()
var/main_color
var/second_color
var/random = rand(1,5)
@@ -57,9 +59,10 @@
if(5)
main_color = "#999999"
second_color = "#EEEEEE"
- human_mob.dna.features["mcolor"] = main_color
- human_mob.dna.features["mcolor2"] = second_color
- human_mob.dna.features["mcolor3"] = second_color
+ features["mcolor"] = main_color
+ features["mcolor2"] = second_color
+ features["mcolor3"] = second_color
+ return features
/datum/species/vulpkanin/get_random_body_markings(list/passed_features)
var/name = pick("Fox", "Floof", "Floofer")
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/xeno.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/xeno.dm
index b4dbe252503..cb73afdc4e1 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/xeno.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/xeno.dm
@@ -12,13 +12,6 @@
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID
mutanttongue = /obj/item/organ/internal/tongue/xeno_hybrid
mutant_bodyparts = list()
- default_mutant_bodyparts = list(
- "tail" = "Xenomorph Tail",
- "xenodorsal" = ACC_RANDOM,
- "xenohead" = ACC_RANDOM,
- "legs" = DIGITIGRADE_LEGS,
- "taur" = "None"
- )
external_organs = list()
payday_modifier = 1.0
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
@@ -34,6 +27,15 @@
meat = /obj/item/food/meat/slab/xeno
skinned_type = /obj/item/stack/sheet/animalhide/xeno
+/datum/species/xeno/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Xenomorph Tail", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "xenodorsal" = list(MUTANT_INDEX_NAME = "Standard", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "xenohead" = list(MUTANT_INDEX_NAME = "Standard", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "legs" = list(MUTANT_INDEX_NAME = DIGITIGRADE_LEGS, MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "taur" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
+
/datum/species/xeno/get_species_description()
return placeholder_description
diff --git a/modular_skyrat/modules/icemoon_additions/code/icecat_recipes.dm b/modular_skyrat/modules/icemoon_additions/code/icecat_recipes.dm
new file mode 100644
index 00000000000..8467bb2f929
--- /dev/null
+++ b/modular_skyrat/modules/icemoon_additions/code/icecat_recipes.dm
@@ -0,0 +1,66 @@
+/obj/item/anointing_oil
+ name = "anointing bloodresin"
+ desc = "And so Helgar Knife-Arm spoke to the Hearth, and decreed that all of the Kin who gave name to beasts would do so with conquest and blood."
+ icon = 'icons/obj/medical/chemical.dmi'
+ icon_state = "potred"
+ throwforce = 0
+ w_class = WEIGHT_CLASS_TINY
+
+ var/being_used = FALSE
+
+/obj/item/anointing_oil/attack(mob/living/target_mob, mob/living/user, params)
+ if (!is_species(user, /datum/species/human/felinid/primitive))
+ to_chat(user, span_warning("You have no idea what this disgusting concoction is used for."))
+ return
+ if(being_used || !ismob(target_mob)) //originally this was going to check if the mob was friendly, but if an icecat wants to name some terror mob while it's tearing chunks out of them, why not?
+ return
+ if(target_mob.ckey)
+ to_chat(user, span_warning("You would never shame a creature so intelligent by not allowing it to choose its own name."))
+ return
+
+ if(try_anoint(target_mob, user))
+ qdel(src)
+ else
+ being_used = FALSE
+
+/obj/item/anointing_oil/proc/try_anoint(mob/living/target_mob, mob/living/user)
+ being_used = TRUE
+
+ var/new_name = sanitize_name(tgui_input_text(user, "Speak forth this beast's new name for all the Kin to hear.", "Input a name", target_mob.name, MAX_NAME_LEN))
+
+ if(!new_name || QDELETED(src) || QDELETED(target_mob) || new_name == target_mob.name || !target_mob.Adjacent(user))
+ being_used = FALSE
+ return FALSE
+
+ target_mob.visible_message(span_notice("[user] leans down and smears twinned streaks of glistening bloodresin upon [target_mob], then straightens up with ritual purpose..."))
+ user.say("Let the ice know you forevermore as +[new_name]+.")
+
+ user.log_message("used [src] on [target_mob], renaming it to [new_name].", LOG_GAME)
+
+ target_mob.name = new_name
+
+ //give the stupid dog zoomies from getting named
+ if(istype(target_mob, /mob/living/basic/mining/wolf))
+ target_mob.emote("awoo")
+ target_mob.emote("spin")
+
+ return TRUE
+
+/obj/item/anointing_oil/examine(mob/user)
+ . = ..()
+ if(is_species(user, /datum/species/human/felinid/primitive))
+ . += span_info("Using this on the local wildlife will allow you to give them a name.")
+
+/datum/crafting_recipe/anointing_oil
+ name = "Anointing Bloodresin"
+ category = CAT_MISC
+
+ //recipe given to icecats as part of their spawner/team setting
+ always_available = FALSE
+
+ reqs = list(
+ /datum/reagent/consumable/liquidgibs = 80,
+ /datum/reagent/blood = 20,
+ )
+
+ result = /obj/item/anointing_oil
diff --git a/modular_skyrat/modules/icemoon_additions/code/pet_commands.dm b/modular_skyrat/modules/icemoon_additions/code/pet_commands.dm
new file mode 100644
index 00000000000..42d202d572a
--- /dev/null
+++ b/modular_skyrat/modules/icemoon_additions/code/pet_commands.dm
@@ -0,0 +1,29 @@
+/datum/component/obeys_commands/RegisterWithParent()
+ . = ..()
+ RegisterSignal(parent, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(on_examine_more))
+
+/datum/component/obeys_commands/UnregisterFromParent()
+ . = ..()
+ UnregisterSignal(parent, COMSIG_ATOM_EXAMINE_MORE)
+
+/datum/component/obeys_commands/on_examine(mob/living/source, mob/user, list/examine_list)
+ . = ..()
+ . += span_italics("You can alt+click [source.p_them()] when adjacent to see available commands.")
+ . += span_italics("You can also examine [source.p_them()] closely to check on [source.p_their()] wounds. Many companions can be healed with sutures or creams!")
+
+/datum/component/obeys_commands/proc/on_examine_more(mob/living/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+
+ if (IS_DEAD_OR_INCAP(source))
+ return
+ if (!(user in source.ai_controller?.blackboard[BB_FRIENDS_LIST]))
+ return
+
+ if (source.health < source.maxHealth*0.2)
+ examine_list += span_bolddanger("[source.p_They()] look[source.p_s()] severely injured.")
+ else if (source.health < source.maxHealth*0.5)
+ examine_list += span_danger("[source.p_They()] look[source.p_s()] moderately injured.")
+ else if (source.health < source.maxHealth*0.8)
+ examine_list += span_warning("[source.p_They()] look[source.p_s()] slightly injured.")
+ else
+ examine_list += span_notice("[source.p_They()] look[source.p_s()] to be in good condition.")
diff --git a/modular_skyrat/modules/medical/code/wounds/synth/blunt/robotic_blunt.dm b/modular_skyrat/modules/medical/code/wounds/synth/blunt/robotic_blunt.dm
index 52995365b01..66cbbb510b2 100644
--- a/modular_skyrat/modules/medical/code/wounds/synth/blunt/robotic_blunt.dm
+++ b/modular_skyrat/modules/medical/code/wounds/synth/blunt/robotic_blunt.dm
@@ -140,7 +140,7 @@
/datum/wound/blunt/robotic/handle_process(seconds_per_tick, times_fired)
. = ..()
- if (!victim || IS_IN_STASIS(victim))
+ if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
return
if (limb.body_zone == BODY_ZONE_HEAD && brain_trauma_group && world.time > next_trauma_cycle)
diff --git a/modular_skyrat/modules/medical/code/wounds/synth/blunt/secures_internals.dm b/modular_skyrat/modules/medical/code/wounds/synth/blunt/secures_internals.dm
index e9e9ce3cb68..d4483dafb0b 100644
--- a/modular_skyrat/modules/medical/code/wounds/synth/blunt/secures_internals.dm
+++ b/modular_skyrat/modules/medical/code/wounds/synth/blunt/secures_internals.dm
@@ -20,7 +20,7 @@
/datum/wound/blunt/robotic/secures_internals/handle_process(seconds_per_tick, times_fired)
. = ..()
- if (!victim || IS_IN_STASIS(victim))
+ if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
return
if (gelled)
diff --git a/modular_skyrat/modules/medical/code/wounds/synth/robotic_burns.dm b/modular_skyrat/modules/medical/code/wounds/synth/robotic_burns.dm
index 24ac18eeab6..16540ac9f36 100644
--- a/modular_skyrat/modules/medical/code/wounds/synth/robotic_burns.dm
+++ b/modular_skyrat/modules/medical/code/wounds/synth/robotic_burns.dm
@@ -126,7 +126,7 @@
if (outgoing_bodytemp_coeff <= 0)
return
var/statis_mult = 1
- if (IS_IN_STASIS(victim)) // stasis heavily reduces the ingoing and outgoing transfer of heat
+ if (HAS_TRAIT(victim, TRAIT_STASIS)) // stasis heavily reduces the ingoing and outgoing transfer of heat
statis_mult *= OVERHEAT_ON_STASIS_HEAT_MULT
var/difference_from_average = max((BODYTEMP_NORMAL - victim.bodytemperature), 0)
diff --git a/modular_skyrat/modules/medical/code/wounds/synth/robotic_slash.dm b/modular_skyrat/modules/medical/code/wounds/synth/robotic_slash.dm
index 1a0c730c68c..2d20b0edbb2 100644
--- a/modular_skyrat/modules/medical/code/wounds/synth/robotic_slash.dm
+++ b/modular_skyrat/modules/medical/code/wounds/synth/robotic_slash.dm
@@ -194,7 +194,7 @@
var/base_mult = 1
if (victim)
- if (IS_IN_STASIS(victim))
+ if (HAS_TRAIT(victim, TRAIT_STASIS))
base_mult *= ELECTRICAL_DAMAGE_ON_STASIS_MULT
if (victim.body_position == LYING_DOWN)
base_mult *= ELECTRICAL_DAMAGE_LYING_DOWN_MULT
diff --git a/modular_skyrat/modules/modular_implants/code/nifs.dm b/modular_skyrat/modules/modular_implants/code/nifs.dm
index 12d9c67ad81..5d2b1aabd4a 100644
--- a/modular_skyrat/modules/modular_implants/code/nifs.dm
+++ b/modular_skyrat/modules/modular_implants/code/nifs.dm
@@ -185,7 +185,7 @@
/obj/item/organ/internal/cyberimp/brain/nif/process(seconds_per_tick)
. = ..()
- if(!linked_mob || broken || IS_IN_STASIS(linked_mob))
+ if(!linked_mob || broken || HAS_TRAIT(linked_mob, TRAIT_STASIS))
return FALSE
if(calibrating)
diff --git a/modular_skyrat/modules/poly_commands/parrot.dm b/modular_skyrat/modules/poly_commands/parrot.dm
index f522edf7da1..3fe164cc6ef 100644
--- a/modular_skyrat/modules/poly_commands/parrot.dm
+++ b/modular_skyrat/modules/poly_commands/parrot.dm
@@ -1,47 +1,19 @@
-#define PARROT_PERCH (1<<0) //Sitting/sleeping, not moving
-#define PARROT_SWOOP (1<<1) //Moving towards or away from a target
-#define PARROT_WANDER (1<<2) //Moving without a specific target in mind
-
/*
* Parrot commands: Made modular
*/
-/mob/living/simple_animal/parrot
+/mob/living/basic/parrot
/// Whether the parrot is on a human's shoulder or not
- var/buckled_to_human = FALSE
-
-/mob/living/simple_animal/parrot/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range)
- . = ..()
- if(check_command(raw_message, speaker))
- return
- if(speaker != src && prob(50)) //Dont imitate ourselves
- if(!radio_freq || prob(10))
- if(speech_buffer.len >= 500)
- speech_buffer -= pick(speech_buffer)
- speech_buffer |= html_decode(raw_message)
- if(speaker == src && !client) //If a parrot squawks in the woods and no one is around to hear it, does it make a sound? This code says yes!
- return raw_message
-
-/mob/living/simple_animal/parrot/proc/perch_on_human(mob/living/carbon/human/human_target)
- if(!human_target)
- return
- forceMove(get_turf(human_target))
- if(human_target.buckle_mob(src, TRUE))
- pixel_y = 9
- pixel_x = pick(-8,8) //pick left or right shoulder
- icon_state = icon_sit
- parrot_state = PARROT_PERCH
- buckled_to_human = TRUE
- to_chat(src, span_notice("You sit on [human_target]'s shoulder."))
+ var/buckled_to_human
/*
* Parrot commands: new code
*/
-/mob/living/simple_animal/parrot/proc/check_command()
+/mob/living/basic/parrot/proc/check_command(message, speaker)
return FALSE // Simply return false for non-Poly parrots
-/mob/living/simple_animal/parrot/poly/check_command(message, speaker)
+/mob/living/basic/parrot/poly/check_command(message, speaker)
var/mob/living/carbon/human/human_target = speaker
if(!istype(human_target))
return FALSE
@@ -58,8 +30,13 @@
else
return FALSE
-/mob/living/simple_animal/parrot/poly/proc/command_perch(mob/living/carbon/human/human_target)
- if (!buckled)
+/mob/living/basic/parrot/toggle_perched(perched)
+ . = ..()
+ if(!perched)
+ buckled_to_human = FALSE
+
+/mob/living/basic/parrot/poly/proc/command_perch(mob/living/carbon/human/human_target)
+ if(!buckled)
buckled_to_human = FALSE
if(LAZYLEN(human_target.buckled_mobs) >= human_target.max_buckled_mobs)
return
@@ -70,26 +47,18 @@
manual_emote("tilts their head at [human_target], before bawking loudly and staying put.")
return
manual_emote("obediently hops up onto [human_target]'s shoulder, spreading their wings for a moment before settling down.")
- perch_on_human(human_target)
+ if(start_perching(human_target))
+ buckled_to_human = TRUE
-/mob/living/simple_animal/parrot/poly/proc/command_hop_off(mob/living/carbon/human/human_target)
- if (!buckled)
+/mob/living/basic/parrot/poly/proc/command_hop_off(mob/living/carbon/human/human_target)
+ if(!buckled)
buckled_to_human = FALSE
if(!buckled_to_human || !buckled)
manual_emote("gives [human_target] a confused look, squawking softly.")
return
- icon_state = icon_living
- parrot_state = PARROT_WANDER
if(buckled)
to_chat(src, span_notice("You are no longer sitting on [human_target]."))
buckled.unbuckle_mob(src, TRUE)
manual_emote("squawks and hops off of [human_target], flying away.")
- buckled = null
- buckled_to_human = FALSE
- pixel_x = initial(pixel_x)
- pixel_y = initial(pixel_y)
-#undef PARROT_PERCH
-#undef PARROT_SWOOP
-#undef PARROT_WANDER
diff --git a/modular_skyrat/modules/primitive_catgirls/code/spawner.dm b/modular_skyrat/modules/primitive_catgirls/code/spawner.dm
index 2e2d9ec0d17..3bed64d3935 100644
--- a/modular_skyrat/modules/primitive_catgirls/code/spawner.dm
+++ b/modular_skyrat/modules/primitive_catgirls/code/spawner.dm
@@ -101,6 +101,7 @@
/datum/crafting_recipe/boneaxe,
/datum/crafting_recipe/bonespear,
/datum/crafting_recipe/bonedagger,
+ /datum/crafting_recipe/anointing_oil,
)
/datum/antagonist/primitive_catgirl/Destroy()
diff --git a/modular_skyrat/modules/primitive_structures/code/storage_structures.dm b/modular_skyrat/modules/primitive_structures/code/storage_structures.dm
index 8c6f872c712..a9c1ce0b9fb 100644
--- a/modular_skyrat/modules/primitive_structures/code/storage_structures.dm
+++ b/modular_skyrat/modules/primitive_structures/code/storage_structures.dm
@@ -57,15 +57,15 @@
icon_state = "producebin"
icon = 'modular_skyrat/modules/primitive_structures/icons/storage.dmi'
resistance_flags = FLAMMABLE
- visible_contents = FALSE
base_build_path = /obj/machinery/smartfridge/producebin
+ contents_icon_state = "produce"
use_power = NO_POWER_USE
light_power = 0
idle_power_usage = 0
circuit = null
has_emissive = FALSE
can_atmos_pass = ATMOS_PASS_YES
- visible_contents = FALSE
+ visible_contents = TRUE
/obj/machinery/smartfridge/producebin/accept_check(obj/item/weapon)
return (istype(weapon, /obj/item/food/grown))
@@ -88,15 +88,15 @@
icon_state = "seedshelf"
icon = 'modular_skyrat/modules/primitive_structures/icons/storage.dmi'
resistance_flags = FLAMMABLE
- visible_contents = FALSE
base_build_path = /obj/machinery/smartfridge/seedshelf
+ contents_icon_state = "seed"
use_power = NO_POWER_USE
light_power = 0
idle_power_usage = 0
circuit = null
has_emissive = FALSE
can_atmos_pass = ATMOS_PASS_YES
- visible_contents = FALSE
+ visible_contents = TRUE
/obj/machinery/smartfridge/seedshelf/accept_check(obj/item/weapon)
return istype(weapon, /obj/item/seeds)
diff --git a/modular_skyrat/modules/primitive_structures/icons/storage.dmi b/modular_skyrat/modules/primitive_structures/icons/storage.dmi
index 2b71389146d..1c2c3626321 100644
Binary files a/modular_skyrat/modules/primitive_structures/icons/storage.dmi and b/modular_skyrat/modules/primitive_structures/icons/storage.dmi differ
diff --git a/modular_skyrat/modules/stasisrework/code/stasissleeper.dm b/modular_skyrat/modules/stasisrework/code/stasissleeper.dm
index ffeb12dc56e..3510fa5c269 100644
--- a/modular_skyrat/modules/stasisrework/code/stasissleeper.dm
+++ b/modular_skyrat/modules/stasisrework/code/stasissleeper.dm
@@ -75,7 +75,7 @@
visible_message(span_notice("[occupant] emerges from [src]!"),
span_notice("You climb out of [src]!"))
open_machine()
- if(IS_IN_STASIS(user))
+ if(HAS_TRAIT(user, TRAIT_STASIS))
thaw_them(user)
/obj/machinery/stasissleeper/proc/stasis_running()
@@ -112,9 +112,9 @@
return
var/mob/living/L_occupant = occupant
if(stasis_running())
- if(!IS_IN_STASIS(L_occupant))
+ if(!HAS_TRAIT(L_occupant, TRAIT_STASIS))
chill_out(L_occupant)
- else if(IS_IN_STASIS(L_occupant))
+ else if(HAS_TRAIT(L_occupant, TRAIT_STASIS))
thaw_them(L_occupant)
/obj/machinery/stasissleeper/screwdriver_act(mob/living/user, obj/item/used_item)
diff --git a/modular_skyrat/modules/synths/code/species/synthetic.dm b/modular_skyrat/modules/synths/code/species/synthetic.dm
index 124985d73da..e3167ab4a4f 100644
--- a/modular_skyrat/modules/synths/code/species/synthetic.dm
+++ b/modular_skyrat/modules/synths/code/species/synthetic.dm
@@ -18,19 +18,8 @@
TRAIT_LITERATE,
TRAIT_NOCRITDAMAGE, // We do our own handling of crit damage.
TRAIT_ROBOTIC_DNA_ORGANS,
- TRAIT_NO_TRANSFORMATION_STING,
)
mutant_bodyparts = list()
- default_mutant_bodyparts = list(
- "tail" = "None",
- "ears" = "None",
- "legs" = "Normal Legs",
- "snout" = "None",
- MUTANT_SYNTH_ANTENNA = "None",
- MUTANT_SYNTH_SCREEN = "None",
- MUTANT_SYNTH_CHASSIS = "Default Chassis",
- MUTANT_SYNTH_HEAD = "Default Head",
- )
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
reagent_flags = PROCESS_SYNTHETIC
payday_modifier = 1.0 // Matches the rest of the pay penalties the non-human crew have
@@ -63,6 +52,18 @@
/// This is the screen that is given to the user after they get revived. On death, their screen is temporarily set to BSOD before it turns off, hence the need for this var.
var/saved_screen = "Blank"
+/datum/species/synthetic/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "ears" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ "snout" = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ MUTANT_SYNTH_ANTENNA = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ MUTANT_SYNTH_SCREEN = list(MUTANT_INDEX_NAME = "None", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ MUTANT_SYNTH_CHASSIS = list(MUTANT_INDEX_NAME = "Default Chassis", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ MUTANT_SYNTH_HEAD = list(MUTANT_INDEX_NAME = "Default Head", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
+
/datum/species/synthetic/spec_life(mob/living/carbon/human/human)
. = ..()
diff --git a/modular_skyrat/modules/teshari/code/_teshari.dm b/modular_skyrat/modules/teshari/code/_teshari.dm
index 84dc35e59c2..ed1663d6f9a 100644
--- a/modular_skyrat/modules/teshari/code/_teshari.dm
+++ b/modular_skyrat/modules/teshari/code/_teshari.dm
@@ -14,11 +14,6 @@
TRAIT_NO_UNDERWEAR,
TRAIT_HAS_MARKINGS,
)
- default_mutant_bodyparts = list(
- "tail" = ACC_RANDOM,
- "ears" = ACC_RANDOM,
- "legs" = "Normal Legs"
- )
digitigrade_customization = DIGITIGRADE_NEVER
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
payday_modifier = 1.0
@@ -53,6 +48,13 @@
BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/mutant/teshari,
)
+/datum/species/teshari/get_default_mutant_bodyparts()
+ return list(
+ "tail" = list(MUTANT_INDEX_NAME = "Teshari (Default)", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "ears" = list(MUTANT_INDEX_NAME = "Teshari Regular", MUTANT_INDEX_CAN_RANDOMIZE = TRUE),
+ "legs" = list(MUTANT_INDEX_NAME = "Normal Legs", MUTANT_INDEX_CAN_RANDOMIZE = FALSE),
+ )
+
/obj/item/organ/internal/tongue/teshari
liked_foodtypes = MEAT
diff --git a/tff_modular/modules/nabbers/code/_nabbers.dm b/tff_modular/modules/nabbers/code/_nabbers.dm
index b18982429cd..79c98824c84 100644
--- a/tff_modular/modules/nabbers/code/_nabbers.dm
+++ b/tff_modular/modules/nabbers/code/_nabbers.dm
@@ -87,6 +87,7 @@
H.apply_damage(NABBER_DAMAGE_ONBURNING, OXY)
/datum/species/nabber/randomize_features(mob/living/carbon/human/human_mob)
+ var/list/features = ..()
var/main_color
var/random = rand(1,6)
switch(random)
@@ -102,9 +103,10 @@
main_color = "#c0ad00"
if(6)
main_color = "#e6ff03"
- human_mob.dna.features["mcolor"] = main_color
- human_mob.dna.features["mcolor2"] = main_color
- human_mob.dna.features["mcolor3"] = main_color
+ features["mcolor"] = main_color
+ features["mcolor2"] = main_color
+ features["mcolor3"] = main_color
+ return features
/datum/species/nabber/prepare_human_for_preview(mob/living/carbon/human/nabber)
var/nabber_color = "#00ac1d"
diff --git a/tff_modular/modules/teshari_reborn/code/teshari.dm b/tff_modular/modules/teshari_reborn/code/teshari.dm
index e6317be4a4f..5c16af2ad85 100644
--- a/tff_modular/modules/teshari_reborn/code/teshari.dm
+++ b/tff_modular/modules/teshari_reborn/code/teshari.dm
@@ -41,9 +41,6 @@
LOADOUT_ITEM_ACCESSORY = TESHARI_ACCESSORIES_ICON,
LOADOUT_ITEM_EARS = TESHARI_EARS_ICON
)
- default_mutant_bodyparts = list(
- "legs" = "Normal Legs"
- )
coldmod = TESHARI_ALT_COLDMOD
heatmod = TESHARI_ALT_HEATMOD
bodytemp_normal = BODYTEMP_NORMAL + (TEHSARI_ALT_TEMP_OFFSET/2)
@@ -76,15 +73,17 @@
. = ..()
teshari_agility.Destroy()
teshari_echolocation.Destroy()
+ qdel(C.GetComponent(/datum/component/weak_body))
C.mob_size = initial(C.mob_size)
/datum/species/teshari/alt/randomize_features(mob/living/carbon/human/human_mob)
- . = ..()
+ var/list/features = ..()
var/main_color = pick(COLOR_GRAY, COLOR_DARK_BROWN, COLOR_ALMOST_BLACK, COLOR_DARK_RED, COLOR_DARK_CYAN)
var/second_color = pick(COLOR_WHITE, COLOR_BLACK, COLOR_BLUE, COLOR_VIOLET)
- human_mob.dna.features["mcolor"] = main_color
- human_mob.dna.features["mcolor2"] = second_color
- human_mob.dna.features["mcolor3"] = second_color
+ features["mcolor"] = main_color
+ features["mcolor2"] = second_color
+ features["mcolor3"] = second_color
+ return features
/datum/species/teshari/alt/create_pref_unique_perks()
var/list/perk_descriptions = list()
diff --git a/tgstation.dme b/tgstation.dme
index 71bad6e81c2..4ea0689bc46 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -154,6 +154,7 @@
#include "code\__DEFINES\movespeed_modification.dm"
#include "code\__DEFINES\multiz.dm"
#include "code\__DEFINES\nitrile.dm"
+#include "code\__DEFINES\nozzle_define.dm"
#include "code\__DEFINES\nuclear_bomb.dm"
#include "code\__DEFINES\obj_flags.dm"
#include "code\__DEFINES\observers.dm"
@@ -674,15 +675,7 @@
#include "code\_onclick\hud\rendering\plane_master_group.dm"
#include "code\_onclick\hud\rendering\render_plate.dm"
#include "code\_onclick\hud\rendering\plane_masters\_plane_master.dm"
-#include "code\_onclick\hud\rendering\plane_masters\camera_static.dm"
-#include "code\_onclick\hud\rendering\plane_masters\clickcatcher.dm"
-#include "code\_onclick\hud\rendering\plane_masters\core_game_planes.dm"
-#include "code\_onclick\hud\rendering\plane_masters\default.dm"
-#include "code\_onclick\hud\rendering\plane_masters\in_world_chat.dm"
-#include "code\_onclick\hud\rendering\plane_masters\lighting.dm"
-#include "code\_onclick\hud\rendering\plane_masters\parallax.dm"
-#include "code\_onclick\hud\rendering\plane_masters\pipecrawl.dm"
-#include "code\_onclick\hud\rendering\plane_masters\simple_plane_masters.dm"
+#include "code\_onclick\hud\rendering\plane_masters\plane_master_subtypes.dm"
#include "code\controllers\admin.dm"
#include "code\controllers\controller.dm"
#include "code\controllers\failsafe.dm"
@@ -954,6 +947,7 @@
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\targeting.dm"
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\tipped_reaction.dm"
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\travel_towards.dm"
+#include "code\datums\ai\basic_mobs\basic_ai_behaviors\unbuckle_mob.dm"
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\ventcrawling.dm"
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\wounded_targeting.dm"
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\write_on_paper.dm"
@@ -1179,6 +1173,7 @@
#include "code\datums\components\life_link.dm"
#include "code\datums\components\light_eater.dm"
#include "code\datums\components\ling_decoy_brain.dm"
+#include "code\datums\components\listen_and_repeat.dm"
#include "code\datums\components\lock_on_cursor.dm"
#include "code\datums\components\lockable_storage.dm"
#include "code\datums\components\magnet.dm"
@@ -1839,6 +1834,7 @@
#include "code\datums\status_effects\debuffs\debuffs.dm"
#include "code\datums\status_effects\debuffs\decloning.dm"
#include "code\datums\status_effects\debuffs\dizziness.dm"
+#include "code\datums\status_effects\debuffs\dna_transformation.dm"
#include "code\datums\status_effects\debuffs\drowsiness.dm"
#include "code\datums\status_effects\debuffs\drugginess.dm"
#include "code\datums\status_effects\debuffs\drunk.dm"
@@ -4694,6 +4690,15 @@
#include "code\modules\mob\living\basic\pets\dog\corgi.dm"
#include "code\modules\mob\living\basic\pets\dog\dog_subtypes.dm"
#include "code\modules\mob\living\basic\pets\dog\strippable_items.dm"
+#include "code\modules\mob\living\basic\pets\parrot\_parrot.dm"
+#include "code\modules\mob\living\basic\pets\parrot\parrot_items.dm"
+#include "code\modules\mob\living\basic\pets\parrot\parrot_subtypes.dm"
+#include "code\modules\mob\living\basic\pets\parrot\poly.dm"
+#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\_parrot_controller.dm"
+#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\ghost_parrot_controller.dm"
+#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_hoarding.dm"
+#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_perching.dm"
+#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parroting_action.dm"
#include "code\modules\mob\living\basic\ruin_defender\flesh.dm"
#include "code\modules\mob\living\basic\ruin_defender\living_floor.dm"
#include "code\modules\mob\living\basic\ruin_defender\skeleton.dm"
@@ -4943,7 +4948,6 @@
#include "code\modules\mob\living\silicon\robot\robot_say.dm"
#include "code\modules\mob\living\simple_animal\animal_defense.dm"
#include "code\modules\mob\living\simple_animal\damage_procs.dm"
-#include "code\modules\mob\living\simple_animal\parrot.dm"
#include "code\modules\mob\living\simple_animal\simple_animal.dm"
#include "code\modules\mob\living\simple_animal\bot\bot.dm"
#include "code\modules\mob\living\simple_animal\bot\bot_announcement.dm"
@@ -6170,6 +6174,7 @@
#include "modular_skyrat\master_files\code\modules\antagonists\_common\antag_datum.dm"
#include "modular_skyrat\master_files\code\modules\antagonists\ashwalker\ashwalker.dm"
#include "modular_skyrat\master_files\code\modules\antagonists\changeling\changeling.dm"
+#include "modular_skyrat\master_files\code\modules\antagonists\changeling\powers\tiny_prick.dm"
#include "modular_skyrat\master_files\code\modules\antagonists\cult\cult_items.dm"
#include "modular_skyrat\master_files\code\modules\antagonists\ert\ert.dm"
#include "modular_skyrat\master_files\code\modules\antagonists\pirate\pirate_outfits.dm"
@@ -6355,6 +6360,7 @@
#include "modular_skyrat\master_files\code\modules\mod\modules\modules_antag.dm"
#include "modular_skyrat\master_files\code\modules\mod\modules\modules_supply.dm"
#include "modular_skyrat\master_files\code\modules\modular_computers\computers\item\laptop_presets.dm"
+#include "modular_skyrat\master_files\code\modules\modular_computers\file_system\programs\maintenance\camera.dm"
#include "modular_skyrat\master_files\code\modules\pai\card.dm"
#include "modular_skyrat\master_files\code\modules\paperwork\employment_contract.dm"
#include "modular_skyrat\master_files\code\modules\paperwork\stamps.dm"
@@ -7169,6 +7175,8 @@
#include "modular_skyrat\modules\hyposprays\code\hyposprays_II.dm"
#include "modular_skyrat\modules\hyposprays\code\hypovials.dm"
#include "modular_skyrat\modules\hyposprays\code\vending_hypospray.dm"
+#include "modular_skyrat\modules\icemoon_additions\code\icecat_recipes.dm"
+#include "modular_skyrat\modules\icemoon_additions\code\pet_commands.dm"
#include "modular_skyrat\modules\ices_events\code\_ICES_globalvars.dm"
#include "modular_skyrat\modules\ices_events\code\ICES_event_config.dm"
#include "modular_skyrat\modules\ices_events\code\ICES_intensity_credits.dm"
diff --git a/tgui/packages/common/keycodes.js b/tgui/packages/common/keycodes.ts
similarity index 100%
rename from tgui/packages/common/keycodes.js
rename to tgui/packages/common/keycodes.ts
diff --git a/tgui/packages/tgui-dev-server/dreamseeker.js b/tgui/packages/tgui-dev-server/dreamseeker.js
index 2b25b155ae0..c81f51e35c5 100644
--- a/tgui/packages/tgui-dev-server/dreamseeker.js
+++ b/tgui/packages/tgui-dev-server/dreamseeker.js
@@ -6,8 +6,8 @@
import { exec } from 'child_process';
import { promisify } from 'util';
-import { createLogger } from './logging.js';
-import { require } from './require.js';
+import { createLogger } from './logging';
+import { require } from './require';
const axios = require('axios');
const logger = createLogger('dreamseeker');
diff --git a/tgui/packages/tgui-dev-server/index.js b/tgui/packages/tgui-dev-server/index.js
index 199e93d8363..460b15d99ad 100644
--- a/tgui/packages/tgui-dev-server/index.js
+++ b/tgui/packages/tgui-dev-server/index.js
@@ -4,8 +4,8 @@
* @license MIT
*/
-import { createCompiler } from './webpack.js';
-import { reloadByondCache } from './reloader.js';
+import { createCompiler } from './webpack';
+import { reloadByondCache } from './reloader';
const noHot = process.argv.includes('--no-hot');
const noTmp = process.argv.includes('--no-tmp');
diff --git a/tgui/packages/tgui-dev-server/link/retrace.js b/tgui/packages/tgui-dev-server/link/retrace.js
index 842de228fdf..949835c7002 100644
--- a/tgui/packages/tgui-dev-server/link/retrace.js
+++ b/tgui/packages/tgui-dev-server/link/retrace.js
@@ -6,9 +6,9 @@
import fs from 'fs';
import { basename } from 'path';
-import { createLogger } from '../logging.js';
-import { require } from '../require.js';
-import { resolveGlob } from '../util.js';
+import { createLogger } from '../logging';
+import { require } from '../require';
+import { resolveGlob } from '../util';
const SourceMap = require('source-map');
const { parse: parseStackTrace } = require('stacktrace-parser');
diff --git a/tgui/packages/tgui-dev-server/link/server.js b/tgui/packages/tgui-dev-server/link/server.js
index 60cc78c1bd9..f0c0d153d3a 100644
--- a/tgui/packages/tgui-dev-server/link/server.js
+++ b/tgui/packages/tgui-dev-server/link/server.js
@@ -6,9 +6,9 @@
import http from 'http';
import { inspect } from 'util';
-import { createLogger, directLog } from '../logging.js';
-import { require } from '../require.js';
-import { loadSourceMaps, retrace } from './retrace.js';
+import { createLogger, directLog } from '../logging';
+import { require } from '../require';
+import { loadSourceMaps, retrace } from './retrace';
const WebSocket = require('ws');
diff --git a/tgui/packages/tgui-dev-server/reloader.js b/tgui/packages/tgui-dev-server/reloader.js
index c13a8afdfcf..aed9a7dcd77 100644
--- a/tgui/packages/tgui-dev-server/reloader.js
+++ b/tgui/packages/tgui-dev-server/reloader.js
@@ -7,10 +7,10 @@
import fs from 'fs';
import os from 'os';
import { basename } from 'path';
-import { DreamSeeker } from './dreamseeker.js';
-import { createLogger } from './logging.js';
-import { resolveGlob, resolvePath } from './util.js';
-import { regQuery } from './winreg.js';
+import { DreamSeeker } from './dreamseeker';
+import { createLogger } from './logging';
+import { resolveGlob, resolvePath } from './util';
+import { regQuery } from './winreg';
const logger = createLogger('reloader');
diff --git a/tgui/packages/tgui-dev-server/util.js b/tgui/packages/tgui-dev-server/util.js
index 9d07b96c71a..d60ebb212fa 100644
--- a/tgui/packages/tgui-dev-server/util.js
+++ b/tgui/packages/tgui-dev-server/util.js
@@ -6,7 +6,7 @@
import fs from 'fs';
import path from 'path';
-import { require } from './require.js';
+import { require } from './require';
const globPkg = require('glob');
diff --git a/tgui/packages/tgui-dev-server/webpack.js b/tgui/packages/tgui-dev-server/webpack.js
index 139610b79ce..1c16345a892 100644
--- a/tgui/packages/tgui-dev-server/webpack.js
+++ b/tgui/packages/tgui-dev-server/webpack.js
@@ -7,10 +7,10 @@
import fs from 'fs';
import { createRequire } from 'module';
import { dirname } from 'path';
-import { loadSourceMaps, setupLink } from './link/server.js';
-import { createLogger } from './logging.js';
-import { reloadByondCache } from './reloader.js';
-import { resolveGlob } from './util.js';
+import { loadSourceMaps, setupLink } from './link/server';
+import { createLogger } from './logging';
+import { reloadByondCache } from './reloader';
+import { resolveGlob } from './util';
const logger = createLogger('webpack');
diff --git a/tgui/packages/tgui-dev-server/winreg.js b/tgui/packages/tgui-dev-server/winreg.js
index b61fddc1a25..d7408b5c390 100644
--- a/tgui/packages/tgui-dev-server/winreg.js
+++ b/tgui/packages/tgui-dev-server/winreg.js
@@ -8,7 +8,7 @@
import { exec } from 'child_process';
import { promisify } from 'util';
-import { createLogger } from './logging.js';
+import { createLogger } from './logging';
const logger = createLogger('winreg');
diff --git a/tgui/packages/tgui-panel/Notifications.js b/tgui/packages/tgui-panel/Notifications.jsx
similarity index 100%
rename from tgui/packages/tgui-panel/Notifications.js
rename to tgui/packages/tgui-panel/Notifications.jsx
diff --git a/tgui/packages/tgui-panel/Panel.js b/tgui/packages/tgui-panel/Panel.jsx
similarity index 100%
rename from tgui/packages/tgui-panel/Panel.js
rename to tgui/packages/tgui-panel/Panel.jsx
diff --git a/tgui/packages/tgui-panel/audio/NowPlayingWidget.js b/tgui/packages/tgui-panel/audio/NowPlayingWidget.jsx
similarity index 100%
rename from tgui/packages/tgui-panel/audio/NowPlayingWidget.js
rename to tgui/packages/tgui-panel/audio/NowPlayingWidget.jsx
diff --git a/tgui/packages/tgui-panel/audio/index.js b/tgui/packages/tgui-panel/audio/index.ts
similarity index 100%
rename from tgui/packages/tgui-panel/audio/index.js
rename to tgui/packages/tgui-panel/audio/index.ts
diff --git a/tgui/packages/tgui-panel/chat/ChatPageSettings.js b/tgui/packages/tgui-panel/chat/ChatPageSettings.jsx
similarity index 100%
rename from tgui/packages/tgui-panel/chat/ChatPageSettings.js
rename to tgui/packages/tgui-panel/chat/ChatPageSettings.jsx
diff --git a/tgui/packages/tgui-panel/chat/ChatPanel.js b/tgui/packages/tgui-panel/chat/ChatPanel.jsx
similarity index 100%
rename from tgui/packages/tgui-panel/chat/ChatPanel.js
rename to tgui/packages/tgui-panel/chat/ChatPanel.jsx
diff --git a/tgui/packages/tgui-panel/chat/ChatTabs.js b/tgui/packages/tgui-panel/chat/ChatTabs.jsx
similarity index 100%
rename from tgui/packages/tgui-panel/chat/ChatTabs.js
rename to tgui/packages/tgui-panel/chat/ChatTabs.jsx
diff --git a/tgui/packages/tgui-panel/chat/constants.js b/tgui/packages/tgui-panel/chat/constants.ts
similarity index 100%
rename from tgui/packages/tgui-panel/chat/constants.js
rename to tgui/packages/tgui-panel/chat/constants.ts
diff --git a/tgui/packages/tgui-panel/chat/index.js b/tgui/packages/tgui-panel/chat/index.ts
similarity index 100%
rename from tgui/packages/tgui-panel/chat/index.js
rename to tgui/packages/tgui-panel/chat/index.ts
diff --git a/tgui/packages/tgui-panel/game/constants.js b/tgui/packages/tgui-panel/game/constants.ts
similarity index 100%
rename from tgui/packages/tgui-panel/game/constants.js
rename to tgui/packages/tgui-panel/game/constants.ts
diff --git a/tgui/packages/tgui-panel/game/index.js b/tgui/packages/tgui-panel/game/index.ts
similarity index 100%
rename from tgui/packages/tgui-panel/game/index.js
rename to tgui/packages/tgui-panel/game/index.ts
diff --git a/tgui/packages/tgui-panel/ping/PingIndicator.js b/tgui/packages/tgui-panel/ping/PingIndicator.jsx
similarity index 100%
rename from tgui/packages/tgui-panel/ping/PingIndicator.js
rename to tgui/packages/tgui-panel/ping/PingIndicator.jsx
diff --git a/tgui/packages/tgui-panel/ping/constants.js b/tgui/packages/tgui-panel/ping/constants.ts
similarity index 100%
rename from tgui/packages/tgui-panel/ping/constants.js
rename to tgui/packages/tgui-panel/ping/constants.ts
diff --git a/tgui/packages/tgui-panel/ping/index.js b/tgui/packages/tgui-panel/ping/index.ts
similarity index 100%
rename from tgui/packages/tgui-panel/ping/index.js
rename to tgui/packages/tgui-panel/ping/index.ts
diff --git a/tgui/packages/tgui-panel/settings/SettingsPanel.js b/tgui/packages/tgui-panel/settings/SettingsPanel.jsx
similarity index 100%
rename from tgui/packages/tgui-panel/settings/SettingsPanel.js
rename to tgui/packages/tgui-panel/settings/SettingsPanel.jsx
diff --git a/tgui/packages/tgui-panel/settings/constants.js b/tgui/packages/tgui-panel/settings/constants.ts
similarity index 100%
rename from tgui/packages/tgui-panel/settings/constants.js
rename to tgui/packages/tgui-panel/settings/constants.ts
diff --git a/tgui/packages/tgui-panel/settings/index.js b/tgui/packages/tgui-panel/settings/index.ts
similarity index 100%
rename from tgui/packages/tgui-panel/settings/index.js
rename to tgui/packages/tgui-panel/settings/index.ts
diff --git a/tgui/packages/tgui/components/Blink.js b/tgui/packages/tgui/components/Blink.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Blink.js
rename to tgui/packages/tgui/components/Blink.jsx
diff --git a/tgui/packages/tgui/components/BlockQuote.js b/tgui/packages/tgui/components/BlockQuote.jsx
similarity index 100%
rename from tgui/packages/tgui/components/BlockQuote.js
rename to tgui/packages/tgui/components/BlockQuote.jsx
diff --git a/tgui/packages/tgui/components/Button.js b/tgui/packages/tgui/components/Button.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Button.js
rename to tgui/packages/tgui/components/Button.jsx
diff --git a/tgui/packages/tgui/components/ByondUi.js b/tgui/packages/tgui/components/ByondUi.jsx
similarity index 100%
rename from tgui/packages/tgui/components/ByondUi.js
rename to tgui/packages/tgui/components/ByondUi.jsx
diff --git a/tgui/packages/tgui/components/Chart.js b/tgui/packages/tgui/components/Chart.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Chart.js
rename to tgui/packages/tgui/components/Chart.jsx
diff --git a/tgui/packages/tgui/components/Collapsible.js b/tgui/packages/tgui/components/Collapsible.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Collapsible.js
rename to tgui/packages/tgui/components/Collapsible.jsx
diff --git a/tgui/packages/tgui/components/ColorBox.js b/tgui/packages/tgui/components/ColorBox.jsx
similarity index 100%
rename from tgui/packages/tgui/components/ColorBox.js
rename to tgui/packages/tgui/components/ColorBox.jsx
diff --git a/tgui/packages/tgui/components/Dimmer.js b/tgui/packages/tgui/components/Dimmer.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Dimmer.js
rename to tgui/packages/tgui/components/Dimmer.jsx
diff --git a/tgui/packages/tgui/components/Divider.js b/tgui/packages/tgui/components/Divider.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Divider.js
rename to tgui/packages/tgui/components/Divider.jsx
diff --git a/tgui/packages/tgui/components/DraggableControl.js b/tgui/packages/tgui/components/DraggableControl.jsx
similarity index 100%
rename from tgui/packages/tgui/components/DraggableControl.js
rename to tgui/packages/tgui/components/DraggableControl.jsx
diff --git a/tgui/packages/tgui/components/FakeTerminal.jsx b/tgui/packages/tgui/components/FakeTerminal.jsx
new file mode 100644
index 00000000000..d6479a25796
--- /dev/null
+++ b/tgui/packages/tgui/components/FakeTerminal.jsx
@@ -0,0 +1,51 @@
+import { Box } from './Box';
+import { Component, Fragment } from 'inferno';
+
+export class FakeTerminal extends Component {
+ constructor(props) {
+ super(props);
+ this.timer = null;
+ this.state = {
+ currentIndex: 0,
+ currentDisplay: [],
+ };
+ }
+
+ tick() {
+ const { props, state } = this;
+ if (state.currentIndex <= props.allMessages.length) {
+ this.setState((prevState) => {
+ return {
+ currentIndex: prevState.currentIndex + 1,
+ };
+ });
+ const { currentDisplay } = state;
+ currentDisplay.push(props.allMessages[state.currentIndex]);
+ } else {
+ clearTimeout(this.timer);
+ setTimeout(props.onFinished, props.finishedTimeout);
+ }
+ }
+
+ componentDidMount() {
+ const { linesPerSecond = 2.5 } = this.props;
+ this.timer = setInterval(() => this.tick(), 1000 / linesPerSecond);
+ }
+
+ componentWillUnmount() {
+ clearTimeout(this.timer);
+ }
+
+ render() {
+ return (
+
+ {this.state.currentDisplay.map((value) => (
+
+ {value}
+
+
+ ))}
+
+ );
+ }
+}
diff --git a/tgui/packages/tgui/components/Grid.js b/tgui/packages/tgui/components/Grid.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Grid.js
rename to tgui/packages/tgui/components/Grid.jsx
diff --git a/tgui/packages/tgui/components/InfinitePlane.js b/tgui/packages/tgui/components/InfinitePlane.jsx
similarity index 100%
rename from tgui/packages/tgui/components/InfinitePlane.js
rename to tgui/packages/tgui/components/InfinitePlane.jsx
diff --git a/tgui/packages/tgui/components/Input.js b/tgui/packages/tgui/components/Input.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Input.js
rename to tgui/packages/tgui/components/Input.jsx
diff --git a/tgui/packages/tgui/components/Knob.js b/tgui/packages/tgui/components/Knob.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Knob.js
rename to tgui/packages/tgui/components/Knob.jsx
diff --git a/tgui/packages/tgui/components/LabeledControls.js b/tgui/packages/tgui/components/LabeledControls.jsx
similarity index 100%
rename from tgui/packages/tgui/components/LabeledControls.js
rename to tgui/packages/tgui/components/LabeledControls.jsx
diff --git a/tgui/packages/tgui/components/Modal.js b/tgui/packages/tgui/components/Modal.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Modal.js
rename to tgui/packages/tgui/components/Modal.jsx
diff --git a/tgui/packages/tgui/components/NoticeBox.js b/tgui/packages/tgui/components/NoticeBox.jsx
similarity index 100%
rename from tgui/packages/tgui/components/NoticeBox.js
rename to tgui/packages/tgui/components/NoticeBox.jsx
diff --git a/tgui/packages/tgui/components/NumberInput.js b/tgui/packages/tgui/components/NumberInput.jsx
similarity index 100%
rename from tgui/packages/tgui/components/NumberInput.js
rename to tgui/packages/tgui/components/NumberInput.jsx
diff --git a/tgui/packages/tgui/components/ProgressBar.js b/tgui/packages/tgui/components/ProgressBar.jsx
similarity index 100%
rename from tgui/packages/tgui/components/ProgressBar.js
rename to tgui/packages/tgui/components/ProgressBar.jsx
diff --git a/tgui/packages/tgui/components/RestrictedInput.js b/tgui/packages/tgui/components/RestrictedInput.jsx
similarity index 100%
rename from tgui/packages/tgui/components/RestrictedInput.js
rename to tgui/packages/tgui/components/RestrictedInput.jsx
diff --git a/tgui/packages/tgui/components/RoundGauge.js b/tgui/packages/tgui/components/RoundGauge.jsx
similarity index 100%
rename from tgui/packages/tgui/components/RoundGauge.js
rename to tgui/packages/tgui/components/RoundGauge.jsx
diff --git a/tgui/packages/tgui/components/Slider.js b/tgui/packages/tgui/components/Slider.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Slider.js
rename to tgui/packages/tgui/components/Slider.jsx
diff --git a/tgui/packages/tgui/components/Table.js b/tgui/packages/tgui/components/Table.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Table.js
rename to tgui/packages/tgui/components/Table.jsx
diff --git a/tgui/packages/tgui/components/Tabs.js b/tgui/packages/tgui/components/Tabs.jsx
similarity index 100%
rename from tgui/packages/tgui/components/Tabs.js
rename to tgui/packages/tgui/components/Tabs.jsx
diff --git a/tgui/packages/tgui/components/TextArea.js b/tgui/packages/tgui/components/TextArea.jsx
similarity index 100%
rename from tgui/packages/tgui/components/TextArea.js
rename to tgui/packages/tgui/components/TextArea.jsx
diff --git a/tgui/packages/tgui/components/TimeDisplay.js b/tgui/packages/tgui/components/TimeDisplay.jsx
similarity index 100%
rename from tgui/packages/tgui/components/TimeDisplay.js
rename to tgui/packages/tgui/components/TimeDisplay.jsx
diff --git a/tgui/packages/tgui/components/index.js b/tgui/packages/tgui/components/index.ts
similarity index 100%
rename from tgui/packages/tgui/components/index.js
rename to tgui/packages/tgui/components/index.ts
diff --git a/tgui/packages/tgui/debug/KitchenSink.js b/tgui/packages/tgui/debug/KitchenSink.jsx
similarity index 100%
rename from tgui/packages/tgui/debug/KitchenSink.js
rename to tgui/packages/tgui/debug/KitchenSink.jsx
diff --git a/tgui/packages/tgui/debug/index.js b/tgui/packages/tgui/debug/index.ts
similarity index 100%
rename from tgui/packages/tgui/debug/index.js
rename to tgui/packages/tgui/debug/index.ts
diff --git a/tgui/packages/tgui/interfaces/AbductorConsole.js b/tgui/packages/tgui/interfaces/AbductorConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/AbductorConsole.js
rename to tgui/packages/tgui/interfaces/AbductorConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/Achievements.js b/tgui/packages/tgui/interfaces/Achievements.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Achievements.js
rename to tgui/packages/tgui/interfaces/Achievements.jsx
diff --git a/tgui/packages/tgui/interfaces/AdminFax.js b/tgui/packages/tgui/interfaces/AdminFax.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/AdminFax.js
rename to tgui/packages/tgui/interfaces/AdminFax.jsx
diff --git a/tgui/packages/tgui/interfaces/AdminPDA.js b/tgui/packages/tgui/interfaces/AdminPDA.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/AdminPDA.js
rename to tgui/packages/tgui/interfaces/AdminPDA.jsx
diff --git a/tgui/packages/tgui/interfaces/AiAirlock.js b/tgui/packages/tgui/interfaces/AiAirlock.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/AiAirlock.js
rename to tgui/packages/tgui/interfaces/AiAirlock.jsx
diff --git a/tgui/packages/tgui/interfaces/AmmoWorkbench.js b/tgui/packages/tgui/interfaces/AmmoWorkbench.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/AmmoWorkbench.js
rename to tgui/packages/tgui/interfaces/AmmoWorkbench.jsx
diff --git a/tgui/packages/tgui/interfaces/AnomalyRefinery.js b/tgui/packages/tgui/interfaces/AnomalyRefinery.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/AnomalyRefinery.js
rename to tgui/packages/tgui/interfaces/AnomalyRefinery.jsx
diff --git a/tgui/packages/tgui/interfaces/AntagInfoSentient.js b/tgui/packages/tgui/interfaces/AntagInfoSentient.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/AntagInfoSentient.js
rename to tgui/packages/tgui/interfaces/AntagInfoSentient.jsx
diff --git a/tgui/packages/tgui/interfaces/AntagInfoShade.js b/tgui/packages/tgui/interfaces/AntagInfoShade.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/AntagInfoShade.js
rename to tgui/packages/tgui/interfaces/AntagInfoShade.jsx
diff --git a/tgui/packages/tgui/interfaces/Apc.js b/tgui/packages/tgui/interfaces/Apc.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Apc.js
rename to tgui/packages/tgui/interfaces/Apc.jsx
diff --git a/tgui/packages/tgui/interfaces/ApcControl.js b/tgui/packages/tgui/interfaces/ApcControl.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ApcControl.js
rename to tgui/packages/tgui/interfaces/ApcControl.jsx
diff --git a/tgui/packages/tgui/interfaces/ArmamentStation.js b/tgui/packages/tgui/interfaces/ArmamentStation.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ArmamentStation.js
rename to tgui/packages/tgui/interfaces/ArmamentStation.jsx
diff --git a/tgui/packages/tgui/interfaces/AtmosControlPanel.js b/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/AtmosControlPanel.js
rename to tgui/packages/tgui/interfaces/AtmosControlPanel.jsx
diff --git a/tgui/packages/tgui/interfaces/BorerChem.js b/tgui/packages/tgui/interfaces/BorerChem.jsx
similarity index 97%
rename from tgui/packages/tgui/interfaces/BorerChem.js
rename to tgui/packages/tgui/interfaces/BorerChem.jsx
index 35eb63857fc..8f5210ed0fa 100644
--- a/tgui/packages/tgui/interfaces/BorerChem.js
+++ b/tgui/packages/tgui/interfaces/BorerChem.jsx
@@ -44,7 +44,7 @@ export const BorerChem = (props, context) => {
disabled={data.onCooldown || data.notEnoughChemicals}
onClick={() =>
act('inject', {
- reagent: chemical.id,
+ reagent: chemical.title,
})
}
/>
diff --git a/tgui/packages/tgui/interfaces/BorgPanel.js b/tgui/packages/tgui/interfaces/BorgPanel.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/BorgPanel.js
rename to tgui/packages/tgui/interfaces/BorgPanel.jsx
diff --git a/tgui/packages/tgui/interfaces/Canister.js b/tgui/packages/tgui/interfaces/Canister.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Canister.js
rename to tgui/packages/tgui/interfaces/Canister.jsx
diff --git a/tgui/packages/tgui/interfaces/Cargo.js b/tgui/packages/tgui/interfaces/Cargo.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Cargo.js
rename to tgui/packages/tgui/interfaces/Cargo.jsx
diff --git a/tgui/packages/tgui/interfaces/CargoImportConsole.js b/tgui/packages/tgui/interfaces/CargoImportConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/CargoImportConsole.js
rename to tgui/packages/tgui/interfaces/CargoImportConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher.js b/tgui/packages/tgui/interfaces/CentcomPodLauncher.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/CentcomPodLauncher.js
rename to tgui/packages/tgui/interfaces/CentcomPodLauncher.jsx
diff --git a/tgui/packages/tgui/interfaces/ChameleonCard.js b/tgui/packages/tgui/interfaces/ChameleonCard.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ChameleonCard.js
rename to tgui/packages/tgui/interfaces/ChameleonCard.jsx
diff --git a/tgui/packages/tgui/interfaces/Changelog.js b/tgui/packages/tgui/interfaces/Changelog.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Changelog.js
rename to tgui/packages/tgui/interfaces/Changelog.jsx
diff --git a/tgui/packages/tgui/interfaces/ChemHeater.js b/tgui/packages/tgui/interfaces/ChemHeater.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ChemHeater.js
rename to tgui/packages/tgui/interfaces/ChemHeater.jsx
diff --git a/tgui/packages/tgui/interfaces/ChemPress.js b/tgui/packages/tgui/interfaces/ChemPress.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ChemPress.js
rename to tgui/packages/tgui/interfaces/ChemPress.jsx
diff --git a/tgui/packages/tgui/interfaces/ChemRecipeDebug.js b/tgui/packages/tgui/interfaces/ChemRecipeDebug.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ChemRecipeDebug.js
rename to tgui/packages/tgui/interfaces/ChemRecipeDebug.jsx
diff --git a/tgui/packages/tgui/interfaces/CircuitModule.js b/tgui/packages/tgui/interfaces/CircuitModule.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/CircuitModule.js
rename to tgui/packages/tgui/interfaces/CircuitModule.jsx
diff --git a/tgui/packages/tgui/interfaces/CivCargoHoldTerminal.js b/tgui/packages/tgui/interfaces/CivCargoHoldTerminal.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/CivCargoHoldTerminal.js
rename to tgui/packages/tgui/interfaces/CivCargoHoldTerminal.jsx
diff --git a/tgui/packages/tgui/interfaces/ClockworkSlab.js b/tgui/packages/tgui/interfaces/ClockworkSlab.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ClockworkSlab.js
rename to tgui/packages/tgui/interfaces/ClockworkSlab.jsx
diff --git a/tgui/packages/tgui/interfaces/CommandReportConsole.js b/tgui/packages/tgui/interfaces/CommandReportConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/CommandReportConsole.js
rename to tgui/packages/tgui/interfaces/CommandReportConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole.js b/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/CommunicationsConsole.js
rename to tgui/packages/tgui/interfaces/CommunicationsConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/CrewConsole.js b/tgui/packages/tgui/interfaces/CrewConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/CrewConsole.js
rename to tgui/packages/tgui/interfaces/CrewConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/CrewConsoleSkyrat.js b/tgui/packages/tgui/interfaces/CrewConsoleSkyrat.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/CrewConsoleSkyrat.js
rename to tgui/packages/tgui/interfaces/CrewConsoleSkyrat.jsx
diff --git a/tgui/packages/tgui/interfaces/CrewConsoleSkyratBlueshield.js b/tgui/packages/tgui/interfaces/CrewConsoleSkyratBlueshield.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/CrewConsoleSkyratBlueshield.js
rename to tgui/packages/tgui/interfaces/CrewConsoleSkyratBlueshield.jsx
diff --git a/tgui/packages/tgui/interfaces/CrewManifest.js b/tgui/packages/tgui/interfaces/CrewManifest.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/CrewManifest.js
rename to tgui/packages/tgui/interfaces/CrewManifest.jsx
diff --git a/tgui/packages/tgui/interfaces/Cryo.js b/tgui/packages/tgui/interfaces/Cryo.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Cryo.js
rename to tgui/packages/tgui/interfaces/Cryo.jsx
diff --git a/tgui/packages/tgui/interfaces/CryopodConsole.js b/tgui/packages/tgui/interfaces/CryopodConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/CryopodConsole.js
rename to tgui/packages/tgui/interfaces/CryopodConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/CyborgBootDebug.js b/tgui/packages/tgui/interfaces/CyborgBootDebug.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/CyborgBootDebug.js
rename to tgui/packages/tgui/interfaces/CyborgBootDebug.jsx
diff --git a/tgui/packages/tgui/interfaces/DisposalUnit.js b/tgui/packages/tgui/interfaces/DisposalUnit.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/DisposalUnit.js
rename to tgui/packages/tgui/interfaces/DisposalUnit.jsx
diff --git a/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleEnzymes.js b/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleEnzymes.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleEnzymes.js
rename to tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleEnzymes.jsx
diff --git a/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleSequencer.js b/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleSequencer.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleSequencer.js
rename to tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleSequencer.jsx
diff --git a/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleStorage.js b/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleStorage.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleStorage.js
rename to tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleStorage.jsx
diff --git a/tgui/packages/tgui/interfaces/DnaConsole/DnaScanner.js b/tgui/packages/tgui/interfaces/DnaConsole/DnaScanner.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/DnaConsole/DnaScanner.js
rename to tgui/packages/tgui/interfaces/DnaConsole/DnaScanner.jsx
diff --git a/tgui/packages/tgui/interfaces/DnaConsole/GeneticMakeupInfo.js b/tgui/packages/tgui/interfaces/DnaConsole/GeneticMakeupInfo.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/DnaConsole/GeneticMakeupInfo.js
rename to tgui/packages/tgui/interfaces/DnaConsole/GeneticMakeupInfo.jsx
diff --git a/tgui/packages/tgui/interfaces/DnaConsole/MutationInfo.js b/tgui/packages/tgui/interfaces/DnaConsole/MutationInfo.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/DnaConsole/MutationInfo.js
rename to tgui/packages/tgui/interfaces/DnaConsole/MutationInfo.jsx
diff --git a/tgui/packages/tgui/interfaces/DnaConsole/constants.js b/tgui/packages/tgui/interfaces/DnaConsole/constants.ts
similarity index 100%
rename from tgui/packages/tgui/interfaces/DnaConsole/constants.js
rename to tgui/packages/tgui/interfaces/DnaConsole/constants.ts
diff --git a/tgui/packages/tgui/interfaces/DnaConsole/index.js b/tgui/packages/tgui/interfaces/DnaConsole/index.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/DnaConsole/index.js
rename to tgui/packages/tgui/interfaces/DnaConsole/index.jsx
diff --git a/tgui/packages/tgui/interfaces/DnaVault.js b/tgui/packages/tgui/interfaces/DnaVault.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/DnaVault.js
rename to tgui/packages/tgui/interfaces/DnaVault.jsx
diff --git a/tgui/packages/tgui/interfaces/DopplerArray.js b/tgui/packages/tgui/interfaces/DopplerArray.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/DopplerArray.js
rename to tgui/packages/tgui/interfaces/DopplerArray.jsx
diff --git a/tgui/packages/tgui/interfaces/Electropack.js b/tgui/packages/tgui/interfaces/Electropack.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Electropack.js
rename to tgui/packages/tgui/interfaces/Electropack.jsx
diff --git a/tgui/packages/tgui/interfaces/EmergencyShuttleConsole.js b/tgui/packages/tgui/interfaces/EmergencyShuttleConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/EmergencyShuttleConsole.js
rename to tgui/packages/tgui/interfaces/EmergencyShuttleConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/EngravedMessage.js b/tgui/packages/tgui/interfaces/EngravedMessage.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/EngravedMessage.js
rename to tgui/packages/tgui/interfaces/EngravedMessage.jsx
diff --git a/tgui/packages/tgui/interfaces/EventPanel.js b/tgui/packages/tgui/interfaces/EventPanel.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/EventPanel.js
rename to tgui/packages/tgui/interfaces/EventPanel.jsx
diff --git a/tgui/packages/tgui/interfaces/ExaminePanel.js b/tgui/packages/tgui/interfaces/ExaminePanel.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ExaminePanel.js
rename to tgui/packages/tgui/interfaces/ExaminePanel.jsx
diff --git a/tgui/packages/tgui/interfaces/ExosuitControlConsole.js b/tgui/packages/tgui/interfaces/ExosuitControlConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ExosuitControlConsole.js
rename to tgui/packages/tgui/interfaces/ExosuitControlConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/ExperimentConfigure.js b/tgui/packages/tgui/interfaces/ExperimentConfigure.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ExperimentConfigure.js
rename to tgui/packages/tgui/interfaces/ExperimentConfigure.jsx
diff --git a/tgui/packages/tgui/interfaces/Filteriffic.js b/tgui/packages/tgui/interfaces/Filteriffic.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Filteriffic.js
rename to tgui/packages/tgui/interfaces/Filteriffic.jsx
diff --git a/tgui/packages/tgui/interfaces/Gateway.js b/tgui/packages/tgui/interfaces/Gateway.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Gateway.js
rename to tgui/packages/tgui/interfaces/Gateway.jsx
diff --git a/tgui/packages/tgui/interfaces/GhostPoolProtection.js b/tgui/packages/tgui/interfaces/GhostPoolProtection.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/GhostPoolProtection.js
rename to tgui/packages/tgui/interfaces/GhostPoolProtection.jsx
diff --git a/tgui/packages/tgui/interfaces/Gps.js b/tgui/packages/tgui/interfaces/Gps.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Gps.js
rename to tgui/packages/tgui/interfaces/Gps.jsx
diff --git a/tgui/packages/tgui/interfaces/GravityGenerator.js b/tgui/packages/tgui/interfaces/GravityGenerator.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/GravityGenerator.js
rename to tgui/packages/tgui/interfaces/GravityGenerator.jsx
diff --git a/tgui/packages/tgui/interfaces/GulagTeleporterConsole.js b/tgui/packages/tgui/interfaces/GulagTeleporterConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/GulagTeleporterConsole.js
rename to tgui/packages/tgui/interfaces/GulagTeleporterConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/Holodeck.js b/tgui/packages/tgui/interfaces/Holodeck.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Holodeck.js
rename to tgui/packages/tgui/interfaces/Holodeck.jsx
diff --git a/tgui/packages/tgui/interfaces/Holopad.js b/tgui/packages/tgui/interfaces/Holopad.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Holopad.js
rename to tgui/packages/tgui/interfaces/Holopad.jsx
diff --git a/tgui/packages/tgui/interfaces/HypnoChair.js b/tgui/packages/tgui/interfaces/HypnoChair.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/HypnoChair.js
rename to tgui/packages/tgui/interfaces/HypnoChair.jsx
diff --git a/tgui/packages/tgui/interfaces/ImplantChair.js b/tgui/packages/tgui/interfaces/ImplantChair.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ImplantChair.js
rename to tgui/packages/tgui/interfaces/ImplantChair.jsx
diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/BasicInput.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/BasicInput.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/IntegratedCircuit/BasicInput.js
rename to tgui/packages/tgui/interfaces/IntegratedCircuit/BasicInput.jsx
diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.js
rename to tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.jsx
diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/ComponentMenu.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/ComponentMenu.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/IntegratedCircuit/ComponentMenu.js
rename to tgui/packages/tgui/interfaces/IntegratedCircuit/ComponentMenu.jsx
diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayComponent.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayComponent.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayComponent.js
rename to tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayComponent.jsx
diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayName.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayName.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayName.js
rename to tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayName.jsx
diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.js
rename to tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.jsx
diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.js
rename to tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.jsx
diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/Port.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/Port.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/IntegratedCircuit/Port.js
rename to tgui/packages/tgui/interfaces/IntegratedCircuit/Port.jsx
diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.js
rename to tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.jsx
diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/constants.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/constants.ts
similarity index 100%
rename from tgui/packages/tgui/interfaces/IntegratedCircuit/constants.js
rename to tgui/packages/tgui/interfaces/IntegratedCircuit/constants.ts
diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/index.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/index.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/IntegratedCircuit/index.js
rename to tgui/packages/tgui/interfaces/IntegratedCircuit/index.jsx
diff --git a/tgui/packages/tgui/interfaces/Intellicard.js b/tgui/packages/tgui/interfaces/Intellicard.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Intellicard.js
rename to tgui/packages/tgui/interfaces/Intellicard.jsx
diff --git a/tgui/packages/tgui/interfaces/Interview.js b/tgui/packages/tgui/interfaces/Interview.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Interview.js
rename to tgui/packages/tgui/interfaces/Interview.jsx
diff --git a/tgui/packages/tgui/interfaces/InterviewManager.js b/tgui/packages/tgui/interfaces/InterviewManager.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/InterviewManager.js
rename to tgui/packages/tgui/interfaces/InterviewManager.jsx
diff --git a/tgui/packages/tgui/interfaces/Jukebox.js b/tgui/packages/tgui/interfaces/Jukebox.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Jukebox.js
rename to tgui/packages/tgui/interfaces/Jukebox.jsx
diff --git a/tgui/packages/tgui/interfaces/KeycardAuth.js b/tgui/packages/tgui/interfaces/KeycardAuth.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/KeycardAuth.js
rename to tgui/packages/tgui/interfaces/KeycardAuth.jsx
diff --git a/tgui/packages/tgui/interfaces/LaborClaimConsole.js b/tgui/packages/tgui/interfaces/LaborClaimConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LaborClaimConsole.js
rename to tgui/packages/tgui/interfaces/LaborClaimConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/LanguageMenu.js b/tgui/packages/tgui/interfaces/LanguageMenu.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LanguageMenu.js
rename to tgui/packages/tgui/interfaces/LanguageMenu.jsx
diff --git a/tgui/packages/tgui/interfaces/LaunchpadConsole.js b/tgui/packages/tgui/interfaces/LaunchpadConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LaunchpadConsole.js
rename to tgui/packages/tgui/interfaces/LaunchpadConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/LibraryConsole.js b/tgui/packages/tgui/interfaces/LibraryConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LibraryConsole.js
rename to tgui/packages/tgui/interfaces/LibraryConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/LibraryScanner.js b/tgui/packages/tgui/interfaces/LibraryScanner.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LibraryScanner.js
rename to tgui/packages/tgui/interfaces/LibraryScanner.jsx
diff --git a/tgui/packages/tgui/interfaces/LibraryVisitor.js b/tgui/packages/tgui/interfaces/LibraryVisitor.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LibraryVisitor.js
rename to tgui/packages/tgui/interfaces/LibraryVisitor.jsx
diff --git a/tgui/packages/tgui/interfaces/Limbgrower.js b/tgui/packages/tgui/interfaces/Limbgrower.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Limbgrower.js
rename to tgui/packages/tgui/interfaces/Limbgrower.jsx
diff --git a/tgui/packages/tgui/interfaces/LoadoutManager.js b/tgui/packages/tgui/interfaces/LoadoutManager.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LoadoutManager.js
rename to tgui/packages/tgui/interfaces/LoadoutManager.jsx
diff --git a/tgui/packages/tgui/interfaces/LuaEditor/CallModal.js b/tgui/packages/tgui/interfaces/LuaEditor/CallModal.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LuaEditor/CallModal.js
rename to tgui/packages/tgui/interfaces/LuaEditor/CallModal.jsx
diff --git a/tgui/packages/tgui/interfaces/LuaEditor/ChunkViewModal.js b/tgui/packages/tgui/interfaces/LuaEditor/ChunkViewModal.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LuaEditor/ChunkViewModal.js
rename to tgui/packages/tgui/interfaces/LuaEditor/ChunkViewModal.jsx
diff --git a/tgui/packages/tgui/interfaces/LuaEditor/ListMapper.js b/tgui/packages/tgui/interfaces/LuaEditor/ListMapper.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LuaEditor/ListMapper.js
rename to tgui/packages/tgui/interfaces/LuaEditor/ListMapper.jsx
diff --git a/tgui/packages/tgui/interfaces/LuaEditor/Log.js b/tgui/packages/tgui/interfaces/LuaEditor/Log.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LuaEditor/Log.js
rename to tgui/packages/tgui/interfaces/LuaEditor/Log.jsx
diff --git a/tgui/packages/tgui/interfaces/LuaEditor/StateSelectModal.js b/tgui/packages/tgui/interfaces/LuaEditor/StateSelectModal.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LuaEditor/StateSelectModal.js
rename to tgui/packages/tgui/interfaces/LuaEditor/StateSelectModal.jsx
diff --git a/tgui/packages/tgui/interfaces/LuaEditor/TaskManager.js b/tgui/packages/tgui/interfaces/LuaEditor/TaskManager.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LuaEditor/TaskManager.js
rename to tgui/packages/tgui/interfaces/LuaEditor/TaskManager.jsx
diff --git a/tgui/packages/tgui/interfaces/LuaEditor/index.js b/tgui/packages/tgui/interfaces/LuaEditor/index.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/LuaEditor/index.js
rename to tgui/packages/tgui/interfaces/LuaEditor/index.jsx
diff --git a/tgui/packages/tgui/interfaces/MODpaint.js b/tgui/packages/tgui/interfaces/MODpaint.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/MODpaint.js
rename to tgui/packages/tgui/interfaces/MODpaint.jsx
diff --git a/tgui/packages/tgui/interfaces/MalfunctionModulePicker.js b/tgui/packages/tgui/interfaces/MalfunctionModulePicker.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/MalfunctionModulePicker.js
rename to tgui/packages/tgui/interfaces/MalfunctionModulePicker.jsx
diff --git a/tgui/packages/tgui/interfaces/MassDriverControl.js b/tgui/packages/tgui/interfaces/MassDriverControl.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/MassDriverControl.js
rename to tgui/packages/tgui/interfaces/MassDriverControl.jsx
diff --git a/tgui/packages/tgui/interfaces/MassSpec.js b/tgui/packages/tgui/interfaces/MassSpec.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/MassSpec.js
rename to tgui/packages/tgui/interfaces/MassSpec.jsx
diff --git a/tgui/packages/tgui/interfaces/MechBayPowerConsole.js b/tgui/packages/tgui/interfaces/MechBayPowerConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/MechBayPowerConsole.js
rename to tgui/packages/tgui/interfaces/MechBayPowerConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/MechpadConsole.js b/tgui/packages/tgui/interfaces/MechpadConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/MechpadConsole.js
rename to tgui/packages/tgui/interfaces/MechpadConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/MedicalKiosk.js b/tgui/packages/tgui/interfaces/MedicalKiosk.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/MedicalKiosk.js
rename to tgui/packages/tgui/interfaces/MedicalKiosk.jsx
diff --git a/tgui/packages/tgui/interfaces/MemoryPanel.js b/tgui/packages/tgui/interfaces/MemoryPanel.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/MemoryPanel.js
rename to tgui/packages/tgui/interfaces/MemoryPanel.jsx
diff --git a/tgui/packages/tgui/interfaces/MicrofusionGunControl.js b/tgui/packages/tgui/interfaces/MicrofusionGunControl.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/MicrofusionGunControl.js
rename to tgui/packages/tgui/interfaces/MicrofusionGunControl.jsx
diff --git a/tgui/packages/tgui/interfaces/Microscope.js b/tgui/packages/tgui/interfaces/Microscope.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Microscope.js
rename to tgui/packages/tgui/interfaces/Microscope.jsx
diff --git a/tgui/packages/tgui/interfaces/MilkingMachine.js b/tgui/packages/tgui/interfaces/MilkingMachine.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/MilkingMachine.js
rename to tgui/packages/tgui/interfaces/MilkingMachine.jsx
diff --git a/tgui/packages/tgui/interfaces/Mule.js b/tgui/packages/tgui/interfaces/Mule.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Mule.js
rename to tgui/packages/tgui/interfaces/Mule.jsx
diff --git a/tgui/packages/tgui/interfaces/Newscaster.js b/tgui/packages/tgui/interfaces/Newscaster.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Newscaster.js
rename to tgui/packages/tgui/interfaces/Newscaster.jsx
diff --git a/tgui/packages/tgui/interfaces/NifPanel.js b/tgui/packages/tgui/interfaces/NifPanel.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NifPanel.js
rename to tgui/packages/tgui/interfaces/NifPanel.jsx
diff --git a/tgui/packages/tgui/interfaces/NifSoulPoem.js b/tgui/packages/tgui/interfaces/NifSoulPoem.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NifSoulPoem.js
rename to tgui/packages/tgui/interfaces/NifSoulPoem.jsx
diff --git a/tgui/packages/tgui/interfaces/NightmareInfo.js b/tgui/packages/tgui/interfaces/NightmareInfo.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NightmareInfo.js
rename to tgui/packages/tgui/interfaces/NightmareInfo.jsx
diff --git a/tgui/packages/tgui/interfaces/NotificationPreferences.js b/tgui/packages/tgui/interfaces/NotificationPreferences.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NotificationPreferences.js
rename to tgui/packages/tgui/interfaces/NotificationPreferences.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosArcade.js b/tgui/packages/tgui/interfaces/NtosArcade.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosArcade.js
rename to tgui/packages/tgui/interfaces/NtosArcade.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosCamera.js b/tgui/packages/tgui/interfaces/NtosCamera.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosCamera.js
rename to tgui/packages/tgui/interfaces/NtosCamera.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosCard.js b/tgui/packages/tgui/interfaces/NtosCard.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosCard.js
rename to tgui/packages/tgui/interfaces/NtosCard.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosCargo.tsx b/tgui/packages/tgui/interfaces/NtosCargo.tsx
index b5009c0aeaf..b478e42e5ae 100644
--- a/tgui/packages/tgui/interfaces/NtosCargo.tsx
+++ b/tgui/packages/tgui/interfaces/NtosCargo.tsx
@@ -1,4 +1,4 @@
-import { CargoContent } from './Cargo.js';
+import { CargoContent } from './Cargo';
import { NtosWindow } from '../layouts';
export const NtosCargo = (props, context) => {
diff --git a/tgui/packages/tgui/interfaces/NtosCrewManifest.js b/tgui/packages/tgui/interfaces/NtosCrewManifest.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosCrewManifest.js
rename to tgui/packages/tgui/interfaces/NtosCrewManifest.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js b/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js
rename to tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosEmojipedia.js b/tgui/packages/tgui/interfaces/NtosEmojipedia.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosEmojipedia.js
rename to tgui/packages/tgui/interfaces/NtosEmojipedia.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosFileManager.js b/tgui/packages/tgui/interfaces/NtosFileManager.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosFileManager.js
rename to tgui/packages/tgui/interfaces/NtosFileManager.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosJobManager.js b/tgui/packages/tgui/interfaces/NtosJobManager.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosJobManager.js
rename to tgui/packages/tgui/interfaces/NtosJobManager.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosMODsuit.js b/tgui/packages/tgui/interfaces/NtosMODsuit.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosMODsuit.js
rename to tgui/packages/tgui/interfaces/NtosMODsuit.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosMain.js b/tgui/packages/tgui/interfaces/NtosMain.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosMain.js
rename to tgui/packages/tgui/interfaces/NtosMain.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosNetChat.js b/tgui/packages/tgui/interfaces/NtosNetChat.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosNetChat.js
rename to tgui/packages/tgui/interfaces/NtosNetChat.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosNetDos.js b/tgui/packages/tgui/interfaces/NtosNetDos.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosNetDos.js
rename to tgui/packages/tgui/interfaces/NtosNetDos.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosNetDownloader.js b/tgui/packages/tgui/interfaces/NtosNetDownloader.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosNetDownloader.js
rename to tgui/packages/tgui/interfaces/NtosNetDownloader.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosNetMonitor.js b/tgui/packages/tgui/interfaces/NtosNetMonitor.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosNetMonitor.js
rename to tgui/packages/tgui/interfaces/NtosNetMonitor.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosNewsArchive.js b/tgui/packages/tgui/interfaces/NtosNewsArchive.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosNewsArchive.js
rename to tgui/packages/tgui/interfaces/NtosNewsArchive.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosNifsoftCatalog.js b/tgui/packages/tgui/interfaces/NtosNifsoftCatalog.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosNifsoftCatalog.js
rename to tgui/packages/tgui/interfaces/NtosNifsoftCatalog.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosPhysScanner.js b/tgui/packages/tgui/interfaces/NtosPhysScanner.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosPhysScanner.js
rename to tgui/packages/tgui/interfaces/NtosPhysScanner.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosPortraitPrinter.js b/tgui/packages/tgui/interfaces/NtosPortraitPrinter.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosPortraitPrinter.js
rename to tgui/packages/tgui/interfaces/NtosPortraitPrinter.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosRecords.js b/tgui/packages/tgui/interfaces/NtosRecords.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosRecords.js
rename to tgui/packages/tgui/interfaces/NtosRecords.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosRoboControl.js b/tgui/packages/tgui/interfaces/NtosRoboControl.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosRoboControl.js
rename to tgui/packages/tgui/interfaces/NtosRoboControl.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosRobotact.js b/tgui/packages/tgui/interfaces/NtosRobotact.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosRobotact.js
rename to tgui/packages/tgui/interfaces/NtosRobotact.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosScipaper.js b/tgui/packages/tgui/interfaces/NtosScipaper.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosScipaper.js
rename to tgui/packages/tgui/interfaces/NtosScipaper.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosSkillTracker.js b/tgui/packages/tgui/interfaces/NtosSkillTracker.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NtosSkillTracker.js
rename to tgui/packages/tgui/interfaces/NtosSkillTracker.jsx
diff --git a/tgui/packages/tgui/interfaces/NtosTechweb.tsx b/tgui/packages/tgui/interfaces/NtosTechweb.tsx
index 844198c6a1f..6bec8c9b0bb 100644
--- a/tgui/packages/tgui/interfaces/NtosTechweb.tsx
+++ b/tgui/packages/tgui/interfaces/NtosTechweb.tsx
@@ -1,4 +1,4 @@
-import { AppTechweb } from './Techweb.js';
+import { AppTechweb } from './Techweb';
export const NtosTechweb = (props, context) => {
return ;
diff --git a/tgui/packages/tgui/interfaces/NuclearBomb.js b/tgui/packages/tgui/interfaces/NuclearBomb.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/NuclearBomb.js
rename to tgui/packages/tgui/interfaces/NuclearBomb.jsx
diff --git a/tgui/packages/tgui/interfaces/OperatingComputer.js b/tgui/packages/tgui/interfaces/OperatingComputer.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/OperatingComputer.js
rename to tgui/packages/tgui/interfaces/OperatingComputer.jsx
diff --git a/tgui/packages/tgui/interfaces/OpposingForcePanel.js b/tgui/packages/tgui/interfaces/OpposingForcePanel.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/OpposingForcePanel.js
rename to tgui/packages/tgui/interfaces/OpposingForcePanel.jsx
diff --git a/tgui/packages/tgui/interfaces/OreRedemptionMachine.js b/tgui/packages/tgui/interfaces/OreRedemptionMachine.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/OreRedemptionMachine.js
rename to tgui/packages/tgui/interfaces/OreRedemptionMachine.jsx
diff --git a/tgui/packages/tgui/interfaces/OrionGame.js b/tgui/packages/tgui/interfaces/OrionGame.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/OrionGame.js
rename to tgui/packages/tgui/interfaces/OrionGame.jsx
diff --git a/tgui/packages/tgui/interfaces/OutfitEditor.js b/tgui/packages/tgui/interfaces/OutfitEditor.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/OutfitEditor.js
rename to tgui/packages/tgui/interfaces/OutfitEditor.jsx
diff --git a/tgui/packages/tgui/interfaces/OutfitManager.js b/tgui/packages/tgui/interfaces/OutfitManager.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/OutfitManager.js
rename to tgui/packages/tgui/interfaces/OutfitManager.jsx
diff --git a/tgui/packages/tgui/interfaces/PaintingMachine.js b/tgui/packages/tgui/interfaces/PaintingMachine.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/PaintingMachine.js
rename to tgui/packages/tgui/interfaces/PaintingMachine.jsx
diff --git a/tgui/packages/tgui/interfaces/ParticleAccelerator.js b/tgui/packages/tgui/interfaces/ParticleAccelerator.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ParticleAccelerator.js
rename to tgui/packages/tgui/interfaces/ParticleAccelerator.jsx
diff --git a/tgui/packages/tgui/interfaces/Photocopier.js b/tgui/packages/tgui/interfaces/Photocopier.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Photocopier.js
rename to tgui/packages/tgui/interfaces/Photocopier.jsx
diff --git a/tgui/packages/tgui/interfaces/PortableGenerator.js b/tgui/packages/tgui/interfaces/PortableGenerator.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/PortableGenerator.js
rename to tgui/packages/tgui/interfaces/PortableGenerator.jsx
diff --git a/tgui/packages/tgui/interfaces/PortablePump.js b/tgui/packages/tgui/interfaces/PortablePump.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/PortablePump.js
rename to tgui/packages/tgui/interfaces/PortablePump.jsx
diff --git a/tgui/packages/tgui/interfaces/PortableTurret.js b/tgui/packages/tgui/interfaces/PortableTurret.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/PortableTurret.js
rename to tgui/packages/tgui/interfaces/PortableTurret.jsx
diff --git a/tgui/packages/tgui/interfaces/PortraitPicker.js b/tgui/packages/tgui/interfaces/PortraitPicker.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/PortraitPicker.js
rename to tgui/packages/tgui/interfaces/PortraitPicker.jsx
diff --git a/tgui/packages/tgui/interfaces/PowerMonitor.js b/tgui/packages/tgui/interfaces/PowerMonitor.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/PowerMonitor.js
rename to tgui/packages/tgui/interfaces/PowerMonitor.jsx
diff --git a/tgui/packages/tgui/interfaces/ProbingConsole.js b/tgui/packages/tgui/interfaces/ProbingConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ProbingConsole.js
rename to tgui/packages/tgui/interfaces/ProbingConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/ProximitySensor.js b/tgui/packages/tgui/interfaces/ProximitySensor.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ProximitySensor.js
rename to tgui/packages/tgui/interfaces/ProximitySensor.jsx
diff --git a/tgui/packages/tgui/interfaces/Radio.js b/tgui/packages/tgui/interfaces/Radio.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Radio.js
rename to tgui/packages/tgui/interfaces/Radio.jsx
diff --git a/tgui/packages/tgui/interfaces/RadioactiveMicrolaser.js b/tgui/packages/tgui/interfaces/RadioactiveMicrolaser.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/RadioactiveMicrolaser.js
rename to tgui/packages/tgui/interfaces/RadioactiveMicrolaser.jsx
diff --git a/tgui/packages/tgui/interfaces/Reagents.js b/tgui/packages/tgui/interfaces/Reagents.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Reagents.js
rename to tgui/packages/tgui/interfaces/Reagents.jsx
diff --git a/tgui/packages/tgui/interfaces/RecordManifest.js b/tgui/packages/tgui/interfaces/RecordManifest.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/RecordManifest.js
rename to tgui/packages/tgui/interfaces/RecordManifest.jsx
diff --git a/tgui/packages/tgui/interfaces/ReligiousTool.js b/tgui/packages/tgui/interfaces/ReligiousTool.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ReligiousTool.js
rename to tgui/packages/tgui/interfaces/ReligiousTool.jsx
diff --git a/tgui/packages/tgui/interfaces/RemoteRobotControl.js b/tgui/packages/tgui/interfaces/RemoteRobotControl.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/RemoteRobotControl.js
rename to tgui/packages/tgui/interfaces/RemoteRobotControl.jsx
diff --git a/tgui/packages/tgui/interfaces/RequestManager.js b/tgui/packages/tgui/interfaces/RequestManager.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/RequestManager.js
rename to tgui/packages/tgui/interfaces/RequestManager.jsx
diff --git a/tgui/packages/tgui/interfaces/RoboticsControlConsole.js b/tgui/packages/tgui/interfaces/RoboticsControlConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/RoboticsControlConsole.js
rename to tgui/packages/tgui/interfaces/RoboticsControlConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/Roulette.js b/tgui/packages/tgui/interfaces/Roulette.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Roulette.js
rename to tgui/packages/tgui/interfaces/Roulette.jsx
diff --git a/tgui/packages/tgui/interfaces/Safe.js b/tgui/packages/tgui/interfaces/Safe.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Safe.js
rename to tgui/packages/tgui/interfaces/Safe.jsx
diff --git a/tgui/packages/tgui/interfaces/ScannerGate.js b/tgui/packages/tgui/interfaces/ScannerGate.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ScannerGate.js
rename to tgui/packages/tgui/interfaces/ScannerGate.jsx
diff --git a/tgui/packages/tgui/interfaces/Secrets.js b/tgui/packages/tgui/interfaces/Secrets.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Secrets.js
rename to tgui/packages/tgui/interfaces/Secrets.jsx
diff --git a/tgui/packages/tgui/interfaces/SelectEquipment.js b/tgui/packages/tgui/interfaces/SelectEquipment.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/SelectEquipment.js
rename to tgui/packages/tgui/interfaces/SelectEquipment.jsx
diff --git a/tgui/packages/tgui/interfaces/SentienceFunBalloon.js b/tgui/packages/tgui/interfaces/SentienceFunBalloon.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/SentienceFunBalloon.js
rename to tgui/packages/tgui/interfaces/SentienceFunBalloon.jsx
diff --git a/tgui/packages/tgui/interfaces/ServerControlPanel.js b/tgui/packages/tgui/interfaces/ServerControlPanel.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ServerControlPanel.js
rename to tgui/packages/tgui/interfaces/ServerControlPanel.jsx
diff --git a/tgui/packages/tgui/interfaces/ServerMonitor.js b/tgui/packages/tgui/interfaces/ServerMonitor.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ServerMonitor.js
rename to tgui/packages/tgui/interfaces/ServerMonitor.jsx
diff --git a/tgui/packages/tgui/interfaces/ShuttleConsole.js b/tgui/packages/tgui/interfaces/ShuttleConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ShuttleConsole.js
rename to tgui/packages/tgui/interfaces/ShuttleConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/ShuttleManipulator.js b/tgui/packages/tgui/interfaces/ShuttleManipulator.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ShuttleManipulator.js
rename to tgui/packages/tgui/interfaces/ShuttleManipulator.jsx
diff --git a/tgui/packages/tgui/interfaces/Signalvib.js b/tgui/packages/tgui/interfaces/Signalvib.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Signalvib.js
rename to tgui/packages/tgui/interfaces/Signalvib.jsx
diff --git a/tgui/packages/tgui/interfaces/SkillPanel.js b/tgui/packages/tgui/interfaces/SkillPanel.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/SkillPanel.js
rename to tgui/packages/tgui/interfaces/SkillPanel.jsx
diff --git a/tgui/packages/tgui/interfaces/SkillStation.js b/tgui/packages/tgui/interfaces/SkillStation.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/SkillStation.js
rename to tgui/packages/tgui/interfaces/SkillStation.jsx
diff --git a/tgui/packages/tgui/interfaces/Sleeper.js b/tgui/packages/tgui/interfaces/Sleeper.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Sleeper.js
rename to tgui/packages/tgui/interfaces/Sleeper.jsx
diff --git a/tgui/packages/tgui/interfaces/SlimeBodySwapper.js b/tgui/packages/tgui/interfaces/SlimeBodySwapper.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/SlimeBodySwapper.js
rename to tgui/packages/tgui/interfaces/SlimeBodySwapper.jsx
diff --git a/tgui/packages/tgui/interfaces/Smes.js b/tgui/packages/tgui/interfaces/Smes.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Smes.js
rename to tgui/packages/tgui/interfaces/Smes.jsx
diff --git a/tgui/packages/tgui/interfaces/Soulcatcher.js b/tgui/packages/tgui/interfaces/Soulcatcher.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Soulcatcher.js
rename to tgui/packages/tgui/interfaces/Soulcatcher.jsx
diff --git a/tgui/packages/tgui/interfaces/SoulcatcherUser.js b/tgui/packages/tgui/interfaces/SoulcatcherUser.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/SoulcatcherUser.js
rename to tgui/packages/tgui/interfaces/SoulcatcherUser.jsx
diff --git a/tgui/packages/tgui/interfaces/SpaceHeater.js b/tgui/packages/tgui/interfaces/SpaceHeater.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/SpaceHeater.js
rename to tgui/packages/tgui/interfaces/SpaceHeater.jsx
diff --git a/tgui/packages/tgui/interfaces/StackingConsole.js b/tgui/packages/tgui/interfaces/StackingConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/StackingConsole.js
rename to tgui/packages/tgui/interfaces/StackingConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/StationAlertConsole.js b/tgui/packages/tgui/interfaces/StationAlertConsole.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/StationAlertConsole.js
rename to tgui/packages/tgui/interfaces/StationAlertConsole.jsx
diff --git a/tgui/packages/tgui/interfaces/SyndContractor.js b/tgui/packages/tgui/interfaces/SyndContractor.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/SyndContractor.js
rename to tgui/packages/tgui/interfaces/SyndContractor.jsx
diff --git a/tgui/packages/tgui/interfaces/Tank.js b/tgui/packages/tgui/interfaces/Tank.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Tank.js
rename to tgui/packages/tgui/interfaces/Tank.jsx
diff --git a/tgui/packages/tgui/interfaces/TankCompressor.js b/tgui/packages/tgui/interfaces/TankCompressor.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/TankCompressor.js
rename to tgui/packages/tgui/interfaces/TankCompressor.jsx
diff --git a/tgui/packages/tgui/interfaces/TankDispenser.js b/tgui/packages/tgui/interfaces/TankDispenser.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/TankDispenser.js
rename to tgui/packages/tgui/interfaces/TankDispenser.jsx
diff --git a/tgui/packages/tgui/interfaces/Techweb.js b/tgui/packages/tgui/interfaces/Techweb.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Techweb.js
rename to tgui/packages/tgui/interfaces/Techweb.jsx
diff --git a/tgui/packages/tgui/interfaces/Telecomms.js b/tgui/packages/tgui/interfaces/Telecomms.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Telecomms.js
rename to tgui/packages/tgui/interfaces/Telecomms.jsx
diff --git a/tgui/packages/tgui/interfaces/Teleporter.js b/tgui/packages/tgui/interfaces/Teleporter.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Teleporter.js
rename to tgui/packages/tgui/interfaces/Teleporter.jsx
diff --git a/tgui/packages/tgui/interfaces/ThermoMachine.js b/tgui/packages/tgui/interfaces/ThermoMachine.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/ThermoMachine.js
rename to tgui/packages/tgui/interfaces/ThermoMachine.jsx
diff --git a/tgui/packages/tgui/interfaces/Thermometer.js b/tgui/packages/tgui/interfaces/Thermometer.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Thermometer.js
rename to tgui/packages/tgui/interfaces/Thermometer.jsx
diff --git a/tgui/packages/tgui/interfaces/TimeClock.js b/tgui/packages/tgui/interfaces/TimeClock.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/TimeClock.js
rename to tgui/packages/tgui/interfaces/TimeClock.jsx
diff --git a/tgui/packages/tgui/interfaces/TrackedPlaytime.js b/tgui/packages/tgui/interfaces/TrackedPlaytime.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/TrackedPlaytime.js
rename to tgui/packages/tgui/interfaces/TrackedPlaytime.jsx
diff --git a/tgui/packages/tgui/interfaces/TramControl.js b/tgui/packages/tgui/interfaces/TramControl.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/TramControl.js
rename to tgui/packages/tgui/interfaces/TramControl.jsx
diff --git a/tgui/packages/tgui/interfaces/TransferValve.js b/tgui/packages/tgui/interfaces/TransferValve.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/TransferValve.js
rename to tgui/packages/tgui/interfaces/TransferValve.jsx
diff --git a/tgui/packages/tgui/interfaces/TrophyAdminPanel.js b/tgui/packages/tgui/interfaces/TrophyAdminPanel.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/TrophyAdminPanel.js
rename to tgui/packages/tgui/interfaces/TrophyAdminPanel.jsx
diff --git a/tgui/packages/tgui/interfaces/Trophycase.js b/tgui/packages/tgui/interfaces/Trophycase.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/Trophycase.js
rename to tgui/packages/tgui/interfaces/Trophycase.jsx
diff --git a/tgui/packages/tgui/interfaces/common/AccessConfig.js b/tgui/packages/tgui/interfaces/common/AccessConfig.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/common/AccessConfig.js
rename to tgui/packages/tgui/interfaces/common/AccessConfig.jsx
diff --git a/tgui/packages/tgui/interfaces/common/AccessList.js b/tgui/packages/tgui/interfaces/common/AccessList.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/common/AccessList.js
rename to tgui/packages/tgui/interfaces/common/AccessList.jsx
diff --git a/tgui/packages/tgui/interfaces/common/BeakerContents.js b/tgui/packages/tgui/interfaces/common/BeakerContents.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/common/BeakerContents.js
rename to tgui/packages/tgui/interfaces/common/BeakerContents.jsx
diff --git a/tgui/packages/tgui/interfaces/common/InterfaceLockNoticeBox.js b/tgui/packages/tgui/interfaces/common/InterfaceLockNoticeBox.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/common/InterfaceLockNoticeBox.js
rename to tgui/packages/tgui/interfaces/common/InterfaceLockNoticeBox.jsx
diff --git a/tgui/packages/tgui/interfaces/common/PortableAtmos.js b/tgui/packages/tgui/interfaces/common/PortableAtmos.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/common/PortableAtmos.js
rename to tgui/packages/tgui/interfaces/common/PortableAtmos.jsx
diff --git a/tgui/packages/tgui/interfaces/common/ReagentLookup.js b/tgui/packages/tgui/interfaces/common/ReagentLookup.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/common/ReagentLookup.js
rename to tgui/packages/tgui/interfaces/common/ReagentLookup.jsx
diff --git a/tgui/packages/tgui/interfaces/common/RecipeLookup.js b/tgui/packages/tgui/interfaces/common/RecipeLookup.jsx
similarity index 100%
rename from tgui/packages/tgui/interfaces/common/RecipeLookup.js
rename to tgui/packages/tgui/interfaces/common/RecipeLookup.jsx
diff --git a/tgui/packages/tgui/layouts/Layout.js b/tgui/packages/tgui/layouts/Layout.jsx
similarity index 100%
rename from tgui/packages/tgui/layouts/Layout.js
rename to tgui/packages/tgui/layouts/Layout.jsx
diff --git a/tgui/packages/tgui/layouts/NtosWindow.js b/tgui/packages/tgui/layouts/NtosWindow.jsx
similarity index 100%
rename from tgui/packages/tgui/layouts/NtosWindow.js
rename to tgui/packages/tgui/layouts/NtosWindow.jsx
diff --git a/tgui/packages/tgui/layouts/Pane.js b/tgui/packages/tgui/layouts/Pane.jsx
similarity index 100%
rename from tgui/packages/tgui/layouts/Pane.js
rename to tgui/packages/tgui/layouts/Pane.jsx
diff --git a/tgui/packages/tgui/layouts/Window.js b/tgui/packages/tgui/layouts/Window.jsx
similarity index 100%
rename from tgui/packages/tgui/layouts/Window.js
rename to tgui/packages/tgui/layouts/Window.jsx
diff --git a/tgui/packages/tgui/layouts/index.js b/tgui/packages/tgui/layouts/index.ts
similarity index 100%
rename from tgui/packages/tgui/layouts/index.js
rename to tgui/packages/tgui/layouts/index.ts
diff --git a/tgui/packages/tgui/routes.tsx b/tgui/packages/tgui/routes.tsx
index 39598c4cc6e..fc2785cde5e 100644
--- a/tgui/packages/tgui/routes.tsx
+++ b/tgui/packages/tgui/routes.tsx
@@ -72,9 +72,9 @@ export const getRoutedComponent = (store: Store) => {
const name = config?.interface;
const interfacePathBuilders = [
(name: string) => `./${name}.tsx`,
- (name: string) => `./${name}.js`,
+ (name: string) => `./${name}.jsx`,
(name: string) => `./${name}/index.tsx`,
- (name: string) => `./${name}/index.js`,
+ (name: string) => `./${name}/index.jsx`,
];
let esModule;
while (!esModule && interfacePathBuilders.length > 0) {
diff --git a/tgui/webpack.config.js b/tgui/webpack.config.js
index 19e2975715f..27a07fd0d09 100644
--- a/tgui/webpack.config.js
+++ b/tgui/webpack.config.js
@@ -33,18 +33,9 @@ module.exports = (env = {}, argv) => {
context: path.resolve(__dirname),
target: ['web', 'es3', 'browserslist:ie 8'],
entry: {
- 'tgui': [
- './packages/tgui-polyfill',
- './packages/tgui',
- ],
- 'tgui-panel': [
- './packages/tgui-polyfill',
- './packages/tgui-panel',
- ],
- 'tgui-say': [
- './packages/tgui-polyfill',
- './packages/tgui-say',
- ],
+ 'tgui': ['./packages/tgui-polyfill', './packages/tgui'],
+ 'tgui-panel': ['./packages/tgui-polyfill', './packages/tgui-panel'],
+ 'tgui-say': ['./packages/tgui-polyfill', './packages/tgui-say'],
},
output: {
path: argv.useTmpFolder
@@ -55,13 +46,13 @@ module.exports = (env = {}, argv) => {
chunkLoadTimeout: 15000,
},
resolve: {
- extensions: ['.tsx', '.ts', '.js'],
+ extensions: ['.tsx', '.ts', '.js', '.jsx'],
alias: {},
},
module: {
rules: [
{
- test: /\.(js|cjs|ts|tsx)$/,
+ test: /\.(js(x)?|cjs|ts(x)?)$/,
use: [
{
loader: require.resolve('babel-loader'),
diff --git a/tools/UpdatePaths/Scripts/79762_simple_to_basic_parrots.txt b/tools/UpdatePaths/Scripts/79762_simple_to_basic_parrots.txt
new file mode 100644
index 00000000000..2f2e9c19a0f
--- /dev/null
+++ b/tools/UpdatePaths/Scripts/79762_simple_to_basic_parrots.txt
@@ -0,0 +1,5 @@
+/mob/living/simple_animal/parrot : /mob/living/basic/parrot{@OLD}
+/mob/living/simple_animal/parrot/natural : /mob/living/basic/parrot{@OLD}
+/mob/living/simple_animal/parrot/poly : /mob/living/basic/parrot/poly{@OLD}
+
+
diff --git a/tools/tgs_scripts/PreCompile.sh b/tools/tgs_scripts/PreCompile.sh
index 499073d9032..9f2f94646ed 100755
--- a/tools/tgs_scripts/PreCompile.sh
+++ b/tools/tgs_scripts/PreCompile.sh
@@ -12,6 +12,7 @@ cd "$1"
. dependencies.sh
cd "$original_dir"
+
# update rust-g
if [ ! -d "rust-g" ]; then
echo "Cloning rust-g..."