diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index fcc650570add2..b12ae4f6da29a 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -577,6 +577,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Trait that determines vulnerability to being stunned from a shove #define TRAIT_STUN_ON_NEXT_SHOVE "stun on next shove" +/// Trait that determines whether our mob gains more strength from drinking during a fist fight +#define TRAIT_DRUNKEN_BRAWLER "drunken brawler" + // METABOLISMS // Various jobs on the station have historically had better reactions // to various drinks and foodstuffs. Security liking donuts is a classic diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 36a166e5341fa..2b446ea25442f 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -207,6 +207,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_DOUBLE_TAP" = TRAIT_DOUBLE_TAP, "TRAIT_DREAMING" = TRAIT_DREAMING, "TRAIT_DRINKS_BLOOD" = TRAIT_DRINKS_BLOOD, + "TRAIT_DRUNKEN_BRAWLER" = TRAIT_DRUNKEN_BRAWLER, "TRAIT_DUMB" = TRAIT_DUMB, "TRAIT_DWARF" = TRAIT_DWARF, "TRAIT_EASILY_WOUNDED" = TRAIT_EASILY_WOUNDED, diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm index 13a4162ff3be9..c0f200c82f7b6 100644 --- a/code/modules/jobs/job_types/bartender.dm +++ b/code/modules/jobs/job_types/bartender.dm @@ -59,6 +59,8 @@ glasses = /obj/item/clothing/glasses/sunglasses/reagent shoes = /obj/item/clothing/shoes/laceup + skillchips = list(/obj/item/skillchip/drunken_brawler) + /datum/outfit/job/bartender/post_equip(mob/living/carbon/human/H, visualsOnly) . = ..() diff --git a/code/modules/library/skill_learning/skillchip.dm b/code/modules/library/skill_learning/skillchip.dm index 4390b09569fa5..b7d6d28c77520 100644 --- a/code/modules/library/skill_learning/skillchip.dm +++ b/code/modules/library/skill_learning/skillchip.dm @@ -502,6 +502,15 @@ activate_message = span_notice("You think of your favourite food and realise that you can rotate its flavour in your mind.") deactivate_message = span_notice("You feel your food-based mind palace crumbling...") +/obj/item/skillchip/drunken_brawler + name = "F0RC3 4DD1CT10N skillchip" + auto_traits = list(TRAIT_DRUNKEN_BRAWLER) + skill_name = "Drunken Unarmed Proficiency" + skill_description = "When intoxicated, you gain increased unarmed effectiveness." + skill_icon = "wine-bottle" + activate_message = span_notice("You honestly could do with a drink. Never know when someone might try and jump you around here.") + deactivate_message = span_notice("You suddenly feel a lot safer going around the station sober... ") + /obj/item/skillchip/musical name = "\improper Old Copy of \"Space Station 13: The Musical\"" desc = "An old copy of \"Space Station 13: The Musical\", \ diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index 9c8d615f94d5c..ad5f850051c67 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -1034,14 +1034,32 @@ GLOBAL_LIST_EMPTY(features_by_species) var/damage = rand(attacking_bodypart.unarmed_damage_low, attacking_bodypart.unarmed_damage_high) var/limb_accuracy = attacking_bodypart.unarmed_effectiveness + // In a brawl, drunkenness can make you swing more wildly and with more force, and thus catch your opponent off guard, but it could also totally throw you off if you're too intoxicated + // But god is it going to make you sick moving too much while drunk + var/user_drunkenness = user.get_drunk_amount() + + if(user_drunkenness && HAS_TRAIT(user, TRAIT_DRUNKEN_BRAWLER)) // Drunken brawlers only need to be intoxicated, doesn't matter how much + limb_accuracy += clamp((user.getFireLoss() + user.getBruteLoss()) * 0.5, 10, 200) + damage += damage * clamp((user.getFireLoss() + user.getBruteLoss()) / 100, 0.3, 2) //Basically a multiplier of how much extra damage you get based on how low your health is overall. A floor of about a 30%. + var/drunken_martial_descriptor = pick("Drunken", "Intoxicated", "Tipsy", "Inebriated", "Delirious", "Day-Drinker's", "Firegut", "Blackout") + atk_verb = "[drunken_martial_descriptor] [atk_verb]" + + else if(user_drunkenness > 30 && user_drunkenness < 60) + limb_accuracy *= 1.2 + user.adjust_disgust(2) + + else if(user_drunkenness >= 60) + limb_accuracy = -limb_accuracy // good luck landing a punch now, you drunk fuck + user.adjust_disgust(5) + var/obj/item/bodypart/affecting = target.get_bodypart(target.get_random_valid_zone(user.zone_selected)) var/miss_chance = 100//calculate the odds that a punch misses entirely. considers stamina and brute damage of the puncher. punches miss by default to prevent weird cases if(attacking_bodypart.unarmed_damage_low) - if((target.body_position == LYING_DOWN) || HAS_TRAIT(user, TRAIT_PERFECT_ATTACKER) || staggered) //kicks and attacks against staggered targets never miss (provided your species deals more than 0 damage) + if((target.body_position == LYING_DOWN) || HAS_TRAIT(user, TRAIT_PERFECT_ATTACKER) || staggered || user_drunkenness && HAS_TRAIT(user, TRAIT_DRUNKEN_BRAWLER)) //kicks and attacks against staggered targets never miss (provided your species deals more than 0 damage). Drunken brawlers while drunk also don't miss miss_chance = 0 else - miss_chance = clamp(UNARMED_MISS_CHANCE_BASE - limb_accuracy + user.getStaminaLoss() + (user.getBruteLoss()*0.5), 0, UNARMED_MISS_CHANCE_MAX) //Limb miss chance + various damage. capped at 80 so there is at least a chance to land a hit. + miss_chance = clamp(UNARMED_MISS_CHANCE_BASE - limb_accuracy + (user.getFireLoss()*0.5 + user.getBruteLoss()*0.5), 0, UNARMED_MISS_CHANCE_MAX) //Limb miss chance + various damage. capped at 80 so there is at least a chance to land a hit. if(!damage || !affecting || prob(miss_chance))//future-proofing for species that have 0 damage/weird cases where no zone is targeted playsound(target.loc, attacking_bodypart.unarmed_miss_sound, 25, TRUE, -1) @@ -1053,6 +1071,20 @@ GLOBAL_LIST_EMPTY(features_by_species) var/armor_block = target.run_armor_check(affecting, MELEE) + // In a brawl, drunkenness is a boon if you're a bit drunk but not too much. Else you're easier to hit. + // But, generally, getting hit while drunk is probably a good way to start throwing up + var/target_drunkenness = target.get_drunk_amount() + if(target_drunkenness && HAS_TRAIT(target, TRAIT_DRUNKEN_BRAWLER)) // Drunken brawlers only need to be intoxicated, doesn't matter how much + armor_block += 20 + + else if(target_drunkenness > 30 && target_drunkenness < 60) + armor_block += 10 + target.adjust_disgust(2) + + else if(target_drunkenness >= 60) + armor_block *= 0.5 + target.adjust_disgust(5) + playsound(target.loc, attacking_bodypart.unarmed_attack_sound, 25, TRUE, -1) if(grappled && attacking_bodypart.grappled_attack_verb) @@ -1080,6 +1112,65 @@ GLOBAL_LIST_EMPTY(features_by_species) target.force_say() log_combat(user, target, "punched") + // If our target is staggered and has sustained enough damage, we can apply a randomly determined status effect to inflict when we punch them. + // The effects are based on the punching effectiveness of our attacker. Some effects are not reachable by the average human, and require augmentation to reach or being a species with a heavy punch effectiveness. + // Or they're just drunk enough. + if(HAS_TRAIT(target, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED) || target.stat == DEAD) //If our target is dead or has specailized armor, there is no way to inflict these effects. + return + + // If our target is staggered, the target's armor, minus our limb effectiveness sets the minimum necessary amount of damage sustained to cause an effect. Minimum 40, max 200 for sanity reasons + if(staggered && (target.getFireLoss()*0.5 + target.getBruteLoss()*0.5) >= min(armor_block - limb_accuracy, 40, 200)) + stagger_combo(user, target, atk_verb, limb_accuracy, armor_block) + +/// Handles the stagger combo effect of our punch. Follows the same logic as the above proc, target is our owner, user is our attacker. +/datum/species/proc/stagger_combo(mob/living/carbon/human/user, mob/living/carbon/human/target, atk_verb = "hit", limb_accuracy = 0, armor_block = 0) + // Randomly determines the effects of our punch. Limb accuracy is a bonus, armor block is a defense + var/roll_them_bones = rand(-20, 20) + limb_accuracy - armor_block + + switch(roll_them_bones) + if (-INFINITY to 0) //Mostly a gimmie, this one just keeps them staggered briefly + target.adjust_staggered_up_to(1 SECONDS, 10 SECONDS) + target.visible_message(span_warning("[user]'s [atk_verb] briefly winds [target]!"), \ + span_warning("You are briefly winded by [user]'s [atk_verb]!"), span_hear("You hear a thud!"), COMBAT_MESSAGE_RANGE, user) + to_chat(user, span_warning("Your [atk_verb] briefly winds [target]!")) + + if (1 to 10) + target.adjust_eye_blur_up_to(5 SECONDS, 10 SECONDS) + target.visible_message(span_warning("[user]'s [atk_verb] hits [target] so hard, their eyes water! Ouch!"), \ + span_warning("You are hit viciously by [user]'s [atk_verb], and your eyes begin to water!"), span_hear("You hear a thud!"), COMBAT_MESSAGE_RANGE, user) + to_chat(user, span_warning("Your [atk_verb] causes [target] to tear up!")) + + if (11 to 30) + target.adjust_dizzy_up_to(5 SECONDS, 10 SECONDS) + target.adjust_eye_blur_up_to(5 SECONDS, 10 SECONDS) + target.adjust_confusion_up_to(5 SECONDS, 10 SECONDS) + target.visible_message(span_warning("[user]'s [atk_verb] hits [target] so hard, they are sent reeling in agony! Damn!"), \ + span_warning("You are hit viciously by [user]'s [atk_verb], and everything becomes a dizzying blur!"), span_hear("You hear a thud!"), COMBAT_MESSAGE_RANGE, user) + to_chat(user, span_warning("Your [atk_verb] causes [target] to go stumbling about in a confuzed daze!")) + + if(31 to 40) + target.adjust_dizzy_up_to(5 SECONDS, 10 SECONDS) + target.adjust_confusion_up_to(5 SECONDS, 10 SECONDS) + target.adjust_temp_blindness_up_to(5 SECONDS, 10 SECONDS) + target.visible_message(span_warning("[user]'s [atk_verb] hits [target] so hard, they are sent reeling blindly in agony! Goddamn!"), \ + span_warning("You are hit viciously by [user]'s [atk_verb], and everything becomes a dizzying, blinding blur!"), span_hear("You hear a thud!"), COMBAT_MESSAGE_RANGE, user) + to_chat(user, span_warning("Your [atk_verb] causes [target] to go stumbling about in a confuzed, blind daze!")) + + if (41 to 45) + target.apply_effect(4 SECONDS, EFFECT_KNOCKDOWN, armor_block) + target.visible_message(span_warning("[user]'s [atk_verb] hits [target] so hard, you knock them off their feet! Holy shit!"), \ + span_warning("You are hit viciously by [user]'s [atk_verb] and sent toppling head over heels!"), span_hear("You hear a sickening thud!"), COMBAT_MESSAGE_RANGE, user) + to_chat(user, span_warning("Your [atk_verb] lands, and you send [target] sailing off their feet!")) + + if (46 to INFINITY) + target.apply_effect(4 SECONDS, EFFECT_KNOCKDOWN, armor_block) + var/obj/item/bodypart/affecting = target.get_bodypart(target.get_random_valid_zone(user.zone_selected)) + target.apply_damage(5, BRUTE, affecting, armor_block, wound_bonus = limb_accuracy * 2) //Mostly for the crunchy wounding effect than actually doing damage + target.visible_message(span_warning("[user]'s [atk_verb] hits [target] so hard, you hit them off their feet with a loud crunch! Fucking hell!"), \ + span_warning("You are hit viciously by [user]'s [atk_verb], and suddenly feel an overwhelming pain as you topple head over heels!"), span_hear("You hear a sickening crack and a loud thud!"), COMBAT_MESSAGE_RANGE, user) + to_chat(user, span_warning("Your [atk_verb] lands, and [target] is sent crashing to the floor with the immense force! Good god!")) + + /datum/species/proc/disarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style) if(user.body_position != STANDING_UP) return FALSE diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index e157840c58a81..7fe844d7bcea8 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1209,7 +1209,7 @@ /mob/living/resist_grab(moving_resist) . = TRUE //If we're in an aggressive grab or higher, we're lying down, we're vulnerable to grabs, or we're staggered and we have some amount of stamina loss, we must resist - if(pulledby.grab_state || body_position == LYING_DOWN || HAS_TRAIT(src, TRAIT_GRABWEAKNESS) || get_timed_status_effect_duration(/datum/status_effect/staggered) && getStaminaLoss() >= 30) + if(pulledby.grab_state || body_position == LYING_DOWN || HAS_TRAIT(src, TRAIT_GRABWEAKNESS) || get_timed_status_effect_duration(/datum/status_effect/staggered) && (getFireLoss()*0.5 + getBruteLoss()*0.5) >= 40) var/altered_grab_state = pulledby.grab_state if((body_position == LYING_DOWN || HAS_TRAIT(src, TRAIT_GRABWEAKNESS) || get_timed_status_effect_duration(/datum/status_effect/staggered)) && pulledby.grab_state < GRAB_KILL) //If prone, resisting out of a grab is equivalent to 1 grab state higher. won't make the grab state exceed the normal max, however altered_grab_state++