diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index 63453f1774a3..82cec3c519f6 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -1540,6 +1540,11 @@
/obj/machinery/door/firedoor,
/turf/open/floor/iron/white/textured,
/area/station/science/xenobiology)
+"ayU" = (
+/obj/effect/turf_decal/tile/neutral/fourcorners,
+/obj/item/radio/intercom/directional/south,
+/turf/open/floor/iron/dark,
+/area/station/security/detectives_office)
"azm" = (
/obj/effect/turf_decal/trimline/dark_blue/filled/line{
dir = 1
@@ -16286,7 +16291,7 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/structure/cable,
/obj/machinery/door/airlock/maintenance{
- name = "Detective Office Maintenance"
+ name = "Detective's Office Maintenance"
},
/obj/effect/mapping_helpers/airlock/access/any/security/detective,
/turf/open/floor/plating,
@@ -19516,7 +19521,7 @@
/obj/machinery/door/airlock/maintenance{
name = "Law Office Maintenance"
},
-/obj/effect/mapping_helpers/airlock/access/any/security/detective,
+/obj/effect/mapping_helpers/airlock/access/any/service/lawyer,
/turf/open/floor/plating,
/area/station/service/lawoffice)
"gtA" = (
@@ -41633,6 +41638,7 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/structure/cable,
+/obj/machinery/door/firedoor,
/obj/machinery/door/airlock/security{
name = "Private Interrogation"
},
@@ -50031,6 +50037,10 @@
/obj/effect/turf_decal/trimline/red/filled/line,
/turf/open/floor/iron/dark,
/area/station/security/execution/transfer)
+"qoA" = (
+/obj/machinery/firealarm/directional/north,
+/turf/open/floor/wood,
+/area/station/service/lawoffice)
"qoB" = (
/obj/structure/sign/poster/random/directional/south,
/obj/effect/spawner/random/structure/crate,
@@ -51265,6 +51275,11 @@
/obj/effect/landmark/start/quartermaster,
/turf/open/floor/wood,
/area/station/command/heads_quarters/qm)
+"qJG" = (
+/obj/effect/turf_decal/tile/neutral/fourcorners,
+/obj/machinery/firealarm/directional/north,
+/turf/open/floor/iron/dark,
+/area/station/security/detectives_office)
"qKk" = (
/obj/effect/turf_decal/stripes/line,
/obj/effect/turf_decal/stripes/line{
@@ -53549,10 +53564,11 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/structure/cable,
+/obj/machinery/door/firedoor,
/obj/machinery/door/airlock/service{
name = "Law Office Quarters"
},
-/obj/effect/mapping_helpers/airlock/access/any/security/detective,
+/obj/effect/mapping_helpers/airlock/access/any/service/lawyer,
/turf/open/floor/iron/dark/textured,
/area/station/service/lawoffice)
"rzs" = (
@@ -59628,6 +59644,10 @@
"txI" = (
/turf/closed/wall/r_wall,
/area/station/maintenance/disposal/incinerator)
+"txJ" = (
+/obj/machinery/firealarm/directional/south,
+/turf/open/floor/wood,
+/area/station/security/detectives_office)
"txT" = (
/turf/open/floor/wood/large,
/area/station/command/heads_quarters/nt_rep)
@@ -71790,6 +71810,10 @@
/obj/machinery/vending/access/command,
/turf/open/floor/wood,
/area/station/command/meeting_room)
+"xrT" = (
+/obj/item/radio/intercom/directional/south,
+/turf/open/floor/wood,
+/area/station/service/lawoffice)
"xrX" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
@@ -73146,6 +73170,10 @@
/obj/machinery/photocopier,
/turf/open/floor/iron,
/area/station/command/heads_quarters/ce)
+"xOB" = (
+/obj/machinery/firealarm/directional/south,
+/turf/open/floor/wood,
+/area/station/service/lawoffice)
"xOH" = (
/turf/closed/wall/r_wall,
/area/station/maintenance/department/engineering/central)
@@ -100455,7 +100483,7 @@ mUw
mUw
xhv
mUw
-mUw
+xOB
dIW
mUw
iua
@@ -100971,9 +100999,9 @@ mUw
bpe
dor
dIW
-mUw
+qoA
pVB
-mUw
+xrT
fPN
wfE
dwJ
@@ -101999,9 +102027,9 @@ mHH
tbg
tFs
hjG
-hPT
+qJG
rKe
-hPT
+ayU
fPN
fhe
dwJ
@@ -102511,7 +102539,7 @@ tbg
tbg
qid
tbg
-tbg
+txJ
hjG
hPT
jUt
diff --git a/code/__DEFINES/economy.dm b/code/__DEFINES/economy.dm
index 32c88cee80cc..694d60dc1162 100644
--- a/code/__DEFINES/economy.dm
+++ b/code/__DEFINES/economy.dm
@@ -62,6 +62,7 @@
#define CIV_JOB_GROW 12
#define CIV_JOB_ATMOS 13
#define CIV_JOB_RANDOM 14
+#define CIV_JOB_SCI_HEAD 15
//By how much should the station's inflation value be multiplied by when dividing the civilian bounty's reward?
#define BOUNTY_MULTIPLIER 10
diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm
index fbf8c92621cd..f0e61c8282c0 100644
--- a/code/__DEFINES/jobs.dm
+++ b/code/__DEFINES/jobs.dm
@@ -192,7 +192,7 @@
#define DEPARTMENT_CENTRAL_COMMAND "Central Command"
#define DEPARTMENT_BITFLAG_LATE (1<<12)
-#define DEPARTMENT_LATE "Late Join"
+#define DEPARTMENT_LATE "Late Arrival"
/* Job datum job_flags */
/// Whether the mob is announced on arrival.
diff --git a/code/__DEFINES/~monkestation/admin.dm b/code/__DEFINES/~monkestation/admin.dm
index 9edb809cbaba..64ddc4de5ccd 100644
--- a/code/__DEFINES/~monkestation/admin.dm
+++ b/code/__DEFINES/~monkestation/admin.dm
@@ -16,3 +16,5 @@
#define AHELP_CLOSEREASON_NONE 0
#define AHELP_CLOSEREASON_IC 1
#define AHELP_CLOSEREASON_MENTOR 2
+
+#define ADMIN_SUSINFO(user) "[ADMIN_LOOKUP(user)] [ADMIN_PP(user)] [ADMIN_INDIVIDUALLOG(user)] [ADMIN_SMITE(user)]"
diff --git a/code/__HELPERS/~monkestation-helpers/clients.dm b/code/__HELPERS/~monkestation-helpers/clients.dm
index 6f824905e3da..3ee482320d98 100644
--- a/code/__HELPERS/~monkestation-helpers/clients.dm
+++ b/code/__HELPERS/~monkestation-helpers/clients.dm
@@ -20,3 +20,29 @@
RETURN_TYPE(/datum/admins)
var/client/client = CLIENT_FROM_VAR(doohickey)
return client?.holder
+
+/proc/should_be_interviewing(mob/target)
+ . = FALSE
+ if(QDELETED(target))
+ return
+ . = target.client?.interviewee
+ var/ckey = target.ckey
+ if(ckey)
+ if(ckey in GLOB.interviews.approved_ckeys)
+ return FALSE
+ var/datum/interview/interview = GLOB.interviews.open_interviews[ckey]
+ if(interview && interview.status != INTERVIEW_APPROVED)
+ return TRUE
+ if(ckey in GLOB.interviews.cooldown_ckeys)
+ return TRUE
+
+/proc/interview_safety(mob/target, context)
+ . = should_be_interviewing(target)
+ if(.)
+ message_admins(span_danger("WARNING: [ADMIN_SUSINFO(target)] has seemingly bypassed an interview! (context: [context]) note: this detection is still wip, tell absolucy if it's causing false positives"))
+ log_admin_private("[key_name(target)] has seemingly bypassed an interview! (context: [context])")
+ if(isnewplayer(target))
+ var/mob/dead/new_player/dingbat = target
+ if(dingbat.ready == PLAYER_READY_TO_PLAY)
+ dingbat.ready = PLAYER_NOT_READY
+ qdel(dingbat.client)
diff --git a/code/_onclick/hud/new_player.dm b/code/_onclick/hud/new_player.dm
index e2ead5de016a..758886a8b788 100644
--- a/code/_onclick/hud/new_player.dm
+++ b/code/_onclick/hud/new_player.dm
@@ -169,6 +169,7 @@
if(!new_client.readied_store)
new_client.readied_store = new(new_player)
new_client.readied_store.ui_interact(new_player)
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(interview_safety), new_player, "readied up"), 1 SECONDS, TIMER_UNIQUE)
else
new_player.ready = PLAYER_NOT_READY
base_icon_state = "not_ready"
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index c2db691f6e98..8d46ec714c30 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -399,6 +399,10 @@ SUBSYSTEM_DEF(ticker)
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/player = i
if(player.ready == PLAYER_READY_TO_PLAY && player.mind)
+ if(interview_safety(player, "readied up"))
+ player.ready = PLAYER_NOT_READY
+ QDEL_IN(player.client, 0)
+ continue
GLOB.joined_player_list += player.ckey
var/chosen_title = player.client?.prefs.alt_job_titles[player.mind.assigned_role.title] || player.mind.assigned_role.title
var/atom/destination = player.mind.assigned_role.get_roundstart_spawn_point(chosen_title)
diff --git a/code/datums/components/life_link.dm b/code/datums/components/life_link.dm
index 34cdd0504c6b..3e38bdfb9c54 100644
--- a/code/datums/components/life_link.dm
+++ b/code/datums/components/life_link.dm
@@ -129,16 +129,14 @@
if(isnull(holder))
return
holder.icon_state = "hud[RoundHealth(host)]"
- var/icon/size_check = icon(mob_parent.icon, mob_parent.icon_state, mob_parent.dir)
- holder.pixel_y = size_check.Height() - world.icon_size
+ holder.pixel_y = mob_parent.get_cached_height() - world.icon_size
/// Update our vital status on the medical hud
/datum/component/life_link/proc/update_med_hud_status(mob/living/mob_parent)
var/image/holder = mob_parent.hud_list?[STATUS_HUD]
if(isnull(holder))
return
- var/icon/size_check = icon(mob_parent.icon, mob_parent.icon_state, mob_parent.dir)
- holder.pixel_y = size_check.Height() - world.icon_size
+ holder.pixel_y = mob_parent.get_cached_height() - world.icon_size
if(host.stat == DEAD || HAS_TRAIT(host, TRAIT_FAKEDEATH))
holder.icon_state = "huddead"
else
diff --git a/code/datums/components/seethrough_mob.dm b/code/datums/components/seethrough_mob.dm
index bae87faf6158..0baf76866983 100644
--- a/code/datums/components/seethrough_mob.dm
+++ b/code/datums/components/seethrough_mob.dm
@@ -55,9 +55,8 @@
for(var/atom/movable/screen/plane_master/seethrough as anything in our_hud.get_true_plane_masters(SEETHROUGH_PLANE))
seethrough.unhide_plane(fool)
- var/icon/current_mob_icon = icon(fool.icon, fool.icon_state)
render_source_atom.pixel_x = -fool.pixel_x
- render_source_atom.pixel_y = ((current_mob_icon.Height() - 32) * 0.5)
+ render_source_atom.pixel_y = ((fool.get_cached_height() - world.icon_size) * 0.5)
initial_render_target_value = fool.render_target
fool.render_target = "*transparent_bigmob[personal_uid]"
diff --git a/code/datums/status_effects/stacking_effect.dm b/code/datums/status_effects/stacking_effect.dm
index 9a682000eb78..783d9334b215 100644
--- a/code/datums/status_effects/stacking_effect.dm
+++ b/code/datums/status_effects/stacking_effect.dm
@@ -128,8 +128,7 @@
if(underlay_file)
status_underlay = mutable_appearance(underlay_file, "[underlay_state][stacks]")
- var/icon/I = icon(owner.icon, owner.icon_state, owner.dir)
- var/icon_height = I.Height()
+ var/icon_height = owner.get_cached_height()
if(status_overlay)
status_overlay.pixel_x = -owner.pixel_x
diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm
index 48ddf2749558..3f2dde1cc0ab 100644
--- a/code/game/data_huds.dm
+++ b/code/game/data_huds.dm
@@ -194,8 +194,7 @@ Medical HUD! Basic mode needs suit sensors on.
return
holder.icon_state = "hud[RoundHealth(src)]"
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
//for carbon suit sensors
/mob/living/carbon/med_hud_set_health()
@@ -208,8 +207,7 @@ Medical HUD! Basic mode needs suit sensors on.
if (isnull(holder))
return
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH)))
if(sensors)
SET_PLANE_EXPLICIT(sensors, ABOVE_LIGHTING_PLANE, src)
@@ -225,9 +223,8 @@ Medical HUD! Basic mode needs suit sensors on.
if (isnull(holder))
return
- var/icon/I = icon(icon, icon_state, dir)
var/virus_threat = check_virus()
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(HAS_TRAIT(src, TRAIT_XENO_HOST))
holder.icon_state = "hudxeno"
else if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH)))
@@ -263,8 +260,7 @@ FAN HUDs! For identifying other fans on-sight.
/mob/living/carbon/human/proc/fan_hud_set_fandom()
var/image/holder = hud_list[FAN_HUD]
- var/icon/hud_icon = icon(icon, icon_state, dir)
- holder.pixel_y = hud_icon.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
holder.icon_state = "hudfan_no"
var/obj/item/clothing/under/undershirt = w_uniform
@@ -294,8 +290,7 @@ Security HUDs! Basic mode shows only the job.
/mob/living/carbon/human/proc/sec_hud_set_ID()
var/image/holder = hud_list[ID_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
var/sechud_icon_state = wear_id?.get_sechud_job_icon_state()
if(!sechud_icon_state || HAS_TRAIT(src, TRAIT_UNKNOWN))
sechud_icon_state = "hudno_id"
@@ -303,7 +298,7 @@ Security HUDs! Basic mode shows only the job.
sec_hud_set_security_status()
//monkestation edit start
var/image/permit_holder = hud_list[PERMIT_HUD]
- permit_holder.pixel_y = I.Height() - world.icon_size
+ permit_holder.pixel_y = get_cached_height() - world.icon_size
var/permit_icon_state = wear_id?.get_gun_permit_iconstate()
if(!permit_icon_state)
permit_icon_state = "hudfan_no"
@@ -320,29 +315,25 @@ Security HUDs! Basic mode shows only the job.
for(var/obj/item/implant/I in implants)
if(istype(I, /obj/item/implant/tracking))
holder = hud_list[IMPTRACK_HUD]
- var/icon/IC = icon(icon, icon_state, dir)
- holder.pixel_y = IC.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
holder.icon_state = "hud_imp_tracking"
set_hud_image_active(IMPTRACK_HUD)
else if(istype(I, /obj/item/implant/chem))
holder = hud_list[IMPCHEM_HUD]
- var/icon/IC = icon(icon, icon_state, dir)
- holder.pixel_y = IC.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
holder.icon_state = "hud_imp_chem"
set_hud_image_active(IMPCHEM_HUD)
if(HAS_TRAIT(src, TRAIT_MINDSHIELD))
holder = hud_list[IMPLOYAL_HUD]
- var/icon/IC = icon(icon, icon_state, dir)
- holder.pixel_y = IC.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
holder.icon_state = "hud_imp_loyal"
set_hud_image_active(IMPLOYAL_HUD)
/mob/living/carbon/human/proc/sec_hud_set_security_status()
var/image/holder = hud_list[WANTED_HUD]
- var/icon/sec_icon = icon(icon, icon_state, dir)
- holder.pixel_y = sec_icon.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
var/perp_name = get_face_name(get_id_name(""))
if(!perp_name || !GLOB.manifest)
@@ -376,8 +367,7 @@ Diagnostic HUDs!
/mob/living/proc/hud_set_nanite_indicator()
var/image/holder = hud_list[NANITE_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
holder.icon_state = null
if(HAS_TRAIT(src, TRAIT_NANITE_MONITORING))
holder.icon_state = "nanite_ping"
@@ -403,8 +393,7 @@ Diagnostic HUDs!
//Sillycone hooks
/mob/living/silicon/proc/diag_hud_set_health()
var/image/holder = hud_list[DIAG_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(stat == DEAD)
holder.icon_state = "huddiagdead"
else
@@ -412,8 +401,7 @@ Diagnostic HUDs!
/mob/living/silicon/proc/diag_hud_set_status()
var/image/holder = hud_list[DIAG_STAT_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
switch(stat)
if(CONSCIOUS)
holder.icon_state = "hudstat"
@@ -425,8 +413,7 @@ Diagnostic HUDs!
//Borgie battery tracking!
/mob/living/silicon/robot/proc/diag_hud_set_borgcell()
var/image/holder = hud_list[DIAG_BATT_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(cell)
var/chargelvl = (cell.charge/cell.maxcharge)
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
@@ -436,8 +423,7 @@ Diagnostic HUDs!
//borg-AI shell tracking
/mob/living/silicon/robot/proc/diag_hud_set_aishell() //Shows tracking beacons on the mech
var/image/holder = hud_list[DIAG_TRACK_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(!shell) //Not an AI shell
holder.icon_state = null
set_hud_image_inactive(DIAG_TRACK_HUD)
@@ -451,8 +437,7 @@ Diagnostic HUDs!
//AI side tracking of AI shell control
/mob/living/silicon/ai/proc/diag_hud_set_deployed() //Shows tracking beacons on the mech
var/image/holder = hud_list[DIAG_TRACK_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(!deployed_shell)
holder.icon_state = null
set_hud_image_inactive(DIAG_TRACK_HUD)
@@ -465,15 +450,13 @@ Diagnostic HUDs!
~~~~~~~~~~~~~~~~~~~~~*/
/obj/vehicle/sealed/mecha/proc/diag_hud_set_mechhealth()
var/image/holder = hud_list[DIAG_MECH_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
holder.icon_state = "huddiag[RoundDiagBar(atom_integrity/max_integrity)]"
/obj/vehicle/sealed/mecha/proc/diag_hud_set_mechcell()
var/image/holder = hud_list[DIAG_BATT_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(cell)
var/chargelvl = cell.charge/cell.maxcharge
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
@@ -482,8 +465,7 @@ Diagnostic HUDs!
/obj/vehicle/sealed/mecha/proc/diag_hud_set_mechstat()
var/image/holder = hud_list[DIAG_STAT_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(internal_damage)
holder.icon_state = "hudwarn"
set_hud_image_active(DIAG_STAT_HUD)
@@ -494,8 +476,7 @@ Diagnostic HUDs!
///Shows tracking beacons on the mech
/obj/vehicle/sealed/mecha/proc/diag_hud_set_mechtracking()
var/image/holder = hud_list[DIAG_TRACK_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
var/new_icon_state //This var exists so that the holder's icon state is set only once in the event of multiple mech beacons.
for(var/obj/item/mecha_parts/mecha_tracking/T in trackers)
if(T.ai_beacon) //Beacon with AI uplink
@@ -508,8 +489,7 @@ Diagnostic HUDs!
///Shows inbuilt camera on the mech; if the camera's view range was affected by an EMP, shows a red blip while it's affected
/obj/vehicle/sealed/mecha/proc/diag_hud_set_camera()
var/image/holder = hud_list[DIAG_CAMERA_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(chassis_camera.is_emp_scrambled)
holder.icon_state = "hudcamera_empd"
return
@@ -520,14 +500,12 @@ Diagnostic HUDs!
~~~~~~~~~~*/
/mob/living/simple_animal/bot/proc/diag_hud_set_bothealth()
var/image/holder = hud_list[DIAG_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
/mob/living/simple_animal/bot/proc/diag_hud_set_botstat() //On (With wireless on or off), Off, EMP'ed
var/image/holder = hud_list[DIAG_STAT_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(bot_mode_flags & BOT_MODE_ON)
holder.icon_state = "hudstat"
else if(stat) //Generally EMP causes this
@@ -537,8 +515,7 @@ Diagnostic HUDs!
/mob/living/simple_animal/bot/proc/diag_hud_set_botmode() //Shows a bot's current operation
var/image/holder = hud_list[DIAG_BOT_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(client) //If the bot is player controlled, it will not be following mode logic!
holder.icon_state = "hudsentient"
return
@@ -559,8 +536,7 @@ Diagnostic HUDs!
/mob/living/simple_animal/bot/mulebot/proc/diag_hud_set_mulebotcell()
var/image/holder = hud_list[DIAG_BATT_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(cell)
var/chargelvl = (cell.charge/cell.maxcharge)
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
@@ -577,3 +553,19 @@ Diagnostic HUDs!
var/image/holder = hud_list[DIAG_AIRLOCK_HUD]
holder.icon_state = "electrified"
set_hud_image_active(DIAG_AIRLOCK_HUD)
+
+#define CACHED_WIDTH_INDEX "width"
+#define CACHED_HEIGHT_INDEX "height"
+/atom/proc/get_cached_width()
+ if (isnull(icon))
+ return 0
+ var/list/dimensions = get_icon_dimensions(icon)
+ return dimensions[CACHED_WIDTH_INDEX]
+
+/atom/proc/get_cached_height()
+ if (isnull(icon))
+ return 0
+ var/list/dimensions = get_icon_dimensions(icon)
+ return dimensions[CACHED_HEIGHT_INDEX]
+#undef CACHED_WIDTH_INDEX
+#undef CACHED_HEIGHT_INDEX
diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
index 168a3f9b0883..eafdb46f9c32 100644
--- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm
+++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
@@ -447,8 +447,7 @@
var/size_matrix = matrix()
if(size_calc_target)
layer = size_calc_target.layer + 0.01
- var/icon/I = icon(size_calc_target.icon, size_calc_target.icon_state, size_calc_target.dir)
- size_matrix = matrix() * (I.Height()/world.icon_size)
+ size_matrix = matrix() * (size_calc_target.get_cached_height() /world.icon_size)
transform = size_matrix //scale the bleed overlay's size based on the target's icon size
var/matrix/M = transform
if(shrink)
diff --git a/code/modules/admin/callproc/callproc.dm b/code/modules/admin/callproc/callproc.dm
index b43d6b01d49b..3ca4a3120320 100644
--- a/code/modules/admin/callproc/callproc.dm
+++ b/code/modules/admin/callproc/callproc.dm
@@ -219,6 +219,17 @@ GLOBAL_PROTECT(LastAdminCalledProc)
if(target == GLOBAL_PROC)
return call("/proc/[procname]")(arglist(arguments))
else if(target != world)
+ // monkestation start: anti-lag
+ if(isatom(target) && procname == "Shake")
+ var/confirmation = UNLINT(tgui_alert(
+ user = usr,
+ message = "Are you SURE you want to call Shake()?\nThis has a VERY HIGH CHANCE of causing immense lag or even a crash!",
+ title = "OH GOD WHY",
+ buttons = list("I'm sure!", "Nope."),
+ ))
+ if(confirmation != "I'm sure!")
+ return
+ // monkestation end
return call(target, procname)(arglist(arguments))
else
log_admin("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]")
diff --git a/code/modules/antagonists/abductor/equipment/gland.dm b/code/modules/antagonists/abductor/equipment/gland.dm
index 056e72fc1a05..9bdf5646898a 100644
--- a/code/modules/antagonists/abductor/equipment/gland.dm
+++ b/code/modules/antagonists/abductor/equipment/gland.dm
@@ -48,8 +48,7 @@
if(!owner)
return
var/image/holder = owner.hud_list[GLAND_HUD]
- var/icon/I = icon(owner.icon, owner.icon_state, owner.dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = owner.get_cached_height() - world.icon_size
if(active_mind_control)
holder.icon_state = "hudgland_active"
else if(mind_control_uses)
diff --git a/code/modules/antagonists/changeling/powers/transform.dm b/code/modules/antagonists/changeling/powers/transform.dm
index 804dd2bffaca..c2c071a7355d 100644
--- a/code/modules/antagonists/changeling/powers/transform.dm
+++ b/code/modules/antagonists/changeling/powers/transform.dm
@@ -117,8 +117,7 @@
. = ..()
if(hud_icon)
var/image/holder = user.hud_list[ID_HUD]
- var/icon/I = icon(user.icon, user.icon_state, user.dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = user.get_cached_height() - world.icon_size
holder.icon_state = hud_icon
/**
diff --git a/code/modules/antagonists/heretic/knowledge/rust_lore.dm b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
index dbe9e4d77178..af8c26c563a8 100644
--- a/code/modules/antagonists/heretic/knowledge/rust_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
@@ -111,9 +111,8 @@
var/turf/mover_turf = get_turf(source)
if(HAS_TRAIT(mover_turf, TRAIT_RUSTY))
ADD_TRAIT(source, TRAIT_BATON_RESISTANCE, type)
- return
-
- REMOVE_TRAIT(source, TRAIT_BATON_RESISTANCE, type)
+ else
+ REMOVE_TRAIT(source, TRAIT_BATON_RESISTANCE, type)
/**
* Signal proc for [COMSIG_LIVING_LIFE].
@@ -129,16 +128,20 @@
return
// Heals all damage + Stamina
- source.adjustBruteLoss(-2, FALSE)
- source.adjustFireLoss(-2, FALSE)
- source.adjustToxLoss(-2, FALSE, forced = TRUE) // Slimes are people to
- source.adjustOxyLoss(-0.5, FALSE)
- source.stamina.adjust(2)
+ var/delta_time = DELTA_WORLD_TIME(SSmobs)
+ var/needs_update = FALSE // Optimization, if nothing changes then don't update our owner's health.
+ needs_update += source.adjustBruteLoss(-2 * delta_time, updating_health = FALSE)
+ needs_update += source.adjustFireLoss(-2 * delta_time, updating_health = FALSE)
+ needs_update += source.adjustToxLoss(-2 * delta_time, updating_health = FALSE, forced = TRUE) // Slimes are people too
+ needs_update += source.adjustOxyLoss(-0.5 * delta_time, updating_health = FALSE)
+ if(needs_update)
+ source.updatehealth()
+ source.stamina.adjust(2 * delta_time)
// Reduces duration of stuns/etc
- source.AdjustAllImmobility(-0.5 SECONDS)
+ source.AdjustAllImmobility(-0.5 SECONDS * delta_time)
// Heals blood loss
if(source.blood_volume < BLOOD_VOLUME_NORMAL)
- source.blood_volume += 2.5 * seconds_per_tick
+ source.blood_volume += 2.5 * delta_time
/datum/heretic_knowledge/mark/rust_mark
name = "Mark of Rust"
@@ -305,11 +308,15 @@
if(!HAS_TRAIT(our_turf, TRAIT_RUSTY))
return
- source.adjustBruteLoss(-4, FALSE)
- source.adjustFireLoss(-4, FALSE)
- source.adjustToxLoss(-4, FALSE, forced = TRUE)
- source.adjustOxyLoss(-4, FALSE)
- source.stamina.adjust(20)
+ var/delta_time = DELTA_WORLD_TIME(SSmobs)
+ var/needs_update = FALSE
+ needs_update += source.adjustBruteLoss(-4 * delta_time, updating_health = FALSE)
+ needs_update += source.adjustFireLoss(-4 * delta_time, updating_health = FALSE)
+ needs_update += source.adjustToxLoss(-4 * delta_time, updating_health = FALSE, forced = TRUE)
+ needs_update += source.adjustOxyLoss(-4 * delta_time, updating_health = FALSE)
+ if(needs_update)
+ source.updatehealth()
+ source.stamina.adjust(20 * delta_time)
/**
* #Rust spread datum
diff --git a/code/modules/cargo/bounty.dm b/code/modules/cargo/bounty.dm
index 92ccafc40f89..b88a2bc37a36 100644
--- a/code/modules/cargo/bounty.dm
+++ b/code/modules/cargo/bounty.dm
@@ -44,8 +44,11 @@
switch(bounty_num)
if(CIV_JOB_BASIC)
chosen_type = pick(subtypesof(/datum/bounty/item/assistant))
- if(CIV_JOB_ROBO)
- chosen_type = pick(subtypesof(/datum/bounty/item/mech))
+ if(CIV_JOB_ROBO) //monkestation edit: bot bounties
+ if(prob(50))
+ chosen_type = pick(subtypesof(/datum/bounty/item/mech))
+ else
+ chosen_type = pick(subtypesof(/datum/bounty/item/bot))
if(CIV_JOB_CHEF)
chosen_type = pick(subtypesof(/datum/bounty/item/chef) + subtypesof(/datum/bounty/reagent/chef))
if(CIV_JOB_SEC)
@@ -67,6 +70,17 @@
chosen_type = pick(subtypesof(/datum/bounty/item/science))
else
chosen_type = pick(subtypesof(/datum/bounty/item/slime))
+ if(CIV_JOB_SCI_HEAD) //monkestation addition : RD bounties. 50% for science bounty, 50% for robo bounty.
+ if(prob(50))
+ if(prob(50))
+ chosen_type = pick(subtypesof(/datum/bounty/item/science))
+ else
+ chosen_type = pick(subtypesof(/datum/bounty/item/slime))
+ else
+ if(prob(50))
+ chosen_type = pick(subtypesof(/datum/bounty/item/mech))
+ else
+ chosen_type = pick(subtypesof(/datum/bounty/item/bot))
if(CIV_JOB_ENG)
chosen_type = pick(subtypesof(/datum/bounty/item/engineering))
if(CIV_JOB_MINE)
diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm
index 9df9c53233b6..679c04335989 100644
--- a/code/modules/clothing/chameleon.dm
+++ b/code/modules/clothing/chameleon.dm
@@ -576,6 +576,9 @@
acid = 50
// MONKESTATION ADDITION START
+/obj/item/clothing/gloves/chameleon
+ clothing_traits = list(TRAIT_CAN_SIGN_ON_COMMS)
+
/obj/item/clothing/gloves/chameleon/attackby(obj/item/W, mob/user, params)
if(W.tool_behaviour != TOOL_MULTITOOL)
return ..()
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 709bbb594f97..ed75f2d2769f 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -493,6 +493,7 @@
max_integrity = 200
resistance_flags = FLAMMABLE
armor_type = /datum/armor/vest_durathread
+ body_parts_covered = CHEST|ARMS //Monke: Durathread covers arms.
dog_fashion = null
/datum/armor/vest_durathread
diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm
index b8ba0f237606..8e15aa246a4e 100644
--- a/code/modules/jobs/job_types/research_director.dm
+++ b/code/modules/jobs/job_types/research_director.dm
@@ -31,7 +31,7 @@
liver_traits = list(TRAIT_ROYAL_METABOLISM, TRAIT_BALLMER_SCIENTIST)
display_order = JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR
- bounty_types = CIV_JOB_SCI
+ bounty_types = CIV_JOB_SCI_HEAD
mail_goodies = list(
/obj/item/storage/box/monkeycubes = 30,
diff --git a/code/modules/mob/dead/new_player/latejoin_menu.dm b/code/modules/mob/dead/new_player/latejoin_menu.dm
index c6831a2fa638..fe7081d07548 100644
--- a/code/modules/mob/dead/new_player/latejoin_menu.dm
+++ b/code/modules/mob/dead/new_player/latejoin_menu.dm
@@ -117,7 +117,7 @@ GLOBAL_DATUM_INIT(latejoin_menu, /datum/latejoin_menu, new)
/datum/latejoin_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
- if(!ui.user.client || ui.user.client.interviewee || !isnewplayer(ui.user))
+ if(!ui.user.client || should_be_interviewing(ui.user) || !isnewplayer(ui.user))
return TRUE
var/mob/dead/new_player/owner = ui.user
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 6286383fa318..5f0a7ecda1cc 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -67,6 +67,10 @@
ready = PLAYER_NOT_READY
return FALSE
+ if(interview_safety(src, "attempting to observe"))
+ qdel(client)
+ return FALSE
+
var/less_input_message
if(SSlag_switch.measures[DISABLE_DEAD_KEYLOOP])
less_input_message = " - Notice: Observer freelook is currently disabled."
@@ -143,6 +147,10 @@
return JOB_AVAILABLE
/mob/dead/new_player/proc/AttemptLateSpawn(rank)
+ if(interview_safety(src, "attempting to latejoin"))
+ qdel(client)
+ return FALSE
+
var/error = IsJobUnavailable(rank)
if(error != JOB_AVAILABLE)
tgui_alert(usr, get_job_unavailable_error_message(error, rank))
diff --git a/code/modules/mob/dead/observer/login.dm b/code/modules/mob/dead/observer/login.dm
index d5f2ab6b5db0..606f2187094f 100644
--- a/code/modules/mob/dead/observer/login.dm
+++ b/code/modules/mob/dead/observer/login.dm
@@ -1,4 +1,7 @@
/mob/dead/observer/Login()
+ if(interview_safety(src, "observing"))
+ qdel(client)
+ return FALSE
. = ..()
if(!. || !client)
return FALSE
diff --git a/code/modules/mob/living/basic/bots/bot_hud.dm b/code/modules/mob/living/basic/bots/bot_hud.dm
index cbadab01360e..3bd24f9f1485 100644
--- a/code/modules/mob/living/basic/bots/bot_hud.dm
+++ b/code/modules/mob/living/basic/bots/bot_hud.dm
@@ -1,13 +1,11 @@
/mob/living/basic/bot/proc/diag_hud_set_bothealth()
var/image/holder = hud_list[DIAG_HUD]
- var/icon/icon_image = icon(icon, icon_state, dir)
- holder.pixel_y = icon_image.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
/mob/living/basic/bot/proc/diag_hud_set_botstat() //On (With wireless on or off), Off, EMP'ed
var/image/holder = hud_list[DIAG_STAT_HUD]
- var/icon/our_icon = icon(icon, icon_state, dir)
- holder.pixel_y = our_icon.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(bot_mode_flags & BOT_MODE_ON)
holder.icon_state = "hudstat"
return
@@ -18,8 +16,7 @@
/mob/living/basic/bot/proc/diag_hud_set_botmode() //Shows a bot's current operation
var/image/holder = hud_list[DIAG_BOT_HUD]
- var/icon/icon_image = icon(icon, icon_state, dir)
- holder.pixel_y = icon_image.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(client) //If the bot is player controlled, it will not be following mode logic!
holder.icon_state = "hudsentient"
return
diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
index 2cdf99b9d2ed..b52c2d137c2d 100644
--- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
+++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
@@ -110,6 +110,11 @@
/obj/item/trash,
/obj/item/food/deadmouse,
/obj/effect/decal/remains,
+ //monkestation addition: start
+ /obj/item/cigbutt,
+ /obj/item/storage/box/foodpack,
+ /obj/item/ammo_casing,
+ //monkestation addition: end
))
///drawings we hunt
var/static/list/cleanable_drawings = typecacheof(list(/obj/effect/decal/cleanable/crayon))
diff --git a/code/modules/mob/living/basic/drone/_drone.dm b/code/modules/mob/living/basic/drone/_drone.dm
index f42a439f1e15..d9ebbb8b1ca2 100644
--- a/code/modules/mob/living/basic/drone/_drone.dm
+++ b/code/modules/mob/living/basic/drone/_drone.dm
@@ -214,14 +214,12 @@
/mob/living/basic/drone/med_hud_set_health()
var/image/holder = hud_list[DIAG_HUD]
- var/icon/hud_icon = icon(icon, icon_state, dir)
- holder.pixel_y = hud_icon.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
/mob/living/basic/drone/med_hud_set_status()
var/image/holder = hud_list[DIAG_STAT_HUD]
- var/icon/hud_icon = icon(icon, icon_state, dir)
- holder.pixel_y = hud_icon.Height() - world.icon_size
+ holder.pixel_y = get_cached_height() - world.icon_size
if(stat == DEAD)
holder.icon_state = "huddead2"
else if(incapacitated())
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 52f2b9eba7f4..7eddf9ddce70 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -712,9 +712,7 @@
/// Returns what the body_position_pixel_y_offset should be if the current size were `value`
/mob/living/proc/get_pixel_y_offset_standing(value)
- var/icon/living_icon = icon(icon)
- var/height = living_icon.Height()
- return (value-1) * height * 0.5
+ return (value - 1) * get_cached_height() * 0.5
/mob/living/proc/update_density()
if(HAS_TRAIT(src, TRAIT_UNDENSE))
@@ -804,7 +802,7 @@
if(!livingdoll.filtered)
livingdoll.filtered = TRUE
var/icon/mob_mask = icon(icon, icon_state)
- if(mob_mask.Height() > world.icon_size || mob_mask.Width() > world.icon_size)
+ if(get_cached_height() > world.icon_size || get_cached_width() > world.icon_size)
var/health_doll_icon_state = health_doll_icon ? health_doll_icon : "megasprite"
mob_mask = icon('icons/hud/screen_gen.dmi', health_doll_icon_state) //swap to something generic if they have no special doll
livingdoll.add_filter("mob_shape_mask", 1, alpha_mask_filter(icon = mob_mask))
diff --git a/code/modules/mob/living/login.dm b/code/modules/mob/living/login.dm
index c6d6b6a5e9d7..fc64fee3ef00 100644
--- a/code/modules/mob/living/login.dm
+++ b/code/modules/mob/living/login.dm
@@ -3,6 +3,10 @@
if(!. || !client)
return FALSE
+ if(interview_safety(src, "client in living mob"))
+ qdel(client)
+ return FALSE
+
//Mind updates
sync_mind()
diff --git a/html/changelogs/archive/2024-12.yml b/html/changelogs/archive/2024-12.yml
index cf9a8f7a98d2..9b13692490e6 100644
--- a/html/changelogs/archive/2024-12.yml
+++ b/html/changelogs/archive/2024-12.yml
@@ -88,3 +88,44 @@
- rscdel: Removed the old bubble room up top for extra space to expand Centcom.
- qol: Pretty up medical centcom while trimming the wasted space as well as add
extra stuff.
+2024-12-08:
+ Absolucy:
+ - bugfix: The recursive symptom varient should now work as intended rather than
+ cascading horribly.
+ - bugfix: The "Quantumly Entangled" (bluespace) symptom varient should work properly
+ now.
+ - bugfix: Symptoms being deleted properly cleanup their attached varients now.
+ - bugfix: Clown operative leaders now properly spawn with their gear.
+ - bugfix: Anime quirk appearance prefs now properly transfer when becoming a roundstart
+ nuke/clown operative.
+ - bugfix: Clean up the inventory/items of players becoming roundstart nuke/clown
+ operatives more thoroughly and quietly.
+ - refactor: Cleaned up code related to roundstart nuke/clown operatives.
+ - bugfix: Heretics healing on rust via Leeching Walk or rust ascension now properly
+ updates health.
+ - qol: Heretics healing on rust via Leeching Walk or rust ascension scales with
+ the world's delta time, making its rate independent of subsystem lag.
+ - admin: Added a warning+confirmation if an admin try to run a specifically laggy
+ proc.
+ Absolucy, SmArtKar, Kapu:
+ - code_imp: Implemented caching for icon sizes which should significantly improve
+ mob health performance due to HUDs constantly fetching icons.
+ Gw0sty:
+ - rscadd: Gave Chameleon gloves the property to transmit radio for sign language.
+ - rscadd: Research directors now get a mixture of scientist and roboticist bounties.
+ - rscadd: Added craft able cleanbot variation, Scrubs Junior, a cleanbot that medical
+ doctors can access.
+ - rscadd: New bounty types for roboticists, bots, requiring various station bots
+ that do not spawn roundstart.
+ - bugfix: Renamed Late Join to Late Arrival
+ - qol: Cleanbots will now burn cigarbutts, loose ammo casings, and food wrappers
+ SirNightKnight:
+ - bugfix: Fixed Nanotrasen Representative's alt title options excluding the original
+ title once picked.
+ Uristthedorf:
+ - balance: Durathread vests now cover your arms.
+ - image: Worn durathread vest sprites show your arms being covered.
+ Wisemonster:
+ - spellcheck: Clarified NT representative's description as being more an "Observe
+ and report" role, and that they cannot give orders to command (They can still
+ make suggestions).
diff --git a/icons/mob/clothing/suits/armor.dmi b/icons/mob/clothing/suits/armor.dmi
index 4c9c39bed485..dbe7305b75a5 100644
Binary files a/icons/mob/clothing/suits/armor.dmi and b/icons/mob/clothing/suits/armor.dmi differ
diff --git a/icons/mob/landmarks.dmi b/icons/mob/landmarks.dmi
index e156a233e732..44a25f5407ed 100644
Binary files a/icons/mob/landmarks.dmi and b/icons/mob/landmarks.dmi differ
diff --git a/monkestation/code/datums/components/crafting/robot.dm b/monkestation/code/datums/components/crafting/robot.dm
new file mode 100644
index 000000000000..4aac9aa661ed
--- /dev/null
+++ b/monkestation/code/datums/components/crafting/robot.dm
@@ -0,0 +1,14 @@
+/datum/crafting_recipe/cleanbot_jr
+ name = "Scrubs Junior"
+ desc = "A little cleaning robot, he graduated from medical school!"
+ result = /mob/living/basic/bot/cleanbot/medbay/jr
+ reqs = list(
+ /obj/item/reagent_containers/cup/bucket = 1,
+ /obj/item/assembly/prox_sensor = 1,
+ /obj/item/assembly/health = 1,
+ /obj/item/bodypart/arm/right/robot = 1,
+
+ )
+ parts = list(/obj/item/reagent_containers/cup/bucket = 1)
+ time = 5 SECONDS
+ category = CAT_ROBOT
diff --git a/monkestation/code/modules/NTrep/NTrep.dm b/monkestation/code/modules/NTrep/NTrep.dm
index bbf3f5c8e4c4..346b2b6963d2 100644
--- a/monkestation/code/modules/NTrep/NTrep.dm
+++ b/monkestation/code/modules/NTrep/NTrep.dm
@@ -1,6 +1,6 @@
/datum/job/nanotrasen_representative
title = JOB_NANOTRASEN_REPRESENTATIVE
- description = "Ensure company interests and Standard Operating Procedure is upheld onboard the station, and get out as soon as you can when it inevitably falls apart."
+ description = "Ensure company interests and report whether Standard Operating Procedure is upheld onboard the station, and get out as soon as you can when it inevitably falls apart. You do not have the authority to give orders, except to the blueshield."
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
department_head = list("CentCom")
faction = FACTION_STATION
@@ -50,6 +50,7 @@
voice_of_god_power = 1.4 //Command staff has authority
alt_titles = list(
+ "Nanotrasen Representative",
"Corporate Liaison",
"Nanotrasen Fax Operater",
"Nanotrasen Official",
diff --git a/monkestation/code/modules/assault_ops/code/areas.dm b/monkestation/code/modules/assault_ops/code/areas.dm
index df2aacfdd998..f3bf44b5bd56 100644
--- a/monkestation/code/modules/assault_ops/code/areas.dm
+++ b/monkestation/code/modules/assault_ops/code/areas.dm
@@ -56,8 +56,7 @@
/obj/effect/landmark/start/assaultop
name = "assaultop"
- icon = 'icons/effects/landmarks_static.dmi'
- icon_state = "snukeop_spawn"
+ icon_state = "Assault Operatives"
delete_after_roundstart = FALSE
/obj/effect/landmark/start/assaultop/Initialize(mapload)
diff --git a/monkestation/code/modules/blueshield/landmarks.dm b/monkestation/code/modules/blueshield/landmarks.dm
index e812eaee96b8..1153629768f6 100644
--- a/monkestation/code/modules/blueshield/landmarks.dm
+++ b/monkestation/code/modules/blueshield/landmarks.dm
@@ -1,2 +1,3 @@
/obj/effect/landmark/start/blueshield
name = "Blueshield"
+ icon_state = "Blueshield"
diff --git a/monkestation/code/modules/cargo/bounties/bot.dm b/monkestation/code/modules/cargo/bounties/bot.dm
new file mode 100644
index 000000000000..3fa7ce5ff14b
--- /dev/null
+++ b/monkestation/code/modules/cargo/bounties/bot.dm
@@ -0,0 +1,23 @@
+/datum/bounty/item/bot/cleanbot_jr //JR. models to prevent them from mass selling station cleanbots
+ name = "Scrubs Junior., PA"
+ description = "Medical is looking worse than the kitchen cold room and janitors are nowhere to be found. We need a cleanbot for medical before the Chief Medical Officer has a breakdown."
+ reward = CARGO_CRATE_VALUE * 2.5
+ wanted_types = list(/mob/living/basic/bot/cleanbot/medbay/jr = TRUE)
+
+/datum/bounty/item/bot/floorbot
+ name = "Floorbot"
+ description = "Out last floorbot went haywire and removed all our floors. So we need another floorbot to replace the priors issues."
+ reward = CARGO_CRATE_VALUE * 3
+ wanted_types = list(/mob/living/simple_animal/bot/floorbot = TRUE)
+
+/datum/bounty/item/bot/honkbot
+ name = "Honkbot"
+ description = "Mr. Gigglesworth birthday is around the corner and we didn't get a present. Ship us off a honkbot to giftwrap please."
+ reward = CARGO_CRATE_VALUE * 5
+ wanted_types = list(/mob/living/simple_animal/bot/secbot/honkbot = TRUE)
+
+/datum/bounty/item/bot/firebot
+ name = "Firebot"
+ description = "An assistant waving around some license broke into atmospherics and its now all on fire. Send us a Firebot before the gas fire leaks further."
+ reward = CARGO_CRATE_VALUE * 4
+ wanted_types = list(/mob/living/simple_animal/bot/firebot = TRUE)
diff --git a/monkestation/code/modules/datums/components/nanites.dm b/monkestation/code/modules/datums/components/nanites.dm
index 394b9f563612..7e099b131f9d 100644
--- a/monkestation/code/modules/datums/components/nanites.dm
+++ b/monkestation/code/modules/datums/components/nanites.dm
@@ -249,8 +249,7 @@
///Updates the nanite volume bar visible in diagnostic HUDs
/datum/component/nanites/proc/set_nanite_bar(remove = FALSE)
var/image/holder = host_mob.hud_list[DIAG_NANITE_FULL_HUD]
- var/icon/I = icon(host_mob.icon, host_mob.icon_state, host_mob.dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = host_mob.get_cached_height() - world.icon_size
holder.icon_state = null
if(remove || stealth)
return //bye icon
diff --git a/monkestation/code/modules/mob/inventory.dm b/monkestation/code/modules/mob/inventory.dm
new file mode 100644
index 000000000000..fb62f87cd4bf
--- /dev/null
+++ b/monkestation/code/modules/mob/inventory.dm
@@ -0,0 +1,14 @@
+/**
+ * Drops and deletes all items in the mob's inventory.
+ *
+ * Argument(s):
+ * * Optional - include_pockets (TRUE/FALSE), whether or not to also clear items in their pockets and suit storage.
+ * * Optional - include_held_items (TRUE/FALSE), whether or not to also clear any held items.
+ */
+/mob/living/proc/clear_inventory(include_pockets = TRUE, include_held_items = TRUE)
+ var/list/items = get_equipped_items(include_pockets)
+ if(include_held_items)
+ items |= held_items
+ for(var/item in items)
+ dropItemToGround(item, force = TRUE, silent = TRUE, invdrop = FALSE)
+ qdel(item)
diff --git a/monkestation/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm b/monkestation/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
new file mode 100644
index 000000000000..efd15941148d
--- /dev/null
+++ b/monkestation/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
@@ -0,0 +1,2 @@
+/mob/living/basic/bot/cleanbot/medbay/jr
+ name = "Scrubs Junior, PA"
diff --git a/monkestation/code/modules/storytellers/converted_events/_base_event.dm b/monkestation/code/modules/storytellers/converted_events/_base_event.dm
index f8c8d5629cbf..03c7c4095660 100644
--- a/monkestation/code/modules/storytellers/converted_events/_base_event.dm
+++ b/monkestation/code/modules/storytellers/converted_events/_base_event.dm
@@ -337,6 +337,8 @@
old_mob.client.prefs.safe_transfer_prefs_to(new_character)
new_character.dna.update_dna_identity()
old_mob.mind.transfer_to(new_character)
+ if(old_mob.has_quirk(/datum/quirk/anime)) // stupid special case bc this quirk is basically janky wrapper shitcode around some optional appearance prefs
+ new_character.add_quirk(/datum/quirk/anime)
if(qdel_old_mob)
qdel(old_mob)
return new_character
diff --git a/monkestation/code/modules/storytellers/converted_events/solo/clown_operative.dm b/monkestation/code/modules/storytellers/converted_events/solo/clown_operative.dm
index 8575c81fbd8e..34b66a876ec7 100644
--- a/monkestation/code/modules/storytellers/converted_events/solo/clown_operative.dm
+++ b/monkestation/code/modules/storytellers/converted_events/solo/clown_operative.dm
@@ -1,127 +1,15 @@
-/datum/round_event_control/antagonist/solo/clown_operative
+/datum/round_event_control/antagonist/solo/nuclear_operative/clown
name = "Roundstart Clown Operative"
tags = list(TAG_DESTRUCTIVE, TAG_COMBAT, TAG_TEAM_ANTAG, TAG_EXTERNAL)
antag_flag = ROLE_CLOWN_OPERATIVE
antag_datum = /datum/antagonist/nukeop/clownop
- typepath = /datum/round_event/antagonist/solo/clown_operative
- shared_occurence_type = SHARED_HIGH_THREAT
- restricted_roles = list(
- JOB_AI,
- JOB_CAPTAIN,
- JOB_NANOTRASEN_REPRESENTATIVE,
- JOB_BLUESHIELD,
- JOB_CHIEF_ENGINEER,
- JOB_CHIEF_MEDICAL_OFFICER,
- JOB_CYBORG,
- JOB_DETECTIVE,
- JOB_HEAD_OF_PERSONNEL,
- JOB_HEAD_OF_SECURITY,
- JOB_PRISONER,
- JOB_RESEARCH_DIRECTOR,
- JOB_SECURITY_OFFICER,
- JOB_WARDEN,
- JOB_BRIG_PHYSICIAN,
- )
- base_antags = 3
- maximum_antags = 5
- enemy_roles = list(
- JOB_AI,
- JOB_CYBORG,
- JOB_CAPTAIN,
- JOB_BLUESHIELD,
- JOB_DETECTIVE,
- JOB_HEAD_OF_SECURITY,
- JOB_SECURITY_OFFICER,
- JOB_SECURITY_ASSISTANT,
- JOB_WARDEN,
- )
- required_enemies = 5
- // I give up, just there should be enough heads with 35 players...
- min_players = 35
- roundstart = TRUE
- earliest_start = 0 SECONDS
+ typepath = /datum/round_event/antagonist/solo/nuclear_operative/clown
weight = 1 //these are meant to be very rare
max_occurrences = 1
event_icon_state = "flukeops"
-/datum/round_event/antagonist/solo/clown_operative
- excute_round_end_reports = TRUE
- end_when = 60000 /// we will end on our own when revs win
- var/static/datum/team/nuclear/nuke_team
- var/set_leader = FALSE
- var/required_role = ROLE_CLOWN_OPERATIVE
-
-/datum/round_event/antagonist/solo/clown_operative/setup()
- . = ..()
- var/obj/machinery/nuclearbomb/syndicate/syndicate_nuke = locate() in GLOB.nuke_list
- if(syndicate_nuke)
- var/turf/nuke_turf = get_turf(syndicate_nuke)
- if(nuke_turf)
- new /obj/machinery/nuclearbomb/syndicate/bananium(nuke_turf)
- qdel(syndicate_nuke)
-
-/datum/round_event/antagonist/solo/clown_operative/add_datum_to_mind(datum/mind/antag_mind)
- var/mob/living/current_mob = antag_mind.current
- SSjob.FreeRole(antag_mind.assigned_role.title)
- var/list/items = current_mob.get_equipped_items(TRUE)
- current_mob.unequip_everything()
- for(var/obj/item/item as anything in items)
- qdel(item)
-
- create_human_mob_copy(get_turf(current_mob), current_mob)
- antag_mind.set_assigned_role(SSjob.GetJobType(/datum/job/clown_operative))
- antag_mind.special_role = ROLE_CLOWN_OPERATIVE
-
- var/datum/mind/most_experienced = get_most_experienced(setup_minds, required_role)
- if(!most_experienced)
- most_experienced = antag_mind
-
- if(!set_leader)
- set_leader = TRUE
- var/datum/antagonist/nukeop/leader/leader_antag_datum = new()
- var/mob/living/carbon/human/leader_mob = most_experienced.current
- leader_mob = create_human_mob_copy(get_turf(leader_mob), leader_mob)
- nuke_team = leader_antag_datum.nuke_team
- most_experienced.add_antag_datum(leader_antag_datum)
- leader_mob.equip_species_outfit(/datum/outfit/syndicate/clownop/leader)
-
- if(antag_mind == most_experienced)
- return
-
- var/datum/antagonist/nukeop/new_op = new antag_datum()
- antag_mind.add_antag_datum(new_op)
-
-
-/datum/round_event/antagonist/solo/clown_operative/round_end_report()
- var/result = nuke_team.get_result()
- switch(result)
- if(NUKE_RESULT_FLUKE)
- SSticker.mode_result = "loss - syndicate nuked - disk secured"
- SSticker.news_report = NUKE_SYNDICATE_BASE
- if(NUKE_RESULT_NUKE_WIN)
- SSticker.mode_result = "win - syndicate nuke"
- SSticker.news_report = STATION_DESTROYED_NUKE
- if(NUKE_RESULT_NOSURVIVORS)
- SSticker.mode_result = "halfwin - syndicate nuke - did not evacuate in time"
- SSticker.news_report = STATION_DESTROYED_NUKE
- if(NUKE_RESULT_WRONG_STATION)
- SSticker.mode_result = "halfwin - blew wrong station"
- SSticker.news_report = NUKE_MISS
- if(NUKE_RESULT_WRONG_STATION_DEAD)
- SSticker.mode_result = "halfwin - blew wrong station - did not evacuate in time"
- SSticker.news_report = NUKE_MISS
- if(NUKE_RESULT_CREW_WIN_SYNDIES_DEAD)
- SSticker.mode_result = "loss - evacuation - disk secured - syndi team dead"
- SSticker.news_report = OPERATIVES_KILLED
- if(NUKE_RESULT_CREW_WIN)
- SSticker.mode_result = "loss - evacuation - disk secured"
- SSticker.news_report = OPERATIVES_KILLED
- if(NUKE_RESULT_DISK_LOST)
- SSticker.mode_result = "halfwin - evacuation - disk not secured"
- SSticker.news_report = OPERATIVE_SKIRMISH
- if(NUKE_RESULT_DISK_STOLEN)
- SSticker.mode_result = "halfwin - detonation averted"
- SSticker.news_report = OPERATIVE_SKIRMISH
- else
- SSticker.mode_result = "halfwin - interrupted"
- SSticker.news_report = OPERATIVE_SKIRMISH
+/datum/round_event/antagonist/solo/nuclear_operative/clown
+ required_role = ROLE_CLOWN_OPERATIVE
+ job_type = /datum/job/clown_operative
+ antag_type = /datum/antagonist/nukeop/clownop
+ leader_antag_type = /datum/antagonist/nukeop/leader/clownop
diff --git a/monkestation/code/modules/storytellers/converted_events/solo/nuclear_operative.dm b/monkestation/code/modules/storytellers/converted_events/solo/nuclear_operative.dm
index f9885a0fdbbe..1da80e43f98f 100644
--- a/monkestation/code/modules/storytellers/converted_events/solo/nuclear_operative.dm
+++ b/monkestation/code/modules/storytellers/converted_events/solo/nuclear_operative.dm
@@ -46,49 +46,34 @@
/datum/round_event/antagonist/solo/nuclear_operative
excute_round_end_reports = TRUE
- var/static/datum/team/nuclear/nuke_team
- var/set_leader = FALSE
var/required_role = ROLE_NUCLEAR_OPERATIVE
- var/datum/mind/most_experienced
+ var/job_type = /datum/job/nuclear_operative
+ var/antag_type = /datum/antagonist/nukeop
+ var/leader_antag_type = /datum/antagonist/nukeop/leader
+ var/datum/team/nuclear/nuke_team
-/datum/round_event/antagonist/solo/nuclear_operative/add_datum_to_mind(datum/mind/antag_mind)
- if(most_experienced == antag_mind)
- return
+/datum/round_event/antagonist/solo/nuclear_operative/start()
+ var/datum/mind/most_experienced = get_most_experienced(setup_minds, required_role) || setup_minds[1]
+ prepare(most_experienced)
+ var/datum/antagonist/nukeop/leader/leader = most_experienced.add_antag_datum(leader_antag_type)
+ nuke_team = leader.nuke_team
+ for(var/datum/mind/antag_mind as anything in setup_minds - most_experienced)
+ prepare(antag_mind)
+ var/datum/antagonist/nukeop/op = new antag_type
+ op.nuke_team = nuke_team
+ antag_mind.add_antag_datum(op)
+
+/// Frees the target mind's job slot, clears and deletes all their items, creates a fresh body for them, and sets
+/datum/round_event/antagonist/solo/nuclear_operative/proc/prepare(datum/mind/antag_mind)
var/mob/living/current_mob = antag_mind.current
SSjob.FreeRole(antag_mind.assigned_role.title)
- var/list/items = current_mob.get_equipped_items(TRUE)
- current_mob.unequip_everything()
- for(var/obj/item/item as anything in items)
- qdel(item)
-
+ current_mob.clear_inventory()
create_human_mob_copy(get_turf(current_mob), current_mob)
- if(!most_experienced)
- most_experienced = get_most_experienced(setup_minds, required_role) || antag_mind
-
- if(!set_leader)
- set_leader = TRUE
- if(antag_mind != most_experienced)
- var/mob/living/leader_mob = most_experienced.current
- SSjob.FreeRole(most_experienced.assigned_role.title)
- var/list/leader_items = leader_mob.get_equipped_items(TRUE)
- leader_mob.unequip_everything()
- for(var/obj/item/item as anything in leader_items)
- qdel(item)
- leader_mob = create_human_mob_copy(get_turf(leader_mob), leader_mob)
- most_experienced.set_assigned_role(SSjob.GetJobType(/datum/job/nuclear_operative))
- most_experienced.special_role = ROLE_NUCLEAR_OPERATIVE
- var/datum/antagonist/nukeop/leader/leader_antag_datum = most_experienced.add_antag_datum(/datum/antagonist/nukeop/leader)
- nuke_team = leader_antag_datum.nuke_team
-
- if(antag_mind == most_experienced)
- return
-
- antag_mind.set_assigned_role(SSjob.GetJobType(/datum/job/nuclear_operative))
- antag_mind.special_role = ROLE_NUCLEAR_OPERATIVE
-
- var/datum/antagonist/nukeop/new_op = new antag_datum()
- antag_mind.add_antag_datum(new_op)
+ antag_mind.set_assigned_role(SSjob.GetJobType(job_type))
+ antag_mind.special_role = required_role
+/datum/round_event/antagonist/solo/nuclear_operative/add_datum_to_mind(datum/mind/antag_mind)
+ CRASH("this should not be called")
/datum/round_event/antagonist/solo/nuclear_operative/round_end_report()
var/result = nuke_team.get_result()
diff --git a/monkestation/code/modules/virology/disease/symptom_varients/base.dm b/monkestation/code/modules/virology/disease/symptom_varients/base.dm
index 98908388dcfb..e42fec75ce8f 100644
--- a/monkestation/code/modules/virology/disease/symptom_varients/base.dm
+++ b/monkestation/code/modules/virology/disease/symptom_varients/base.dm
@@ -51,6 +51,7 @@
return FALSE
host_symptom.run_effect(host_disease.affected_mob, host_disease)
COOLDOWN_START(src, host_cooldown, cooldown_time)
+ return TRUE
/datum/symptom_varient/proc/Copy(datum/symptom/new_symp)
return new type(new_symp)
diff --git a/monkestation/code/modules/virology/disease/symptom_varients/bluespace.dm b/monkestation/code/modules/virology/disease/symptom_varients/bluespace.dm
index 1535df0d293e..e4f302d75da0 100644
--- a/monkestation/code/modules/virology/disease/symptom_varients/bluespace.dm
+++ b/monkestation/code/modules/virology/disease/symptom_varients/bluespace.dm
@@ -1,4 +1,4 @@
-GLOBAL_LIST_INIT(bluespace_varient_list, list())
+GLOBAL_LIST_EMPTY_TYPED(bluespace_varient_list, /datum/symptom_varient/bluespace)
/datum/symptom_varient/bluespace
name = "Quantumly Entangled"
@@ -10,11 +10,12 @@ GLOBAL_LIST_INIT(bluespace_varient_list, list())
var/bluespace_id = 0
var/static/last_bluespace_id = 0
-/datum/symptom_varient/bluespace/New(datum/symptom/host)
+/datum/symptom_varient/bluespace/New(datum/symptom/host, bluespace_id)
. = ..()
GLOB.bluespace_varient_list += src
- last_bluespace_id++
- bluespace_id = last_bluespace_id
+ if(isnull(bluespace_id))
+ bluespace_id = last_bluespace_id++
+ src.bluespace_id = bluespace_id
/datum/symptom_varient/bluespace/Destroy(force)
GLOB.bluespace_varient_list -= src
@@ -24,15 +25,11 @@ GLOBAL_LIST_INIT(bluespace_varient_list, list())
. = ..()
RegisterSignal(host_symptom, COMSIG_SYMPTOM_TRIGGER, PROC_REF(propagate))
-/datum/symptom_varient/bluespace/proc/propagate()
- for(var/datum/symptom_varient/bluespace/bluespace as anything in GLOB.bluespace_varient_list)
- if(bluespace_id != bluespace.bluespace_id)
- continue
- COOLDOWN_START(bluespace, host_cooldown, cooldown_time)
+/datum/symptom_varient/bluespace/Copy(datum/symptom/new_symp)
+ return new /datum/symptom_varient/bluespace(new_symp, bluespace_id)
+/datum/symptom_varient/bluespace/proc/propagate()
for(var/datum/symptom_varient/bluespace/bluespace as anything in GLOB.bluespace_varient_list)
- if(bluespace_id != bluespace.bluespace_id)
- continue
- if(!bluespace.host_disease)
+ if(QDELETED(bluespace) || bluespace_id != bluespace.bluespace_id)
continue
- bluespace.host_symptom.run_effect(bluespace.host_disease.return_parent(), bluespace.host_disease)
+ bluespace.trigger_symptom()
diff --git a/monkestation/code/modules/virology/disease/symptom_varients/recursive.dm b/monkestation/code/modules/virology/disease/symptom_varients/recursive.dm
index 530f46aef0ab..8964be62b117 100644
--- a/monkestation/code/modules/virology/disease/symptom_varients/recursive.dm
+++ b/monkestation/code/modules/virology/disease/symptom_varients/recursive.dm
@@ -17,10 +17,9 @@
UnregisterSignal(host_symptom, COMSIG_SYMPTOM_TRIGGER)
/datum/symptom_varient/recursive/proc/start_chain()
- trigger_symptom()
-
- addtimer(CALLBACK(src, PROC_REF(trigger)), 2 SECONDS)
- addtimer(CALLBACK(src, PROC_REF(trigger)), 4 SECONDS)
+ if(trigger_symptom())
+ addtimer(CALLBACK(src, PROC_REF(trigger)), 2 SECONDS, TIMER_DELETE_ME)
+ addtimer(CALLBACK(src, PROC_REF(trigger)), 4 SECONDS, TIMER_DELETE_ME)
/datum/symptom_varient/recursive/proc/trigger()
host_symptom.run_effect(host_disease.affected_mob, host_disease)
diff --git a/monkestation/code/modules/virology/disease/symtoms/_symptom.dm b/monkestation/code/modules/virology/disease/symtoms/_symptom.dm
index 23764bd1bc00..6cde395c57e4 100644
--- a/monkestation/code/modules/virology/disease/symtoms/_symptom.dm
+++ b/monkestation/code/modules/virology/disease/symtoms/_symptom.dm
@@ -32,6 +32,9 @@
var/datum/symptom_varient/attached_varient
// This is our attached varient used for updating desc and Symptom copy code.
+/datum/symptom/Destroy(force)
+ QDEL_NULL(attached_varient)
+ return ..()
/datum/symptom/proc/minormutate()
if (prob(20))
diff --git a/monkestation/code/modules/virology/disease/symtoms/helpful/plasma_heal.dm b/monkestation/code/modules/virology/disease/symtoms/helpful/plasma_heal.dm
index e03d47862812..66ac164d47b3 100644
--- a/monkestation/code/modules/virology/disease/symtoms/helpful/plasma_heal.dm
+++ b/monkestation/code/modules/virology/disease/symtoms/helpful/plasma_heal.dm
@@ -24,8 +24,9 @@
/datum/symptom/plasma_heal/first_activate(mob/living/carbon/mob, datum/disease/advanced/disease)
. = ..()
+ if(!HAS_TRAIT(mob, TRAIT_PLASMA_LOVER_METABOLISM))
+ to_chat(mob, span_notice("You suddenly love plasma."))
ADD_TRAIT(mob, TRAIT_PLASMA_LOVER_METABOLISM, type)
- to_chat(mob, span_notice("You suddenly love plasma."))
/datum/symptom/plasma_heal/side_effect(mob/living/mob)
. = ..()
diff --git a/tgstation.dme b/tgstation.dme
index f1e1c49817d1..8f57109c94c9 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -5958,6 +5958,7 @@
#include "monkestation\code\datums\components\turf_healing.dm"
#include "monkestation\code\datums\components\uplink.dm"
#include "monkestation\code\datums\components\wound_converter.dm"
+#include "monkestation\code\datums\components\crafting\robot.dm"
#include "monkestation\code\datums\components\riding\riding_mob.dm"
#include "monkestation\code\datums\components\riding\riding_vehicle.dm"
#include "monkestation\code\datums\diseases\advance\symptoms\clockwork.dm"
@@ -6992,6 +6993,7 @@
#include "monkestation\code\modules\can_spessmen_feel_pain\pain\status_effects\pain_limp.dm"
#include "monkestation\code\modules\can_spessmen_feel_pain\pain\status_effects\sharp_pain.dm"
#include "monkestation\code\modules\can_spessmen_feel_pain\pain\status_effects\temp_pack.dm"
+#include "monkestation\code\modules\cargo\bounties\bot.dm"
#include "monkestation\code\modules\cargo\bounties\pathology.dm"
#include "monkestation\code\modules\cargo\crates\costumes_toys.dm"
#include "monkestation\code\modules\cargo\crates\emergency.dm"
@@ -7497,6 +7499,7 @@
#include "monkestation\code\modules\mining\accelerators\shotgun.dm"
#include "monkestation\code\modules\mining\lavaland\megafauna_loot.dm"
#include "monkestation\code\modules\mining\lavaland\tendril_loot.dm"
+#include "monkestation\code\modules\mob\inventory.dm"
#include "monkestation\code\modules\mob\login.dm"
#include "monkestation\code\modules\mob\mob.dm"
#include "monkestation\code\modules\mob\mob_defines.dm"
@@ -7528,6 +7531,7 @@
#include "monkestation\code\modules\mob\living\living_update_icons.dm"
#include "monkestation\code\modules\mob\living\status_procs.dm"
#include "monkestation\code\modules\mob\living\basic\animatronic.dm"
+#include "monkestation\code\modules\mob\living\basic\bots\cleanbot\cleanbot.dm"
#include "monkestation\code\modules\mob\living\basic\bots\medbot\medbot.dm"
#include "monkestation\code\modules\mob\living\basic\drone\_drone.dm"
#include "monkestation\code\modules\mob\living\basic\drone\bardrone.dm"