diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm
index 9aa0dd4d818..8ecaa7c3645 100644
--- a/code/__DEFINES/admin.dm
+++ b/code/__DEFINES/admin.dm
@@ -105,6 +105,8 @@
#define ADMIN_PUNISHMENT_SHOES "Knot Shoes"
#define ADMIN_PUNISHMENT_DOCK "Dock Pay"
#define ADMIN_PUNISHMENT_BREAD "Bread"
+/// Applies [/datum/component/omen] to the target, optionally permanently.
+#define ADMIN_PUNISHMENT_BADLUCK "Bad Luck"
#define AHELP_ACTIVE 1
#define AHELP_CLOSED 2
diff --git a/code/datums/components/omen.dm b/code/datums/components/omen.dm
index 3d8872c6aeb..d065530f679 100644
--- a/code/datums/components/omen.dm
+++ b/code/datums/components/omen.dm
@@ -12,15 +12,25 @@
/// Whatever's causing the omen, if there is one. Destroying the vessel won't stop the omen, but we destroy the vessel (if one exists) upon the omen ending
var/obj/vessel
-/datum/component/omen/Initialize(silent=FALSE, vessel)
+ /// Whether this is a permanent omen that cannot be removed by any non-admin means.
+ var/permanent = FALSE
+
+/datum/component/omen/Initialize(silent = FALSE, _vessel, _permanent = FALSE)
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
- var/mob/person = parent
+ vessel = _vessel
+ permanent = _permanent
if(!silent)
- to_chat(person, "You get a bad feeling...")
- src.vessel = vessel
+ var/warning = "You get a bad feeling..."
+ if(permanent)
+ warning += " A very bad feeling... As if you are surrounded by a twisted aura of pure malevolence..."
+ to_chat(parent, "[warning]")
+
/datum/component/omen/Destroy(force, silent)
+ if(!silent)
+ var/mob/living/person = parent
+ to_chat(person, "You feel a horrible omen lifted off your shoulders!")
if(vessel)
vessel.visible_message("[vessel] burns up in a sinister flash, taking an evil energy with it...")
vessel = null
@@ -43,14 +53,40 @@
/datum/component/omen/proc/check_accident(atom/movable/our_guy)
SIGNAL_HANDLER_DOES_SLEEP
+ if(!isliving(our_guy))
+ return
+
+ var/mob/living/living_guy = our_guy
+
if(!prob(15))
return
- for(var/t in get_adjacent_open_turfs(our_guy))
+ var/our_guy_pos = get_turf(living_guy)
+ for(var/turf_content in our_guy_pos)
+ if(istype(turf_content, /obj/machinery/door/airlock))
+ to_chat(living_guy, "A malevolent force launches your body to the floor...")
+ var/obj/machinery/door/airlock/darth_airlock = turf_content
+ living_guy.apply_status_effect(STATUS_EFFECT_PARALYZED, 10, TRUE)
+ darth_airlock.close(force_crush = TRUE)
+ if(!permanent)
+ qdel(src)
+ return
+
+ for(var/t in get_adjacent_open_turfs(living_guy))
var/turf/the_turf = t
+
+ if(the_turf.zPassOut(living_guy, DOWN) && living_guy.can_zFall(the_turf))
+ to_chat(living_guy, "A malevolent force guides you towards the edge...")
+ living_guy.throw_at(the_turf, 1, 10, force = MOVE_FORCE_EXTREMELY_STRONG)
+ if(!permanent)
+ qdel(src)
+ return
+
for(var/obj/machinery/vending/darth_vendor in the_turf)
if(darth_vendor.tiltable)
- darth_vendor.tilt(our_guy)
- qdel(src)
+ to_chat(living_guy, "A malevolent force tugs at the [darth_vendor]...")
+ darth_vendor.tilt(living_guy)
+ if(!permanent)
+ qdel(src)
return
/// If we get knocked down, see if we have a really bad slip and bash our head hard
@@ -68,13 +104,17 @@
our_guy.visible_message("[our_guy] hits [our_guy.p_their()] head really badly falling down!", "You hit your head really badly falling down!")
the_head.receive_damage(75)
our_guy.adjustOrganLoss(ORGAN_SLOT_BRAIN, 100)
- qdel(src)
+ if(!permanent)
+ qdel(src)
/// Hijack the mood system to see if we get the blessing mood event to cancel the omen
/datum/component/omen/proc/check_bless(mob/living/our_guy, category)
SIGNAL_HANDLER
+ if(permanent)
+ return
+
if(category != "blessing")
return
- to_chat(our_guy, "You feel a horrible omen lifted off your shoulders!")
+
qdel(src)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index d405d7cf212..a1ae4cc424c 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1139,7 +1139,7 @@
return TRUE
-/obj/machinery/door/airlock/close(forced=0)
+/obj/machinery/door/airlock/close(forced = FALSE, force_crush = FALSE)
if(operating || welded || locked || seal)
return
if(density)
@@ -1147,7 +1147,9 @@
if(!forced)
if(!hasPower() || wires.is_cut(WIRE_BOLTS))
return
- if(safe)
+
+ var/dangerous_close = !safe || force_crush
+ if(!dangerous_close)
for(var/atom/movable/M in get_turf(src))
if(M.density && M != src) //something is blocking the door
autoclose_in(DOOR_CLOSE_WAIT)
@@ -1178,7 +1180,7 @@
flags_1 |= PREVENT_CLICK_UNDER_1
air_update_turf(1)
sleep(4)
- if(!safe)
+ if(dangerous_close)
crush()
if(visible && !glass)
set_opacity(1)
@@ -1187,7 +1189,7 @@
update_icon(AIRLOCK_CLOSED, 1)
operating = FALSE
delayed_close_requested = FALSE
- if(safe)
+ if(!dangerous_close)
CheckForMobs()
return TRUE
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 04010ed9446..a801098c01f 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -1128,7 +1128,8 @@ Traitors and the like can also be revived with the previous role mostly intact.
ADMIN_PUNISHMENT_SCARIFY,
ADMIN_PUNISHMENT_SHOES,
ADMIN_PUNISHMENT_DOCK,
- ADMIN_PUNISHMENT_BREAD
+ ADMIN_PUNISHMENT_BREAD,
+ ADMIN_PUNISHMENT_BADLUCK
)
var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in sortList(punishment_list)
@@ -1325,6 +1326,13 @@ Traitors and the like can also be revived with the previous role mostly intact.
var/mutable_appearance/transform_scanline = mutable_appearance('icons/effects/effects.dmi',"transform_effect")
target.transformation_animation(bread_appearance,time= 5 SECONDS,transform_overlay=transform_scanline,reset_after=TRUE)
addtimer(CALLBACK(GLOBAL_PROC, .proc/breadify, target), 5 SECONDS)
+ if(ADMIN_PUNISHMENT_BADLUCK)
+ if(!isliving(target))
+ to_chat(usr, "This must be used on a /mob/living type of mob.", confidential = TRUE)
+ return
+ var/silent = alert("Do you want to apply the omen with a player notification?", "Notify Player?", "Notify", "Silent") == "Silent"
+ var/permanent = alert("Would you like this to be permanent or removed automatically after the first accident?", "Permanent?", "Permanent", "Temporary") == "Permanent"
+ target.AddComponent(/datum/component/omen, silent, null, permanent)
punish_log(target, punishment)