diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index fab0781ad7c..4b17843f2e8 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -54,6 +54,8 @@ #define STATUS_EFFECT_HOPE /datum/status_effect/hope // Healing that scales when injured (but not in deep crit), and nice messages to keep the hope +#define STATUS_EFFECT_DRILL_PAYBACK /datum/status_effect/drill_payback // Slight antistun and healing, along with visual effect. Works only in range of the vault, and for 30 seconds after it ends. + ///////////// // DEBUFFS // ///////////// diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index f6c8880a485..bd9282432b6 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -99,6 +99,10 @@ /obj/screen/fullscreen/impaired icon_state = "impairedoverlay" +/obj/screen/fullscreen/payback + icon_state = "payback" + show_when_dead = TRUE + /obj/screen/fullscreen/flash icon = 'icons/mob/screen_gen.dmi' screen_loc = "WEST,SOUTH to EAST,NORTH" diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index cd534e4b6d9..392f9ecba61 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -547,6 +547,43 @@ else to_chat(owner, "[pick(un_hopeful_messages)]") +/datum/status_effect/drill_payback + duration = -1 + status_type = STATUS_EFFECT_UNIQUE + alert_type = null + var/drilled_successfully = FALSE + var/times_warned = 0 + var/obj/structure/safe/drilled + +/datum/status_effect/drill_payback/on_creation(mob/living/new_owner, obj/structure/safe/S) + drilled = S + return ..() + +/datum/status_effect/drill_payback/on_apply() + owner.overlay_fullscreen("payback", /obj/screen/fullscreen/payback, 0) + addtimer(CALLBACK(src, PROC_REF(payback_phase_2)), 2.7 SECONDS) + return TRUE + +/datum/status_effect/drill_payback/proc/payback_phase_2() + owner.clear_fullscreen("payback") + owner.overlay_fullscreen("payback", /obj/screen/fullscreen/payback, 1) + +/datum/status_effect/drill_payback/tick() //They are not staying down. This will be a fight. + if(!drilled_successfully && (get_dist(owner, drilled) >= 9)) //We don't want someone drilling the safe at arivals then raiding bridge with the buff + to_chat(owner, "Get back to the safe, they are going to get the drill!") + times_warned++ + if(times_warned >= 6) + owner.remove_status_effect(STATUS_EFFECT_DRILL_PAYBACK) + return + if(owner.stat != DEAD) + owner.adjustBruteLoss(-3) + owner.adjustFireLoss(-3) + owner.adjustStaminaLoss(-25) + +/datum/status_effect/drill_payback/on_remove() + . = ..() + owner.clear_fullscreen("payback") + /datum/status_effect/thrall_net id = "thrall_net" tick_interval = 2 SECONDS diff --git a/code/datums/uplink_items/uplink_nuclear.dm b/code/datums/uplink_items/uplink_nuclear.dm index d81eb1005d4..66cc4fc68d6 100644 --- a/code/datums/uplink_items/uplink_nuclear.dm +++ b/code/datums/uplink_items/uplink_nuclear.dm @@ -410,10 +410,10 @@ // DEVICES AND TOOLS /datum/uplink_item/device_tools/diamond_drill - name = "Diamond Tipped Thermal Safe Drill" - desc = "A diamond tipped thermal drill with magnetic clamps for the purpose of quickly drilling hardened objects. Guaranteed 100% jam proof." + name = "Amplifying Diamond Tipped Thermal Safe Drill" + desc = "A diamond tipped thermal drill with magnetic clamps for the purpose of quickly drilling hardened objects. Comes with built in security detection and nanite system, to keep you up if security comes a-knocking." reference = "DDRL" - item = /obj/item/thermal_drill/diamond_drill + item = /obj/item/thermal_drill/diamond_drill/syndicate cost = 1 uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) diff --git a/code/datums/uplink_items/uplink_traitor.dm b/code/datums/uplink_items/uplink_traitor.dm index 47fccd6761a..6cc5fc092c0 100644 --- a/code/datums/uplink_items/uplink_traitor.dm +++ b/code/datums/uplink_items/uplink_traitor.dm @@ -373,10 +373,10 @@ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) /datum/uplink_item/device_tools/thermal_drill // Nukies get Diamond Tipped Thermal Safe Drill instead - name = "Thermal Safe Drill" - desc = "A tungsten carbide thermal drill with magnetic clamps for the purpose of drilling hardened objects. Guaranteed 100% jam proof." + name = "Amplifying Thermal Safe Drill" + desc = "A tungsten carbide thermal drill with magnetic clamps for the purpose of drilling hardened objects. Comes with built in security detection and nanite system, to keep you up if security comes a-knocking." reference = "DRL" - item = /obj/item/thermal_drill + item = /obj/item/thermal_drill/syndicate cost = 1 excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) diff --git a/code/game/objects/items/devices/thermal_drill.dm b/code/game/objects/items/devices/thermal_drill.dm index 55dac21f348..884c60db5aa 100644 --- a/code/game/objects/items/devices/thermal_drill.dm +++ b/code/game/objects/items/devices/thermal_drill.dm @@ -6,8 +6,11 @@ w_class = WEIGHT_CLASS_GIGANTIC force = 15.0 var/time_multiplier = 1 + var/payback = FALSE + var/spotted = FALSE var/datum/looping_sound/thermal_drill/soundloop var/datum/effect_system/spark_spread/spark_system + var/datum/song/song /obj/item/thermal_drill/New() ..() @@ -15,14 +18,54 @@ spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(1, 0, src) spark_system.attach(src) + song = new(src, SSinstruments.synthesizer_instrument_ids) /obj/item/thermal_drill/Destroy() QDEL_NULL(soundloop) QDEL_NULL(spark_system) + QDEL_NULL(song) return ..() +/obj/item/thermal_drill/attack_self(mob/user) + add_fingerprint(user) + ui_interact(user) + +/obj/item/thermal_drill/ui_data(mob/user) + return song.ui_data(user) + +/obj/item/thermal_drill/ui_interact(mob/user) + if(!payback) + return + song.ui_interact(user) + +/obj/item/thermal_drill/ui_act(action, params) + if(..()) + return + return song.ui_act(action, params) + +/** + * Whether the instrument should stop playing + * + * Arguments: + * * user - The user + */ +/obj/item/thermal_drill/proc/should_stop_playing(mob/user) + if(!payback && spotted) + return TRUE + return FALSE + +/obj/item/thermal_drill/syndicate + name = "amplified thermal safe drill" + desc = "A tungsten carbide thermal drill with magnetic clamps for the purpose of drilling hardened objects. Comes with an inbuilt morale booster and security detector, to assist in drilling." + payback = TRUE + /obj/item/thermal_drill/diamond_drill name = "diamond tipped thermal safe drill" desc = "A diamond tipped thermal drill with magnetic clamps for the purpose of quickly drilling hardened objects. Guaranteed 100% jam proof." icon_state = "diamond_drill" time_multiplier = 0.5 + +/obj/item/thermal_drill/diamond_drill/syndicate + name = "amplified diamond tipped thermal safe drill" + desc = "A diamond tipped thermal drill with magnetic clamps for the purpose of quickly drilling hardened objects. Comes with an inbuilt morale booster and security detector, to assist in drilling." + payback = TRUE diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm index 765b61c5f7e..af25037ff74 100644 --- a/code/game/objects/items/weapons/storage/uplink_kits.dm +++ b/code/game/objects/items/weapons/storage/uplink_kits.dm @@ -78,7 +78,7 @@ /obj/item/clothing/gloves/color/latex/nitrile, // 0 TC /obj/item/clothing/mask/gas/clown_hat, // 0TC /obj/item/grenade/plastic/c4, // 1TC - /obj/item/thermal_drill/diamond_drill, // 1TC + /obj/item/thermal_drill/diamond_drill/syndicate, // 1TC /obj/item/encryptionkey/syndicate) // 2TC var/static/list/implant = list( // 40TC diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm index f16683d709d..1d26ccdb968 100644 --- a/code/game/objects/structures/safe.dm +++ b/code/game/objects/structures/safe.dm @@ -62,6 +62,8 @@ GLOBAL_LIST_EMPTY(safes) var/drill_y_offset = -3 /// Used by [/obj/item/paper/safe_code] to tell the codes through a paper spawned on map load. var/known_by = list() + /// Who placed the drill on the safe. Used to trigger the status effect + var/mob/living/carbon/human/driller /obj/structure/safe/Initialize(mapload) . = ..() @@ -83,9 +85,10 @@ GLOBAL_LIST_EMPTY(safes) drill.soundloop.stop() drill.forceMove(loc) drill = null - + driller = null QDEL_NULL(progress_bar) QDEL_NULL(drill_overlay) + clear_payback() //Lets not leave the overlay if an admin deletes the safe during the event return ..() /obj/structure/safe/process() @@ -96,6 +99,8 @@ GLOBAL_LIST_EMPTY(safes) add_overlay(progress_bar) if(prob(DRILL_SPARK_CHANCE)) drill.spark_system.start() + if(!drill.spotted && drill.payback) + security_check() /obj/structure/safe/examine(mob/user) . = ..() @@ -149,14 +154,17 @@ GLOBAL_LIST_EMPTY(safes) drill_start_time = world.time drill.soundloop.start() update_icon() + driller = user START_PROCESSING(SSobj, src) if("Turn Off") - if(do_after(user, 2 SECONDS, target = src)) + if(do_after(user, 10 SECONDS, target = src)) //Can't be too easy to turn off deltimer(drill_timer) drill_timer = null drill.soundloop.stop() cut_overlay(progress_bar) update_icon() + driller.remove_status_effect(STATUS_EFFECT_DRILL_PAYBACK) + driller = null STOP_PROCESSING(SSobj, src) if("Remove Drill") if(drill_timer) @@ -322,6 +330,43 @@ GLOBAL_LIST_EMPTY(safes) add_fingerprint(usr) +/obj/structure/safe/proc/security_check() + if(get_dist(src, driller) >= 9) + return //You need to be near the drill if you want to get the buff. + for(var/mob/living/carbon/human/H in view(9, src)) + if(H.job in list("Security Officer", "Detective", "Warden", "Head of Security", "Captain", "Clown") || H.mind.special_role == SPECIAL_ROLE_ERT) + if(H.mind && H.mind.special_role && H.mind.special_role != SPECIAL_ROLE_ERT) + continue + drill.spotted = TRUE + security_assualt_in_progress() + return + for(var/mob/living/carbon/human/H in view(9, driller)) + if(H.job in list("Security Officer", "Detective", "Warden", "Head of Security", "Captain", "Clown") || H.mind.special_role == SPECIAL_ROLE_ERT) + if(H.mind && H.mind.special_role && H.mind.special_role != SPECIAL_ROLE_ERT) + continue + drill.spotted = TRUE + security_assualt_in_progress() + return + +/obj/structure/safe/proc/security_assualt_in_progress() + drill.atom_say("Security spotted. Nanites deployed. Give them hell.") + driller.apply_status_effect(STATUS_EFFECT_DRILL_PAYBACK, src) + drill.song.start_playing(driller) + notify_ghosts("Security assault in progress in [get_area(src)]!", enter_link="(Click to jump to!)", source = src, action = NOTIFY_FOLLOW) + for(var/mob/dead/observer/O in GLOB.player_list) + O.overlay_fullscreen("payback", /obj/screen/fullscreen/payback, 0) + addtimer(CALLBACK(src, PROC_REF(ghost_payback_phase_2)), 2.7 SECONDS) + +/obj/structure/safe/proc/ghost_payback_phase_2() + for(var/mob/dead/observer/O in GLOB.player_list) + O.clear_fullscreen("payback") + O.overlay_fullscreen("payback", /obj/screen/fullscreen/payback, 1) + addtimer(CALLBACK(src, PROC_REF(ghost_payback_phase_2)), 2 MINUTES) + +/obj/structure/safe/proc/clear_payback() + for(var/mob/dead/observer/O in GLOB.player_list) + O.clear_fullscreen("payback") + /** * Called every dial turn to determine whether the safe should unlock or not. */ @@ -361,6 +406,13 @@ GLOBAL_LIST_EMPTY(safes) playsound(loc, 'sound/machines/ding.ogg', 50, 1) cut_overlay(progress_bar) update_icon() + if(drill.payback) + var/datum/status_effect/drill_payback/D = driller.has_status_effect(STATUS_EFFECT_DRILL_PAYBACK) + if(D) + D.drilled_successfully = TRUE + addtimer(CALLBACK(driller, TYPE_PROC_REF(/mob/living, remove_status_effect), STATUS_EFFECT_DRILL_PAYBACK), 30 SECONDS) //Give them time to escape + drill.payback = FALSE //Can't be used again / no more adding timers + drill.song.stop_playing() STOP_PROCESSING(SSobj, src) /** diff --git a/code/modules/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm index 1491ac71857..bcb235d099b 100644 --- a/code/modules/instruments/songs/_song.dm +++ b/code/modules/instruments/songs/_song.dm @@ -414,3 +414,12 @@ var/obj/structure/musician/M = parent return M.should_stop_playing(user) +// subtype for thermal drills +/datum/song/thermal_drill + +/datum/song/thermal_drill/should_stop_playing(mob/user) + . = ..() + if(.) + return TRUE + var/obj/item/thermal_drill/D = parent + return D.should_stop_playing(user) diff --git a/icons/mob/screen_full.dmi b/icons/mob/screen_full.dmi index d1aa736b352..316f4770e71 100644 Binary files a/icons/mob/screen_full.dmi and b/icons/mob/screen_full.dmi differ