diff --git a/code/__DEFINES/melee.dm b/code/__DEFINES/melee.dm index 3987040dc31d..32749af19073 100644 --- a/code/__DEFINES/melee.dm +++ b/code/__DEFINES/melee.dm @@ -20,7 +20,7 @@ #define MARTIALART_WORLDBREAKER "worldbreaker" #define MARTIALART_SPACIALLDOMINANCE "absolute spacial dominance" #define MARTIALART_LIGHTNINGFLOW "lightning flow" - +#define MARTIALART_CORPORATEJUDO "corporate judo" //Weapon stat defines #define SWING_SPEED "swing_speed" diff --git a/code/datums/martial/corporate_judo.dm b/code/datums/martial/corporate_judo.dm new file mode 100644 index 000000000000..0a78bc218493 --- /dev/null +++ b/code/datums/martial/corporate_judo.dm @@ -0,0 +1,246 @@ +/* + * Corporate Judo + * An alternative method of dealing with tiders that isn't a stunbaton for security officers. +*/ + +#define DISCOMBOULATE_COMBO "DG" +#define EYEPOKE_COMBO "DH" +#define JUDOTHROW_COMBO "GD" +#define ARMBAR_COMBO "DDG" +#define WHEELTHROW_COMBO "GGD" + +/datum/martial_art/corporate_judo + name = "Corporate Judo" + id = MARTIALART_CORPORATEJUDO + help_verb = /mob/living/carbon/human/proc/corporate_judo_help + nonlethal = TRUE + /// Only allow use of this martial arts if in the main services areas (bar and kitchen); if for some reason, we want to give to the bartender in the future. + var/service_only = FALSE + +/datum/martial_art/corporate_judo/can_use(mob/living/carbon/human/user) + var/area/current_area = get_area(user) + if(service_only) + var/list/restricted_areas = list(/area/crew_quarters/kitchen, /area/crew_quarters/bar) + for(var/area/restricted_area in restricted_areas) + if(istype(current_area, restricted_area)) + return ..() + return FALSE + return ..() + +/datum/martial_art/corporate_judo/teach(mob/living/carbon/human/user, make_temporary = FALSE) + ..() + ADD_TRAIT(user, TRAIT_NO_STUN_WEAPONS, "corporate judo") + +/datum/martial_art/corporate_judo/on_remove(mob/living/carbon/human/user) + REMOVE_TRAIT(user, TRAIT_NO_STUN_WEAPONS, "corporate judo") + return ..() + +/datum/martial_art/corporate_judo/disarm_act(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(!can_use(user) || !can_use(target)) + return FALSE + add_to_streak("D", target) + if(handle_combos(user, target)) + return TRUE + return FALSE + +/datum/martial_art/corporate_judo/grab_act(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(!can_use(user) || !can_use(target)) + return FALSE + add_to_streak("G", target) + if(handle_combos(user, target)) + return TRUE + return FALSE + +/datum/martial_art/corporate_judo/harm_act(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(!can_use(user) || !can_use(target)) + return FALSE + add_to_streak("H", target) + if(handle_combos(user, target)) + return TRUE + return FALSE + +/datum/martial_art/corporate_judo/proc/handle_combos(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(!can_use(user) || !can_use(target)) + return FALSE + if(findtext(streak, ARMBAR_COMBO)) + if(armbar(user, target)) // Can fail. + streak = "" + return TRUE + if(findtext(streak, WHEELTHROW_COMBO)) + if(wheelthrow(user, target)) // Can fail. + streak = "" + return TRUE + if(findtext(streak, DISCOMBOULATE_COMBO)) + streak = "" + discomboulate(user, target) // Will always be true. + return TRUE + if(findtext(streak, EYEPOKE_COMBO)) + streak = "" + eyepoke(user, target) // Will always be true. + return TRUE + if(findtext(streak, JUDOTHROW_COMBO)) + if(judo_throw(user, target)) // Can fail. + streak = "" + return TRUE + + return FALSE // Continue on with the normal act. + +/// Inflicts stamina damage and confuses the target. +/datum/martial_art/corporate_judo/proc/discomboulate(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_attack_animation(target, ATTACK_EFFECT_PUNCH) + target.visible_message( + span_warning("[user] strikes [target] in the head with [user.p_their()] palm!"), + span_userdanger("[user] strikes you with [user.p_their()] palm!") + ) + playsound(get_turf(user), 'sound/weapons/slap.ogg', 40, TRUE, -1) + target.apply_damage(10, STAMINA) + target.set_confusion_if_lower(5 SECONDS) + log_combat(user, target, "discombobulated (Corporate Judo)") + return TRUE + +/// Inflicts brute/stamina damage and tries to temporarily blind/blur the target's vision. +/datum/martial_art/corporate_judo/proc/eyepoke(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_attack_animation(target, ATTACK_EFFECT_PUNCH) + + var/has_head = target.get_bodypart(BODY_ZONE_PRECISE_EYES) + if(!has_head || !user.can_inject(target, FALSE, BODY_ZONE_PRECISE_EYES)) + var/msg = has_head ? "Their eyes are too protected!" : "They do not have a head!" + to_chat(user, span_warning(msg)) + target.visible_message( + span_warning("[user] tries to jabs [target] in [user.p_their()] eyes, but fails!"), + span_userdanger("[user] tries to jab you in the eyes, but fails!") + ) + playsound(get_turf(target), 'sound/weapons/whip.ogg', 40, TRUE, -1) + + user.apply_damage(5, BRUTE, user.get_active_hand()) // Owch, your fingers. + target.apply_damage(5, STAMINA) // A small prize for trying anyways. + log_combat(user, target, "eyepoked fail (Corporate Judo)") + return TRUE + + // Your fingers function like a screwdriver that can bypass normal eye protection. + // Protection reduce the damage and other effects. Lack of eyes increase damage, but completely negates other effects. + target.visible_message( + span_warning("[user] jabs [target] in [user.p_their()] eyes!"), + span_userdanger("[user] jabs you in the eyes!") + ) + playsound(get_turf(target), 'sound/weapons/whip.ogg', 40, TRUE, -1) + + var/eyes_protected = target.is_eyes_covered() + var/obj/item/organ/eyes/eyes = target.getorganslot(ORGAN_SLOT_EYES) + if(!eyes) + var/damage = eyes_protected ? 20 : 30 + target.apply_damage(damage, STAMINA) // I do not want to imagine fingers in empty eye holes. + log_combat(user, target, "eyepoked no-eye (Corporate Judo)") + return TRUE + + var/blindness_duration = eyes_protected ? 2 SECONDS: 4 SECONDS + var/blurriness_duration = eyes_protected ? 10 : 20 // Intentionally not multiplied by seconds. + var/damage = eyes_protected ? 10 : 20 + target.adjust_temp_blindness_up_to(blindness_duration, blindness_duration) + target.adjust_blurriness(max(0, blurriness_duration - target.eye_blurry)) + + target.apply_damage(damage, STAMINA) + log_combat(user, target, "eyepoked (Corporate Judo)") + return TRUE + +/datum/martial_art/corporate_judo/proc/judo_throw(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(!(user.mobility_flags & MOBILITY_STAND)) // User standing. + return FALSE + if(!(target.mobility_flags & MOBILITY_STAND)) // Target standing. + return FALSE + + user.do_attack_animation(target, ATTACK_EFFECT_DISARM) + target.visible_message( + span_warning("[user] judo throws [target] to ground!"), + span_userdanger("[user] judo throws you to the ground!") + ) + playsound(get_turf(target), 'sound/effects/hit_kick.ogg', 40, TRUE, -1) + + target.apply_damage(25, STAMINA) + target.Knockdown(3 SECONDS) + log_combat(user, target, "judothrow (Corporate Judo)") + return TRUE + +/datum/martial_art/corporate_judo/proc/armbar(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(!(user.mobility_flags & MOBILITY_STAND)) // User standing. + return FALSE + if((target.mobility_flags & MOBILITY_STAND)) // Target not standing. + return FALSE + + user.do_attack_animation(target, ATTACK_EFFECT_DISARM) + target.visible_message( + span_warning("[user] puts [target] into an armbar!"), + span_userdanger("[user] wrestles you into an armbar!") + ) + playsound(get_turf(user), 'sound/weapons/slashmiss.ogg', 40, TRUE, -1) + + target.apply_damage(50, STAMINA) + log_combat(user, target, "armbar (Corporate Judo)") + return TRUE + +/datum/martial_art/corporate_judo/proc/wheelthrow(mob/living/carbon/human/user, mob/living/carbon/human/target) + if((target.mobility_flags & MOBILITY_STAND)) // Target not standing. + return FALSE + if(target.getStaminaLoss() < 50) // Target must have taken 50 stamina damage for this finisher. + return FALSE + + user.do_attack_animation(target, ATTACK_EFFECT_DISARM) + target.visible_message( + span_warning("[user] raises [target] over [user.p_their()] shoulder, and slams [target.p_them()] into the ground!"), + span_userdanger("[user] throws you over [user.p_their()] shoulder, slamming you into the ground!") + ) + playsound(get_turf(user), 'sound/magic/tail_swing.ogg', 40, TRUE, -1) + target.SpinAnimation(0.5 SECONDS, 1) + target.apply_damage(120, STAMINA) + target.set_confusion_if_lower(10 SECONDS) + log_combat(user, target, "wheelthrow (Corporate Judo)") + return TRUE + +/mob/living/carbon/human/proc/corporate_judo_help() + set name = "Remember Teachings" // Soo creative, wow. + set desc = "You try to remember the teachings of Corporate Judo." + set category = "Corporate Judo" + var/list/combined_msg = list() + + combined_msg += "You try to remember the teachings of Corporate Judo." + combined_msg += span_notice("As long you know Corporate Judo, you cannot use any stunning weapons such as stunbatons and flashes.") + combined_msg += "[span_notice("Discomboulate")]: Disarm Grab. Deals 10 stamina damage and 5 seconds of confusion." + combined_msg += "[span_notice("Eye Poke")]: Disarm Harm. Deals 20 stamina damage, 20 seconds of blurriness, and 4 seconds of blindness. Effects is halved if they have basic eye protection (e.g. any eyewear). Completely negated if they have lots of eye protection (e.g. hardsuit helmets)." + combined_msg += "[span_notice("Judo Throw")]: Grab Disarm. Deals 25 stamina damage and knockdowns for 3 seconds. Only works on standing targets and if you are standing." + combined_msg += "[span_notice("Armbar")]: Disarm Disarm Grab. Deals 50 stamina damage. Only works on downed targets and if you are standing." + combined_msg += "[span_notice("Wheel Throw")]: Grab Grab Disarm. Deals 120 stamina damage and confuses for 10 seconds. Only works on downed targets that have 50 stamina damage or more." + to_chat(usr, examine_block(combined_msg.Join("\n"))) + +// Apparently, all belts are storage belts. Wrestling belt is the closest we're gonna get. +/obj/item/storage/belt/corporate_judo + name = "\improper Corporate Judo Belt" + desc = "Teaches the wearer NT Corporate Judo." + icon = 'icons/obj/clothing/belts.dmi' + lefthand_file = 'icons/mob/inhands/equipment/belt_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/belt_righthand.dmi' + icon_state = "judobelt" + item_state = "judo" + w_class = WEIGHT_CLASS_BULKY + var/datum/martial_art/corporate_judo/style = new + +/obj/item/storage/belt/corporate_judo/Initialize(mapload) + . = ..() + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + STR.max_items = 1 + STR.set_holdable(list(/obj/item/book/manual/wiki/security_space_law)) + +/obj/item/storage/belt/corporate_judo/equipped(mob/user, slot) + . = ..() + if(!ishuman(user)) + return + if(slot == ITEM_SLOT_BELT) + var/mob/living/carbon/human/human_user = user + style.teach(human_user, 1) + +/obj/item/storage/belt/corporate_judo/dropped(mob/user) + . = ..() + if(!ishuman(user)) + return + var/mob/living/carbon/human/human_user = user + if(human_user.get_item_by_slot(ITEM_SLOT_BELT) == src) + style.remove(human_user) diff --git a/code/datums/status_effects/debuffs/blindness.dm b/code/datums/status_effects/debuffs/blindness.dm new file mode 100644 index 000000000000..26c148e39e52 --- /dev/null +++ b/code/datums/status_effects/debuffs/blindness.dm @@ -0,0 +1,45 @@ +/// Helper macro, for ease of expanding checks for mobs which cannot be blinded +/// There are no reason why these cannot be blinded, it is simply for "design reasons" (these things shouldn't be blinded) + +#define CAN_BE_BLIND(mob) (!isanimal(mob) && !isbrain(mob) && !isrevenant(mob)) + +/// This status effect handles applying a temporary blind to the mob. +/datum/status_effect/temporary_blindness + id = "temporary_blindness" + tick_interval = 2 SECONDS + alert_type = null + remove_on_fullheal = TRUE + +/datum/status_effect/temporary_blindness/on_creation(mob/living/new_owner, duration = 10 SECONDS) + src.duration = duration + return ..() + +/datum/status_effect/temporary_blindness/on_apply() + if(!CAN_BE_BLIND(owner)) + return FALSE + + owner.become_blind(id) + return TRUE + +/datum/status_effect/temporary_blindness/on_remove() + owner.cure_blind(id) + +/datum/status_effect/temporary_blindness/tick(delta_time, times_fired) + if(owner.stat == DEAD) + return + + // Temp. blindness heals faster if our eyes are covered + if(!HAS_TRAIT_FROM(src, TRAIT_BLIND, EYES_COVERED)) + return + + // Knocks 2 seconds off of our duration + // If we should be deleted, give a message letting them know + var/mob/living/stored_owner = owner + if(remove_duration(2 SECONDS)) + to_chat(stored_owner, span_green("Your eyes start to feel better!")) + return + // Otherwise add a chance to let them know that it's working + else if(DT_PROB(5, delta_time)) + var/obj/item/thing_covering_eyes = owner.is_eyes_covered() + // "Your blindfold soothes your eyes", for example + to_chat(owner, span_green("Your [thing_covering_eyes?.name || "eye covering"] soothes your eyes.")) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index 12a29dddd2fe..8ceb3568d0ac 100755 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -75,6 +75,7 @@ new /obj/item/radio/security(src) new /obj/item/storage/backpack/duffelbag/clothing/sec/head(src) new /obj/item/barrier_taperoll/police(src) + new /obj/item/storage/belt/corporate_judo(src) /obj/structure/closet/secure_closet/warden name = "\proper warden's locker" diff --git a/icons/mob/inhands/equipment/belt_lefthand.dmi b/icons/mob/inhands/equipment/belt_lefthand.dmi index 39d634317334..45f175e059f9 100644 Binary files a/icons/mob/inhands/equipment/belt_lefthand.dmi and b/icons/mob/inhands/equipment/belt_lefthand.dmi differ diff --git a/icons/mob/inhands/equipment/belt_righthand.dmi b/icons/mob/inhands/equipment/belt_righthand.dmi index 0d144eed638a..31356478ebea 100644 Binary files a/icons/mob/inhands/equipment/belt_righthand.dmi and b/icons/mob/inhands/equipment/belt_righthand.dmi differ diff --git a/icons/obj/clothing/belts.dmi b/icons/obj/clothing/belts.dmi index 7736d7d2ab26..de487872be10 100644 Binary files a/icons/obj/clothing/belts.dmi and b/icons/obj/clothing/belts.dmi differ diff --git a/yogstation.dme b/yogstation.dme index 710605c1580a..8a78e30b3e90 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -714,6 +714,7 @@ #include "code\datums\mapgen\Cavegens\LavalandGenerator.dm" #include "code\datums\martial\boxing.dm" #include "code\datums\martial\buster_style.dm" +#include "code\datums\martial\corporate_judo.dm" #include "code\datums\martial\cqc.dm" #include "code\datums\martial\flying_fang.dm" #include "code\datums\martial\hunterfu.dm" @@ -772,6 +773,7 @@ #include "code\datums\status_effects\status_effect.dm" #include "code\datums\status_effects\wound_effects.dm" #include "code\datums\status_effects\buffs\buffs.dm" +#include "code\datums\status_effects\debuffs\blindness.dm" #include "code\datums\status_effects\debuffs\blue_eye.dm" #include "code\datums\status_effects\debuffs\confusion.dm" #include "code\datums\status_effects\debuffs\debuffs.dm"