diff --git a/code/__DEFINES/alerts.dm b/code/__DEFINES/alerts.dm
index ff1b29e883e..af8eb6f8353 100644
--- a/code/__DEFINES/alerts.dm
+++ b/code/__DEFINES/alerts.dm
@@ -30,3 +30,10 @@
/** Silicon related */
#define ALERT_LOCKED "locked"
+/** Mech related */
+// SCS-3 Cage
+#define CAGE_STAGE_ZERO "stage_zero"
+#define CAGE_STAGE_ONE "stage_one"
+#define CAGE_STAGE_TWO "stage_two"
+#define CAGE_STAGE_THREE "stage_three"
+
diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm
index 92565ab1354..d0ee17e48bd 100644
--- a/code/__DEFINES/traits/sources.dm
+++ b/code/__DEFINES/traits/sources.dm
@@ -110,6 +110,10 @@
#define STAMINA_TRAIT "stamina"
+/// source trait for /obj/item/mecha_parts/mecha_equipment/cage
+#define MECH_SUPRESSED_TRAIT "mech_supress"
+
+
/// trait associated to resting
#define RESTING_TRAIT "resting"
/// trait associated to a stat value or range of
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index 4a0132a6645..3a8e66de69b 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -563,6 +563,43 @@ so as to remain in compliance with the most up-to-date laws."
desc = "Maintenance protocols are currently in effect, most actions disabled."
icon_state = "locked"
+/atom/movable/screen/alert/empty_alert
+ name = ""
+ desc = ""
+
+// MECH MODULES
+
+// cage module
+/atom/movable/screen/alert/mech_cage
+ name = "Ты не должен это видеть"
+ desc = "Ну и это тоже"
+ icon = 'icons/obj/mecha/mecha_cage.dmi'
+ var/stage_define
+
+/atom/movable/screen/alert/mech_cage/zero
+ name = "Нулевой этап"
+ desc = "Модуль не работает."
+ icon_state = "stage_0"
+ stage_define = CAGE_STAGE_ZERO
+
+/atom/movable/screen/alert/mech_cage/one
+ name = "Первый этап"
+ desc = "Модуль работает в режиме удержания."
+ icon_state = "stage_1"
+ stage_define = CAGE_STAGE_ONE
+
+/atom/movable/screen/alert/mech_cage/two
+ name = "Второй этап"
+ desc = "Модуль работает в режиме удержания цели в наручниках."
+ icon_state = "stage_2"
+ stage_define = CAGE_STAGE_TWO
+
+/atom/movable/screen/alert/mech_cage/three
+ name = "Третий этап"
+ desc = "Модуль работает в режиме заключения."
+ icon_state = "stage_3"
+ stage_define = CAGE_STAGE_THREE
+
//GUARDIANS
/atom/movable/screen/alert/cancharge
name = "Charge Ready"
diff --git a/code/datums/spells/ethereal_jaunt.dm b/code/datums/spells/ethereal_jaunt.dm
index 35188b7f1fe..6fe7cb3797c 100644
--- a/code/datums/spells/ethereal_jaunt.dm
+++ b/code/datums/spells/ethereal_jaunt.dm
@@ -32,6 +32,9 @@
/obj/effect/proc_holder/spell/ethereal_jaunt/proc/do_jaunt(mob/living/target)
playsound(get_turf(target), sound_in, 50, TRUE, -1)
+ // mech supress escape
+ if(HAS_TRAIT_FROM(target, TRAIT_IMMOBILIZED, MECH_SUPRESSED_TRAIT))
+ target.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_FLOORED), MECH_SUPRESSED_TRAIT)
ADD_TRAIT(target, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src))
var/turf/mobloc = get_turf(target)
var/obj/effect/dummy/spell_jaunt/holder = new jaunt_type_path(mobloc)
diff --git a/code/game/gamemodes/shadowling/shadowling_abilities.dm b/code/game/gamemodes/shadowling/shadowling_abilities.dm
index 6418d850862..78f287d2f27 100644
--- a/code/game/gamemodes/shadowling/shadowling_abilities.dm
+++ b/code/game/gamemodes/shadowling/shadowling_abilities.dm
@@ -134,6 +134,9 @@
return
playsound(user.loc, 'sound/effects/bamf.ogg', 50, 1)
+ // mech supress escape
+ if(HAS_TRAIT_FROM(user, TRAIT_IMMOBILIZED, MECH_SUPRESSED_TRAIT))
+ user.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_FLOORED), MECH_SUPRESSED_TRAIT)
user.visible_message("[user] vanishes in a puff of black mist!", "You enter the space between worlds as a passageway.")
user.SetStunned(0)
user.SetWeakened(0)
diff --git a/code/game/mecha/combat/sidewinder.dm b/code/game/mecha/combat/sidewinder.dm
index fdd825087bc..93fe240bbd7 100644
--- a/code/game/mecha/combat/sidewinder.dm
+++ b/code/game/mecha/combat/sidewinder.dm
@@ -87,3 +87,5 @@
ME.attach(src)
ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang/clusterbang
ME.attach(src)
+ ME = new /obj/item/mecha_parts/mecha_equipment/cage
+ ME.attach(src)
diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm
index cde0a1a6108..ae4b6796668 100644
--- a/code/game/mecha/equipment/mecha_equipment.dm
+++ b/code/game/mecha/equipment/mecha_equipment.dm
@@ -23,6 +23,7 @@
var/selectable = MODULE_SELECTABLE_FULL
var/harmful = FALSE //Controls if equipment can be used to attack by a pacifist.
var/integrated = FALSE // Preventing modules from getting detached.
+ var/alert_category = "mecha_module" //change if you want custom alerts
/obj/item/mecha_parts/mecha_equipment/proc/update_chassis_page()
@@ -181,6 +182,9 @@
return
if(chassis.occupant)
remove_targeted_action()
+ if(chassis.selected == src)
+ if(selectable == MODULE_SELECTABLE_FULL)
+ chassis.occupant.clear_alert(alert_category)
detach_act()
moveto = moveto || get_turf(chassis)
if(Move(moveto))
@@ -224,10 +228,27 @@
return
/obj/item/mecha_parts/mecha_equipment/proc/select_module()
+ select_set_alert()
chassis.selected = src
chassis.occupant_message(span_notice("You switch to [src]."))
chassis.visible_message("[chassis] raises [src]")
send_byjax(chassis.occupant, "exosuit.browser", "eq_list", chassis.get_equipment_list())
+/obj/item/mecha_parts/mecha_equipment/proc/select_set_alert()
+ if(selectable == MODULE_SELECTABLE_FULL)
+ var/mob/living/carbon/occupant = chassis.occupant
+ if(chassis.selected)
+ occupant.clear_alert(chassis.selected.alert_category)
+ return throw_default_alert(occupant)
+ return FALSE
+
+/obj/item/mecha_parts/mecha_equipment/proc/throw_default_alert(var/mob/living/carbon/occupant)
+ if(alert_category == "mecha_module")
+ var/atom/movable/screen/alert/empty_alert/default_alert = occupant.throw_alert(alert_category, /atom/movable/screen/alert/empty_alert, new_master = src)
+ default_alert.name = name
+ default_alert.desc = "Выбран модуль [src.name]"
+ return TRUE
+ return FALSE
+
/obj/item/mecha_parts/mecha_equipment/proc/toggle_module()
return
diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm
index e4490d9fe6a..d201b4adb2e 100644
--- a/code/game/mecha/equipment/tools/other_tools.dm
+++ b/code/game/mecha/equipment/tools/other_tools.dm
@@ -1,5 +1,5 @@
// Teleporter, Wormhole generator, Gravitational catapult, Armor booster modules,
-// Repair droid, Tesla Energy relay, Generators
+// Repair droid, Tesla Energy relay, Generators, SCS-3 Cage
////////////////////////////////////////////// TELEPORTER ///////////////////////////////////////////////
@@ -590,3 +590,285 @@
var/obj/mecha/working/W = loc
W.slow_pressure_step_in = initial(W.slow_pressure_step_in)
W.fast_pressure_step_in = initial(W.fast_pressure_step_in)
+
+
+// SCS-3 CAGE
+
+/obj/item/mecha_parts/mecha_equipment/cage
+ name = "Клетка SCS-3"
+ desc = "Модуль для экзокостюмов, используемый в задержании преступников."
+ icon_state = "mecha_cage"
+ origin_tech = "combat=6;materials=5"
+ equip_cooldown = 3 SECONDS
+ energy_drain = 500
+ range = MECHA_MELEE
+ salvageable = FALSE
+ harmful = FALSE
+ alert_category = "mecha_cage"
+
+ var/mob/living/carbon/prisoner
+ var/mob/living/carbon/holding
+ ///for custom icons
+ var/datum/action/innate/mecha/select_module/button
+ ///wacky case
+ var/current_stage
+ var/obj/effect/supress/supress_effect
+
+/obj/item/mecha_parts/mecha_equipment/cage/can_attach(obj/mecha/M)
+ if(..())
+ if(locate(src) in M.equipment)
+ return FALSE
+ if(istype(M, /obj/mecha/combat/durand) || istype(M, /obj/mecha/combat/lockersyndie) || istype(M, /obj/mecha/combat/marauder))
+ return TRUE
+ else if(M.emagged == TRUE)
+ return TRUE
+ return FALSE
+
+/obj/item/mecha_parts/mecha_equipment/cage/Destroy()
+ for(var/atom/movable/AM in src)
+ AM.forceMove(get_turf(src))
+ if(holding)
+ stop_supressing(holding)
+
+ prisoner = null
+ holding = null
+ return ..()
+
+/obj/item/mecha_parts/mecha_equipment/cage/select_set_alert()
+ . = ..()
+ if(!.)
+ if(prisoner)
+ change_alert(CAGE_STAGE_THREE)
+ else if(holding)
+ if(!holding.handcuffed)
+ change_alert(CAGE_STAGE_ONE)
+ else
+ change_alert(CAGE_STAGE_TWO)
+ else
+ change_alert(CAGE_STAGE_ZERO)
+
+/obj/item/mecha_parts/mecha_equipment/cage/action(mob/living/carbon/target)
+ if(!action_checks(target))
+ return FALSE
+ if(!istype(target))
+ return FALSE
+
+ var/same_target = target == holding
+ var/supress_check = target.IsStamcrited() || (target.health <= HEALTH_THRESHOLD_CRIT) || target.stat != CONSCIOUS
+
+ //SUPRESSING
+ if(((holding && !same_target) || !holding) && supress_check)
+ supress_action(target)
+ return TRUE
+
+ //HANDCUFFING
+ if(same_target && !target.handcuffed)
+ handcuff_action(target)
+ return TRUE
+
+ //PUTTING INTO MECH
+ if(same_target && target.handcuffed)
+ insert_action(target)
+ return TRUE
+
+ occupant_message(span_notice("[target] не может быть удержа[genderize_ru(target.gender, "н", "на", "но", "ны")], так как [target] не находится в критическом состоянии"))
+ return FALSE
+
+/obj/item/mecha_parts/mecha_equipment/cage/proc/supress_action(mob/living/carbon/target)
+ if(holding)
+ occupant_message(span_notice("Ты перестаёшь удерживать [holding], и начинаешь удерживать [target]..."))
+ chassis.visible_message(span_warning("[chassis] перестаёт удерживать [holding] и сменяется на [target]."))
+ stop_supressing(holding)
+ set_supress_effect(target)
+ if(!do_after_cooldown(target))
+ qdel(supress_effect)
+ supress_effect = null
+ return FALSE
+ if(!prisoner)
+ change_alert(CAGE_STAGE_ONE)
+ supress(target)
+ else
+ occupant_message(span_notice("Ты начинаешь удерживать [target]..."))
+ chassis.visible_message(span_warning("[chassis] начинает удерживать [target]."))
+ supress_effect = new(target.loc)
+ set_supress_effect(target)
+ if(!do_after_cooldown(target))
+ qdel(supress_effect)
+ supress_effect = null
+ return FALSE
+ if(!prisoner)
+ change_alert(CAGE_STAGE_ONE)
+ supress(target)
+
+/obj/item/mecha_parts/mecha_equipment/cage/proc/handcuff_action(mob/living/carbon/target)
+ occupant_message(span_notice("Ты начинаешь сковывать [target]..."))
+ chassis.visible_message(span_warning("[chassis] начинает сковывать [target]."))
+ if(!do_after_cooldown(target))
+ return FALSE
+ if(!prisoner)
+ change_alert(CAGE_STAGE_TWO)
+ target.apply_restraints(new /obj/item/restraints/handcuffs, ITEM_SLOT_HANDCUFFED, TRUE)
+ occupant_message(span_notice("Ты успешно сковал [target]..."))
+ chassis.visible_message(span_warning("[chassis] успешно сковал [target]."))
+ add_attack_logs(chassis.occupant, target, "shackled")
+
+/obj/item/mecha_parts/mecha_equipment/cage/proc/insert_action(mob/living/carbon/target)
+ if(!prisoner_insertion_check(target))
+ return FALSE
+ if(!button)
+ for(var/datum/action/innate/mecha/select_module/H in chassis.occupant.actions)
+ if(H.button_icon_state == "mecha_cage")
+ button = H
+ break
+
+ change_state("mecha_cage_activate")
+ occupant_message(span_notice("Ты начинаешь помещать [target] внутрь клетки..."))
+ chassis.visible_message(span_warning("[chassis] начинает помещать [target] внутрь клетки."))
+ if(!do_after_cooldown(target))
+ change_state("mecha_cage")
+ return FALSE
+ change_state("mecha_cage_activated")
+ change_alert(CAGE_STAGE_THREE)
+ prisoner = target
+ target.forceMove(src)
+ stop_supressing(target)
+ UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
+ RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_escape))
+ update_equip_info()
+ occupant_message(span_notice("[target] успешно загруже[genderize_ru(target.gender, "н", "на", "но", "ны")]."))
+ chassis.visible_message(span_warning("[chassis] загружает [target] в клетку."))
+ log_message("[target] loaded.")
+
+/obj/item/mecha_parts/mecha_equipment/cage/proc/supress(mob/living/carbon/target)
+ RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
+ holding = target
+ target.add_traits(list(TRAIT_IMMOBILIZED, TRAIT_FLOORED), MECH_SUPRESSED_TRAIT)
+ target.move_resist = MOVE_FORCE_STRONG
+ supress_effect.icon_state = "applied"
+
+/obj/item/mecha_parts/mecha_equipment/cage/proc/stop_supressing(mob/living/carbon/target)
+ UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
+ holding = null
+ target.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_FLOORED), MECH_SUPRESSED_TRAIT)
+ target.move_resist = MOVE_FORCE_DEFAULT
+ qdel(supress_effect)
+ supress_effect = null
+
+ if(!prisoner)
+ change_alert(CAGE_STAGE_ZERO)
+
+/obj/item/mecha_parts/mecha_equipment/cage/proc/on_moved(mob/living/carbon/target)
+ SIGNAL_HANDLER
+ stop_supressing(target)
+
+/obj/item/mecha_parts/mecha_equipment/cage/proc/on_escape(mob/living/carbon/target)
+ SIGNAL_HANDLER
+ occupant_message("[prisoner] сбежа[genderize_ru(target.gender, "л", "ла", "ло", "лы")].")
+ log_message("[prisoner] сбежа[genderize_ru(target.gender, "л", "ла", "ло", "лы")].")
+ prisoner = null
+ if(holding)
+ if(holding.handcuffed)
+ change_alert(CAGE_STAGE_TWO)
+ else
+ change_alert(CAGE_STAGE_ONE)
+ else
+ change_alert(CAGE_STAGE_ZERO)
+ change_state("mecha_cage")
+ update_equip_info()
+ UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
+
+/obj/item/mecha_parts/mecha_equipment/cage/proc/change_state(icon)
+ button.button_icon_state = icon
+ flick(icon, button)
+ button.UpdateButtonIcon()
+
+/obj/item/mecha_parts/mecha_equipment/cage/proc/change_alert(var/stage_define)
+ var/mob/living/carbon/H = chassis.occupant
+ for(var/I in subtypesof(/atom/movable/screen/alert/mech_cage))
+ var/atom/movable/screen/alert/mech_cage/alert = I
+ if(alert.stage_define == stage_define)
+ H.throw_alert(alert_category, alert)
+ break
+
+ current_stage = stage_define
+
+
+/obj/item/mecha_parts/mecha_equipment/cage/proc/set_supress_effect(mob/living/carbon/target)
+ supress_effect = new(target.loc)
+ flick("effect_on_doll", supress_effect)
+
+/obj/item/mecha_parts/mecha_equipment/cage/proc/prisoner_insertion_check(mob/living/carbon/target)
+ if(target.buckled)
+ occupant_message(span_warning("[target] не помест[pluralize_ru(target.gender, "ится", "ятся")] в клетку, так как [target] прикова[genderize_ru(target.gender, "н", "нна", "нно", "нны")] к [target.buckled]!"))
+ return FALSE
+ if(target.has_buckled_mobs())
+ occupant_message(span_warning("[target] не помест[pluralize_ru(target.gender, "ится", "ятся")] в клетку из-за прикованных к [genderize_ru(target.gender, "нему", "ней", "нему", "ним")] животным!"))
+ return FALSE
+ if(prisoner)
+ occupant_message(span_warning("Клетка уже занята!"))
+ return FALSE
+ return TRUE
+
+/obj/item/mecha_parts/mecha_equipment/cage/proc/eject(force)
+ if(!action_checks(src))
+ return FALSE
+ if(!prisoner)
+ return FALSE
+ if(holding)
+ if(holding.handcuffed)
+ change_alert(CAGE_STAGE_TWO)
+ else
+ change_alert(CAGE_STAGE_ONE)
+ else
+ change_alert(CAGE_STAGE_ZERO)
+ UnregisterSignal(prisoner, COMSIG_MOVABLE_MOVED)
+ prisoner.forceMove(get_turf(src))
+ if(!force)
+ occupant_message("[prisoner] извлеч[genderize_ru(prisoner.gender, "ён", "ена", "ено", "ены")].")
+ log_message("[prisoner] извлеч[genderize_ru(prisoner.gender, "ён", "ена", "ено", "ены")].")
+ else
+ occupant_message("[prisoner] сбежа[genderize_ru(prisoner.gender, "л", "ла", "ло", "ли")].")
+ log_message("[prisoner] сбежа[genderize_ru(prisoner.gender, "л", "ла", "ло", "ли")].")
+ prisoner = null
+ change_state("mecha_cage")
+ update_equip_info()
+
+/obj/item/mecha_parts/mecha_equipment/cage/can_detach()
+ if(prisoner || holding)
+ occupant_message(span_warning("Невозможно отсоединить [src] - модуль работает!"))
+ return FALSE
+ return TRUE
+
+/obj/item/mecha_parts/mecha_equipment/cage/detach_act()
+ button = null
+
+/obj/item/mecha_parts/mecha_equipment/cage/get_module_equip_info()
+ if(prisoner)
+ return "
\[Задержанн[genderize_ru(prisoner.gender, "ый", "ая", "ое", "ые")]: [prisoner] \]
Eject"
+
+/obj/item/mecha_parts/mecha_equipment/cage/Topic(href,href_list)
+ ..()
+ var/datum/topic_input/afilter = new /datum/topic_input(href,href_list)
+ if(afilter.get("eject"))
+ eject(FALSE)
+ return
+
+/obj/item/mecha_parts/mecha_equipment/cage/container_resist()
+ if(prisoner.get_item_by_slot(ITEM_SLOT_CLOTH_OUTER))
+ var/obj/item/clothing/suit/straight_jacket/H = prisoner.get_item_by_slot(ITEM_SLOT_CLOTH_OUTER)
+ prisoner.cuff_resist(H, FALSE)
+ return
+ if(prisoner.handcuffed)
+ prisoner.cuff_resist(prisoner.handcuffed, FALSE)
+ return
+ if(do_after(prisoner, 30 SECONDS, prisoner))
+ eject(TRUE)
+
+/obj/effect/supress
+ name = "Клешни экзокостюма"
+ desc = "Сейчас кого-то своруют..."
+ icon = 'icons/misc/supress_effect.dmi'
+ icon_state = "effect_on_doll"
+ anchored = TRUE
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ plane = ABOVE_GAME_PLANE
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 77bf40091d7..e155496d2db 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -21,6 +21,7 @@
var/can_move = 0 // time of next allowed movement
var/mech_enter_time = 4 SECONDS // Entering mecha time
var/mob/living/carbon/occupant = null
+
var/step_in = 10 //make a step in step_in/10 sec.
var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South.
var/normal_step_energy_drain = 10
@@ -409,6 +410,11 @@
occupant_message(span_danger("Unable to move while in zoom mode."))
last_message = world.time
return FALSE
+ if(locate(/obj/item/mecha_parts/mecha_equipment/cage) in equipment)
+ var/obj/item/mecha_parts/mecha_equipment/cage/H = locate(/obj/item/mecha_parts/mecha_equipment/cage) in equipment
+ if(H.holding)
+ occupant_message(span_notice("Ты перестаёшь удерживать [H.holding]."))
+ H.stop_supressing(H.holding)
//Turns strafe OFF if not enough energy to step (with actuator module only)
if(strafe && actuator && !has_charge(actuator.energy_per_step))
@@ -1165,6 +1171,10 @@
GrantActions(AI, FALSE)
else
GrantActions(AI, !AI.can_dominate_mechs)
+ if(selected)
+ var/atom/movable/screen/alert/empty_alert/default_alert = AI.throw_alert(selected.alert_category, /atom/movable/screen/alert/empty_alert, new_master = selected)
+ default_alert.name = selected.name
+ default_alert.desc = "Выбран модуль [selected.name]"
/////////////////////////////////////
//////// Atmospheric stuff ////////
@@ -1321,6 +1331,10 @@
occupant << sound(nominalsound, volume = 50)
if(state)
H.throw_alert("locked", /atom/movable/screen/alert/mech_maintenance)
+ if(selected)
+ var/atom/movable/screen/alert/empty_alert/default_alert = H.throw_alert(selected.alert_category, /atom/movable/screen/alert/empty_alert, new_master = selected)
+ default_alert.name = selected.name
+ default_alert.desc = "Выбран модуль [selected.name]"
return TRUE
else
return FALSE
@@ -1406,11 +1420,20 @@
if(!occupant)
return
var/atom/movable/mob_container
+ if(selected)
+ occupant.clear_alert(selected.alert_category)
occupant.clear_alert("charge")
occupant.clear_alert("locked")
occupant.clear_alert("mech damage")
occupant.clear_alert("mechaport")
occupant.clear_alert("mechaport_d")
+
+ if(locate(/obj/item/mecha_parts/mecha_equipment/cage) in equipment)
+ var/obj/item/mecha_parts/mecha_equipment/cage/H = locate(/obj/item/mecha_parts/mecha_equipment/cage) in equipment
+ if(H.holding)
+ occupant_message(span_notice("Ты перестаёшь удерживать [H.holding]."))
+ H.stop_supressing(H.holding)
+
if(occupant && occupant.client)
occupant.client.mouse_pointer_icon = initial(occupant.client.mouse_pointer_icon)
if(ishuman(occupant))
@@ -1655,7 +1678,6 @@
diag_hud_set_mechstat()
diag_hud_set_mechtracking()
-
/obj/mecha/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list())
var/image/I = image('icons/mob/talk.dmi', bubble_loc, bubble_state, FLY_LAYER)
SET_PLANE_EXPLICIT(I, ABOVE_GAME_PLANE, src)
diff --git a/code/game/objects/items/weapons/implants/implant_freedom.dm b/code/game/objects/items/weapons/implants/implant_freedom.dm
index 50a020207b0..e5c18813223 100644
--- a/code/game/objects/items/weapons/implants/implant_freedom.dm
+++ b/code/game/objects/items/weapons/implants/implant_freedom.dm
@@ -15,6 +15,9 @@
to_chat(imp_in, "You feel a faint click.")
if(iscarbon(imp_in))
var/mob/living/carbon/C_imp_in = imp_in
+ // mech supress escape
+ if(HAS_TRAIT_FROM(C_imp_in, TRAIT_IMMOBILIZED, MECH_SUPRESSED_TRAIT))
+ C_imp_in.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_FLOORED), MECH_SUPRESSED_TRAIT)
C_imp_in.uncuff()
if(C_imp_in.pulledby)
var/mob/living/grabber = C_imp_in.pulledby
@@ -24,7 +27,12 @@
playsound(C_imp_in.loc, 'sound/weapons/egloves.ogg', 75, TRUE)
grabber.stop_pulling()
C_imp_in.client?.move_delay = world.time // to skip move delay we probably got from resisting the grab
-
+ // mech cage container escape
+ if(istype(C_imp_in.loc, /obj/item/mecha_parts/mecha_equipment/cage))
+ var/obj/item/mecha_parts/mecha_equipment/cage/container = C_imp_in.loc
+ C_imp_in.forceMove(get_turf(container))
+ container.prisoner = null
+ container.update_equip_info()
if(!uses)
qdel(src)
diff --git a/code/modules/antagonists/changeling/powers/biodegrade.dm b/code/modules/antagonists/changeling/powers/biodegrade.dm
index 90e65b8ce30..15f0519895a 100644
--- a/code/modules/antagonists/changeling/powers/biodegrade.dm
+++ b/code/modules/antagonists/changeling/powers/biodegrade.dm
@@ -49,6 +49,21 @@
addtimer(CALLBACK(src, PROC_REF(dissolve_restraint), user, res_suit), 3 SECONDS)
used = TRUE
+ // mech supress escape
+ if(HAS_TRAIT_FROM(user, TRAIT_IMMOBILIZED, MECH_SUPRESSED_TRAIT))
+ user.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_FLOORED), MECH_SUPRESSED_TRAIT)
+ used = TRUE
+
+ // mech cage container escape
+ if(istype(user.loc, /obj/item/mecha_parts/mecha_equipment/cage))
+ var/obj/item/mecha_parts/mecha_equipment/cage/container = user.loc
+ var/obj/mecha/mech = container.chassis
+ mech.visible_message(span_warning("Камера содержания [mech] начинает плавиться!"), \
+ span_warning("Мы изрыгаем кислотную жидкость на стенки клетки!"))
+ user.forceMove(get_turf(container))
+ container.prisoner = null
+ container.update_equip_info()
+
if(istype(user.loc, /obj/structure/closet) && !used)
var/obj/structure/closet/closet = user.loc
if(!istype(closet))
diff --git a/code/modules/antagonists/vampire/vampire_powers/goon_vampire_powers.dm b/code/modules/antagonists/vampire/vampire_powers/goon_vampire_powers.dm
index f4fd1721bff..575debedfec 100644
--- a/code/modules/antagonists/vampire/vampire_powers/goon_vampire_powers.dm
+++ b/code/modules/antagonists/vampire/vampire_powers/goon_vampire_powers.dm
@@ -58,6 +58,9 @@
/obj/effect/proc_holder/spell/vampire/goon/self/rejuvenate/cast(list/targets, mob/living/carbon/human/user = usr)
+ // mech supress escape
+ if(HAS_TRAIT_FROM(user, TRAIT_IMMOBILIZED, MECH_SUPRESSED_TRAIT))
+ user.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_FLOORED), MECH_SUPRESSED_TRAIT)
user.SetWeakened(0)
user.SetStunned(0)
user.SetKnockdown(0)
diff --git a/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm b/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm
index 96381501944..c07beebb0d5 100644
--- a/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm
+++ b/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm
@@ -97,6 +97,9 @@
/obj/effect/proc_holder/spell/vampire/self/rejuvenate/cast(list/targets, mob/living/user = usr)
+ // mech supress escape
+ if(HAS_TRAIT_FROM(user, TRAIT_IMMOBILIZED, MECH_SUPRESSED_TRAIT))
+ user.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_FLOORED), MECH_SUPRESSED_TRAIT)
user.SetWeakened(0)
user.SetStunned(0)
user.SetKnockdown(0)
diff --git a/code/modules/economy/robotic_quests/mech_types.dm b/code/modules/economy/robotic_quests/mech_types.dm
index edd66d9c5c7..0f0f0db5150 100644
--- a/code/modules/economy/robotic_quests/mech_types.dm
+++ b/code/modules/economy/robotic_quests/mech_types.dm
@@ -147,4 +147,5 @@
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/amlg,
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack,
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang,
+ /obj/item/mecha_parts/mecha_equipment/cage,
)
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index e361291f828..7b34d151ed3 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -1047,6 +1047,17 @@
construction_time = 10 SECONDS
category = list("Exosuit Equipment")
+/datum/design/mech_cage
+ name = "Клетка SCS-3"
+ desc = "Модуль для экзокостюмов, используемый для задержания и заключения преступников."
+ id = "mech_cage"
+ build_type = MECHFAB
+ req_tech = (list("materials" = 7, "combat" = 7))
+ build_path = /obj/item/mecha_parts/mecha_equipment/cage
+ materials = list(MAT_METAL=10000, MAT_TITANIUM=4000, MAT_SILVER=2000, MAT_DIAMOND=1000)
+ construction_time = 10 SECONDS
+ category = list("Exosuit Equipment")
+
// Exosuit Weapons
/datum/design/mech_grenade_launcher
diff --git a/icons/misc/supress_effect.dmi b/icons/misc/supress_effect.dmi
new file mode 100644
index 00000000000..e86a1be90b8
Binary files /dev/null and b/icons/misc/supress_effect.dmi differ
diff --git a/icons/obj/mecha/mecha_cage.dmi b/icons/obj/mecha/mecha_cage.dmi
new file mode 100644
index 00000000000..f121f03806a
Binary files /dev/null and b/icons/obj/mecha/mecha_cage.dmi differ
diff --git a/icons/obj/mecha/mecha_equipment.dmi b/icons/obj/mecha/mecha_equipment.dmi
index d31ccb93fa1..b27c26e1012 100644
Binary files a/icons/obj/mecha/mecha_equipment.dmi and b/icons/obj/mecha/mecha_equipment.dmi differ