Adds the "Jestographic Sequencer", a clown-exclusive traitor item (#18933)

* Initial commit

* Removes ability for AI/door remotes to interface with cmagged doors

* Adds cleaning method, fixes windoor functionality, makes cmag slippery

* Cleaning cmagged doors checks for remaining space cleaner, cleanup

* Reduced TC cost from 6 to 4, fixed small bug

* Code review super happy funtime

* Adds cooldown to cmag airlock chuckle

* Cleanup, cmag door laugh no longer sounds through walls (again)

* Prevents blank spare IDs from passing through cmagged airlocks

* Cleanup

* Cmagged state is now carried by a trait instead of a global var, cleanup

* Adds early return that I missed, fixes small bug)
This commit is contained in:
FloFluoro
2022-09-05 15:35:45 -05:00
committed by GitHub
parent 53f70608a2
commit 87caa9532b
10 changed files with 165 additions and 4 deletions
+1
View File
@@ -199,6 +199,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Show what machine/door wires do when held.
#define TRAIT_SHOW_WIRE_INFO "show_wire_info"
#define TRAIT_BUTCHERS_HUMANS "butchers_humans"
#define TRAIT_CMAGGED "cmagged"
//
// common trait sources
+2 -1
View File
@@ -71,7 +71,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
),
/obj/item = list(
"TRAIT_SHOW_WIRE_INFO" = TRAIT_SHOW_WIRE_INFO,
"TRAIT_BUTCHER_HUMANS" = TRAIT_BUTCHERS_HUMANS
"TRAIT_BUTCHER_HUMANS" = TRAIT_BUTCHERS_HUMANS,
"TRAIT_CMAGGED" = TRAIT_CMAGGED
)
))
+9
View File
@@ -182,6 +182,15 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
surplus = 75
job = list("Clown")
/datum/uplink_item/jobspecific/cmag
name = "Jestographic Sequencer"
desc = "The jestographic sequencer, also known as a cmag, is a small card that inverts the access on any door it's used on. Perfect for locking command out of their own departments. Honk!"
reference = "CMG"
item = /obj/item/card/cmag
cost = 4
surplus = 75
job = list("Clown")
/datum/uplink_item/jobspecific/trick_revolver
name = "Trick Revolver"
desc = "A revolver that will fire backwards and kill whoever attempts to use it. Perfect for those pesky vigilante or just a good laugh."
+4 -1
View File
@@ -423,7 +423,7 @@
SHOULD_CALL_PARENT(TRUE)
if(updates & NONE)
return // NONE is being sent on purpose, and thus no signal should be sent.
return // NONE is being sent on purpose, and thus no signal should be sent.
updates &= ~SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON, updates)
if(updates & UPDATE_ICON_STATE)
@@ -514,6 +514,9 @@
/atom/proc/unemag()
return
/atom/proc/cmag_act()
return
/**
* Respond to a radioactive wave hitting this atom
*
+16 -1
View File
@@ -605,6 +605,8 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
. = ..()
if(emagged)
. += "<span class='warning'>Its access panel is smoking slightly.</span>"
if(HAS_TRAIT(src, TRAIT_CMAGGED))
. += "<span class='warning'>The access panel is coated in yellow ooze...</span>"
if(note)
if(!in_range(user, src))
. += "There's a [note.name] pinned to the front. You can't [note_type() == "note" ? "read" : "see"] it from here."
@@ -802,7 +804,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
/obj/machinery/door/airlock/proc/ai_control_check(mob/user)
if(!issilicon(user))
return TRUE
if(emagged)
if(emagged || HAS_TRAIT(src, TRAIT_CMAGGED))
to_chat(user, "<span class='warning'>Unable to interface: Internal error.</span>")
return FALSE
if(!canAIControl())
@@ -1336,6 +1338,19 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
emagged = TRUE
return 1
/obj/machinery/door/airlock/cmag_act(mob/user)
if(operating || HAS_TRAIT(src, TRAIT_CMAGGED) || !density || !arePowerSystemsOn())
return
operating = TRUE
update_icon(AIRLOCK_EMAG, 1)
sleep(6)
if(QDELETED(src))
return
operating = FALSE
update_icon(AIRLOCK_CLOSED, 1)
ADD_TRAIT(src, TRAIT_CMAGGED, "clown_emag")
return TRUE
/obj/machinery/door/airlock/emp_act(severity)
. = ..()
if(prob(20 / severity))
+84 -1
View File
@@ -36,6 +36,9 @@
var/unres_sides = 0
//Multi-tile doors
var/width = 1
//Whether nonstandard door sounds (cmag laughter) are off cooldown.
var/sound_ready = TRUE
var/sound_cooldown = 1 SECONDS
/// ID for the window tint button, or another external control
var/id
@@ -116,8 +119,14 @@
if(world.time - mecha.occupant.last_bumped <= 10)
return
if(mecha.occupant && allowed(mecha.occupant) || check_access_list(mecha.operation_req_access))
if(HAS_TRAIT(src, TRAIT_CMAGGED))
cmag_switch(FALSE, mecha.occupant)
return
open()
else
if(HAS_TRAIT(src, TRAIT_CMAGGED))
cmag_switch(TRUE, mecha.occupant)
return
do_animate("deny")
return
@@ -149,6 +158,9 @@
if(density && !emagged)
if(allowed(user))
if(HAS_TRAIT(src, TRAIT_CMAGGED))
cmag_switch(FALSE, user)
return
open()
if(isbot(user))
var/mob/living/simple_animal/bot/B = user
@@ -156,6 +168,9 @@
else
if(pry_open_check(user))
return
if(HAS_TRAIT(src, TRAIT_CMAGGED))
cmag_switch(TRUE, user)
return
do_animate("deny")
/obj/machinery/door/proc/pry_open_check(mob/user)
@@ -204,10 +219,18 @@
return
if(requiresID() && (allowed(user) || user.can_advanced_admin_interact()))
if(density)
if(HAS_TRAIT(src, TRAIT_CMAGGED) && !user.can_advanced_admin_interact()) //cmag should not prevent admin intervention
cmag_switch(FALSE, user)
return
open()
else
if(HAS_TRAIT(src, TRAIT_CMAGGED) && !user.can_advanced_admin_interact())
return
close()
return TRUE
if(HAS_TRAIT(src, TRAIT_CMAGGED))
cmag_switch(TRUE, user)
return
if(density)
do_animate("deny")
@@ -226,7 +249,28 @@
/obj/machinery/door/proc/try_to_crowbar(mob/user, obj/item/I)
return
/obj/machinery/door/proc/clean_cmag_ooze(obj/item/I, mob/user) //Emags are Engineering's problem, cmags are the janitor's problem
var/cleaning = FALSE
if(istype(I, /obj/item/reagent_containers/spray/cleaner))
var/obj/item/reagent_containers/spray/cleaner/C = I
if(C.reagents.total_volume >= C.amount_per_transfer_from_this)
cleaning = TRUE
else
return
if(istype(I, /obj/item/soap))
cleaning = TRUE
if(!cleaning)
return
user.visible_message("<span class='notice'>[user] starts to clean the ooze off the access panel.</span>", "<span class='notice'>You start to clean the ooze off the access panel.</span>")
if(do_after(user, 50, target = src))
user.visible_message("<span class='notice'>[user] cleans the ooze off [src].</span>", "<span class='notice'>You clean the ooze off [src].</span>")
REMOVE_TRAIT(src, TRAIT_CMAGGED, "clown_emag")
/obj/machinery/door/attackby(obj/item/I, mob/user, params)
if(HAS_TRAIT(src, TRAIT_CMAGGED))
clean_cmag_ooze(I, user)
if(user.a_intent != INTENT_HARM && istype(I, /obj/item/twohanded/fireaxe))
try_to_crowbar(user, I)
return 1
@@ -235,7 +279,6 @@
return 1
return ..()
/obj/machinery/door/crowbar_act(mob/user, obj/item/I)
if(user.a_intent == INTENT_HARM)
return
@@ -272,6 +315,46 @@
emagged = TRUE
return TRUE
/obj/machinery/door/cmag_act(mob/user)
if(!density)
return
flick("door_spark", src)
sleep(6) //The cmag doesn't automatically open doors. It inverts access, not provides it!
ADD_TRAIT(src, TRAIT_CMAGGED, "clown_emag")
return TRUE
//Proc for inverting access on cmagged doors."canopen" should always return the OPPOSITE of the normal result.
/obj/machinery/door/proc/cmag_switch(canopen, mob/living/user)
if(!canopen || locked || !hasPower())
if(density) //Windoors can still do their deny animation in unpowered environments, this bugs out if density isn't checked for
do_animate("deny")
if(hasPower() && sound_ready)
playsound(loc, 'sound/machines/honkbot_evil_laugh.ogg', 25, TRUE, ignore_walls = FALSE)
soundcooldown()
return
if(ishuman(user))
var/mob/living/carbon/human/H = user
var/obj/item/card/id/idcard = H.get_idcard()
if(!idcard?.assignment) //Humans can't game inverted access by taking their ID off or using spare IDs.
if(!density)
return
do_animate("deny")
to_chat(H, "<span class='warning'>The airlock speaker chuckles: 'What's wrong, pal? Lost your ID? Nyuk nyuk nyuk!'</span>")
if(sound_ready)
playsound(loc, 'sound/machines/honkbot_evil_laugh.ogg', 25, TRUE, ignore_walls = FALSE)
soundcooldown() //Thanks, mechs
return
if(density)
open()
else
close()
/obj/machinery/door/proc/soundcooldown()
if(!sound_ready)
return
sound_ready = FALSE
addtimer(VARSET_CALLBACK(src, sound_ready, TRUE), sound_cooldown)
/obj/machinery/door/proc/toggle_polarization()
return
+25
View File
@@ -62,6 +62,8 @@
. = ..()
if(emagged)
. += "<span class='warning'>Its access panel is smoking slightly.</span>"
if(HAS_TRAIT(src, TRAIT_CMAGGED))
. += "<span class='warning'>The access panel is coated in yellow ooze...</span>"
/obj/machinery/door/window/emp_act(severity)
. = ..()
@@ -83,8 +85,14 @@
if(ismecha(AM))
var/obj/mecha/mecha = AM
if(mecha.occupant && allowed(mecha.occupant))
if(HAS_TRAIT(src, TRAIT_CMAGGED))
cmag_switch(FALSE)
return
open_and_close()
else
if(HAS_TRAIT(src, TRAIT_CMAGGED))
cmag_switch(TRUE)
return
do_animate("deny")
return
if(!SSticker)
@@ -98,8 +106,14 @@
return
add_fingerprint(user)
if(!requiresID() || allowed(user))
if(HAS_TRAIT(src, TRAIT_CMAGGED))
cmag_switch(FALSE, user)
return
open_and_close()
else
if(HAS_TRAIT(src, TRAIT_CMAGGED))
cmag_switch(TRUE, user)
return
do_animate("deny")
/obj/machinery/door/window/unrestricted_side(mob/M)
@@ -247,6 +261,17 @@
open(2)
return 1
/obj/machinery/door/window/cmag_act(mob/user, obj/weapon)
if(operating || !density || HAS_TRAIT(src, TRAIT_CMAGGED))
return
ADD_TRAIT(src, TRAIT_CMAGGED, "clown_emag")
operating = TRUE
flick("[base_state]spark", src)
playsound(src, "sparks", 75, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
sleep(6)
operating = FALSE
return TRUE
/obj/machinery/door/window/attackby(obj/item/I, mob/living/user, params)
//If it's in the process of opening/closing, ignore the click
if(operating)
+3
View File
@@ -48,6 +48,9 @@
/obj/item/door_remote/afterattack(obj/machinery/door/airlock/D, mob/user)
if(!istype(D))
return
if(HAS_TRAIT(D, TRAIT_CMAGGED))
to_chat(user, "<span class='danger'>The door doesn't respond to [src]!</span>")
return
if(D.is_special)
to_chat(user, "<span class='danger'>[src] cannot access this kind of door!</span>")
return
@@ -80,6 +80,27 @@
return
A.emag_act(user)
/obj/item/card/cmag
desc = "It's a card coated in a slurry of electromagnetic bananium."
name = "jestographic sequencer"
icon_state = "cmag"
item_state = "card-id"
origin_tech = "magnets=2;syndicate=2"
flags = NOBLUDGEON
flags_2 = NO_MAT_REDEMPTION_2
/obj/item/card/cmag/Initialize(mapload)
. = ..()
AddComponent(/datum/component/slippery, src, 16 SECONDS, 100)
/obj/item/card/cmag/attack()
return
/obj/item/card/cmag/afterattack(atom/target, mob/user, proximity)
if(!proximity)
return
target.cmag_act(user)
/obj/item/card/id
name = "identification card"
desc = "A card used to provide ID and determine access across the station."
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB