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"