From 3e7eaeabcef0ecae274e509a4202a37c8eeb5370 Mon Sep 17 00:00:00 2001 From: Ryll Ryll <3589655+Ryll-Ryll@users.noreply.github.com> Date: Sun, 7 Feb 2021 03:53:22 -0500 Subject: [PATCH] Improves Kissing (#56698) Co-authored-by: Mothblocks <35135081+Jared-Fogle@users.noreply.github.com> --- code/__DEFINES/traits.dm | 5 + code/_globalvars/traits.dm | 4 +- .../mood_events/generic_positive_events.dm | 9 + code/datums/quirks/negative.dm | 1 + code/game/objects/items/clown_items.dm | 3 +- code/game/objects/items/cosmetics.dm | 95 +++--- code/game/objects/items/crayons.dm | 9 +- code/game/objects/items/hand_items.dm | 319 ++++++++++++++++++ code/game/objects/items/weaponry.dm | 217 ------------ code/modules/mob/living/carbon/human/human.dm | 22 +- code/modules/mob/living/emote.dm | 18 +- .../surgery/bodyparts/dismemberment.dm | 3 +- code/modules/surgery/bodyparts/head.dm | 3 + sound/{weapons => }/attributions.txt | 2 + sound/effects/kiss.ogg | Bin 0 -> 8415 bytes tgstation.dme | 1 + 16 files changed, 429 insertions(+), 282 deletions(-) create mode 100644 code/game/objects/items/hand_items.dm rename sound/{weapons => }/attributions.txt (93%) create mode 100644 sound/effects/kiss.ogg diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index c7fbb102bdf..eebbe742302 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -242,6 +242,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_COAGULATING "coagulating" //from coagulant reagents, this doesn't affect the bleeding itself but does affect the bleed warning messages /// The holder of this trait has antennae or whatever that hurt a ton when noogied #define TRAIT_ANTENNAE "antennae" +/// Blowing kisses actually does damage to the victim +#define TRAIT_KISS_OF_DEATH "kiss_of_death" #define TRAIT_NOBLEED "nobleed" //This carbon doesn't bleed @@ -350,6 +352,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_BADTOUCH "bad_touch" #define TRAIT_EXTROVERT "extrovert" #define TRAIT_INTROVERT "introvert" +#define TRAIT_ANXIOUS "anxious" ///Trait for dryable items #define TRAIT_DRYABLE "trait_dryable" ///Trait for dried items @@ -479,6 +482,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define ELEMENT_TRAIT "element_trait" /// Trait granted by [/obj/item/clothing/head/helmet/space/hardsuit/berserker] #define BERSERK_TRAIT "berserk_trait" +/// Trait granted by lipstick +#define LIPSTICK_TRAIT "lipstick_trait" /** * Trait granted by [/mob/living/carbon/Initialize] and diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 0e98cecae95..2d0697ccaeb 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -156,7 +156,9 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SNOB" = TRAIT_SNOB, "TRAIT_BALD" = TRAIT_BALD, "TRAIT_BADTOUCH" = TRAIT_BADTOUCH, - "TRAIT_NOBLEED" = TRAIT_NOBLEED + "TRAIT_NOBLEED" = TRAIT_NOBLEED, + "TRAIT_KISS_OF_DEATH" = TRAIT_KISS_OF_DEATH, + "TRAIT_ANXIOUS" = TRAIT_ANXIOUS, ), /obj/item/bodypart = list( diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm index bd65ccf29b3..b4f7427262e 100644 --- a/code/datums/mood_events/generic_positive_events.dm +++ b/code/datums/mood_events/generic_positive_events.dm @@ -223,3 +223,12 @@ description = "Watching fish in aquarium is calming.\n" mood_change = 3 timeout = 1.5 MINUTES + +/datum/mood_event/kiss + description = "Someone blew a kiss at me, I must be a real catch!\n" + mood_change = 1.5 + timeout = 2 MINUTES + +/datum/mood_event/kiss/add_effects(mob/beau) + if(beau) + description = "[beau.name] blew a kiss at me, I must be a real catch!\n" diff --git a/code/datums/quirks/negative.dm b/code/datums/quirks/negative.dm index 5bdb6854e4b..ec2c50da4a2 100644 --- a/code/datums/quirks/negative.dm +++ b/code/datums/quirks/negative.dm @@ -484,6 +484,7 @@ lose_text = "You feel easier about talking again." //if only it were that easy! medical_record_text = "Patient is usually anxious in social encounters and prefers to avoid them." hardcore_value = 4 + mob_trait = TRAIT_ANXIOUS var/dumb_thing = TRUE /datum/quirk/social_anxiety/add() diff --git a/code/game/objects/items/clown_items.dm b/code/game/objects/items/clown_items.dm index 92e0530f262..7640952bbab 100644 --- a/code/game/objects/items/clown_items.dm +++ b/code/game/objects/items/clown_items.dm @@ -139,8 +139,7 @@ user.visible_message("\the [user] washes \the [target]'s mouth out with [src.name]!", "You wash \the [target]'s mouth out with [src.name]!") //washes mouth out with soap sounds better than 'the soap' here if(user.zone_selected == "mouth") if(human_user.lip_style) user.mind?.adjust_experience(/datum/skill/cleaning, CLEAN_SKILL_GENERIC_WASH_XP) - human_user.lip_style = null //removes lipstick - human_user.update_body() + human_user.update_lips(null) decreaseUses(user) return else if(istype(target, /obj/structure/window)) diff --git a/code/game/objects/items/cosmetics.dm b/code/game/objects/items/cosmetics.dm index cabdcdf449c..de4c587a5f4 100644 --- a/code/game/objects/items/cosmetics.dm +++ b/code/game/objects/items/cosmetics.dm @@ -7,6 +7,8 @@ w_class = WEIGHT_CLASS_TINY var/colour = "red" var/open = FALSE + /// A trait that's applied while someone has this lipstick applied, and is removed when the lipstick is removed + var/lipstick_trait /obj/item/lipstick/purple name = "purple lipstick" @@ -21,6 +23,10 @@ name = "black lipstick" colour = "black" +/obj/item/lipstick/black/death + name = "Kiss of Death" + lipstick_trait = TRAIT_KISS_OF_DEATH + /obj/item/lipstick/random name = "lipstick" icon_state = "random_lipstick" @@ -44,60 +50,55 @@ icon_state = "lipstick" /obj/item/lipstick/attack(mob/M, mob/user) - if(!open) + if(!open || !ismob(M)) return - if(!ismob(M)) - return - - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.is_mouth_covered()) - to_chat(user, "Remove [ H == user ? "your" : "[H.p_their()]" ] mask!") - return - if(H.lip_style) //if they already have lipstick on - to_chat(user, "You need to wipe off the old lipstick first!") - return - if(H == user) - user.visible_message("[user] does [user.p_their()] lips with \the [src].", \ - "You take a moment to apply \the [src]. Perfect!") - H.lip_style = "lipstick" - H.lip_color = colour - H.update_body() - else - user.visible_message("[user] begins to do [H]'s lips with \the [src].", \ - "You begin to apply \the [src] on [H]'s lips...") - if(do_after(user, 20, target = H)) - user.visible_message("[user] does [H]'s lips with \the [src].", \ - "You apply \the [src] on [H]'s lips.") - H.lip_style = "lipstick" - H.lip_color = colour - H.update_body() - else + if(!ishuman(M)) to_chat(user, "Where are the lips on that?") + return + + var/mob/living/carbon/human/target = M + if(target.is_mouth_covered()) + to_chat(user, "Remove [ target == user ? "your" : "[target.p_their()]" ] mask!") + return + if(target.lip_style) //if they already have lipstick on + to_chat(user, "You need to wipe off the old lipstick first!") + return + + if(target == user) + user.visible_message("[user] does [user.p_their()] lips with \the [src].", \ + "You take a moment to apply \the [src]. Perfect!") + target.update_lips("lipstick", colour, lipstick_trait) + return + + user.visible_message("[user] begins to do [target]'s lips with \the [src].", \ + "You begin to apply \the [src] on [target]'s lips...") + if(!do_after(user, 2 SECONDS, target = target)) + return + user.visible_message("[user] does [target]'s lips with \the [src].", \ + "You apply \the [src] on [target]'s lips.") + target.update_lips("lipstick", colour, lipstick_trait) + //you can wipe off lipstick with paper! /obj/item/paper/attack(mob/M, mob/user) - if(user.zone_selected == BODY_ZONE_PRECISE_MOUTH) - if(!ismob(M)) - return + if(user.zone_selected != BODY_ZONE_PRECISE_MOUTH || !ishuman(M)) + return ..() + + var/mob/living/carbon/human/target = M + if(target == user) + to_chat(user, "You wipe off the lipstick with [src].") + target.update_lips(null) + return + + user.visible_message("[user] begins to wipe [target]'s lipstick off with \the [src].", \ + "You begin to wipe off [target]'s lipstick...") + if(!do_after(user, 10, target = target)) + return + user.visible_message("[user] wipes [target]'s lipstick off with \the [src].", \ + "You wipe off [target]'s lipstick.") + target.update_lips(null) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H == user) - to_chat(user, "You wipe off the lipstick with [src].") - H.lip_style = null - H.update_body() - else - user.visible_message("[user] begins to wipe [H]'s lipstick off with \the [src].", \ - "You begin to wipe off [H]'s lipstick...") - if(do_after(user, 10, target = H)) - user.visible_message("[user] wipes [H]'s lipstick off with \the [src].", \ - "You wipe off [H]'s lipstick.") - H.lip_style = null - H.update_body() - else - ..() /obj/item/razor name = "electric razor" diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 376d17b5269..0f064ee187d 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -687,9 +687,7 @@ paint_color = "#C0C0C0" update_icon() if(actually_paints) - H.lip_style = "spray_face" - H.lip_color = paint_color - H.update_body() + H.update_lips("spray_face", paint_color) var/used = use_charges(user, 10, FALSE) reagents.trans_to(user, used, volume_multiplier, transfered_by = user, methods = VAPOR) @@ -740,10 +738,7 @@ flash_color(C, flash_color=paint_color, flash_time=40) if(ishuman(C) && actually_paints) var/mob/living/carbon/human/H = C - H.lip_style = "spray_face" - H.lip_color = paint_color - H.update_body() - + H.update_lips("spray_face", paint_color) . = use_charges(user, 10, FALSE) var/fraction = min(1, . / reagents.maximum_volume) reagents.expose(C, VAPOR, fraction * volume_multiplier) diff --git a/code/game/objects/items/hand_items.dm b/code/game/objects/items/hand_items.dm new file mode 100644 index 00000000000..dd5a39d603b --- /dev/null +++ b/code/game/objects/items/hand_items.dm @@ -0,0 +1,319 @@ +// For all of the items that are really just the user's hand used in different ways, mostly (all, really) from emotes + +/obj/item/circlegame + name = "circled hand" + desc = "If somebody looks at this while it's below your waist, you get to bop them." + icon_state = "madeyoulook" + force = 0 + throwforce = 0 + item_flags = DROPDEL | ABSTRACT | HAND_ITEM + attack_verb_continuous = list("bops") + attack_verb_simple = list("bop") + +/obj/item/circlegame/Initialize() + . = ..() + var/mob/living/owner = loc + if(!istype(owner)) + return + RegisterSignal(owner, COMSIG_PARENT_EXAMINE, .proc/ownerExamined) + +/obj/item/circlegame/Destroy() + var/mob/owner = loc + if(istype(owner)) + UnregisterSignal(owner, COMSIG_PARENT_EXAMINE) + return ..() + +/obj/item/circlegame/dropped(mob/user) + UnregisterSignal(user, COMSIG_PARENT_EXAMINE) //loc will have changed by the time this is called, so Destroy() can't catch it + // this is a dropdel item. + return ..() + +/// Stage 1: The mistake is made +/obj/item/circlegame/proc/ownerExamined(mob/living/owner, mob/living/sucker) + SIGNAL_HANDLER + + if(!istype(sucker) || !in_range(owner, sucker)) + return + addtimer(CALLBACK(src, .proc/waitASecond, owner, sucker), 4) + +/// Stage 2: Fear sets in +/obj/item/circlegame/proc/waitASecond(mob/living/owner, mob/living/sucker) + if(QDELETED(sucker) || QDELETED(src) || QDELETED(owner)) + return + + if(owner == sucker) // big mood + to_chat(owner, "Wait a second... you just looked at your own [src.name]!") + addtimer(CALLBACK(src, .proc/selfGottem, owner), 10) + else + to_chat(sucker, "Wait a second... was that a-") + addtimer(CALLBACK(src, .proc/GOTTEM, owner, sucker), 6) + +/// Stage 3A: We face our own failures +/obj/item/circlegame/proc/selfGottem(mob/living/owner) + if(QDELETED(src) || QDELETED(owner)) + return + + playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1) + owner.visible_message("[owner] shamefully bops [owner.p_them()]self with [owner.p_their()] [src.name].", "You shamefully bop yourself with your [src.name].", \ + "You hear a dull thud!") + log_combat(owner, owner, "bopped", src.name, "(self)") + owner.do_attack_animation(owner) + owner.apply_damage(100, STAMINA) + owner.Knockdown(10) + qdel(src) + +/// Stage 3B: We face our reckoning (unless we moved away or they're incapacitated) +/obj/item/circlegame/proc/GOTTEM(mob/living/owner, mob/living/sucker) + if(QDELETED(sucker)) + return + + if(QDELETED(src) || QDELETED(owner)) + to_chat(sucker, "Nevermind... must've been your imagination...") + return + + if(!in_range(owner, sucker) || !(owner.mobility_flags & MOBILITY_USE)) + to_chat(sucker, "Phew... you moved away before [owner] noticed you saw [owner.p_their()] [src.name]...") + return + + to_chat(owner, "[sucker] looks down at your [src.name] before trying to avert [sucker.p_their()] eyes, but it's too late!") + to_chat(sucker, "[owner] sees the fear in your eyes as you try to look away from [owner.p_their()] [src.name]!") + + owner.face_atom(sucker) + if(owner.client) + owner.client.give_award(/datum/award/achievement/misc/gottem, owner) // then everybody clapped + + playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1) + owner.do_attack_animation(sucker) + + if(HAS_TRAIT(owner, TRAIT_HULK)) + owner.visible_message("[owner] bops [sucker] with [owner.p_their()] [src.name] much harder than intended, sending [sucker.p_them()] flying!", \ + "You bop [sucker] with your [src.name] much harder than intended, sending [sucker.p_them()] flying!", "You hear a sickening sound of flesh hitting flesh!", ignored_mobs=list(sucker)) + to_chat(sucker, "[owner] bops you incredibly hard with [owner.p_their()] [src.name], sending you flying!") + sucker.apply_damage(50, STAMINA) + sucker.Knockdown(50) + log_combat(owner, sucker, "bopped", src.name, "(setup- Hulk)") + var/atom/throw_target = get_edge_target_turf(sucker, owner.dir) + sucker.throw_at(throw_target, 6, 3, owner) + else + owner.visible_message("[owner] bops [sucker] with [owner.p_their()] [src.name]!", "You bop [sucker] with your [src.name]!", \ + "You hear a dull thud!", ignored_mobs=list(sucker)) + sucker.apply_damage(15, STAMINA) + log_combat(owner, sucker, "bopped", src.name, "(setup)") + to_chat(sucker, "[owner] bops you with [owner.p_their()] [src.name]!") + qdel(src) + +/obj/item/slapper + name = "slapper" + desc = "This is how real men fight." + icon_state = "latexballon" + inhand_icon_state = "nothing" + force = 0 + throwforce = 0 + item_flags = DROPDEL | ABSTRACT | HAND_ITEM + attack_verb_continuous = list("slaps") + attack_verb_simple = list("slap") + hitsound = 'sound/effects/snap.ogg' + +/obj/item/slapper/attack(mob/M, mob/living/carbon/human/user) + if(ishuman(M)) + var/mob/living/carbon/human/L = M + if(L && L.dna && L.dna.species) + L.dna.species.stop_wagging_tail(M) + user.do_attack_animation(M) + playsound(M, 'sound/weapons/slap.ogg', 50, TRUE, -1) + user.visible_message("[user] slaps [M]!", + "You slap [M]!",\ + "You hear a slap.") + return + +/obj/item/noogie + name = "noogie" + desc = "Get someone in an aggressive grab then use this on them to ruin their day." + icon_state = "latexballon" + inhand_icon_state = "nothing" + force = 0 + throwforce = 0 + item_flags = DROPDEL | ABSTRACT | HAND_ITEM + +/obj/item/noogie/attack(mob/living/carbon/target, mob/living/carbon/human/user) + if(!istype(target)) + to_chat(user, "You don't think you can give this a noogie!") + return + + if(!(target?.get_bodypart(BODY_ZONE_HEAD)) || user.pulling != target || user.grab_state < GRAB_AGGRESSIVE || user.getStaminaLoss() > 80) + return FALSE + + var/obj/item/bodypart/head/the_head = target.get_bodypart(BODY_ZONE_HEAD) + if((target.get_biological_state() != BIO_FLESH_BONE && target.get_biological_state() != BIO_JUST_FLESH) || !the_head.is_organic_limb()) + to_chat(user, "You can't noogie [target], [target.p_they()] [target.p_have()] no skin on [target.p_their()] head!") + return + + // [user] gives [target] a [prefix_desc] noogie[affix_desc]! + var/brutal_noogie = FALSE // was it an extra hard noogie? + var/prefix_desc = "rough" + var/affix_desc = "" + var/affix_desc_target = "" + + if(HAS_TRAIT(target, TRAIT_ANTENNAE)) + prefix_desc = "violent" + affix_desc = "on [target.p_their()] sensitive antennae" + affix_desc_target = "on your highly sensitive antennae" + brutal_noogie = TRUE + if(user.dna?.check_mutation(HULK)) + prefix_desc = "sickeningly brutal" + brutal_noogie = TRUE + + var/message_others = "[prefix_desc] noogie[affix_desc]" + var/message_target = "[prefix_desc] noogie[affix_desc_target]" + + user.visible_message("[user] begins giving [target] a [message_others]!", "You start giving [target] a [message_others]!", vision_distance=COMBAT_MESSAGE_RANGE, ignored_mobs=target) + to_chat(target, "[user] starts giving you a [message_target]!") + + if(!do_after(user, 1.5 SECONDS, target)) + to_chat(user, "You fail to give [target] a noogie!") + to_chat(target, "[user] fails to give you a noogie!") + return + + if(brutal_noogie) + SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "noogie_harsh", /datum/mood_event/noogie_harsh) + else + SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "noogie", /datum/mood_event/noogie) + + noogie_loop(user, target, 0) + +/// The actual meat and bones of the noogie'ing +/obj/item/noogie/proc/noogie_loop(mob/living/carbon/human/user, mob/living/carbon/target, iteration) + if(!(target?.get_bodypart(BODY_ZONE_HEAD)) || user.pulling != target) + return FALSE + + if(user.getStaminaLoss() > 80) + to_chat(user, "You're too tired to continue giving [target] a noogie!") + to_chat(target, "[user] is too tired to continue giving you a noogie!") + return + + var/damage = rand(1, 5) + if(HAS_TRAIT(target, TRAIT_ANTENNAE)) + damage += rand(3,7) + if(user.dna?.check_mutation(HULK)) + damage += rand(3,7) + + if(damage >= 5) + target.emote("scream") + + log_combat(user, target, "given a noogie to", addition = "([damage] brute before armor)") + target.apply_damage(damage, BRUTE, BODY_ZONE_HEAD) + user.adjustStaminaLoss(iteration + 5) + playsound(get_turf(user), pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg'), 50) + + if(prob(33)) + user.visible_message("[user] continues noogie'ing [target]!", "You continue giving [target] a noogie!", vision_distance=COMBAT_MESSAGE_RANGE, ignored_mobs=target) + to_chat(target, "[user] continues giving you a noogie!") + + if(!do_after(user, 1 SECONDS + (iteration * 2), target)) + to_chat(user, "You fail to give [target] a noogie!") + to_chat(target, "[user] fails to give you a noogie!") + return + + iteration++ + noogie_loop(user, target, iteration) + + +/obj/item/kisser + name = "kiss" + desc = "I want you all to know, everyone and anyone, to seal it with a kiss." + icon = 'icons/mob/animal.dmi' + icon_state = "heart" + inhand_icon_state = "nothing" + force = 0 + throwforce = 0 + item_flags = DROPDEL | ABSTRACT | HAND_ITEM + /// The kind of projectile this version of the kiss blower fires + var/kiss_type = /obj/projectile/kiss + +/obj/item/kisser/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + var/obj/projectile/blown_kiss = new kiss_type(get_turf(user)) + user.visible_message("[user] blows \a [blown_kiss] at [target]!", "You blow \a [blown_kiss] at [target]!") + + //Shooting Code: + blown_kiss.spread = 0 + blown_kiss.original = target + blown_kiss.fired_from = user + blown_kiss.firer = user // don't hit ourself that would be really annoying + blown_kiss.impacted = list(user = TRUE) // just to make sure we don't hit the wearer + blown_kiss.preparePixelProjectile(target, user) + blown_kiss.fire() + qdel(src) + +/obj/item/kisser/death + name = "kiss of death" + desc = "If looks could kill, they'd be this." + color = COLOR_BLACK + kiss_type = /obj/projectile/kiss/death + +/obj/projectile/kiss + name = "kiss" + icon = 'icons/mob/animal.dmi' + icon_state = "heart" + hitsound = 'sound/effects/kiss.ogg' + hitsound_wall = 'sound/effects/kiss.ogg' + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + speed = 1.6 + damage_type = BRUTE + damage = 0 + nodamage = TRUE // love can't actually hurt you + armour_penetration = 100 // but if it could, it would cut through even the thickest plate + flag = MAGIC // and most importantly, love is magic~ + +/obj/projectile/kiss/fire(angle, atom/direct_target) + if(firer) + name = "[name] blown by [firer]" + return ..() + +/obj/projectile/kiss/on_hit(atom/target, blocked, pierce_hit) + def_zone = BODY_ZONE_HEAD // let's keep it PG, people + . = ..() + if(!ismob(target)) + return + SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "kiss", /datum/mood_event/kiss, firer) + var/mob/living/target_living = target + if(!(HAS_TRAIT(target_living, TRAIT_ANXIOUS) && prob(30))) + return + + // flustered!!! + var/other_msg + var/self_msg + var/roll = rand(1, 3) + switch(roll) + if(1) + other_msg = "stumbles slightly, turning a bright red!" + self_msg = "You lose control of your limbs for a moment as your blood rushes to your face, turning it bright red!" + target_living.add_confusion(rand(5, 10)) + if(2) + other_msg = "stammers softly for a moment before choking on something!" + self_msg = "You feel your tongue disappear down your throat as you fight to remember how to make words!" + addtimer(CALLBACK(target_living, /atom/movable.proc/say, pick("Uhhh...", "O-oh, uhm...", "I- uhhhhh??", "You too!!", "What?")), rand(0.5 SECONDS, 1.5 SECONDS)) + target_living.stuttering += rand(5, 15) + if(3) + other_msg = "locks up with a stunned look on [target_living.p_their()] face, staring at [firer ? firer : "the ceiling"]!" + self_msg = "Your brain completely fails to process what just happened, leaving you rooted in place staring [firer ? "at [firer]" : "the ceiling"] for what feels like an eternity!" + target_living.face_atom(firer) + target_living.Stun(rand(3 SECONDS, 8 SECONDS)) + + target_living.visible_message("[target_living] [other_msg]", "Whoa! [self_msg]") + +/obj/projectile/kiss/death + name = "kiss of death" + nodamage = FALSE // okay i kinda lied about love not being able to hurt you + damage = 35 + wound_bonus = 0 + sharpness = SHARP_POINTY + color = COLOR_BLACK + +/obj/projectile/kiss/death/on_hit(atom/target, blocked, pierce_hit) + . = ..() + if(!iscarbon(target)) + return + var/mob/living/carbon/heartbreakee = target + var/obj/item/organ/heart/dont_go_breakin_my_heart = heartbreakee.getorganslot(ORGAN_SLOT_HEART) + dont_go_breakin_my_heart.applyOrganDamage(999) diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index 3dcf6d30e7e..d2e225377b8 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -722,223 +722,6 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 else qdel(target) -/obj/item/circlegame - name = "circled hand" - desc = "If somebody looks at this while it's below your waist, you get to bop them." - icon_state = "madeyoulook" - force = 0 - throwforce = 0 - item_flags = DROPDEL | ABSTRACT | HAND_ITEM - attack_verb_continuous = list("bops") - attack_verb_simple = list("bop") - -/obj/item/circlegame/Initialize() - . = ..() - var/mob/living/owner = loc - if(!istype(owner)) - return - RegisterSignal(owner, COMSIG_PARENT_EXAMINE, .proc/ownerExamined) - -/obj/item/circlegame/Destroy() - var/mob/owner = loc - if(istype(owner)) - UnregisterSignal(owner, COMSIG_PARENT_EXAMINE) - return ..() - -/obj/item/circlegame/dropped(mob/user) - UnregisterSignal(user, COMSIG_PARENT_EXAMINE) //loc will have changed by the time this is called, so Destroy() can't catch it - // this is a dropdel item. - return ..() - -/// Stage 1: The mistake is made -/obj/item/circlegame/proc/ownerExamined(mob/living/owner, mob/living/sucker) - SIGNAL_HANDLER - - if(!istype(sucker) || !in_range(owner, sucker)) - return - addtimer(CALLBACK(src, .proc/waitASecond, owner, sucker), 4) - -/// Stage 2: Fear sets in -/obj/item/circlegame/proc/waitASecond(mob/living/owner, mob/living/sucker) - if(QDELETED(sucker) || QDELETED(src) || QDELETED(owner)) - return - - if(owner == sucker) // big mood - to_chat(owner, "Wait a second... you just looked at your own [src.name]!") - addtimer(CALLBACK(src, .proc/selfGottem, owner), 10) - else - to_chat(sucker, "Wait a second... was that a-") - addtimer(CALLBACK(src, .proc/GOTTEM, owner, sucker), 6) - -/// Stage 3A: We face our own failures -/obj/item/circlegame/proc/selfGottem(mob/living/owner) - if(QDELETED(src) || QDELETED(owner)) - return - - playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1) - owner.visible_message("[owner] shamefully bops [owner.p_them()]self with [owner.p_their()] [src.name].", "You shamefully bop yourself with your [src.name].", \ - "You hear a dull thud!") - log_combat(owner, owner, "bopped", src.name, "(self)") - owner.do_attack_animation(owner) - owner.apply_damage(100, STAMINA) - owner.Knockdown(10) - qdel(src) - -/// Stage 3B: We face our reckoning (unless we moved away or they're incapacitated) -/obj/item/circlegame/proc/GOTTEM(mob/living/owner, mob/living/sucker) - if(QDELETED(sucker)) - return - - if(QDELETED(src) || QDELETED(owner)) - to_chat(sucker, "Nevermind... must've been your imagination...") - return - - if(!in_range(owner, sucker) || !(owner.mobility_flags & MOBILITY_USE)) - to_chat(sucker, "Phew... you moved away before [owner] noticed you saw [owner.p_their()] [src.name]...") - return - - to_chat(owner, "[sucker] looks down at your [src.name] before trying to avert [sucker.p_their()] eyes, but it's too late!") - to_chat(sucker, "[owner] sees the fear in your eyes as you try to look away from [owner.p_their()] [src.name]!") - - owner.face_atom(sucker) - if(owner.client) - owner.client.give_award(/datum/award/achievement/misc/gottem, owner) // then everybody clapped - - playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1) - owner.do_attack_animation(sucker) - - if(HAS_TRAIT(owner, TRAIT_HULK)) - owner.visible_message("[owner] bops [sucker] with [owner.p_their()] [src.name] much harder than intended, sending [sucker.p_them()] flying!", \ - "You bop [sucker] with your [src.name] much harder than intended, sending [sucker.p_them()] flying!", "You hear a sickening sound of flesh hitting flesh!", ignored_mobs=list(sucker)) - to_chat(sucker, "[owner] bops you incredibly hard with [owner.p_their()] [src.name], sending you flying!") - sucker.apply_damage(50, STAMINA) - sucker.Knockdown(50) - log_combat(owner, sucker, "bopped", src.name, "(setup- Hulk)") - var/atom/throw_target = get_edge_target_turf(sucker, owner.dir) - sucker.throw_at(throw_target, 6, 3, owner) - else - owner.visible_message("[owner] bops [sucker] with [owner.p_their()] [src.name]!", "You bop [sucker] with your [src.name]!", \ - "You hear a dull thud!", ignored_mobs=list(sucker)) - sucker.apply_damage(15, STAMINA) - log_combat(owner, sucker, "bopped", src.name, "(setup)") - to_chat(sucker, "[owner] bops you with [owner.p_their()] [src.name]!") - qdel(src) - -/obj/item/slapper - name = "slapper" - desc = "This is how real men fight." - icon_state = "latexballon" - inhand_icon_state = "nothing" - force = 0 - throwforce = 0 - item_flags = DROPDEL | ABSTRACT | HAND_ITEM - attack_verb_continuous = list("slaps") - attack_verb_simple = list("slap") - hitsound = 'sound/effects/snap.ogg' - -/obj/item/slapper/attack(mob/M, mob/living/carbon/human/user) - if(ishuman(M)) - var/mob/living/carbon/human/L = M - if(L && L.dna && L.dna.species) - L.dna.species.stop_wagging_tail(M) - user.do_attack_animation(M) - playsound(M, 'sound/weapons/slap.ogg', 50, TRUE, -1) - user.visible_message("[user] slaps [M]!", - "You slap [M]!",\ - "You hear a slap.") - return - -/obj/item/noogie - name = "noogie" - desc = "Get someone in an aggressive grab then use this on them to ruin their day." - icon_state = "latexballon" - inhand_icon_state = "nothing" - force = 0 - throwforce = 0 - item_flags = DROPDEL | ABSTRACT | HAND_ITEM - -/obj/item/noogie/attack(mob/living/carbon/target, mob/living/carbon/human/user) - if(!istype(target)) - to_chat(user, "You don't think you can give this a noogie!") - return - - if(!(target?.get_bodypart(BODY_ZONE_HEAD)) || user.pulling != target || user.grab_state < GRAB_AGGRESSIVE || user.getStaminaLoss() > 80) - return FALSE - - var/obj/item/bodypart/head/the_head = target.get_bodypart(BODY_ZONE_HEAD) - if((target.get_biological_state() != BIO_FLESH_BONE && target.get_biological_state() != BIO_JUST_FLESH) || !the_head.is_organic_limb()) - to_chat(user, "You can't noogie [target], [target.p_they()] [target.p_have()] no skin on [target.p_their()] head!") - return - - // [user] gives [target] a [prefix_desc] noogie[affix_desc]! - var/brutal_noogie = FALSE // was it an extra hard noogie? - var/prefix_desc = "rough" - var/affix_desc = "" - var/affix_desc_target = "" - - if(HAS_TRAIT(target, TRAIT_ANTENNAE)) - prefix_desc = "violent" - affix_desc = "on [target.p_their()] sensitive antennae" - affix_desc_target = "on your highly sensitive antennae" - brutal_noogie = TRUE - if(user.dna?.check_mutation(HULK)) - prefix_desc = "sickeningly brutal" - brutal_noogie = TRUE - - var/message_others = "[prefix_desc] noogie[affix_desc]" - var/message_target = "[prefix_desc] noogie[affix_desc_target]" - - user.visible_message("[user] begins giving [target] a [message_others]!", "You start giving [target] a [message_others]!", vision_distance=COMBAT_MESSAGE_RANGE, ignored_mobs=target) - to_chat(target, "[user] starts giving you a [message_target]!") - - if(!do_after(user, 1.5 SECONDS, target)) - to_chat(user, "You fail to give [target] a noogie!") - to_chat(target, "[user] fails to give you a noogie!") - return - - if(brutal_noogie) - SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "noogie_harsh", /datum/mood_event/noogie_harsh) - else - SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "noogie", /datum/mood_event/noogie) - - noogie_loop(user, target, 0) - -/// The actual meat and bones of the noogie'ing -/obj/item/noogie/proc/noogie_loop(mob/living/carbon/human/user, mob/living/carbon/target, iteration) - if(!(target?.get_bodypart(BODY_ZONE_HEAD)) || user.pulling != target) - return FALSE - - if(user.getStaminaLoss() > 80) - to_chat(user, "You're too tired to continue giving [target] a noogie!") - to_chat(target, "[user] is too tired to continue giving you a noogie!") - return - - var/damage = rand(1, 5) - if(HAS_TRAIT(target, TRAIT_ANTENNAE)) - damage += rand(3,7) - if(user.dna?.check_mutation(HULK)) - damage += rand(3,7) - - if(damage >= 5) - target.emote("scream") - - log_combat(user, target, "given a noogie to", addition = "([damage] brute before armor)") - target.apply_damage(damage, BRUTE, BODY_ZONE_HEAD) - user.adjustStaminaLoss(iteration + 5) - playsound(get_turf(user), pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg'), 50) - - if(prob(33)) - user.visible_message("[user] continues noogie'ing [target]!", "You continue giving [target] a noogie!", vision_distance=COMBAT_MESSAGE_RANGE, ignored_mobs=target) - to_chat(target, "[user] continues giving you a noogie!") - - if(!do_after(user, 1 SECONDS + (iteration * 2), target)) - to_chat(user, "You fail to give [target] a noogie!") - to_chat(target, "[user] fails to give you a noogie!") - return - - iteration++ - noogie_loop(user, target, iteration) - /obj/item/proc/can_trigger_gun(mob/living/user) if(!user.can_use_guns(src)) return FALSE diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 430b2509a49..9f966a18367 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -735,14 +735,28 @@ return TRUE /** - * Cleans the lips of any lipstick. Returns TRUE if the lips had any lipstick and was thus cleaned + * Used to update the makeup on a human and apply/remove lipstick traits, then store/unstore them on the head object in case it gets severed + */ +/mob/living/carbon/human/proc/update_lips(new_style, new_colour, apply_trait) + lip_style = new_style + lip_color = new_colour + update_body() + + var/obj/item/bodypart/head/hopefully_a_head = get_bodypart(BODY_ZONE_HEAD) + REMOVE_TRAITS_IN(src, LIPSTICK_TRAIT) + hopefully_a_head?.stored_lipstick_trait = null + + if(new_style && apply_trait) + ADD_TRAIT(src, apply_trait, LIPSTICK_TRAIT) + hopefully_a_head?.stored_lipstick_trait = apply_trait + +/** + * A wrapper for [mob/living/carbon/human/proc/update_lips] that tells us if there were lip styles to change */ /mob/living/carbon/human/proc/clean_lips() if(isnull(lip_style) && lip_color == initial(lip_color)) return FALSE - lip_style = null - lip_color = initial(lip_color) - update_body() + update_lips(null) return TRUE /** diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 4a620418bd0..dcbd19c7bbc 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -196,8 +196,22 @@ /datum/emote/living/kiss key = "kiss" key_third_person = "kisses" - message = "blows a kiss." - message_param = "blows a kiss to %t." + +/datum/emote/living/kiss/run_emote(mob/living/user, params, type_override, intentional) + . = ..() + if(!.) + return + var/kiss_type = /obj/item/kisser + + if(HAS_TRAIT(user, TRAIT_KISS_OF_DEATH)) + kiss_type = /obj/item/kisser/death + + var/obj/item/kiss_blower = new kiss_type(user) + if(user.put_in_hands(kiss_blower)) + to_chat(user, "You ready your kiss-blowing hand.") + else + qdel(kiss_blower) + to_chat(user, "You're incapable of blowing a kiss in your current state.") /datum/emote/living/laugh key = "laugh" diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index b8418ac7c64..6fd34a8c9c9 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -414,8 +414,7 @@ H.hairstyle = hairstyle H.facial_hair_color = facial_hair_color H.facial_hairstyle = facial_hairstyle - H.lip_style = lip_style - H.lip_color = lip_color + H.update_lips(lip_style, lip_color, stored_lipstick_trait) if(real_name) C.real_name = real_name real_name = "" diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index d5d220b6f2a..c45c3e4eff0 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -37,6 +37,8 @@ var/lip_style = null var/lip_color = "white" + var/stored_lipstick_trait + /obj/item/bodypart/head/Destroy() QDEL_NULL(brainmob) //order is sensitive, see warning in handle_atom_del() below @@ -139,6 +141,7 @@ hairstyle = "Bald" facial_hairstyle = "Shaved" lip_style = null + stored_lipstick_trait = null else if(!animal_origin) var/mob/living/carbon/human/H = C diff --git a/sound/weapons/attributions.txt b/sound/attributions.txt similarity index 93% rename from sound/weapons/attributions.txt rename to sound/attributions.txt index d42313fe061..1c5a6f388d7 100644 --- a/sound/weapons/attributions.txt +++ b/sound/attributions.txt @@ -16,3 +16,5 @@ revolverspin1, revolverspin2, and revolverspin3 are cut from a recording captured by the BBC "Gunfire & Guns: Revolver, Chambers spinning.": http://bbcsfx.acropolis.org.uk/assets/07027165.wav bbc.co.uk - (c) copyright 2018 BBC + +kiss sound: https://freesound.org/people/stereostereo/sounds/117355/ \ No newline at end of file diff --git a/sound/effects/kiss.ogg b/sound/effects/kiss.ogg new file mode 100644 index 0000000000000000000000000000000000000000..699b13a8de84a65421744ea450e42f51824666da GIT binary patch literal 8415 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 Dv|lH6 literal 0 HcmV?d00001 diff --git a/tgstation.dme b/tgstation.dme index e6352b5f58b..e56742ed58c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1058,6 +1058,7 @@ #include "code\game\objects\items\flamethrower.dm" #include "code\game\objects\items\gift.dm" #include "code\game\objects\items\granters.dm" +#include "code\game\objects\items\hand_items.dm" #include "code\game\objects\items\handcuffs.dm" #include "code\game\objects\items\his_grace.dm" #include "code\game\objects\items\holosign_creator.dm"