diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 915dda724d53c..104b108bb9ccb 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -3,222 +3,3 @@
# This list is alphabetized by User -> Filename and separated into sections for Maintainers/Contributors KEEP IT THAT WAY
# In the event that people are to be informed of changes
# to the same file or dir, add them to the end of under Multiple Owners
-
-# MAINTAINERS
-
-# Dominion/Cyberboss
-
-/.github/workflows/update_tgs_dmapi.yml @Cyberboss
-/.tgs.yml @Cyberboss
-/code/world.dm @Cyberboss
-/code/__DEFINES/tgs.config.dm @Cyberboss
-/code/__DEFINES/tgs.dm @Cyberboss
-/code/__DEFINES/_globals.dm @Cyberboss
-/code/__HELPERS/chat.dm @Cyberboss
-/code/__HELPERS/jatum.dm @Cyberboss
-/code/game/world.dm @Cyberboss
-/code/controllers/subsystem/atoms.dm @Cyberboss
-/code/controllers/globals.dm @Cyberboss
-/code/datums/helper_datums/getrev.dm @Cyberboss
-/code/modules/tgs/ @Cyberboss
-/code/ze_genesis_call/ @Cyberboss
-/tools/tgs_test/ @Cyberboss
-
-# Cobby
-
-/code/modules/reagents/ @ExcessiveUseOfCobblestone
-/code/modules/research/designs/medical_designs.dm @ExcessiveUseOfCobblestone
-/code/game/objects/items/storage/medkit.dm @ExcessiveUseOfCobblestone
-
-# Fikou
-
-/code/modules/awaymissions/ @Fikou
-/code/modules/mining/ @Fikou
-/code/modules/mod/ @Fikou
-/code/modules/mapfluff/ruins/lavalandruin_code/ @Fikou
-/code/modules/mapfluff/ruins/lavaland_ruin_code.dm @Fikou
-
-# JohnFulpWizard
-
-/code/modules/mob/living/simple_animal/bot/ @JohnFulpWillard
-/code/modules/modular_computers/ @JohnFulpWillard
-
-# Kylerace
-
-/code/__DEFINES/spatial_gridmap.dm @Kylerace
-/code/controllers/subsystem/spatial_gridmap.dm @Kylerace
-
-# LemonInTheDark
-
-/.github/guides/VISUALS.md @LemonInTheDark
-/code/_onclick/hud/ @LemonInTheDark
-/code/__DEFINES/layers.dm @LemonInTheDark
-
-# Mothblocks
-
-/.github/workflows/ @Mothblocks
-/code/game/gamemodes/ @Mothblocks
-/code/modules/autowiki/ @Mothblocks
-/code/modules/unit_tests/ @Mothblocks
-/code/modules/client/preferences/ @Mothblocks
-/code/modules/client/preferences_menu.dm @Mothblocks
-/tgui/packages/tgui/interfaces/PreferencesMenu/ @Mothblocks
-/tools/ezdb/ @Mothblocks
-/tools/maplint/source/ @Mothblocks
-/tools/pull_request_hooks/ @Mothblocks
-/tools/screenshot-test-comparison/ @Mothblocks
-/tools/test_merge_bot/ @Mothblocks
-
-# MrMelbert
-
-/code/modules/hydroponics/ @MrMelbert
-
-# ninjanomnom
-
-/code/controllers/subsystem/dcs.dm @ninjanomnom
-/code/datums/signals.dm @ninjanomnom
-/code/datums/components/_component.dm @ninjanomnom
-/code/datums/elements/_element.dm @ninjanomnom
-/code/datums/greyscale/_greyscale_config.dm @ninjanomnom
-/code/datums/greyscale/json_reader.dm @ninjanomnom
-/code/datums/greyscale/layer.dm @ninjanomnom
-
-# Ryll-Ryll/Shaps
-
-/code/datums/wounds/ @Ryll-Ryll
-/code/datums/status_effects/wound_effects.dm @Ryll-Ryll
-/code/__DEFINES/wounds.dm @Ryll-Ryll
-
-# san7890
-
-/code/game/area/ @san7890
-/icons/area/ @san7890
-
-# stylemistake
-
-/code/__DEFINES/chat.dm @stylemistake
-/code/__DEFINES/tgui.dm @stylemistake
-/code/controllers/subsystem/chat.dm @stylemistake
-/code/controllers/subsystem/ping.dm @stylemistake
-/code/controllers/subsystem/tgui.dm @stylemistake
-/code/modules/tgchat/ @stylemistake
-/code/modules/tgui/ @stylemistake
-/code/modules/tgui_panel/ @stylemistake
-/tgui/ @stylemistake
-
-# stylemistake (explicitly disowned)
-
-/tgui/packages/tgui/interfaces/
-/tgui/packages/tgui/styles/interfaces/
-/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss
-/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss
-
-# SuperNovaa41
-
-/code/modules/forensics/ @SuperNovaa41
-/code/datums/mood.dm @SuperNovaa41
-
-# Time-Green
-
-/code/modules/plumbing/ @Time-Green
-/code/modules/surgery/organs/external/ @Time-Green
-
-# tralezab
-/code/__DEFINES/basic_mobs.dm @tralezab
-/code/datums/ai/ @tralezab
-/code/modules/religion/ @tralezab
-
-# Watermelon914
-
-/code/modules/wiremod/ @Watermelon914
-/code/modules/antagonists/traitor/ @Watermelon914
-/code/controllers/subsystem/tts.dm @Watermelon914
-
-# ZephyrTFA
-
-/code/__HELPERS/admin_verb.dm @ZephyrTFA
-/code/controllers/subsystem/admin_verbs.dm @ZephyrTFA
-/code/datums/json_savefile.dm @ZephyrTFA
-/code/datums/armor/ @ZephyrTFA
-/code/modules/admin/verbs/ @ZephyrTFA
-/code/modules/logging/ @ZephyrTFA
-/tools/ci/check_grep.sh @ZephyrTFA
-
-
-# CONTRIBUTORS
-
-# Jordie0608
-
-/code/controllers/subsystem/dbcore.dm @Jordie0608
-/tools/SQLAlertEmail/ @Jordie0608
-
-# Kapu1178
-
-/code/modules/surgery/bodyparts/ @Kapu1178
-/code/modules/surgery/organs/ @Kapu1178
-/code/modules/mob/living/carbon/carbon_update_icons.dm @Kapu1178
-/code/modules/mob/living/carbon/human/human_update_icons.dm @Kapu1178
-
-# MrStonedOne
-
-/code/__DEFINES/MC.dm @MrStonedOne
-/code/controllers/admin.dm @MrStonedOne
-/code/controllers/master.dm @MrStonedOne
-/code/controllers/failsafe.dm @MrStonedOne
-/code/controllers/subsystem.dm @MrStonedOne
-/code/controllers/subsystem/timer.dm @MrStonedOne
-/code/controllers/configuration/entries @MrStonedOne
-/config/ @MrStonedOne
-
-# NamelessFairy
-
-/code/modules/capture_the_flag/ @NamelessFairy
-/_maps/map_files/CTF/ @NamelessFairy
-
-# Pickle-Coding
-
-/code/__DEFINES/atmospherics/ @Pickle-Coding
-/code/__DEFINES/reactions.dm @Pickle-Coding
-/code/modules/atmospherics/ @Pickle-Coding
-/code/modules/power/ @Pickle-Coding
-
-# MULTIPLE OWNERS
-
-/SQL/ @Jordie0608 @MrStonedOne
-
-/_maps/ @EOBGames @Maurukas @MMMiracles @san7890 @ShizCalev
-
-/icons/ @Imaginos16 @Krysonism @Twaticus @Wallemations
-/icons/ass/ @Ghilker @tralezab
-
-/code/__DEFINES/atmospherics/ @Ghilker @LemonInTheDark
-
-/code/__HELPERS/logging/ @dragomagol @ZephyrTFA
-
-/code/controllers/subsystem/air.dm @LemonInTheDark @MrStonedOne
-
-/code/modules/atmospherics/ @Ghilker @LemonInTheDark
-
-/code/modules/client/preferences.dm @Mothblocks @ZephyrTFA
-/code/modules/client/preferences_savefile.dm @Mothblocks @ZephyrTFA
-
-/code/modules/jobs/job_types/chief_medical_officer.dm @ExcessiveUseOfCobblestone @Ryll-Ryll
-/code/modules/jobs/job_types/medical_doctor.dm @ExcessiveUseOfCobblestone @Ryll-Ryll
-/code/modules/jobs/job_types/paramedic.dm @ExcessiveUseOfCobblestone @Ryll-Ryll
-
-/code/modules/mob/living/basic/ @Jacquerel @san7890 @tralezab
-
-/code/modules/surgery/ @ExcessiveUseOfCobblestone @Ryll-Ryll
-
-/tools/build/ @MrStonedOne @stylemistake
-/tools/tgs_scripts/ @Cyberboss @MrStonedOne
-
-/tools/WebhookProcessor/ @BraveMole @TiviPlus
-
-# Expensive files that touching basically always cause performance problems
-## Init times
-**/*_EXPENSIVE.dm @Mothblocks @LemonInTheDark
-
-# SIC SEMPER TYRANNIS
-
-/code/modules/hydroponics/grown/citrus.dm @optimumtact
diff --git a/_maps/shuttles/emergency_cruise.dmm b/_maps/shuttles/emergency_cruise.dmm
index 7e20dc7d47d58..8045930388cef 100644
--- a/_maps/shuttles/emergency_cruise.dmm
+++ b/_maps/shuttles/emergency_cruise.dmm
@@ -1648,7 +1648,7 @@
/turf/open/floor/carpet/executive,
/area/shuttle/escape)
"Jl" = (
-/mob/living/simple_animal/bot/vibebot,
+/mob/living/basic/bot/vibebot,
/turf/open/floor/iron,
/area/shuttle/escape)
"Jo" = (
diff --git a/code/__DEFINES/ai/bot_keys.dm b/code/__DEFINES/ai/bot_keys.dm
index 61189861141d7..05c0b7aff1899 100644
--- a/code/__DEFINES/ai/bot_keys.dm
+++ b/code/__DEFINES/ai/bot_keys.dm
@@ -109,3 +109,16 @@ DEFINE_BITFIELD(honkbot_flags, list(
///key that holds our honk ability
#define BB_HONK_ABILITY "honk_ability"
+//vibebots
+///key that holds our partying ability
+#define BB_VIBEBOT_PARTY_ABILITY "party_ability"
+///key that holds our birthday song
+#define BB_VIBEBOT_BIRTHDAY_SONG "birthday_song"
+///key that holds happy songs we play to depressed targets
+#define BB_VIBEBOT_HAPPY_SONG "happy_song"
+///key that holds grim song we play when emagged
+#define BB_VIBEBOT_GRIM_SONG "GRIM_song"
+///key that holds neutral targets we vibe with
+#define BB_VIBEBOT_PARTY_TARGET "party_target"
+///key that holds our instrument
+#define BB_VIBEBOT_INSTRUMENT "instrument"
diff --git a/code/__DEFINES/atom_hud.dm b/code/__DEFINES/atom_hud.dm
index 7df79b7e57a26..86de96f07a1d1 100644
--- a/code/__DEFINES/atom_hud.dm
+++ b/code/__DEFINES/atom_hud.dm
@@ -81,6 +81,7 @@
#define SECHUD_ASSISTANT "hudassistant"
#define SECHUD_ATMOSPHERIC_TECHNICIAN "hudatmospherictechnician"
#define SECHUD_BARTENDER "hudbartender"
+#define SECHUD_BITAVATAR "hudbitavatar"
#define SECHUD_BITRUNNER "hudbitrunner"
#define SECHUD_BOTANIST "hudbotanist"
#define SECHUD_BRIDGE_ASSISTANT "hudbridgeassistant"
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index b8acef70a0648..f1a48df79faee 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -309,7 +309,7 @@
#define SLIME_EVOLUTION_THRESHOLD 10
//Slime evolution cost in nutrition
-#define SLIME_EVOLUTION_COST 200
+#define SLIME_EVOLUTION_COST 100
//Slime extract crossing. Controls how many extracts is required to feed to a slime to core-cross.
#define SLIME_EXTRACT_CROSSING_REQUIRED 10
diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm
index 62ae5a7394a0a..c050c73f23133 100644
--- a/code/__DEFINES/obj_flags.dm
+++ b/code/__DEFINES/obj_flags.dm
@@ -87,6 +87,8 @@
#define INEDIBLE_CLOTHING (1<<16)
/// Headgear/helmet allows internals
#define HEADINTERNALS (1<<17)
+/// Prevents masks from getting adjusted from enabling internals
+#define INTERNALS_ADJUST_EXEMPT (1<<18)
/// Integrity defines for clothing (not flags but close enough)
#define CLOTHING_PRISTINE 0 // We have no damage on the clothing
diff --git a/code/__DEFINES/research/slimes.dm b/code/__DEFINES/research/slimes.dm
index e03c6af8f581d..03671ee24d2a0 100644
--- a/code/__DEFINES/research/slimes.dm
+++ b/code/__DEFINES/research/slimes.dm
@@ -11,15 +11,15 @@
#define SLIME_MAX_POWER 10
///The maximum amount of nutrition a slime can contain
-#define SLIME_MAX_NUTRITION 1000
+#define SLIME_MAX_NUTRITION 200
///The starting nutrition of a slime
-#define SLIME_STARTING_NUTRITION 700
+#define SLIME_STARTING_NUTRITION 100
/// Above it we grow our amount_grown and our power_level, below it we can eat
-#define SLIME_GROW_NUTRITION 800
+#define SLIME_GROW_NUTRITION 150
/// Below this, we feel hungry
-#define SLIME_HUNGER_NUTRITION 500
+#define SLIME_HUNGER_NUTRITION 50
/// Below this, we feel starving
-#define SLIME_STARVE_NUTRITION 200
+#define SLIME_STARVE_NUTRITION 10
///The slime is not hungry. It might try to feed anyways.
#define SLIME_HUNGER_NONE 0
diff --git a/code/__DEFINES/song.dm b/code/__DEFINES/song.dm
index 782a7923ea14f..0e7e9f0ce0692 100644
--- a/code/__DEFINES/song.dm
+++ b/code/__DEFINES/song.dm
@@ -15,3 +15,9 @@
#define MONKEY_SONG "BPM: 200\nC4/0,14,C,A4-F2,F3,A3,F-F2,A-F,F4,G4,F,D4-Bb2-G2\nD3,G3,D-G2,G3-G2,D,D4-G3,D,B4-B2,G,B3,G-B2,B3-B2\nG4,A4,G,E4-C3,E3,G3,E-C,G-C,E,E4-G,E,C5-E-A3,C4\nA-E3,C,E4-C3,A4-C4,B4-A3-A2,C5-C4,D5-F-B3,D4,B-F3\nD,F4-D3,D4,F-B-B2,G4-D,A4-C-F3,F,C/2,B3/2,A3-C3/2\nB/2,C4,E-C3,F4,G-C,F-F3,F-C,C4/2,B/2,A-A2/2,G3/2\nF/I"
///song played by the mook bard
#define MOOK_SONG "BPM: 240\nA5,B5,C#6,D6,E6/0.17,A/0.5,A/0.25,A3/0.25\nA4/0.25,C#5/0.25,E5/0.25,A/0.25,C#/0.25,E/0.12\nC#6/0.25,C#/0.25,E6/0.25,A3/0.25,A4/0.25\nC#5/0.25,E5/0.25,A/0.25,C#/0.25,E/0.25,D/0.25\nG6/0.25,D/0.17,F6/0.17,C#6/0.5,E6/0.5,D4/0.25\nA/0.25,D5/0.25,F5/0.25,A/0.25,D/0.25,F/0.25\nD6/0.08,F6/0.08,D4/0.25,A/0.25,D5/0.25,F5/0.25\nCn4/0.2,B/0.17,D6/0.17,G5/0.5,G/0.25,B3/0.25\nD4/0.25,G4/0.25,B4/0.25,D/0.25,G/0.25,B/0.12\nB5/0.25,B/0.25,D6/0.25,G3/0.25,G4/0.25,B4/0.25\nF/0.25,G/0.25,B/0.25,F/0.25,D/0.25,F6/0.25\nC6/0.17,E/0.17,B5/0.5,D#/0.5,C4/0.25,G/0.25\nC5/0.25,E5/0.25,G/0.25,C/0.25,E/0.25,C6/0.08\nE6/0.08,C4/0.25,Dn4/0.25,E4/0.25,A5/0.17,B/0.5\nC6/0.25,F5/0.08,F4/0.08,C5/0.08,E5/0.12,G5/0.12\nC6/0.25,E6/0.25,E4/0.08,C5/0.08,B/0.17,F6/0.17\nE6/0.5,B/0.25,E4/0.08,G#4/0.08,C6/0.17,D6/0.5\nE6/0.25,A3/0.25,E4/0.25,C5/0.25,Gn3/0.25\nF5/0.12,A5/0.12,A6/0.25,F3/0.25,F4/0.12,A4/0.12\nC/0.12,F6/0.17,A6/0.17,G#6/0.5,A/0.25,F3/0.25\nF4/0.12,A4/0.12,D#5/0.12,B/0.17,G#/0.17,B6/0.5\nB5/0.25,G#/0.25,E3/0.25,E4/0.12,G#4/0.12\nDn/0.12,E6/0.08,E3/0.25,F#3/0.25,G#3/0.25\nE5/0.17,A5/0.17,E/0.5,E/0.25,A3/0.25,C#4/0.25\nE4/0.25,C#/0.25,E/0.12,A5/0.5,B/0.5,C#6/0.5\nD6/0.5,A3/0.25,C#4/0.25,E/0.25,C#/0.25,E/0.25\nE6/0.08,Gn/0.25,E4/0.25,A4/0.25,C#5/0.25,E/0.25\nA/0.25,C#/0.25,E6/0.17,E/0.5,Fn6/0.5,G6/0.5\nG3/0.25,E4/0.25,A/0.25,C#/0.25,E/0.25,A/0.25\nC#/0.25,F/0.08,A6/0.08,F3/0.25,F4/0.25,A4/0.25\nCn/0.25,F/0.25,A/0.25,C/0.25,G6/0.12,A6/0.12\nG A G F6 G3/0.25 D4/0.25 G4/0.25 B4/0.25 D/0.25\nG/0.25 B/0.25 E6/0.12 G6/0.12 F/0.71 G/0.71 F/0.71\nE3/0.25 E4/0.25 G4/0.25 B/0.25 E/0.25 G/0.25 B/0.25\nA5/0.08 E6/0.08 A3/0.25 E4/0.25 A4/0.25 C#/0.25 E/0.25 A/0.25 C#/0.25 D6/0.17 E6/0.5 F/0.25 B3/0.25 D4/0.12 F4/0.12 B4/0.12 F6/0.25 E/0.25 D6/0.25 G#3/0.25 E4/0.12 G#4/0.12 B/0.12 Cn6/0.12 D/0.25 A3/0.25 A4/0.25 C5/0.25 E5/0.25 G#3/0.25 Gn/0.25 C4/0.25 E4/0.25 A/"
+///song played by the vibebot when cheering people up
+#define VIBEBOT_CHEER_SONG "BPM: 360\nE4/0.5,B4-G4/0.5,G-B-E/0.5,D4,Gb,A4-D-G/0.25\nGn-B3-E/0.5,G-B-E/0.5,A3-D-Gb/0.5,E-Gn-B/0.17,E,G\nB4/0.5,E-B-G/0.5,D,Gb,A4-D-G/0.25,B3-Gn-E/0.5\nG-B-E/0.5,A3-D-Gb/0.5,E-Gn-B/0.17,E-G-C5/0.5,G,E,G\nC,B4-Eb-Gb/0.5,B3-E-G/0.5,B-E-G3/0.5,C-Gn4-En/0.5\nG,E,G,C,Eb-Gb-B4/0.17,C-Gn-En/0.5,G,E,G,C\nB-C-Eb-Gb/0.5,B3-E-G/0.5,E-B-G3/0.5,C-En-Gn4,E,G,C\nE,G,A4-Gb-D5/0.17,E/0.5,B4-Gn/0.5,G-B-E/0.5,D4,Gb\nA-D-G/0.25,Gn-B3-E/0.5,G-B-E/0.5,A3-D-Gb/0.5\nE-Gn-B/0.17,E,G,B4/0.5,E-B-G/0.5,D,Gb,A4-D-G/0.25\nB3-Gn-E/0.5,G-B-E/0.5,A3-D-Gb/0.5,E-Gn-B/0.17\nE-G-C/0.5,G,E,G,C,B4-Eb-Gb/0.5,B3-E-G/0.5\nB-E-G3/0.5,C-Gn4-En/0.5,G,E,G,C,Eb-Gb-B4/0.17\nC-Gn-En/0.5,G,E,G,C,B-C-Eb-Gb/0.5,B3-E-G/0.5\nE-B-G3/0.5,C-En-Gn4,E,G,C,E,G,A4-Gb-D5/0.17\nGn-E-B/0.5,B-G3-E,E3-G-B,G4-E4-B/0.5,A3-D4-Gb/0.5\nA-D-G3/0.5,D3-G-A/0.5,Gn4-E-B/0.5,B-G3-E,E3-G-B\nG4-E4-B/0.5,A-D4-Gb/0.17,A4-F4-C4/0.5,C-A3-F\nF3-A-C,C-F4-A4/0.5,B-Gn-E/0.5,B-G3-E/0.5\nG-E3-B/0.5,A3-D-Gb4/0.5,D-A-G3/0.5,D3-A-G/0.5\nA-D4-G4/0.17,Gn-E4-B/0.5,B-G3-E,E3-G-B,G4-E4-B/0.5\nA-D-Gb/0.5,A-D-G3/0.5,D3-G-A/0.5,Gn4-E-B/0.5\nB-G3-E,E3-G-B,G4-E4-B/0.5,A-D4-Gb/0.17,A4-F-C/0.5\nC-A3-F,F3-A-C,C-F4-A4/0.5,B-Gn-E/0.5,B-G3-E/0.5\nG-E3-B/0.5,A3-D-Gb4/0.5,D-A-G3/0.5,D3-A-G/0.5\nE4-A-Db4/0.17,A2,E3,Gn-B-E4,A,E3,B-G-E4\nA3-Dn-F-D3/0.5,F3-A-D4/0.5,D3/0.5,A2,E3,B-E4-G,A\nE3,B-G-E4,F-D4-A3/0.17,A2,E3,B-E4-G,A,E3,B-G-E4\nB-A3-F4-D-D3/0.5,F3-D4-A/0.5,F-A-D3/0.5,A2,E3\nB-E4-G,A,E3,B-E4-G,F-A3-D4-D3/0.17,A2,E3,G-B-E4,A\nE3,B-G-E4,A3-D4-F4-D3/0.5,F3-A-D4/0.5,D3/0.5,A2,E3\nB-E4-G,A,E3,B-G-E4,F-D4-A3/0.17,A2,E3,B-E4-G,A,E3\nB-G-E4,B-A3-F4-D-D3/0.5,F3-D4-A/0.5,F-A-D3/0.5,A2\nE3,B-E4-G,A,E3,B-E4-G,F-A3-D4-D3/0.17,E/0.5\nB4-G4/0.5,G-B-E/0.5,D4,Gb,A4-D-G/0.25,Gn-B3-E/0.5\nG-B-E/0.5,A3-D-Gb/0.5,E-Gn-B/0.17,E,G,B4/0.5\nE-B-G/0.5,D,Gb,A4-D-G/0.25,B3-Gn-E/0.5,G-B-E/0.5\nA3-D-Gb/0.5,E-Gn-B/0.17,E-G-C5/0.5,G,E,G,C\nB4-Eb-Gb/0.5,B3-E-G/0.5,B-E-G3/0.5,C-Gn4-En/0.5,G\nE,G,C,Eb-Gb-B4/0.17,C-Gn-En/0.5,G,E,G,C\nB-Eb-Gb/0.5,B3-E-G/0.5,E-B-G3/0.53,En-Gn4/8,C,G,E\nB4,G,E,B3-E-G/0.07"
+///grim music played by the vibebot
+#define VIBEBOT_GRIM_MUSIC "BPM: 92\nG5/0.5,C#7-F#5/1.08,G6-G5/0.52,D6-D5,B6-E5/0.52\nG6-G5/1.08,D6-D5/0.34,Cn6-C5-G6/0.5,D,B5-F#6/13\nB4/0.52,C6-C5-G,A4/0.28,G-G5/0.5\nC#7-F#-F#5/1.08,G6-G5/0.52,D6-D5,B6-E/0.52,G-G6\nB5,D7/13,D6/0.52,Cn6,B6-B5,G-G5,G/0.5\nF#-C#7/1.08,G6-G5/0.22,G/0.5,C#-F#/1.08\nG6-G5/0.52,D-D5,B6-E/0.52,G6-G5/1.08,D6-D5/0.34\nCn6-C5-G6/0.5,D,B5-F#6/13,B4/0.52,C6-G-C5/0.25\nG-G5/0.5,C#7-F#-F#5/1.08,G6-G5/0.52,D6-D5\nB6-E/0.52,G-G6,B5,D7/13,D6/0.52,Cn6,B6-B5,G-G5\nG/0.5,F#-C#7/1.08,G6-G5/0.65"
+///happy birthday music we play to the birthday boy
+#define VIBEBOT_HAPPY_BIRTHDAY "BPM: 120\nG4/0.5,G4/0.25,A4/0.5,G4/0.5,C5/0.5,B4/1\nG4/0.5,G4/0.25,A4/0.5,G4/0.5,D5/0.5,C5/1\nG4/0.5,G4/0.5,G5/0.5,E5/0.5,C5/0.5,B4/0.5,A4/0.5\nF5/0.5,F5/0.5,E5/0.5,C5/0.5,D5/0.5,C5/1"
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 6cc90893c0f7e..8d2b54ab72f87 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -1167,4 +1167,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// This trait lets you attach limbs to any player without surgery.
#define TRAIT_EASY_ATTACH "easy_attach"
+///Trait given to the birthday boy
+#define TRAIT_BIRTHDAY_BOY "birthday_boy"
// END TRAIT DEFINES
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index ead7b324cea13..f98178d537fa3 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -99,6 +99,7 @@ DEFINE_BITFIELD(clothing_flags, list(
"THICKMATERIAL" = THICKMATERIAL,
"VOICEBOX_DISABLED" = VOICEBOX_DISABLED,
"VOICEBOX_TOGGLABLE" = VOICEBOX_TOGGLABLE,
+ "INTERNALS_ADJUST_EXEMPT" = INTERNALS_ADJUST_EXEMPT,
))
DEFINE_BITFIELD(datum_flags, list(
diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm
index 81fe08373b31a..dce2dc69a50ae 100644
--- a/code/_globalvars/lists/names.dm
+++ b/code/_globalvars/lists/names.dm
@@ -24,6 +24,7 @@ GLOBAL_LIST_INIT(nightmare_names, world.file2list("strings/names/nightmare.txt")
GLOBAL_LIST_INIT(megacarp_first_names, world.file2list("strings/names/megacarp1.txt"))
GLOBAL_LIST_INIT(megacarp_last_names, world.file2list("strings/names/megacarp2.txt"))
GLOBAL_LIST_INIT(cyberauth_names, world.file2list("strings/names/cyberauth.txt"))
+GLOBAL_LIST_INIT(hacker_aliases, world.file2list("strings/names/hackers.txt"))
GLOBAL_LIST_INIT(syndicate_monkey_names, world.file2list("strings/names/syndicate_monkey.txt"))
GLOBAL_LIST_INIT(cargorilla_names, world.file2list("strings/names/cargorilla.txt"))
GLOBAL_LIST_INIT(guardian_first_names, world.file2list("strings/names/guardian_descriptions.txt"))
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 62805b578d559..5d1e88667b738 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -140,6 +140,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_BATON_RESISTANCE" = TRAIT_BATON_RESISTANCE,
"TRAIT_BEAST_EMPATHY" = TRAIT_BEAST_EMPATHY,
"TRAIT_BEING_BLADE_SHIELDED" = TRAIT_BEING_BLADE_SHIELDED,
+ "TRAIT_BIRTHDAY_BOY" = TRAIT_BIRTHDAY_BOY,
"TRAIT_BLOB_ALLY" = TRAIT_BLOB_ALLY,
"TRAIT_BLOCK_SHUTTLE_MOVEMENT" = TRAIT_BLOCK_SHUTTLE_MOVEMENT,
"TRAIT_BLOOD_CLANS" = TRAIT_BLOOD_CLANS,
diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm
index 58e9746cbf086..7b46a7b06e803 100644
--- a/code/datums/ai/_ai_controller.dm
+++ b/code/datums/ai/_ai_controller.dm
@@ -625,7 +625,7 @@ multiple modular subtrees with behaviors
/datum/ai_controller/proc/post_blackboard_key_set(key)
if (isnull(pawn))
return
- SEND_SIGNAL(pawn, COMSIG_AI_BLACKBOARD_KEY_SET(key))
+ SEND_SIGNAL(pawn, COMSIG_AI_BLACKBOARD_KEY_SET(key), key)
/**
* Adds the passed "thing" to the associated key
diff --git a/code/datums/components/crafting/robot.dm b/code/datums/components/crafting/robot.dm
index 11a5887bf91c5..2398a8b54fbbe 100644
--- a/code/datums/components/crafting/robot.dm
+++ b/code/datums/components/crafting/robot.dm
@@ -119,7 +119,7 @@
/datum/crafting_recipe/vibebot
name = "Vibebot"
- result = /mob/living/simple_animal/bot/vibebot
+ result = /mob/living/basic/bot/vibebot
reqs = list(
/obj/item/light/bulb = 2,
/obj/item/bodypart/head/robot = 1,
diff --git a/code/datums/components/trapdoor.dm b/code/datums/components/trapdoor.dm
index f76dcf35e705a..32b72c48853e5 100644
--- a/code/datums/components/trapdoor.dm
+++ b/code/datums/components/trapdoor.dm
@@ -349,8 +349,8 @@
. = ..()
AddElement(/datum/element/openspace_item_click_handler)
-/obj/item/trapdoor_kit/handle_openspace_click(turf/target, mob/user, click_parameters)
- interact_with_atom(target, user, click_parameters)
+/obj/item/trapdoor_kit/handle_openspace_click(turf/target, mob/user, list/modifiers)
+ interact_with_atom(target, user, modifiers)
/obj/item/trapdoor_kit/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
var/turf/target_turf = get_turf(interacting_with)
diff --git a/code/datums/elements/openspace_item_click_handler.dm b/code/datums/elements/openspace_item_click_handler.dm
index f34bd0f0bda1f..9059223fb0c35 100644
--- a/code/datums/elements/openspace_item_click_handler.dm
+++ b/code/datums/elements/openspace_item_click_handler.dm
@@ -8,22 +8,19 @@
. = ..()
if(!isitem(target))
return ELEMENT_INCOMPATIBLE
- RegisterSignal(target, COMSIG_ITEM_INTERACTING_WITH_ATOM, PROC_REF(divert_interaction))
+ RegisterSignal(target, COMSIG_RANGED_ITEM_INTERACTING_WITH_ATOM, PROC_REF(divert_interaction))
/datum/element/openspace_item_click_handler/Detach(datum/source)
- UnregisterSignal(source, COMSIG_ITEM_INTERACTING_WITH_ATOM)
+ UnregisterSignal(source, COMSIG_RANGED_ITEM_INTERACTING_WITH_ATOM)
return ..()
//Invokes the proctype with a turf above as target.
-/datum/element/openspace_item_click_handler/proc/divert_interaction(obj/item/source, mob/user, atom/target, click_parameters)
+/datum/element/openspace_item_click_handler/proc/divert_interaction(obj/item/source, mob/user, atom/target, list/modifiers)
SIGNAL_HANDLER
if((target.z == 0) || (user.z == 0) || target.z == user.z)
return NONE
- var/turf/checked_turf = get_turf(target)
- while(!isnull(checked_turf))
- checked_turf = GET_TURF_ABOVE(checked_turf)
- if(checked_turf?.z == user.z && user.CanReach(checked_turf, source))
- INVOKE_ASYNC(source, TYPE_PROC_REF(/obj/item, handle_openspace_click), checked_turf, user, click_parameters)
- return ITEM_INTERACT_BLOCKING
-
+ var/turf/target_turf = parse_caught_click_modifiers(modifiers, get_turf(user.client?.eye || user), user.client)
+ if(target_turf?.z == user.z && user.CanReach(target_turf, source))
+ INVOKE_ASYNC(source, TYPE_PROC_REF(/obj/item, handle_openspace_click), target_turf, user, modifiers)
+ return ITEM_INTERACT_BLOCKING
return NONE
diff --git a/code/datums/id_trim/outfits.dm b/code/datums/id_trim/outfits.dm
index 2e7aebff26a1b..a2944a469f43e 100644
--- a/code/datums/id_trim/outfits.dm
+++ b/code/datums/id_trim/outfits.dm
@@ -62,6 +62,7 @@
trim_state = "trim_bitavatar"
department_color = COLOR_BLACK
subdepartment_color = COLOR_GREEN
+ sechud_icon_state = SECHUD_BITAVATAR
/// Trim for cyber police in the Virtual Domain.
/datum/id_trim/cyber_police
diff --git a/code/datums/station_traits/neutral_traits.dm b/code/datums/station_traits/neutral_traits.dm
index e066079802d7a..0ecb49f96a063 100644
--- a/code/datums/station_traits/neutral_traits.dm
+++ b/code/datums/station_traits/neutral_traits.dm
@@ -197,6 +197,7 @@
if(length(birthday_options))
birthday_person = pick(birthday_options)
birthday_person_name = birthday_person.real_name
+ ADD_TRAIT(birthday_person, TRAIT_BIRTHDAY_BOY, REF(src))
addtimer(CALLBACK(src, PROC_REF(announce_birthday)), 10 SECONDS)
/datum/station_trait/birthday/proc/check_valid_override()
diff --git a/code/game/objects/effects/anomalies/_anomalies.dm b/code/game/objects/effects/anomalies/_anomalies.dm
index ee02cab9e036b..ce9bab6a511cc 100644
--- a/code/game/objects/effects/anomalies/_anomalies.dm
+++ b/code/game/objects/effects/anomalies/_anomalies.dm
@@ -113,12 +113,13 @@
qdel(src)
-/obj/effect/anomaly/attackby(obj/item/weapon, mob/user, params)
- if(weapon.tool_behaviour == TOOL_ANALYZER && anomaly_core)
+/obj/effect/anomaly/analyzer_act(mob/living/user, obj/item/analyzer/tool)
+ if(!isnull(anomaly_core))
to_chat(user, span_notice("Analyzing... [src]'s unstable field is fluctuating along frequency [format_frequency(anomaly_core.frequency)], code [anomaly_core.code]."))
- return TRUE
+ return ITEM_INTERACT_SUCCESS
+ to_chat(user, span_notice("Analyzing... [src]'s unstable field is not fluctuating along a stable frequency."))
+ return ITEM_INTERACT_BLOCKING
- return ..()
///Stabilize an anomaly, letting it stay around forever or untill destabilizes by a player. An anomaly without a core can't be signalled, but can be destabilized
/obj/effect/anomaly/proc/stabilize(anchor = FALSE, has_core = TRUE)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 79dd9d7e6ed05..6a63488d4cb8e 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -1426,7 +1426,7 @@
mob_loc.update_clothing(slot_flags)
/// Called on [/datum/element/openspace_item_click_handler/proc/on_afterattack]. Check the relative file for information.
-/obj/item/proc/handle_openspace_click(turf/target, mob/user, click_parameters)
+/obj/item/proc/handle_openspace_click(turf/target, mob/user, list/modifiers)
stack_trace("Undefined handle_openspace_click() behaviour. Ascertain the openspace_item_click_handler element has been attached to the right item and that its proc override doesn't call parent.")
/**
diff --git a/code/game/objects/items/holosign_creator.dm b/code/game/objects/items/holosign_creator.dm
index ff4d0260c4f35..926131151e92b 100644
--- a/code/game/objects/items/holosign_creator.dm
+++ b/code/game/objects/items/holosign_creator.dm
@@ -26,8 +26,8 @@
AddElement(/datum/element/openspace_item_click_handler)
RegisterSignal(src, COMSIG_OBJ_PAINTED, TYPE_PROC_REF(/obj/item/holosign_creator, on_color_change))
-/obj/item/holosign_creator/handle_openspace_click(turf/target, mob/user, click_parameters)
- interact_with_atom(target, user, click_parameters)
+/obj/item/holosign_creator/handle_openspace_click(turf/target, mob/user, list/modifiers)
+ interact_with_atom(target, user, modifiers)
/obj/item/holosign_creator/examine(mob/user)
. = ..()
diff --git a/code/game/objects/items/rcd/RCD.dm b/code/game/objects/items/rcd/RCD.dm
index 2a7611235310c..cf254e447818d 100644
--- a/code/game/objects/items/rcd/RCD.dm
+++ b/code/game/objects/items/rcd/RCD.dm
@@ -70,6 +70,7 @@
construction_mode = mode
GLOB.rcd_list += src
+ AddElement(/datum/element/openspace_item_click_handler)
/obj/item/construction/rcd/Destroy()
QDEL_NULL(airlock_electronics)
@@ -420,6 +421,9 @@
rcd_create(interacting_with, user)
return ITEM_INTERACT_SUCCESS
+/obj/item/construction/rcd/handle_openspace_click(turf/target, mob/user, list/modifiers)
+ interact_with_atom(target, user, modifiers)
+
/obj/item/construction/rcd/proc/detonate_pulse()
audible_message("[src] begins to vibrate and \
buzz loudly!","[src] begins \
diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm
index 6011e0b396159..82f19d09d9692 100644
--- a/code/game/objects/items/stacks/rods.dm
+++ b/code/game/objects/items/stacks/rods.dm
@@ -63,8 +63,8 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
slapcraft_recipes = slapcraft_recipe_list,\
)
-/obj/item/stack/rods/handle_openspace_click(turf/target, mob/user, click_parameters)
- target.attackby(src, user, click_parameters)
+/obj/item/stack/rods/handle_openspace_click(turf/target, mob/user, list/modifiers)
+ target.attackby(src, user, list2params(modifiers))
/obj/item/stack/rods/get_main_recipes()
. = ..()
diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm
index 700116ac1e18f..073a35ed1013c 100644
--- a/code/game/objects/items/stacks/tiles/tile_types.dm
+++ b/code/game/objects/items/stacks/tiles/tile_types.dm
@@ -101,8 +101,8 @@
playsound(target_plating, 'sound/weapons/genhit.ogg', 50, TRUE)
return target_plating
-/obj/item/stack/tile/handle_openspace_click(turf/target, mob/user, click_parameters)
- target.attackby(src, user, click_parameters)
+/obj/item/stack/tile/handle_openspace_click(turf/target, mob/user, list/modifiers)
+ target.attackby(src, user, list2params(modifiers))
//Grass
/obj/item/stack/tile/grass
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index b4d35efdda12a..bdbfa79001ddf 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -415,6 +415,16 @@
if(tank_assembly)
tank_assembly.attack_hand()
+/obj/item/tank/attack_self(mob/user, modifiers)
+ if (tank_assembly)
+ tank_assembly.attack_self(user)
+ return TRUE
+ return ..()
+
+/obj/item/tank/attack_self_secondary(mob/user, modifiers)
+ . = ..()
+ ui_interact(user)
+
/obj/item/tank/Move()
. = ..()
if(tank_assembly)
diff --git a/code/modules/antagonists/space_dragon/carp_rift.dm b/code/modules/antagonists/space_dragon/carp_rift.dm
index 4b8a20acba8d1..828ee94587fcb 100644
--- a/code/modules/antagonists/space_dragon/carp_rift.dm
+++ b/code/modules/antagonists/space_dragon/carp_rift.dm
@@ -19,7 +19,7 @@
return
var/area/rift_location = get_area(owner)
if(!(rift_location in dragon.chosen_rift_areas))
- owner.balloon_alert(owner, "can't summon a rift here!")
+ owner.balloon_alert(owner, "can't summon a rift here! check your objectives!")
return
for(var/obj/structure/carp_rift/rift as anything in dragon.rift_list)
var/area/used_location = get_area(rift)
diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm
index 1d8936e6068da..5c7f5208254f0 100644
--- a/code/modules/assembly/mousetrap.dm
+++ b/code/modules/assembly/mousetrap.dm
@@ -153,7 +153,7 @@
* * user: The mob handling the trap
*/
/obj/item/assembly/mousetrap/proc/clumsy_check(mob/living/carbon/human/user)
- if(!armed)
+ if(!armed || !user)
return FALSE
if((HAS_TRAIT(user, TRAIT_DUMB) || HAS_TRAIT(user, TRAIT_CLUMSY)) && prob(50))
var/which_hand = BODY_ZONE_PRECISE_L_HAND
diff --git a/code/modules/bitrunning/components/avatar_connection.dm b/code/modules/bitrunning/components/avatar_connection.dm
index b533e2b5661d6..a92e8ef3d2e6e 100644
--- a/code/modules/bitrunning/components/avatar_connection.dm
+++ b/code/modules/bitrunning/components/avatar_connection.dm
@@ -60,9 +60,16 @@
var/datum/action/avatar_domain_info/action = new(help_datum)
action.Grant(avatar)
+ var/client/our_client = old_body.client
+ var/alias = our_client?.prefs?.read_preference(/datum/preference/name/hacker_alias) || pick(GLOB.hacker_aliases)
+
+ if(alias && avatar.real_name != alias)
+ avatar.fully_replace_character_name(avatar.real_name, alias)
+
avatar.playsound_local(avatar, 'sound/magic/blink.ogg', 25, TRUE)
avatar.set_static_vision(2 SECONDS)
- avatar.set_temp_blindness(1 SECONDS)
+ avatar.set_temp_blindness(1 SECONDS) // I'm in
+
/datum/component/avatar_connection/PostTransfer()
var/obj/machinery/netpod/pod = netpod_ref?.resolve()
@@ -74,6 +81,7 @@
pod.avatar_ref = WEAKREF(parent)
+
/datum/component/avatar_connection/RegisterWithParent()
ADD_TRAIT(parent, TRAIT_TEMPORARY_BODY, REF(src))
/**
@@ -87,6 +95,7 @@
RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(on_sever_connection))
RegisterSignal(parent, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(on_linked_damage))
+
/datum/component/avatar_connection/UnregisterFromParent()
REMOVE_TRAIT(parent, TRAIT_TEMPORARY_BODY, REF(src))
UnregisterSignal(parent, list(
@@ -98,6 +107,7 @@
COMSIG_MOB_APPLY_DAMAGE,
))
+
/// Disconnects the avatar and returns the mind to the old_body.
/datum/component/avatar_connection/proc/full_avatar_disconnect(cause_damage = FALSE, datum/source)
#ifndef UNIT_TESTS
@@ -115,6 +125,7 @@
qdel(src)
+
/// Triggers whenever the server gets a loot crate pushed to goal area
/datum/component/avatar_connection/proc/on_domain_completed(datum/source, atom/entered)
SIGNAL_HANDLER
@@ -127,6 +138,7 @@
new_master = entered,
)
+
/// Transfers damage from the avatar to the old_body
/datum/component/avatar_connection/proc/on_linked_damage(datum/source, damage, damage_type, def_zone, blocked, ...)
SIGNAL_HANDLER
@@ -147,6 +159,7 @@
if(old_body.stat > SOFT_CRIT) // KO!
full_avatar_disconnect(cause_damage = TRUE)
+
/// Handles minds being swapped around in subsequent avatars
/datum/component/avatar_connection/proc/on_mind_transfer(datum/mind/source, mob/living/previous_body)
SIGNAL_HANDLER
@@ -157,6 +170,7 @@
source.current.TakeComponent(src)
+
/// Triggers when someone starts prying open our netpod
/datum/component/avatar_connection/proc/on_netpod_crowbar(datum/source, mob/living/intruder)
SIGNAL_HANDLER
@@ -171,6 +185,7 @@
alert.name = "Netpod Breached"
alert.desc = "Someone is prying open the netpod. Find an exit."
+
/// Triggers when the netpod is taking damage and is under 50%
/datum/component/avatar_connection/proc/on_netpod_damaged(datum/source)
SIGNAL_HANDLER
@@ -184,24 +199,28 @@
alert.name = "Integrity Compromised"
alert.desc = "The netpod is damaged. Find an exit."
+
//if your bitrunning avatar somehow manages to acquire and consume a red pill, they will be ejected from the Matrix
/datum/component/avatar_connection/proc/disconnect_if_red_pill(datum/source, obj/item/reagent_containers/pill/pill, mob/feeder)
SIGNAL_HANDLER
if(pill.icon_state == "pill4")
full_avatar_disconnect()
+
/// Triggers when a safe disconnect is called
/datum/component/avatar_connection/proc/on_safe_disconnect(datum/source)
SIGNAL_HANDLER
full_avatar_disconnect()
+
/// Received message to sever connection
/datum/component/avatar_connection/proc/on_sever_connection(datum/source)
SIGNAL_HANDLER
full_avatar_disconnect(cause_damage = TRUE, source = source)
+
/// Triggers when the server is shutting down
/datum/component/avatar_connection/proc/on_shutting_down(datum/source, mob/living/hackerman)
SIGNAL_HANDLER
@@ -216,6 +235,7 @@
alert.name = "Domain Rebooting"
alert.desc = "The domain is rebooting. Find an exit."
+
/// Triggers whenever an antag steps onto an exit turf and the server is emagged
/datum/component/avatar_connection/proc/on_station_spawn(datum/source)
SIGNAL_HANDLER
@@ -230,6 +250,7 @@
alert.name = "Security Breach"
alert.desc = "A hostile entity is breaching the safehouse. Find an exit."
+
/// Server has spawned a ghost role threat
/datum/component/avatar_connection/proc/on_threat_created(datum/source)
SIGNAL_HANDLER
@@ -243,6 +264,7 @@
alert.name = "Threat Detected"
alert.desc = "Data stream abnormalities present."
+
/// Returns the mind to the old body
/datum/component/avatar_connection/proc/return_to_old_body()
var/datum/mind/old_mind = old_mind_ref?.resolve()
diff --git a/code/modules/bitrunning/server/obj_generation.dm b/code/modules/bitrunning/server/obj_generation.dm
index e8dbd72228c1d..34a870a3426d5 100644
--- a/code/modules/bitrunning/server/obj_generation.dm
+++ b/code/modules/bitrunning/server/obj_generation.dm
@@ -76,9 +76,6 @@
var/obj/item/card/id/outfit_id = avatar.wear_id
if(outfit_id)
- outfit_id.assignment = "Bit Avatar"
- outfit_id.registered_name = avatar.real_name
-
outfit_id.registered_account = new()
outfit_id.registered_account.replaceable = FALSE
diff --git a/code/modules/client/preferences/names.dm b/code/modules/client/preferences/names.dm
index 9afc8da18c1aa..8456eb9a62709 100644
--- a/code/modules/client/preferences/names.dm
+++ b/code/modules/client/preferences/names.dm
@@ -174,3 +174,15 @@
return TRUE
return FALSE
+
+
+/// The name to use while bitrunning
+/datum/preference/name/hacker_alias
+ explanation = "Hacker alias"
+ group = "bitrunning"
+ savefile_key = "hacker_alias"
+ allow_numbers = TRUE
+ relevant_job = /datum/job/bitrunner
+
+/datum/preference/name/hacker_alias/create_default_value()
+ return pick(GLOB.hacker_aliases)
diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm
index 807e795568821..ef90fcf97e10a 100644
--- a/code/modules/clothing/masks/gasmask.dm
+++ b/code/modules/clothing/masks/gasmask.dm
@@ -221,6 +221,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
visor_flags_cover = MASKCOVERSEYES
visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT
resistance_flags = FIRE_PROOF
+ clothing_flags = parent_type::clothing_flags | INTERNALS_ADJUST_EXEMPT
/datum/armor/gas_welding
melee = 10
diff --git a/code/modules/mob/living/basic/bots/vibebot/vibebot.dm b/code/modules/mob/living/basic/bots/vibebot/vibebot.dm
new file mode 100644
index 0000000000000..c872e1ea5959c
--- /dev/null
+++ b/code/modules/mob/living/basic/bots/vibebot/vibebot.dm
@@ -0,0 +1,30 @@
+/mob/living/basic/bot/vibebot
+ name = "\improper Vibebot"
+ desc = "A little robot. It's just vibing, doing its thing."
+ icon = 'icons/mob/silicon/aibots.dmi'
+ icon_state = "vibebot1"
+ base_icon_state = "vibebot"
+ pass_flags = PASSMOB | PASSFLAPS
+ light_system = OVERLAY_LIGHT
+ light_range = 6
+ ai_controller = /datum/ai_controller/basic_controller/bot/vibebot
+ light_power = 2
+
+ hackables = "vibing scanners"
+ radio_key = /obj/item/encryptionkey/headset_service
+ radio_channel = RADIO_CHANNEL_SERVICE
+ bot_type = VIBE_BOT
+ data_hud_type = DATA_HUD_DIAGNOSTIC_BASIC
+ path_image_color = "#2cac12"
+ possessed_message = "You are a vibebot! Maintain the station's vibes to the best of your ability!"
+
+/mob/living/basic/bot/vibebot/Initialize(mapload)
+ . = ..()
+ var/static/list/innate_actions = list(
+ /datum/action/cooldown/mob_cooldown/bot/vibe = BB_VIBEBOT_PARTY_ABILITY,
+ )
+
+ grant_actions_by_list(innate_actions)
+ var/obj/item/instrument/piano_synth/piano = new(src)
+ ai_controller.set_blackboard_key(BB_SONG_INSTRUMENT, piano)
+ update_appearance(UPDATE_ICON)
diff --git a/code/modules/mob/living/basic/bots/vibebot/vibebot_abilities.dm b/code/modules/mob/living/basic/bots/vibebot/vibebot_abilities.dm
new file mode 100644
index 0000000000000..b3fcec9813b0b
--- /dev/null
+++ b/code/modules/mob/living/basic/bots/vibebot/vibebot_abilities.dm
@@ -0,0 +1,59 @@
+/**
+ * Vibebot's vibe ability
+ *
+ * Given to vibebots so sentient ones can change/reset thier colors at will.
+ */
+#define VIBE_MOOD_TIMER 30 SECONDS
+/datum/action/cooldown/mob_cooldown/bot/vibe
+ name = "Vibe"
+ desc = "Use on yourself to remove color!"
+ click_to_activate = TRUE
+ button_icon = 'icons/mob/actions/actions_minor_antag.dmi'
+ button_icon_state = "funk"
+ ///cooldown to apply a new mood
+ COOLDOWN_DECLARE(change_mood)
+
+/datum/action/cooldown/mob_cooldown/bot/vibe/Grant(mob/granted_to)
+ . = ..()
+ if(isnull(granted_to))
+ return
+ RegisterSignal(granted_to, COMSIG_BOT_RESET, PROC_REF(remove_colors))
+
+/datum/action/cooldown/mob_cooldown/bot/vibe/Activate(atom/target)
+ if(target == owner)
+ remove_colors()
+ return TRUE
+ vibe()
+ StartCooldown()
+ return TRUE
+
+///Gives a random color
+/datum/action/cooldown/mob_cooldown/bot/vibe/proc/vibe()
+ var/mob/living/basic/bot/bot_owner = owner
+ var/final_color = (bot_owner.bot_access_flags & BOT_COVER_EMAGGED) ? COLOR_GRAY : "#[random_color()]"
+ owner.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY)
+ owner.add_atom_colour(final_color, TEMPORARY_COLOUR_PRIORITY)
+ owner.set_light_color(owner.color)
+ if(!COOLDOWN_FINISHED(src, change_mood))
+ return
+ var/mood_to_add = bot_owner.bot_access_flags & BOT_COVER_EMAGGED ? /datum/mood_event/depressing_party : /datum/mood_event/festive_party
+ for(var/mob/living/carbon/human/human_target in oview(1, owner))
+ human_target.add_mood_event("vibebot_party", mood_to_add)
+ COOLDOWN_START(src, change_mood, VIBE_MOOD_TIMER)
+
+///Removes all colors
+/datum/action/cooldown/mob_cooldown/bot/vibe/proc/remove_colors()
+ owner.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY)
+ owner.set_light_color(null)
+
+/datum/mood_event/depressing_party
+ description = "That was a really grim party..."
+ mood_change = -1
+ timeout = 30 SECONDS
+
+/datum/mood_event/festive_party
+ description = "That was a really fantastic party!"
+ mood_change = 2
+ timeout = 30 SECONDS
+
+#undef VIBE_MOOD_TIMER
diff --git a/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm b/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm
new file mode 100644
index 0000000000000..945b09274d783
--- /dev/null
+++ b/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm
@@ -0,0 +1,88 @@
+/datum/ai_controller/basic_controller/bot/vibebot
+ blackboard = list(
+ BB_UNREACHABLE_LIST_COOLDOWN = 2 MINUTES,
+ BB_VIBEBOT_HAPPY_SONG = VIBEBOT_CHEER_SONG,
+ BB_VIBEBOT_GRIM_SONG = VIBEBOT_GRIM_MUSIC,
+ BB_VIBEBOT_BIRTHDAY_SONG = VIBEBOT_HAPPY_BIRTHDAY,
+ )
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/respond_to_summon,
+ /datum/ai_planning_subtree/manage_unreachable_list,
+ /datum/ai_planning_subtree/find_party_friends,
+ /datum/ai_planning_subtree/find_patrol_beacon,
+ )
+ reset_keys = list(
+ BB_BEACON_TARGET,
+ BB_PREVIOUS_BEACON_TARGET,
+ BB_VIBEBOT_PARTY_TARGET,
+ BB_BOT_SUMMON_TARGET,
+ )
+ ai_traits = PAUSE_DURING_DO_AFTER
+
+/datum/ai_controller/basic_controller/bot/vibebot/TryPossessPawn(atom/new_pawn)
+ . = ..()
+ if(. & AI_CONTROLLER_INCOMPATIBLE)
+ return
+ RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_SET(BB_VIBEBOT_PARTY_TARGET), PROC_REF(play_music))
+
+/datum/ai_controller/basic_controller/bot/vibebot/proc/play_music(datum/source, blackboard_key)
+ SIGNAL_HANDLER
+
+ var/mob/living/basic/bot/living_bot = pawn
+ var/obj/item/instrument/instrument = blackboard[BB_SONG_INSTRUMENT]
+ if(isnull(instrument))
+ return
+ var/atom/target = blackboard[blackboard_key]
+ var/datum/song/song = instrument.song
+ song.stop_playing()
+ var/song_lines
+ if(living_bot.bot_access_flags & BOT_COVER_EMAGGED)
+ song_lines = blackboard[BB_VIBEBOT_GRIM_SONG]
+ else
+ song_lines = HAS_TRAIT(target, TRAIT_BIRTHDAY_BOY) ? blackboard[BB_VIBEBOT_BIRTHDAY_SONG] : blackboard[BB_VIBEBOT_HAPPY_SONG]
+ if(isnull(song_lines))
+ return
+ song.ParseSong(new_song = song_lines)
+ song.start_playing(pawn)
+ addtimer(CALLBACK(song, TYPE_PROC_REF(/datum/song, stop_playing)), 10 SECONDS) //in 10 seconds, stop playing music
+
+///subtree we use to find party friends in general
+/datum/ai_planning_subtree/find_party_friends
+
+/datum/ai_planning_subtree/find_party_friends/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ var/static/list/type_to_search = typecacheof(list(/mob/living/carbon/human))
+ if(!controller.blackboard_key_exists(BB_VIBEBOT_PARTY_TARGET))
+ controller.queue_behavior(/datum/ai_behavior/bot_search/party_friends, BB_VIBEBOT_PARTY_TARGET, type_to_search)
+ return
+
+ controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_clear_target/vibebot_party, BB_VIBEBOT_PARTY_ABILITY, BB_VIBEBOT_PARTY_TARGET)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+///behavior we use to party with people
+/datum/ai_behavior/targeted_mob_ability/and_clear_target/vibebot_party
+ behavior_flags = AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_REQUIRE_MOVEMENT
+
+/datum/ai_behavior/targeted_mob_ability/and_clear_target/vibebot_party/setup(datum/ai_controller/controller, ability_key, target_key)
+ . = ..()
+ var/atom/target = controller.blackboard[target_key]
+ if(QDELETED(target))
+ return FALSE
+ set_movement_target(controller, target)
+
+/datum/ai_behavior/targeted_mob_ability/and_clear_target/vibebot_party/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key)
+ var/atom/target = controller.blackboard[target_key]
+ controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE)
+ if(succeeded)
+ var/mob/living/living_pawn = controller.pawn
+ living_pawn.manual_emote("celebrates with [target]!")
+ living_pawn.emote("flip")
+ return ..()
+
+///behavior that searches for party friends
+/datum/ai_behavior/bot_search/party_friends
+ action_cooldown = 5 SECONDS
+
+/datum/ai_behavior/bot_search/party_friends/valid_target(datum/ai_controller/basic_controller/bot/controller, mob/living/carbon/human/my_target)
+ if(my_target.stat != CONSCIOUS || isnull(my_target.mind))
+ return FALSE
+ return (my_target.mob_mood.mood_level < MOOD_LEVEL_NEUTRAL || HAS_TRAIT(my_target, TRAIT_BIRTHDAY_BOY))
diff --git a/code/modules/mob/living/basic/slime/life.dm b/code/modules/mob/living/basic/slime/life.dm
index 68cd33ce7750b..d101b48fea7a1 100644
--- a/code/modules/mob/living/basic/slime/life.dm
+++ b/code/modules/mob/living/basic/slime/life.dm
@@ -36,10 +36,10 @@
///Handles the consumption of nutrition, and growth
/mob/living/basic/slime/proc/handle_nutrition(seconds_per_tick = SSMOBS_DT)
if(hunger_disabled) //God as my witness, I will never go hungry again
- set_nutrition(700)
+ set_nutrition(100)
return
- if(SPT_PROB(7.5, seconds_per_tick))
+ if(SPT_PROB(1.25, seconds_per_tick))
adjust_nutrition((life_stage == SLIME_LIFE_STAGE_ADULT ? -1 : -0.5) * seconds_per_tick)
if(nutrition < SLIME_STARVE_NUTRITION)
@@ -63,7 +63,7 @@
if (SLIME_GROW_NUTRITION <= nutrition)
if(amount_grown < SLIME_EVOLUTION_THRESHOLD)
- adjust_nutrition(-10 * seconds_per_tick)
+ adjust_nutrition(-2.5 * seconds_per_tick)
amount_grown++
if(powerlevel < SLIME_MAX_POWER && SPT_PROB(30-powerlevel*2, seconds_per_tick))
diff --git a/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm b/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
index 2286f65b79758..1776e69358139 100644
--- a/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
+++ b/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
@@ -4,7 +4,7 @@
#define DOAFTER_SOURCE_SPACE_DRAGON_INTERACTION "space dragon interaction"
/**
- * Advanced stage of the space carp life cycle, spawned as a midround antagonist or via traitor transformation.
+ * Advanced stage of the space carp life cycle, spawned as a midround antagonist
* Can eat corpses to heal, blow people back with its wings, and obviously as a dragon it breathes fire. It can even tear through walls.
* The midround even version also creates rifts which summon carp, and heals when near them.
*/
@@ -47,6 +47,9 @@
death_message = "screeches in agony as it collapses to the floor, its life extinguished."
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30)
can_buckle_to = FALSE
+ lighting_cutoff_red = 12
+ lighting_cutoff_green = 15
+ lighting_cutoff_blue = 34
/// The colour of the space dragon
var/chosen_colour
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 19e4b7a43deb6..b1efa734321c5 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -292,8 +292,8 @@
return toggle_open_internals(tank, is_external)
// Use mask in absence of tube.
if(isclothing(wear_mask) && ((wear_mask.visor_flags & MASKINTERNALS) || (wear_mask.clothing_flags & MASKINTERNALS)))
- // Adjust dishevelled breathing mask back onto face.
- if (wear_mask.up)
+ // Adjust dishevelled breathing mask back onto face unless it is exempt.
+ if ((wear_mask.up) && !(wear_mask.clothing_flags & INTERNALS_ADJUST_EXEMPT))
wear_mask.adjust_visor(src)
return toggle_open_internals(tank, is_external)
// Use helmet in absence of tube or valid mask.
diff --git a/code/modules/mob/living/simple_animal/bot/vibebot.dm b/code/modules/mob/living/simple_animal/bot/vibebot.dm
deleted file mode 100644
index 582b1b5371da5..0000000000000
--- a/code/modules/mob/living/simple_animal/bot/vibebot.dm
+++ /dev/null
@@ -1,97 +0,0 @@
-/mob/living/simple_animal/bot/vibebot
- name = "\improper Vibebot"
- desc = "A little robot. It's just vibing, doing its thing."
- icon = 'icons/mob/silicon/aibots.dmi'
- icon_state = "vibebot1"
- base_icon_state = "vibebot"
- density = FALSE
- anchored = FALSE
- health = 25
- maxHealth = 25
- pass_flags = PASSMOB | PASSFLAPS
- light_system = OVERLAY_LIGHT
- light_range = 6
- light_power = 2
-
- hackables = "vibing scanners"
- radio_key = /obj/item/encryptionkey/headset_service
- radio_channel = RADIO_CHANNEL_SERVICE
- bot_type = VIBE_BOT
- data_hud_type = DATA_HUD_DIAGNOSTIC_BASIC
- path_image_color = "#2cac12"
- possessed_message = "You are a vibebot! Maintain the station's vibes to the best of your ability!"
-
- ///The vibe ability given to vibebots, so sentient ones can still change their color.
- var/datum/action/innate/vibe/vibe_ability
-
-/mob/living/simple_animal/bot/vibebot/Initialize(mapload)
- . = ..()
- vibe_ability = new(src)
- vibe_ability.Grant(src)
- update_appearance(UPDATE_ICON)
-
-/mob/living/simple_animal/bot/vibebot/Destroy()
- QDEL_NULL(vibe_ability)
- return ..()
-
-/mob/living/simple_animal/bot/vibebot/handle_automated_action()
- . = ..()
- if(!.)
- return
-
- if(bot_mode_flags & BOT_MODE_ON)
- vibe_ability.Trigger()
-
- if(!(bot_mode_flags & BOT_MODE_AUTOPATROL))
- return
-
- switch(mode)
- if(BOT_IDLE, BOT_START_PATROL)
- start_patrol()
- if(BOT_PATROL)
- bot_patrol()
-
-/mob/living/simple_animal/bot/vibebot/turn_off()
- vibe_ability.remove_colors()
- return ..()
-
-/**
- * Vibebot's vibe ability
- *
- * Given to vibebots so sentient ones can change/reset thier colors at will.
- */
-/datum/action/innate/vibe
- name = "Vibe"
- desc = "LMB: Change vibe color. RMB: Reset vibe color."
- button_icon = 'icons/mob/actions/actions_minor_antag.dmi'
- button_icon_state = "funk"
-
-/datum/action/innate/vibe/IsAvailable(feedback = FALSE)
- . = ..()
- if(!.)
- return FALSE
- if(isbot(owner))
- var/mob/living/simple_animal/bot/bot_mob = owner
- if(!(bot_mob.bot_mode_flags & BOT_MODE_ON))
- return FALSE
- return TRUE
-
-/datum/action/innate/vibe/Trigger(trigger_flags)
- . = ..()
- if(!.)
- return
- if(trigger_flags & TRIGGER_SECONDARY_ACTION)
- remove_colors()
- else
- vibe()
-
-///Gives a random color
-/datum/action/innate/vibe/proc/vibe()
- owner.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY)
- owner.add_atom_colour("#[random_color()]", TEMPORARY_COLOUR_PRIORITY)
- owner.set_light_color(owner.color)
-
-///Removes all colors
-/datum/action/innate/vibe/proc/remove_colors()
- owner.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY)
- owner.set_light_color(null)
diff --git a/code/modules/research/techweb/_techweb.dm b/code/modules/research/techweb/_techweb.dm
index b4b137d8e2187..3c920f6b9a6fe 100644
--- a/code/modules/research/techweb/_techweb.dm
+++ b/code/modules/research/techweb/_techweb.dm
@@ -108,7 +108,7 @@
/datum/techweb/proc/add_point_list(list/pointlist)
for(var/i in pointlist)
if((i in SSresearch.point_types) && pointlist[i] > 0)
- research_points[i] += pointlist[i]
+ research_points[i] = FLOOR(research_points[i] + pointlist[i], 0.1)
/datum/techweb/proc/add_points_all(amount)
var/list/l = SSresearch.point_types.Copy()
@@ -119,7 +119,7 @@
/datum/techweb/proc/remove_point_list(list/pointlist)
for(var/i in pointlist)
if((i in SSresearch.point_types) && pointlist[i] > 0)
- research_points[i] = max(0, research_points[i] - pointlist[i])
+ research_points[i] = FLOOR(max(0, research_points[i] - pointlist[i]), 0.1)
/datum/techweb/proc/remove_points_all(amount)
var/list/l = SSresearch.point_types.Copy()
@@ -130,7 +130,7 @@
/datum/techweb/proc/modify_point_list(list/pointlist)
for(var/i in pointlist)
if((i in SSresearch.point_types) && pointlist[i] != 0)
- research_points[i] = max(0, research_points[i] + pointlist[i])
+ research_points[i] = FLOOR(max(0, research_points[i] + pointlist[i]), 0.1)
/datum/techweb/proc/modify_points_all(amount)
var/list/l = SSresearch.point_types.Copy()
diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm
index ea4ef0e9b7ed1..5dbe284026d59 100644
--- a/code/modules/research/xenobiology/xenobio_camera.dm
+++ b/code/modules/research/xenobiology/xenobio_camera.dm
@@ -426,13 +426,20 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
if(!isopenturf(target_turf))
return
+ var/cleanup = FALSE
var/mob/camera/ai_eye/remote/xenobio/remote_eye = user.remote_control
var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin
if(!xeno_console.validate_area(user, remote_eye, target_turf))
return
- xeno_console.feed_slime(user, target_turf)
+ for(var/mob/monkey in target_turf)
+ if(ismonkey(monkey) && monkey.stat == DEAD)
+ cleanup = TRUE
+ xeno_console.monkey_recycle(user, monkey)
+
+ if(!cleanup)
+ xeno_console.feed_slime(user, target_turf)
///Picks up a dead monkey for recycling
/obj/machinery/computer/camera_advanced/xenobio/proc/XenoMonkeyClickCtrl(mob/living/user, mob/living/carbon/human/target_mob)
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 5685b12b9ef62..66f42c1e039ba 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -76,6 +76,9 @@
///Current lipstick trait, if any (such as TRAIT_KISS_OF_DEATH)
var/stored_lipstick_trait
+ /// How many teeth the head's species has, humans have 32 so that's the default. Used for a limit to dental pill implants.
+ var/teeth_count = 32
+
/// Offset to apply to equipment worn on the ears
var/datum/worn_feature_offset/worn_ears_offset
/// Offset to apply to equipment worn on the eyes
diff --git a/code/modules/surgery/bodyparts/species_parts/ethereal_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/ethereal_bodyparts.dm
index 20e4b58660795..05645ed20df2e 100644
--- a/code/modules/surgery/bodyparts/species_parts/ethereal_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/ethereal_bodyparts.dm
@@ -103,3 +103,4 @@
icon_state = "lustrous_head"
limb_id = SPECIES_ETHEREAL_LUSTROUS
head_flags = NONE
+ teeth_count = 0 // bro you seen these thinsg. they got a crystal for a head aint no teeth here
diff --git a/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm
index 157e5b04fe68e..350e2f32883fb 100644
--- a/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm
@@ -3,6 +3,8 @@
limb_id = SPECIES_LIZARD
is_dimorphic = FALSE
head_flags = HEAD_LIPS|HEAD_EYESPRITES|HEAD_EYECOLOR|HEAD_EYEHOLES|HEAD_DEBRAIN
+ // lizardshave many teeth
+ teeth_count = 72
/obj/item/bodypart/chest/lizard
icon_greyscale = 'icons/mob/human/species/lizard/bodyparts.dmi'
diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
index fa3ab9cc49d39..f9a71a4e6d4dd 100644
--- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
@@ -5,6 +5,7 @@
burn_modifier = 2
head_flags = HEAD_EYESPRITES|HEAD_DEBRAIN
biological_state = (BIO_FLESH|BIO_BLOODED)
+ teeth_count = 0
/obj/item/bodypart/chest/snail
limb_id = SPECIES_SNAIL
@@ -51,6 +52,7 @@
is_dimorphic = FALSE
should_draw_greyscale = FALSE
head_flags = NONE
+ teeth_count = 0
/obj/item/bodypart/chest/abductor
limb_id = SPECIES_ABDUCTOR
@@ -146,6 +148,7 @@
///LUMINESCENT
/obj/item/bodypart/head/jelly/luminescent
limb_id = SPECIES_LUMINESCENT
+ teeth_count = 0
/obj/item/bodypart/chest/jelly/luminescent
limb_id = SPECIES_LUMINESCENT
@@ -250,6 +253,7 @@
is_dimorphic = FALSE
should_draw_greyscale = FALSE
head_flags = HEAD_EYESPRITES|HEAD_EYEHOLES|HEAD_DEBRAIN
+ teeth_count = 0
/obj/item/bodypart/chest/fly
limb_id = SPECIES_FLYPERSON
@@ -367,6 +371,7 @@
is_dimorphic = TRUE
burn_modifier = 1.25
head_flags = NONE
+ teeth_count = 0
/obj/item/bodypart/chest/mushroom
limb_id = SPECIES_MUSHROOM
@@ -436,6 +441,8 @@
should_draw_greyscale = FALSE
dmg_overlay_type = null
head_flags = NONE
+ // too hard to drill through
+ teeth_count = 0
/obj/item/bodypart/head/golem/Initialize(mapload)
worn_ears_offset = new(
diff --git a/code/modules/surgery/bodyparts/species_parts/moth_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/moth_bodyparts.dm
index 375b37ca434d6..323cef05b8c5d 100644
--- a/code/modules/surgery/bodyparts/species_parts/moth_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/moth_bodyparts.dm
@@ -6,6 +6,7 @@
is_dimorphic = FALSE
should_draw_greyscale = FALSE
head_flags = HEAD_LIPS|HEAD_EYESPRITES|HEAD_EYEHOLES|HEAD_DEBRAIN //what the fuck, moths have lips?
+ teeth_count = 0
/obj/item/bodypart/chest/moth
icon = 'icons/mob/human/species/moth/bodyparts.dmi'
diff --git a/code/modules/surgery/dental_implant.dm b/code/modules/surgery/dental_implant.dm
index d720039d56b7e..3c645f240d484 100644
--- a/code/modules/surgery/dental_implant.dm
+++ b/code/modules/surgery/dental_implant.dm
@@ -2,16 +2,35 @@
name = "Dental implant"
possible_locs = list(BODY_ZONE_PRECISE_MOUTH)
steps = list(
- /datum/surgery_step/drill,
+ /datum/surgery_step/drill/pill,
/datum/surgery_step/insert_pill,
)
+/datum/surgery_step/drill/pill/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ . = ..()
+ var/count = 0
+ var/obj/item/bodypart/head/teeth_receptangle = target.get_bodypart(BODY_ZONE_HEAD)
+
+ ASSERT(teeth_receptangle)
+
+ for(var/obj/item/reagent_containers/pill/dental in teeth_receptangle)
+ count++
+
+ if(teeth_receptangle.teeth_count == 0)
+ to_chat(user, span_notice("[user] has no teeth, doofus!"))
+ return SURGERY_STEP_FAIL
+
+ if(count >= teeth_receptangle.teeth_count)
+ to_chat(user, span_notice("[user]'s teeth have all been replaced with pills already!"))
+ return SURGERY_STEP_FAIL
+
/datum/surgery_step/insert_pill
name = "insert pill"
implements = list(/obj/item/reagent_containers/pill = 100)
time = 16
/datum/surgery_step/insert_pill/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+
display_results(
user,
target,
@@ -25,7 +44,8 @@
if(!istype(tool))
return FALSE
- user.transferItemToLoc(tool, target, TRUE)
+ // Pills go into head
+ user.transferItemToLoc(tool, target.get_bodypart(BODY_ZONE_HEAD), TRUE)
var/datum/action/item_action/activate_pill/pill_action = new(tool)
pill_action.name = "Activate [tool.name]"
diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm
index 7db63d6b48df6..706fe290cccf4 100644
--- a/code/modules/unit_tests/simple_animal_freeze.dm
+++ b/code/modules/unit_tests/simple_animal_freeze.dm
@@ -21,7 +21,6 @@
/mob/living/simple_animal/bot/secbot/grievous,
/mob/living/simple_animal/bot/secbot/grievous/toy,
/mob/living/simple_animal/bot/secbot/pingsky,
- /mob/living/simple_animal/bot/vibebot,
/mob/living/simple_animal/hostile,
/mob/living/simple_animal/hostile/asteroid,
/mob/living/simple_animal/hostile/asteroid/curseblob,
diff --git a/code/modules/vehicles/mecha/combat/durand.dm b/code/modules/vehicles/mecha/combat/durand.dm
index b44478582c031..c6fcae75bc98f 100644
--- a/code/modules/vehicles/mecha/combat/durand.dm
+++ b/code/modules/vehicles/mecha/combat/durand.dm
@@ -271,7 +271,9 @@ own integrity back to max. Shield is automatically dropped if we run out of powe
return
. = ..()
flick("shield_impact", src)
- if(!chassis.use_energy((max_integrity - atom_integrity) * 0.1 * STANDARD_CELL_CHARGE))
+ if(!.)
+ return
+ if(!chassis.use_energy(. * (STANDARD_CELL_CHARGE / 15)))
chassis.cell?.charge = 0
for(var/O in chassis.occupants)
var/mob/living/occupant = O
diff --git a/html/changelogs/AutoChangeLog-pr-83810.yml b/html/changelogs/AutoChangeLog-pr-83810.yml
new file mode 100644
index 0000000000000..7ad0ea2812cd5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-83810.yml
@@ -0,0 +1,4 @@
+author: "NewyearnewmeUwu"
+delete-after: True
+changes:
+ - qol: " The xenobio console's monkey placing command also clears dead monkeys on the tile."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83811.yml b/html/changelogs/AutoChangeLog-pr-83811.yml
new file mode 100644
index 0000000000000..2aaf9471f4b48
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-83811.yml
@@ -0,0 +1,4 @@
+author: "mc-oofert"
+delete-after: True
+changes:
+ - qol: "space dragon can see in the dark and the invalid rift location alert is more informative"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83831.yml b/html/changelogs/AutoChangeLog-pr-83831.yml
new file mode 100644
index 0000000000000..b3e655a55ddf2
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-83831.yml
@@ -0,0 +1,7 @@
+author: "carlarctg"
+delete-after: True
+changes:
+ - bugfix: "There's now a limit to how many dental implants you can cram into your mouth, which is governed by your species' teeth limit."
+ - refactor: "Most species have 32 teeth, due to being based on humans, weirding me out when thinking about their teeth, or lack of enough information"
+ - refactor: "Moths and flypeople have NO teeth. They CAN'T get dental implants. I'm NERFING moths."
+ - refactor: "Lizards have seventy-five (!!!) teeth. Lizards are weird."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84043.yml b/html/changelogs/AutoChangeLog-pr-84043.yml
new file mode 100644
index 0000000000000..5397404f75b8d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84043.yml
@@ -0,0 +1,5 @@
+author: "mc-oofert"
+delete-after: True
+changes:
+ - bugfix: "durand shield doesnt immediately depower the mech when taking stamina damage"
+ - balance: "durand shield is a bit stronger"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84072.yml b/html/changelogs/AutoChangeLog-pr-84072.yml
new file mode 100644
index 0000000000000..c36813ba598d8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84072.yml
@@ -0,0 +1,4 @@
+author: "GPeckman"
+delete-after: True
+changes:
+ - bugfix: "Analyzers should work on bioscrambler anomalies again."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84075.yml b/html/changelogs/AutoChangeLog-pr-84075.yml
new file mode 100644
index 0000000000000..427cc4f1ae6d0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84075.yml
@@ -0,0 +1,4 @@
+author: "GoblinBackwards"
+delete-after: True
+changes:
+ - bugfix: "Interacting with an assembly bomb in hand will now allow you to interact with the attached assembly instead of opening the gas tank UI. Right-clicking will display the gas tank UI instead."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84135.yml b/html/changelogs/AutoChangeLog-pr-84135.yml
new file mode 100644
index 0000000000000..b503e29e311d5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84135.yml
@@ -0,0 +1,4 @@
+author: "Guestify"
+delete-after: True
+changes:
+ - bugfix: "The visor of the welding mask no longer goes down when you enable internals"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84143.yml b/html/changelogs/AutoChangeLog-pr-84143.yml
new file mode 100644
index 0000000000000..68b36d831d4f6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84143.yml
@@ -0,0 +1,5 @@
+author: "Ben10Omintrix"
+delete-after: True
+changes:
+ - refactor: "vibebots are not basic bots"
+ - rscadd: "vibebots will now seek out the depressed and cheer them up"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84255.yml b/html/changelogs/AutoChangeLog-pr-84255.yml
new file mode 100644
index 0000000000000..95d0dd0b54f27
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84255.yml
@@ -0,0 +1,4 @@
+author: "FlufflesTheDog"
+delete-after: True
+changes:
+ - bugfix: "multi-z hole repair works better, especially when the turf below is blocked by items"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84278.yml b/html/changelogs/AutoChangeLog-pr-84278.yml
new file mode 100644
index 0000000000000..e29caa1b8963f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84278.yml
@@ -0,0 +1,4 @@
+author: "hyperjll"
+delete-after: True
+changes:
+ - balance: "Thanks to incredible strides in selective slime breeding, slimes require substantially less nutrients to grow into adults, and split into children."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84279.yml b/html/changelogs/AutoChangeLog-pr-84279.yml
new file mode 100644
index 0000000000000..9fcd48259c7bb
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84279.yml
@@ -0,0 +1,5 @@
+author: "jlsnow301"
+delete-after: True
+changes:
+ - rscadd: "Bitrunning: You can now choose your hacker alias in prefs."
+ - rscadd: "Bit avatars get orbit icons."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84282.yml b/html/changelogs/AutoChangeLog-pr-84282.yml
new file mode 100644
index 0000000000000..fe26b6cb2c010
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84282.yml
@@ -0,0 +1,4 @@
+author: "SmArtKar"
+delete-after: True
+changes:
+ - bugfix: "RND console now properly rounds research points"
\ No newline at end of file
diff --git a/icons/mob/huds/hud.dmi b/icons/mob/huds/hud.dmi
index a3bbdf2ede075..086e886bab7b2 100644
Binary files a/icons/mob/huds/hud.dmi and b/icons/mob/huds/hud.dmi differ
diff --git a/strings/names/hackers.txt b/strings/names/hackers.txt
new file mode 100644
index 0000000000000..e2722fb09fd21
--- /dev/null
+++ b/strings/names/hackers.txt
@@ -0,0 +1,46 @@
+Ne0Phyte
+CipherX
+Gh0stWire
+L33tByt3
+Vortex9
+Syn4pse
+DarkMatt3r
+QuantumDr1ft
+Crash0verr1de
+Z3r0C00l
+NyxShad0w
+Gl1tchR3aper
+CryptoWra1th
+Raz0rEdge
+VoidWalk3r
+Neur0mancer
+EchoH3xx
+DataSt0rm
+CyberS1ren
+Rogu3Protocol
+Nexu5Prim3
+CircuitBr3aker
+EnigmaPulse
+PhantomBit
+FluxC4pac1tor
+OmegaC0dec
+Ne0nRon1n
+Senpa1Sw1pe
+MechaM0chii
+SakuraSn1per
+WaifuWarri0r
+KatanaKid99
+xXDarkL0rd69Xx
+H4xx0rNinja
+CyberNinja1337
+Ub3r1337Sk1llz
+PhrEaK420
+IRCWizard69
+EliteWar3zKing
+Y2KPwn3r
+BBSPh34r
+BlackWill0w69
+2038Pr0blem
+Sp4ceW1nd
+B0bbyT4bles
+C0deInjector
diff --git a/tgstation.dme b/tgstation.dme
index 722dd189bf7e4..c1b726fcc4cff 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -4679,6 +4679,9 @@
#include "code\modules\mob\living\basic\bots\hygienebot\hygienebot_ai.dm"
#include "code\modules\mob\living\basic\bots\medbot\medbot.dm"
#include "code\modules\mob\living\basic\bots\medbot\medbot_ai.dm"
+#include "code\modules\mob\living\basic\bots\vibebot\vibebot.dm"
+#include "code\modules\mob\living\basic\bots\vibebot\vibebot_abilities.dm"
+#include "code\modules\mob\living\basic\bots\vibebot\vibebot_ai.dm"
#include "code\modules\mob\living\basic\clown\clown.dm"
#include "code\modules\mob\living\basic\clown\clown_ai.dm"
#include "code\modules\mob\living\basic\cult\shade.dm"
@@ -5129,7 +5132,6 @@
#include "code\modules\mob\living\simple_animal\bot\mulebot.dm"
#include "code\modules\mob\living\simple_animal\bot\secbot.dm"
#include "code\modules\mob\living\simple_animal\bot\SuperBeepsky.dm"
-#include "code\modules\mob\living\simple_animal\bot\vibebot.dm"
#include "code\modules\mob\living\simple_animal\hostile\dark_wizard.dm"
#include "code\modules\mob\living\simple_animal\hostile\hostile.dm"
#include "code\modules\mob\living\simple_animal\hostile\illusion.dm"
diff --git a/tgui/packages/tgui/interfaces/Orbit/OrbitContent.tsx b/tgui/packages/tgui/interfaces/Orbit/OrbitContent.tsx
index 91502644faade..24aab8eba74bc 100644
--- a/tgui/packages/tgui/interfaces/Orbit/OrbitContent.tsx
+++ b/tgui/packages/tgui/interfaces/Orbit/OrbitContent.tsx
@@ -15,7 +15,7 @@ type ContentSection = {
/**
* The primary content display for points of interest.
- * Renders a scrollable section replete collapsibles for each
+ * Renders a scrollable section replete with collapsibles for each
* observable group.
*/
export function OrbitContent(props) {
diff --git a/tools/UpdatePaths/Scripts/84143_vibebots.txt b/tools/UpdatePaths/Scripts/84143_vibebots.txt
new file mode 100644
index 0000000000000..e5474e517e350
--- /dev/null
+++ b/tools/UpdatePaths/Scripts/84143_vibebots.txt
@@ -0,0 +1 @@
+/mob/living/simple_animal/bot/secbot/vibebot/@SUBTYPES : /mob/living/basic/bot/vibebot/@SUBTYPES{@OLD}
\ No newline at end of file