diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 84eed5483a..3259d9f800 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -15,6 +15,8 @@ #define DISABLE_DEATHRATTLE (1<<12) #define DISABLE_ARRIVALRATTLE (1<<13) #define COMBOHUD_LIGHTING (1<<14) +#define VERB_CONSENT (1<<15) // Skyrat - ERP Mechanic Addition +#define LEWD_VERB_SOUNDS (1<<16) // Skyrat - ERP Mechanic Addition #define TOGGLES_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|MEMBER_PUBLIC|INTENT_STYLE|MIDROUND_ANTAG|SOUND_INSTRUMENTS|SOUND_SHIP_AMBIENCE|SOUND_PRAYERS|SOUND_ANNOUNCEMENTS) diff --git a/code/__HELPERS/_cit_helpers.dm b/code/__HELPERS/_cit_helpers.dm index 5e57dd5328..461bff1da6 100644 --- a/code/__HELPERS/_cit_helpers.dm +++ b/code/__HELPERS/_cit_helpers.dm @@ -102,27 +102,29 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE) if(!src.holder) return message_admins("[key_name_admin(usr)] manually reloaded mentors") -/mob/living/carbon/proc/has_penis() +/*pretty much everything got moved to modular_skyrat (interactions) +/mob/living/carbon/has_penis() // Skyrat Change var/obj/item/organ/genital/G = getorganslot(ORGAN_SLOT_PENIS) if(G && istype(G, /obj/item/organ/genital/penis)) return TRUE return FALSE -/mob/living/carbon/proc/has_balls() +/mob/living/carbon/proc/has_balls() // Skyrat Change var/obj/item/organ/genital/G = getorganslot(ORGAN_SLOT_TESTICLES) if(G && istype(G, /obj/item/organ/genital/testicles)) return TRUE return FALSE -/mob/living/carbon/proc/has_vagina() +/mob/living/carbon/has_vagina() // Skyrat Change if(getorganslot(ORGAN_SLOT_VAGINA)) return TRUE return FALSE -/mob/living/carbon/proc/has_breasts() +/mob/living/carbon/has_breasts() // Skyrat Change if(getorganslot(ORGAN_SLOT_BREASTS)) return TRUE return FALSE +*/ /mob/living/carbon/proc/is_groin_exposed(list/L) if(!L) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index aba380f3e9..bf1c2505da 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -82,6 +82,14 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/be_random_body = 0 //whether we'll have a random body every round var/gender = MALE //gender of character (well duh) var/age = 30 //age of character + //SKYRAT CHANGES BEGIN + var/erppref = "Ask" + var/nonconpref = "Ask" + var/vorepref = "Ask" + var/extremepref = "No" //This is for extreme shit, maybe even literal shit, better to keep it on no by default + var/extremeharm = "No" //If "extreme content" is enabled, this option serves as a toggle for the related interactions to cause damage or not + var/language = "" + //SKYRAT CHANGES END var/underwear = "Nude" //underwear type var/undie_color = "FFF" var/undershirt = "Nude" //undershirt type @@ -164,7 +172,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) ) var/custom_speech_verb = "default" //if your say_mod is to be something other than your races var/custom_tongue = "default" //if your tongue is to be something other than your races - var/language = "" var/list/custom_names = list() var/preferred_ai_core_display = "Blue" @@ -369,7 +376,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "" + mutant_category = 0 if(pref_species.mutant_bodyparts["mam_ears"]) if(!mutant_category) @@ -1086,6 +1100,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(4) // Content preferences dat += "
" dat += "

Flavor Text

" - dat += "Set Examine Text
" + dat += "Set Examine Text" //skyrat -
moved one line down if(length(features["flavor_text"]) <= 40) if(!length(features["flavor_text"])) dat += "\[...\]" @@ -396,6 +403,12 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "[features["ooc_notes"]]" else dat += "[TextPreview(features["ooc_notes"])]...
" + //SKYRAT EDIT + dat += "Set OOC Notes
" + dat += "ERP : [erppref]
" + dat += "Non-Con : [nonconpref]
" + dat += "Vore : [vorepref]
" + //END OF SKYRAT EDIT dat += "

Body

" dat += "Gender:[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]
" if(gender != NEUTER && pref_species.sexes) @@ -606,6 +619,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) mutant_category++ if(mutant_category >= MAX_MUTANT_ROWS) dat += "
" dat += "

Fetish content prefs

" + dat += "Allow Lewd Verbs: [(toggles & VERB_CONSENT) ? "Yes":"No"]
" // Skyrat - ERP Mechanic Addition + dat += "Mute Lewd Verb Sounds: [(toggles & LEWD_VERB_SOUNDS) ? "Yes":"No"]
" // Skyrat - ERP Mechanic Addition dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"]
" dat += "Voracious MediHound sleepers: [(cit_toggles & MEDIHOUND_SLEEPER) ? "Yes" : "No"]
" dat += "Hear Vore Sounds: [(cit_toggles & EATING_NOISES) ? "Yes" : "No"]
" @@ -1102,6 +1118,11 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Hypno: [(cit_toggles & NEVER_HYPNO) ? "Disallowed" : "Allowed"]
" dat += "Aphrodisiacs: [(cit_toggles & NO_APHRO) ? "Disallowed" : "Allowed"]
" dat += "Ass Slapping: [(cit_toggles & NO_ASS_SLAP) ? "Disallowed" : "Allowed"]
" + //SKYRAT EDIT + dat += "Extreme ERP verbs : [extremepref]
" // https://youtu.be/0YrU9ASVw6w + if(extremepref != "No") + dat += " [extremeharm]
" + //END OF SKYRAT EDIT dat += "
" dat += "
" if(5) // Custom keybindings @@ -2453,6 +2474,50 @@ GLOBAL_LIST_EMPTY(preferences_datums) damagescreenshake = 1 if("nameless") nameless = !nameless + + //Skyrat begin + if("erp_pref") + switch(erppref) + if("Yes") + erppref = "Ask" + if("Ask") + erppref = "No" + if("No") + erppref = "Yes" + if("noncon_pref") + switch(nonconpref) + if("Yes") + nonconpref = "Ask" + if("Ask") + nonconpref = "No" + if("No") + nonconpref = "Yes" + if("vore_pref") + switch(vorepref) + if("Yes") + vorepref = "Ask" + if("Ask") + vorepref = "No" + if("No") + vorepref = "Yes" + //Skyrat edit - *someone* offered me actual money for this shit + if("extremepref") //i hate myself for doing this + switch(extremepref) //why the fuck did this need to use cycling instead of input from a list + if("Yes") //seriously this confused me so fucking much + extremepref = "Ask" + if("Ask") + extremepref = "No" + extremeharm = "No" + if("No") + extremepref = "Yes" + if("extremeharm") + switch(extremeharm) + if("Yes") //this is cursed code + extremeharm = "No" + if("No") + extremeharm = "Yes" + if(extremepref == "No") + extremeharm = "No" //END CITADEL EDIT if("publicity") if(unlock_content) @@ -2573,6 +2638,12 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("hear_midis") toggles ^= SOUND_MIDI + if("verb_consent") // Skyrat - ERP Mechanic Addition + toggles ^= VERB_CONSENT // Skyrat - ERP Mechanic Addition + + if("mute_lewd_verb_sounds") // Skyrat - ERP Mechanic Addition + toggles ^= LEWD_VERB_SOUNDS // Skyrat - ERP Mechanic Addition + if("lobby_music") toggles ^= SOUND_LOBBY if((toggles & SOUND_LOBBY) && user.client && isnewplayer(user)) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index d32184edb5..b75f7a6357 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -151,6 +151,12 @@ var/list/msg = list() + if(client && client.prefs) // Skyrat Change + if(client.prefs.toggles & VERB_CONSENT) // Skyrat Change + . += "[t_His] player has allowed lewd verbs.\n" // Skyrat Change + else // Skyrat Change + . += "[t_His] player has not allowed lewd verbs.\n" // Skyrat Change + var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) var/list/disabled = list() for(var/X in bodyparts) diff --git a/modular_citadel/code/modules/client/preferences_savefile.dm b/modular_citadel/code/modules/client/preferences_savefile.dm index c747c4cf32..c71c6b018b 100644 --- a/modular_citadel/code/modules/client/preferences_savefile.dm +++ b/modular_citadel/code/modules/client/preferences_savefile.dm @@ -14,6 +14,20 @@ features["mcolor2"] = sanitize_hexcolor(features["mcolor2"], 3, 0) features["mcolor3"] = sanitize_hexcolor(features["mcolor3"], 3, 0) + //skyrat begin + erppref = sanitize_text(S["erp_pref"], "Ask") + if(!length(erppref)) erppref = "Ask" + nonconpref = sanitize_text(S["noncon_pref"], "Ask") + if(!length(nonconpref)) nonconpref = "Ask" + vorepref = sanitize_text(S["vore_pref"], "Ask") + if(!length(vorepref)) vorepref = "Ask" + extremepref = sanitize_text(S["extremepref"], "No") //god has forsaken me + if(!length(extremepref)) + extremepref = "No" + extremeharm = sanitize_text(S["extremeharm"], "No") + if(!length(extremeharm) || (extremepref = "No")) + extremeharm = "No" + //skyrat end /datum/preferences/proc/cit_character_pref_save(savefile/S) //ipcs @@ -37,3 +51,9 @@ WRITE_FILE(S["feature_flavor_text"], features["flavor_text"]) WRITE_FILE(S["silicon_feature_flavor_text"], features["silicon_flavor_text"]) + //skyrat stuff + WRITE_FILE(S["erp_pref"], erppref) + WRITE_FILE(S["noncon_pref"], nonconpref) + WRITE_FILE(S["vore_pref"], vorepref) + WRITE_FILE(S["extremepref"], extremepref) + WRITE_FILE(S["extremeharm"], extremeharm) diff --git a/sandcode/code/datums/shuttles.dm b/sandcode/code/datums/shuttles.dm index 6f863122c2..590f2e1b02 100644 --- a/sandcode/code/datums/shuttles.dm +++ b/sandcode/code/datums/shuttles.dm @@ -21,4 +21,8 @@ /datum/map_template/shuttle/sandcode/scioutpost port_id = "axsscioutpost" name = "Science Outpost Access Shuttle" - suffix = "smol" \ No newline at end of file + suffix = "smol" + +/datum/map_template/shuttle/mining/large //port tg mining shuttle + suffix = "large" + name = "mining shuttle (Large)" diff --git a/sandcode/code/interactions/_interaction.dm b/sandcode/code/interactions/_interaction.dm new file mode 100644 index 0000000000..0236ec0e59 --- /dev/null +++ b/sandcode/code/interactions/_interaction.dm @@ -0,0 +1,183 @@ +/********************************** +*******Interactions code by HONKERTRON feat TestUnit******** +**Contains a lot ammount of ERP and MEHANOYEBLYA** +**CREDIT TO ATMTA STATION FOR MOST OF THIS CODE, I ONLY MADE IT WORK IN /vg/ - Matt +** Rewritten 30/08/16 by Zuhayr, sry if I removed anything important. +**I removed ERP and replaced it with handholding. Nothing of worth was lost. - Vic +**Fuck you, Vic. ERP is back. - TT +**>using var/ on everything, also TRUE +***********************************/ + + +// Rectum? Damn near killed 'em. +var/list/interactions + +/proc/make_interactions(interaction) + if(!interactions) + interactions = list() + for(var/itype in subtypesof(/datum/interaction)) + var/datum/interaction/I = new itype() + interactions[I.command] = I + +/mob/living/carbon/human/proc/list_interaction_attributes() + var/dat = "" + if(has_hands()) + dat += "...have hands." + if(has_mouth()) + if(dat != "") + dat += "
" + dat += "...have a mouth, which is [mouth_is_free() ? "uncovered" : "covered"]." + return dat + +/datum/interaction + var/command = "interact" + var/description = "Interact with them." + var/simple_message + var/simple_style = "notice" + var/write_log_user + var/write_log_target + + var/interaction_sound + + var/max_distance = 1 + var/require_ooc_consent = FALSE + var/require_user_mouth + var/require_user_hands + var/require_target_mouth + var/require_target_hands + var/needs_physical_contact + + var/user_is_target = FALSE //Boolean. Pretty self explanatory. + +/datum/interaction/proc/evaluate_user(mob/living/carbon/human/user, silent = TRUE) + if(user.get_refraction_dif()) + if(!silent) //bye spam + to_chat(user, "You're still exhausted from the last time. You need to wait [DisplayTimeText(user.get_refraction_dif(), TRUE)] until you can do that!") + return FALSE + + if(require_user_mouth) + if(!user.has_mouth()) + if(!silent) + to_chat(user, "You don't have a mouth.") + return FALSE + + if(!user.mouth_is_free()) + if(!silent) + to_chat(user, "Your mouth is covered.") + return FALSE + + if(require_user_hands && !user.has_hands()) + if(!silent) + to_chat(user, "You don't have hands.") + return FALSE + + if(user.last_interaction_time < world.time) + return TRUE + return FALSE + +/datum/interaction/proc/evaluate_target(mob/living/carbon/human/user, mob/living/carbon/human/target, silent = TRUE) + if(!user_is_target) + if(user == target) + if(!silent) + to_chat(user, "You can't do that to yourself.") + return FALSE + + if(require_target_mouth) + if(!target.has_mouth()) + if(!silent) + to_chat(user, "They don't have a mouth.") + return FALSE + + if(!target.mouth_is_free()) + if(!silent) + to_chat(user, "Their mouth is covered.") + return FALSE + + if(require_target_hands && !target.has_hands()) + if(!silent) + to_chat(user, "They don't have hands.") + return FALSE + + return TRUE + +/datum/interaction/proc/get_action_link_for(mob/living/carbon/human/user, mob/living/carbon/human/target) + return "[description]
" + +/datum/interaction/Topic(href, href_list) + if(..()) + return TRUE + if(href_list["action"]) + do_action(locate(href_list["action_user"]), locate(href_list["action_target"])) + return TRUE + return FALSE + +/datum/interaction/proc/do_action(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(!user_is_target) + if(user == target) //tactical href fix + to_chat(user, "You cannot target yourself.") + return + if(get_dist(user, target) > max_distance) + //user << "They are too far away." + user.visible_message("They are too far away.") + return + if(needs_physical_contact && !(user.Adjacent(target) && target.Adjacent(user))) + //user << "You cannot get to them." + user.visible_message("You cannot get to them.") + return + if(!evaluate_user(user, silent = FALSE)) + return + if(!evaluate_target(user, target, silent = FALSE)) + return + + if(write_log_user) + user.log_message("[write_log_user] [target]", INDIVIDUAL_ATTACK_LOG) + if(write_log_target) + target.log_message("[write_log_target] [user]", INDIVIDUAL_ATTACK_LOG) + + display_interaction(user, target) + post_interaction(user, target) + + //if(write_log_user) + //add_logs(target, user, "fucked") + //user.attack_log += text("\[[time_stamp()]\] [write_log_user] [target.name] ([target.ckey])") + //if(write_log_target) + //add_logs(target, user, "fucked2") + //target.attack_log += text("\[[time_stamp()]\] [write_log_target] [user.name] ([user.ckey])") + +/datum/interaction/proc/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(simple_message) + var/use_message = replacetext(simple_message, "USER", "\the [user]") + use_message = replacetext(use_message, "TARGET", "\the [target]") + user.visible_message("[capitalize(use_message)]") + +/datum/interaction/proc/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.last_interaction_time = world.time + 6 + if(interaction_sound) + playsound(get_turf(user), interaction_sound, 50, 1, -1) + return +/* +/atom/movable/attack_hand(mob/living/carbon/human/user) + . = ..() + if(can_buckle && buckled_mob) + if(user_unbuckle_mob(user)) + return TRUE + +/atom/movable/MouseDrop_T(mob/living/carbon/human/M, mob/living/carbon/human/user) + . = ..() + if(can_buckle && istype(M) && !buckled_mob) + if(user_buckle_mob(M, user)) + return TRUE + + +/atom/movable/attack_hand(mob/living/carbon/human/user) + . = ..() + if(can_buckle && buckled_mob) + if(user_unbuckle_mob(user)) + return TRUE + +/atom/movable/MouseDrop_T(mob/living/carbon/human/M, mob/living/carbon/human/user) + . = ..() + if(can_buckle && istype(M) && !buckled_mob) + if(user_buckle_mob(M, user)) + return TRUE +*/ diff --git a/sandcode/code/interactions/interaction_definitions.dm b/sandcode/code/interactions/interaction_definitions.dm new file mode 100644 index 0000000000..040e63d1f8 --- /dev/null +++ b/sandcode/code/interactions/interaction_definitions.dm @@ -0,0 +1,71 @@ +/datum/interaction/handshake + command = "handshake" + description = "Shake their hand." + simple_message = "USER shakes the hand of TARGET." + require_user_hands = 1 + needs_physical_contact = 1 + +/datum/interaction/pat + command = "pat" + description = "Pat their shoulder." + simple_message = "USER pats TARGET's shoulder." + require_user_hands = 1 + needs_physical_contact = 1 + +/datum/interaction/cheer + command = "cheer" + description = "Cheer them on." + require_user_mouth = 1 + simple_message = "USER cheers TARGET on!" + +/datum/interaction/highfive + command = "highfive" + description = "Give them a high-five." + require_user_mouth = 1 + simple_message = "USER high fives TARGET!" + interaction_sound = 'sandcode/sound/interactions/slap.ogg' + needs_physical_contact = 1 + +/datum/interaction/headpat + command = "headpat" + description = "Pat their head. Aww..." + require_user_hands = 1 + simple_message = "USER headpats TARGET!" + needs_physical_contact = 1 + +/datum/interaction/salute + command = "salute" + description = "Give them a firm salute!" + require_user_hands = 1 + simple_message = "USER salutes TARGET sharply!" + max_distance = 25 + +/datum/interaction/fistbump + command = "fistbump" + description = "Bump it!" + require_user_hands = 1 + simple_message = "USER fistbumps TARGET! Yeah!" + needs_physical_contact = 1 + +/datum/interaction/pinkypromise + command = "pinkypromise" + description = "Make a pinky promise with them!" + require_user_hands = 1 + simple_message = "USER hooks their pinky with TARGET's! Pinky Promise!" + needs_physical_contact = 1 + +/datum/interaction/bird + command = "bird" + description = "Flip them the bird!" + require_user_hands = 1 + simple_message = "USER gives TARGET the bird!" + max_distance = 25 + +/datum/interaction/holdhand + command = "holdhand" + description = "Hold their hand." + require_user_hands = 1 + simple_message = "USER holds TARGET's hand." + max_distance = 25 + needs_physical_contact = 1 + max_distance = 25 diff --git a/sandcode/code/interactions/interaction_interface.dm b/sandcode/code/interactions/interaction_interface.dm new file mode 100644 index 0000000000..9d73cd8092 --- /dev/null +++ b/sandcode/code/interactions/interaction_interface.dm @@ -0,0 +1,65 @@ +/mob/proc/try_interaction() + return + +/* +/mob/living/carbon/human/MouseDrop(var/mob/living/carbon/human/dropped_on, mob/living/carbon/human/user as mob) + if(src != dropped_on && !src.restrained()) + try_interaction(dropped_on) + return + return ..() +*/ + +/mob/living/carbon/human/MouseDrop_T(mob/M as mob, mob/living/carbon/human/user as mob) + . = ..() + if(M == src || src == usr || M != usr) + return + if(usr.restrained()) + return + if(!ishuman(src)) + return + + user.try_interaction(src) + +/mob/living/carbon/human/verb/interact_with() + set name = "Interact With" + set desc = "Perform an interaction with someone." + set category = "IC" + set src in view() + + if(!usr.restrained() && ishuman(src)) + usr.try_interaction(src) + +/mob/living/carbon/human/try_interaction(mob/living/carbon/human/partner) + var/dat + if(partner != src) + dat = "
Interacting with \the [partner]...

" + else + dat = "
Interacting with yourself...

" + + dat += "You...
[list_interaction_attributes(src)]
" + if(partner != src) + dat += "They...
[partner.list_interaction_attributes(src)]
" + + make_interactions() + for(var/interaction_key in interactions) + var/datum/interaction/I = interactions[interaction_key] + if(I.evaluate_user(src) && I.evaluate_target(src, partner)) + dat += I.get_action_link_for(src, partner) + + var/datum/browser/popup = new(usr, "interactions", "Interactions", 340, 480) + popup.set_content(dat) + popup.open() + +/* +/atom/movable/attack_hand(mob/living/carbon/human/user) + . = ..() + if(can_buckle && buckled_mob) + if(user_unbuckle_mob(user)) + return 1 + +/atom/movable/MouseDrop_T(mob/living/carbon/human/M, mob/living/carbon/human/user) + . = ..() + if(can_buckle && istype(M) && !buckled_mob) + if(user_buckle_mob(M, user)) + return 1 +*/ diff --git a/sandcode/code/interactions/interaction_mob.dm b/sandcode/code/interactions/interaction_mob.dm new file mode 100644 index 0000000000..f75fcdc299 --- /dev/null +++ b/sandcode/code/interactions/interaction_mob.dm @@ -0,0 +1,50 @@ +/mob/living/proc/has_hands() + return FALSE + +/mob/living/has_hands() + return TRUE//(can_use_hand("l_hand") || can_use_hand("r_hand")) + +/mob/living/proc/has_mouth() + return TRUE + +/mob/living/proc/mouth_is_free() + return TRUE + +/mob/living/proc/foot_is_free() + return TRUE + +/mob/living/carbon/human/has_mouth() + var/obj/item/bodypart/head/headass + for(var/obj/item/bodypart/head/shoeonhead in bodyparts) + headass = shoeonhead + if(headass) + return TRUE + return FALSE + +/mob/living/mouth_is_free() + if(istype(src, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + return !H.wear_mask + else + return TRUE + +/mob/living/foot_is_free() + if(istype(src, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + return !H.shoes + else + return TRUE + +///atom/movable/attack_hand(mob/living/carbon/human/user) +// . = ..() +// if(can_buckle && buckled_mob) +// if(user_unbuckle_mob(user)) +// return 1 +/* +/atom/movable/MouseDrop_T(mob/living/carbon/human/M, mob/living/carbon/human/user) + . = ..() + if(can_buckle && istype(M) && !buckled_mob) + if(user_buckle_mob(M, user)) + return TRUE + +*/ diff --git a/sandcode/code/interactions/lewd/_lewd.dm b/sandcode/code/interactions/lewd/_lewd.dm new file mode 100644 index 0000000000..9e55d22568 --- /dev/null +++ b/sandcode/code/interactions/lewd/_lewd.dm @@ -0,0 +1,1689 @@ +#define CUM_TARGET_MOUTH "mouth" +#define CUM_TARGET_THROAT "throat" +#define CUM_TARGET_VAGINA "vagina" +#define CUM_TARGET_ANUS "anus" +#define CUM_TARGET_HAND "hand" +#define CUM_TARGET_BREASTS "breasts" +#define CUM_TARGET_FEET "feet" +//Weird defines go here +#define CUM_TARGET_EARS "ears" +#define CUM_TARGET_EYES "eyes" +// +#define GRINDING_FACE_WITH_ANUS "faceanus" +#define GRINDING_FACE_WITH_FEET "facefeet" +#define GRINDING_MOUTH_WITH_FEET "mouthfeet" +#define THIGH_SMOTHERING "thigh_smother" +#define NUTS_TO_FACE "nut_face" + +#define NORMAL_LUST 10 +#define LOW_LUST 1 + +#define REQUIRE_NONE 0 +#define REQUIRE_EXPOSED 1 +#define REQUIRE_UNEXPOSED 2 +#define REQUIRE_ANY 3 + +/*-------------------------------------------------- + -------------------MOB STUFF---------------------- + -------------------------------------------------- +*/ +//I'm sorry, lewd should not have mob procs such as life() and such in it. //NO SHIT IT SHOULDNT I REMOVED THEM + +/proc/playlewdinteractionsound(turf/turf_source, soundin, vol as num, vary, extrarange as num ,frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S, envwet = -10000, envdry = 0, manual_x, manual_y) + var/list/hearing_mobs + for(var/mob/H in get_hearers_in_view(4, turf_source)) + if(!H.client || (H.client.prefs.toggles & LEWD_VERB_SOUNDS)) + continue + LAZYADD(hearing_mobs, H) + for(var/mob/H in hearing_mobs) + H.playsound_local(turf_source, soundin, vol, vary, frequency, falloff) + +/mob/living + var/has_penis = FALSE + var/has_vagina = FALSE + var/has_breasts = FALSE + var/last_partner + var/last_orifice + var/obj/item/organ/last_genital + var/lastmoan + var/sexual_potency = 15 + var/lust_tolerance = 100 + var/lastlusttime = 0 + var/lust = 0 + var/multiorgasms = 1 + var/refractory_period = 0 + var/last_interaction_time = 0 + var/datum/interaction/lewd/last_lewd_datum //Recording our last lewd datum allows us to do stuff like custom cum messages. + //Yes i feel like an idiot writing this. + var/cleartimer //Timer for clearing the "last_lewd_datum". This prevents some oddities. + +/mob/living/proc/clear_lewd_datum() + last_lewd_datum = null + last_genital = null + +/mob/living/Initialize() + . = ..() + sexual_potency = rand(10,25) + lust_tolerance = rand(75,200) + +/mob/living/proc/get_refraction_dif() + var/dif = (refractory_period - world.time) + if(dif < 0) + return 0 + else + return dif + + +/mob/living/proc/add_lust(add) + var/cur = src.get_lust() //GetLust handles per-time lust loss + if((cur + add) < 0) //in case we retract lust + lust = 0 + else + lust = cur + add + + +/mob/living/proc/get_lust() + var/curtime = world.time + var/dif = (curtime - lastlusttime)/10 //how much lust would we lose over time + if((lust - dif) < 0) + lust = 0 + else + lust = lust - dif + + lastlusttime = world.time + return lust + +/mob/living/proc/set_lust(num) + lust = num + lastlusttime = world.time + +/mob/living/proc/has_penis(var/nintendo = REQUIRE_ANY) + var/mob/living/carbon/C = src + if(istype(C)) + var/obj/item/organ/genital/peepee = C.getorganslot(ORGAN_SLOT_PENIS) + if(peepee) + switch(nintendo) + if(REQUIRE_ANY) + return TRUE + if(REQUIRE_EXPOSED) + if(peepee.is_exposed()) + return TRUE + else + return FALSE + if(REQUIRE_UNEXPOSED) + if(!peepee.is_exposed()) + return TRUE + else + return FALSE + else + return TRUE + return FALSE + +/mob/living/proc/has_balls(var/nintendo = REQUIRE_ANY) + var/mob/living/carbon/C = src + if(istype(C)) + var/obj/item/organ/genital/peepee = C.getorganslot(ORGAN_SLOT_TESTICLES) + if(peepee) + switch(nintendo) + if(REQUIRE_ANY) + return TRUE + if(REQUIRE_EXPOSED) + if(peepee.is_exposed()) + return TRUE + else + return FALSE + if(REQUIRE_UNEXPOSED) + if(!peepee.is_exposed()) + return TRUE + else + return FALSE + else + return TRUE + return FALSE + +/mob/living/proc/has_vagina(var/nintendo = REQUIRE_ANY) + var/mob/living/carbon/C = src + if(istype(C)) + var/obj/item/organ/genital/peepee = C.getorganslot(ORGAN_SLOT_VAGINA) + if(peepee) + switch(nintendo) + if(REQUIRE_ANY) + return TRUE + if(REQUIRE_EXPOSED) + if(peepee.is_exposed()) + return TRUE + else + return FALSE + if(REQUIRE_UNEXPOSED) + if(!peepee.is_exposed()) + return TRUE + else + return FALSE + else + return TRUE + return FALSE + +/mob/living/proc/has_breasts(var/nintendo = REQUIRE_ANY) + var/mob/living/carbon/C = src + if(istype(C)) + var/obj/item/organ/genital/peepee = C.getorganslot(ORGAN_SLOT_BREASTS) + if(peepee) + switch(nintendo) + if(REQUIRE_ANY) + return TRUE + if(REQUIRE_EXPOSED) + if(peepee.is_exposed()) + return TRUE + else + return FALSE + if(REQUIRE_UNEXPOSED) + if(!peepee.is_exposed()) + return TRUE + else + return FALSE + else + return TRUE + return FALSE + +/mob/living/proc/has_anus(var/nintendo = REQUIRE_ANY) + switch(nintendo) + if(REQUIRE_EXPOSED) + if(is_bottomless()) + return TRUE + else + return FALSE + if(REQUIRE_ANY) + return TRUE + if(REQUIRE_UNEXPOSED) + if(!is_bottomless()) + return TRUE + else + return FALSE + else + return TRUE + +/mob/living/proc/has_hand(var/nintendo = REQUIRE_ANY) + if(iscarbon(src)) + var/mob/living/carbon/C = src + var/handcount = 0 + var/covered = 0 + var/iscovered = FALSE + for(var/obj/item/bodypart/l_arm/L in C.bodyparts) + handcount++ + for(var/obj/item/bodypart/r_arm/R in C.bodyparts) + handcount++ + if(C.get_item_by_slot(ITEM_SLOT_HANDS)) + var/obj/item/clothing/gloves/G = C.get_item_by_slot(ITEM_SLOT_HANDS) + covered = G.body_parts_covered + if(covered & HANDS) + iscovered = TRUE + switch(nintendo) + if(REQUIRE_ANY) + return handcount + if(REQUIRE_EXPOSED) + if(iscovered) + return FALSE + else + return handcount + if(REQUIRE_UNEXPOSED) + if(!iscovered) + return FALSE + else + return handcount + else + return handcount + return FALSE + +/mob/living/proc/has_feet(var/nintendo = REQUIRE_ANY) + if(iscarbon(src)) + var/mob/living/carbon/C = src + var/feetcount = 0 + var/covered = 0 + var/iscovered = FALSE + for(var/obj/item/bodypart/l_leg/L in C.bodyparts) + feetcount++ + for(var/obj/item/bodypart/r_leg/R in C.bodyparts) + feetcount++ + if(C.get_item_by_slot(SLOT_SHOES)) + var/obj/item/clothing/shoes/S = C.get_item_by_slot(SLOT_SHOES) + covered = S.body_parts_covered + if(covered & FEET) + iscovered = TRUE + switch(nintendo) + if(REQUIRE_ANY) + return feetcount + if(REQUIRE_EXPOSED) + if(iscovered) + return FALSE + else + return feetcount + if(REQUIRE_UNEXPOSED) + if(!iscovered) + return FALSE + else + return feetcount + else + return feetcount + return FALSE + +/mob/living/proc/get_num_feet() + return has_feet(REQUIRE_ANY) + +//weird procs go here +/mob/living/proc/has_ears(var/nintendo = REQUIRE_ANY) + var/mob/living/carbon/C = src + if(istype(C)) + var/obj/item/organ/peepee = C.getorganslot(ORGAN_SLOT_EARS) + if(peepee) + switch(nintendo) + if(REQUIRE_ANY) + return TRUE + if(REQUIRE_EXPOSED) + if(C.get_item_by_slot(ITEM_SLOT_EARS)) + return FALSE + else + return TRUE + if(REQUIRE_UNEXPOSED) + if(!C.get_item_by_slot(ITEM_SLOT_EARS)) + return FALSE + else + return TRUE + else + return TRUE + return FALSE + +/mob/living/proc/has_earsockets(var/nintendo = REQUIRE_ANY) + var/mob/living/carbon/C = src + if(istype(C)) + var/obj/item/organ/peepee = C.getorganslot(ORGAN_SLOT_EARS) + if(!peepee) + switch(nintendo) + if(REQUIRE_ANY) + return TRUE + if(REQUIRE_EXPOSED) + if(C.get_item_by_slot(ITEM_SLOT_EARS)) + return FALSE + else + return TRUE + if(REQUIRE_UNEXPOSED) + if(!C.get_item_by_slot(ITEM_SLOT_EARS)) + return FALSE + else + return TRUE + else + return TRUE + return FALSE + +/mob/living/proc/has_eyes(var/nintendo = REQUIRE_ANY) + var/mob/living/carbon/C = src + if(istype(C)) + var/obj/item/organ/peepee = C.getorganslot(ORGAN_SLOT_EYES) + if(peepee) + switch(nintendo) + if(REQUIRE_ANY) + return TRUE + if(REQUIRE_EXPOSED) + if(C.get_item_by_slot(ITEM_SLOT_EYES)) + return FALSE + else + return TRUE + if(REQUIRE_UNEXPOSED) + if(!C.get_item_by_slot(ITEM_SLOT_EYES)) + return FALSE + else + return TRUE + else + return TRUE + return FALSE + +/mob/living/proc/has_eyesockets(var/nintendo = REQUIRE_ANY) + var/mob/living/carbon/C = src + if(istype(C)) + var/obj/item/organ/peepee = C.getorganslot(ORGAN_SLOT_EYES) + if(!peepee) + switch(nintendo) + if(REQUIRE_ANY) + return TRUE + if(REQUIRE_EXPOSED) + if(get_item_by_slot(ITEM_SLOT_EYES)) + return FALSE + else + return TRUE + if(REQUIRE_UNEXPOSED) + if(!get_item_by_slot(ITEM_SLOT_EYES)) + return FALSE + else + return TRUE + else + return TRUE + return FALSE +// + + +/mob/living/proc/is_topless() + if(istype(src, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + if((!(H.wear_suit) || !(H.wear_suit.body_parts_covered & CHEST)) && (!(H.w_uniform) || !(H.w_uniform.body_parts_covered & CHEST))) + return TRUE + else + return TRUE + +/mob/living/proc/is_bottomless() + if(istype(src, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + if((!(H.wear_suit) || !(H.wear_suit.body_parts_covered & GROIN)) && (!(H.w_uniform) || !(H.w_uniform.body_parts_covered & GROIN))) + return TRUE + else + return TRUE +/* +/proc/cum_splatter(target, var/mob/living/user) // Like blood_splatter(), but much more questionable on a resume. + if(user.has_penis() && !user.has_vagina()) + new /obj/effect/decal/cleanable/semen(get_turf(target)) + else if(user.has_vagina() && !user.has_penis()) + new /obj/effect/decal/cleanable/femcum(get_turf(target)) + else if(user.has_vagina() && user.has_penis()) + new /obj/effect/decal/cleanable/femcum(get_turf(target)) + new /obj/effect/decal/cleanable/semen(get_turf(target)) + //var/obj/effect/decal/cleanable/cum/C = (get_turf(target)) + //C.add_blood_DNA(list(data["blood_DNA"] = data["blood_type"])) +*/ +/mob/living/proc/moan() + if(!(prob(get_lust() / lust_tolerance * 65))) + return + var/moan = rand(1, 7) + if(moan == lastmoan) + moan-- + if(!is_muzzled()) + visible_message(message = "\The [src] [pick("moans", "moans in pleasure")].", ignored_mobs = get_unconsenting()) + //playsound(get_turf(src), "code/game/lewd/sound/interactions/moan_[gender == FEMALE ? "f" : "m"][rand(1, 7)].ogg", 70, 1, 0) + if(is_muzzled())//immursion + visible_message("[src] [pick("mimes a pleasured moan","moans in silence")].") + lastmoan = moan + +/mob/living/proc/cum(mob/living/partner, target_orifice) + var/message + var/cumin = FALSE + if(src != partner) + if(!last_genital) + if(has_penis()) + if(!istype(partner)) + target_orifice = null + switch(target_orifice) + if(CUM_TARGET_MOUTH) + if(partner.has_mouth() && partner.mouth_is_free()) + message = "cums right in \the [partner]'s mouth." + cumin = TRUE + else + message = "cums on \the [partner]'s face." + if(CUM_TARGET_THROAT) + if(partner.has_mouth() && partner.mouth_is_free()) + message = "shoves deep into \the [partner]'s throat and cums." + cumin = TRUE + else + message = "cums on \the [partner]'s face." + if(CUM_TARGET_VAGINA) + if(partner.has_vagina(REQUIRE_EXPOSED)) + message = "cums in \the [partner]'s pussy." + cumin = TRUE + else + message = "cums on \the [partner]'s belly." + if(CUM_TARGET_ANUS) + if(partner.has_anus(REQUIRE_EXPOSED)) + message = "cums in \the [partner]'s asshole." + cumin = TRUE + else + message = "cums on \the [partner]'s backside." + if(CUM_TARGET_HAND) + if(partner.has_hand(REQUIRE_ANY)) + message = "cums in \the [partner]'s hand." + else + message = "cums on \the [partner]." + if(CUM_TARGET_BREASTS) + if(partner.has_breasts(REQUIRE_EXPOSED)) + message = "cums onto \the [partner]'s breasts." + else + message = "cums on \the [partner]'s chest and neck." + if(NUTS_TO_FACE) + if(partner.has_mouth() && partner.mouth_is_free()) + message = "vigorously ruts their nutsack into \the [partner]'s mouth before shooting their thick, sticky jizz all over their eyes and hair." + if(THIGH_SMOTHERING) + if(src.has_penis(REQUIRE_EXPOSED)) //it already checks for the cock before, why the hell would you do this redundant shit + message = "keeps \the [partner] locked in their thighs as their cock throbs, dumping its heavy load all over their face." + else + message = "reaches their peak, locking their legs around \the [partner]'s head extra hard as they cum straight onto the head stuck between their thighs" + cumin = TRUE + if(CUM_TARGET_FEET) + if(!last_lewd_datum.require_target_num_feet) + if(partner.has_feet()) + message = "cums on \the [partner]'s [partner.has_feet() == 1 ? pick("foot", "sole") : pick("feet", "soles")]." + else + message = "cums on the floor!" + else + if(partner.has_feet()) + message = "cums on \the [partner]'s [last_lewd_datum.require_target_feet == 1 ? pick("foot", "sole") : pick("feet", "soles")]." + else + message = "cums on the floor!" + //weird shit goes here + if(CUM_TARGET_EARS) + if(partner.has_ears()) + message = "cums inside \the [partner]'s ear." + else + message = "cums inside \the [partner]'s earsocket." + cumin = TRUE + if(CUM_TARGET_EYES) + if(partner.has_eyes()) + message = "cums on \the [partner]'s eyeball." + else + message = "cums inside \the [partner]'s eyesocket." + cumin = TRUE + // + + else + message = "cums on the floor!" + else if(has_vagina()) + if(!istype(partner)) + target_orifice = null + + switch(target_orifice) + if(CUM_TARGET_MOUTH) + if(partner.has_mouth() && partner.mouth_is_free()) + message = "squirts right in \the [partner]'s mouth." + cumin = TRUE + else + message = "squirts on \the [partner]'s face." + if(CUM_TARGET_THROAT) + if(partner.has_mouth() && partner.mouth_is_free()) + message = "rubs their vagina against \the [partner]'s mouth and cums." + cumin = TRUE + else + message = "squirts on \the [partner]'s face." + if(CUM_TARGET_VAGINA) + if(partner.has_vagina(REQUIRE_EXPOSED)) + message = "squirts on \the [partner]'s pussy." + cumin = TRUE + else + message = "squirts on \the [partner]'s belly." + if(CUM_TARGET_ANUS) + if(partner.has_anus(REQUIRE_EXPOSED)) + message = "squirts on \the [partner]'s asshole." + cumin = TRUE + else + message = "squirts on \the [partner]'s backside." + if(CUM_TARGET_HAND) + if(partner.has_hand(REQUIRE_ANY)) + message = "squirts on \the [partner]'s hand." + else + message = "squirts on \the [partner]." + if(CUM_TARGET_BREASTS) + if(partner.has_breasts(REQUIRE_EXPOSED)) + message = "squirts onto \the [partner]'s breasts." + else + message = "squirts on \the [partner]'s chest and neck." + if(NUTS_TO_FACE) + if(partner.has_mouth() && partner.mouth_is_free()) + message = "vigorously ruts their clit into \the [partner]'s mouth before shooting their femcum all over their eyes and hair." + if(THIGH_SMOTHERING) + message = "keeps \the [partner] locked in their thighs as they orgasm, squirting over their face." + cumin = TRUE + if(CUM_TARGET_FEET) + if(!last_lewd_datum.require_target_num_feet) + if(partner.has_feet()) + message = "squirts on \the [partner]'s [partner.has_feet() == 1 ? pick("foot", "sole") : pick("feet", "soles")]." + else + message = "squirts on the floor!" + else + if(partner.has_feet()) + message = "squirts on \the [partner]'s [last_lewd_datum.require_target_feet == 1 ? pick("foot", "sole") : pick("feet", "soles")]." + else + message = "squirts on the floor!" + //weird shit goes here + if(CUM_TARGET_EARS) + if(partner.has_ears()) + message = "squirts on \the [partner]'s ear." + else + message = "squirts on \the [partner]'s earsocket." + cumin = TRUE + if(CUM_TARGET_EYES) + if(partner.has_eyes()) + message = "squirts on \the [partner]'s eyeball." + else + message = "squirts on \the [partner]'s eyesocket." + cumin = TRUE + // + + else + message = "squirts on the floor!" + + else + message = pick("orgasms violently!", "twists in orgasm.") + else + switch(last_genital.type) + if(/obj/item/organ/genital/penis) + if(!istype(partner)) + target_orifice = null + + switch(target_orifice) + if(CUM_TARGET_MOUTH) + if(partner.has_mouth() && partner.mouth_is_free()) + message = "cums right in \the [partner]'s mouth." + cumin = TRUE + else + message = "cums on \the [partner]'s face." + if(CUM_TARGET_THROAT) + if(partner.has_mouth() && partner.mouth_is_free()) + message = "shoves deep into \the [partner]'s throat and cums." + cumin = TRUE + else + message = "cums on \the [partner]'s face." + if(CUM_TARGET_VAGINA) + if(partner.has_vagina(REQUIRE_EXPOSED)) + message = "cums in \the [partner]'s pussy." + cumin = TRUE + else + message = "cums on \the [partner]'s belly." + if(CUM_TARGET_ANUS) + if(partner.has_anus(REQUIRE_EXPOSED)) + message = "cums in \the [partner]'s asshole." + cumin = TRUE + else + message = "cums on \the [partner]'s backside." + if(CUM_TARGET_HAND) + if(partner.has_hand()) + message = "cums in \the [partner]'s hand." + else + message = "cums on \the [partner]." + if(CUM_TARGET_BREASTS) + if(partner.is_topless() && partner.has_breasts()) + message = "cums onto \the [partner]'s breasts." + else + message = "cums on \the [partner]'s chest and neck." + if(NUTS_TO_FACE) + if(partner.has_mouth() && partner.mouth_is_free()) + message = "vigorously ruts their nutsack into \the [partner]'s mouth before shooting their thick, sticky jizz all over their eyes and hair." + if(THIGH_SMOTHERING) + if(src.has_penis()) //it already checks for the cock before, why the hell would you do this redundant shit + message = "keeps \the [partner] locked in their thighs as their cock throbs, dumping its heavy load all over their face." + else + message = "reaches their peak, locking their legs around \the [partner]'s head extra hard as they cum straight onto the head stuck between their thighs" + cumin = TRUE + if(CUM_TARGET_FEET) + if(!last_lewd_datum || !last_lewd_datum.require_target_num_feet) + if(partner.has_feet()) + message = "cums on \the [partner]'s [partner.has_feet() == 1 ? pick("foot", "sole") : pick("feet", "soles")]." + else + message = "cums on the floor!" + else + if(partner.has_feet()) + message = "cums on \the [partner]'s [last_lewd_datum.require_target_feet == 1 ? pick("foot", "sole") : pick("feet", "soles")]." + else + message = "cums on the floor!" + //weird shit goes here + if(CUM_TARGET_EARS) + if(partner.has_ears()) + message = "cums inside \the [partner]'s ear." + else + message = "cums inside \the [partner]'s earsocket." + cumin = TRUE + if(CUM_TARGET_EYES) + if(partner.has_eyes()) + message = "cums on \the [partner]'s eyeball." + else + message = "cums inside \the [partner]'s eyesocket." + cumin = TRUE + // + else + message = "cums on the floor!" + if(/obj/item/organ/genital/vagina) + if(!istype(partner)) + target_orifice = null + + switch(target_orifice) + if(CUM_TARGET_MOUTH) + if(partner.has_mouth() && partner.mouth_is_free()) + message = "squirts right in \the [partner]'s mouth." + cumin = TRUE + else + message = "squirts on \the [partner]'s face." + if(CUM_TARGET_THROAT) + if(partner.has_mouth() && partner.mouth_is_free()) + message = "rubs their vagina against \the [partner]'s mouth and cums." + cumin = TRUE + else + message = "squirts on \the [partner]'s face." + if(CUM_TARGET_VAGINA) + if(partner.has_vagina(REQUIRE_EXPOSED)) + message = "squirts on \the [partner]'s pussy." + cumin = TRUE + else + message = "squirts on \the [partner]'s belly." + if(CUM_TARGET_ANUS) + if(partner.has_anus(REQUIRE_EXPOSED)) + message = "squirts on \the [partner]'s asshole." + cumin = TRUE + else + message = "squirts on \the [partner]'s backside." + if(CUM_TARGET_HAND) + if(partner.has_hand()) + message = "squirts on \the [partner]'s hand." + else + message = "squirts on \the [partner]." + if(CUM_TARGET_BREASTS) + if(partner.has_breasts(REQUIRE_EXPOSED)) + message = "squirts onto \the [partner]'s breasts." + else + message = "squirts on \the [partner]'s chest and neck." + if(NUTS_TO_FACE) + if(partner.has_mouth() && partner.mouth_is_free()) + message = "vigorously ruts their clit into \the [partner]'s mouth before shooting their femcum all over their eyes and hair." + + if(THIGH_SMOTHERING) + message = "keeps \the [partner] locked in their thighs as they orgasm, squirting over their face." + + if(CUM_TARGET_FEET) + if(!last_lewd_datum || !last_lewd_datum.require_target_num_feet) + if(partner.has_feet()) + message = "squirts on \the [partner]'s [partner.has_feet() == 1 ? pick("foot", "sole") : pick("feet", "soles")]." + else + message = "squirts on the floor!" + else + if(partner.has_feet()) + message = "squirts on \the [partner]'s [last_lewd_datum.require_target_feet == 1 ? pick("foot", "sole") : pick("feet", "soles")]." + else + message = "squirts on the floor!" + //weird shit goes here + if(CUM_TARGET_EARS) + if(partner.has_ears()) + message = "squirts on \the [partner]'s ear." + else + message = "squirts on \the [partner]'s earsocket." + cumin = TRUE + if(CUM_TARGET_EYES) + if(partner.has_eyes()) + message = "squirts on \the [partner]'s eyeball." + else + message = "squirts on \the [partner]'s eyesocket." + cumin = TRUE + // + else + message = "squirts on the floor!" + else + message = pick("orgasms violently!", "twists in orgasm.") + else //todo: better self cum messages + message = "cums all over themselves!" + if(gender == MALE) + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/final_m1.ogg', + 'sandcode/sound/interactions/final_m2.ogg', + 'sandcode/sound/interactions/final_m3.ogg', + 'sandcode/sound/interactions/final_m4.ogg', + 'sandcode/sound/interactions/final_m5.ogg'), 90, 1, 0)//, pitch = get_age_pitch()) + else if(gender == FEMALE) + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/final_f1.ogg', + 'sandcode/sound/interactions/final_f2.ogg', + 'sandcode/sound/interactions/final_f3.ogg'), 70, 1, 0)//, pitch = get_age_pitch()) + else + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/final_f1.ogg', + 'sandcode/sound/interactions/final_f2.ogg', + 'sandcode/sound/interactions/final_f3.ogg'), 70, 1, 0)//, pitch = get_age_pitch()) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + multiorgasms += 1 + + /*if(multiorgasms == 1) + add_logs(partner, src, "came on")*/ + + if(multiorgasms > (sexual_potency * 0.34)) //AAAAA, WE DONT WANT NEGATIVES HERE, RE + refractory_period = world.time + rand(300, 900) - sexual_potency//sex cooldown + src.set_drugginess(rand(20, 30)) + else + refractory_period = world.time + rand(300, 900) - sexual_potency + src.set_drugginess(rand(5, 10)) + if(multiorgasms < sexual_potency) + if(ishuman(src)) + var/mob/living/carbon/human/H = src + if(!partner) + H.mob_climax(TRUE, TRUE) + else + H.mob_climax(TRUE, partner, cumin) + set_lust(0) +/* +/mob/living/cum(mob/living/partner, target_orifice) + . = ..() + if(multiorgasms < sexual_potency) + cum_splatter((partner ? partner : src), src) +*/ +/mob/living/proc/is_fucking(mob/living/partner, orifice) + if(partner == last_partner && orifice == last_orifice) + return TRUE + return FALSE + +/mob/living/proc/set_is_fucking(mob/living/partner, orifice, var/obj/item/organ/genital/genepool) + last_partner = partner + last_orifice = orifice + last_genital = genepool + +/mob/living/proc/do_fucking_animation(fuckdir) // Today I wrote 'var/fuckdir' with a straight face. Not a milestone I ever expected to pass. dont worry, we dont use heretic proc/name(var/dir) + if(!fuckdir) + return + + dir = fuckdir + var/pixel_x_diff = 0 + var/pixel_y_diff = 0 + var/final_pixel_y = initial(pixel_y) + + if(fuckdir & NORTH) + pixel_y_diff = 8 + else if(fuckdir & SOUTH) + pixel_y_diff = -8 + + if(fuckdir & EAST) + pixel_x_diff = 8 + else if(fuckdir & WEST) + pixel_x_diff = -8 + + if(pixel_x_diff == 0 && pixel_y_diff == 0) + pixel_x_diff = rand(-3,3) + pixel_y_diff = rand(-3,3) + animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2) + animate(pixel_x = initial(pixel_x), pixel_y = initial(pixel_y), time = 2) + return + + animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2) + animate(pixel_x = initial(pixel_x), pixel_y = final_pixel_y, time = 2) + +/*-------------------------------------------------- + ---------------LEWD PROCESS DATUM----------------- + -------------------------------------------------- + */ + +/mob/living/proc/do_oral(mob/living/partner, var/fucktarget = "penis") + var/message + var/obj/item/organ/genital/peepee = null + var/lust_increase = NORMAL_LUST + + if(partner.is_fucking(src, CUM_TARGET_MOUTH)) + if(prob(partner.sexual_potency)) + if(istype(src, /mob/living)) // Argh. + var/mob/living/H = src + H.adjustOxyLoss(3) + message = "goes in deep on \the [partner]." + lust_increase += 5 + else + var/improv = FALSE + switch(fucktarget) + if("vagina") + if(partner.has_vagina()) + message = pick( + "licks \the [partner]'s pussy.", + "runs their tongue up the shape of \the [partner]'s pussy.", + "traces \the [partner]'s slit with their tongue.", + "darts the tip of their tongue around \the [partner]'s clit.", + "laps slowly at \the [partner].", + "kisses \the [partner]'s delicate folds.", + "tastes \the [partner].", + ) + else + improv = TRUE + if("penis") + if(partner.has_penis()) + message = pick( + "sucks \the [partner]'s off.", + "runs their tongue up the shape of \the [partner]'s cock.", + "traces \the [partner]'s cock with their tongue.", + "darts the tip of their tongue around tip of \the [partner]'s cock.", + "laps slowly at \the [partner]'s shaft.", + "kisses the base of \the [partner]'s shaft.", + "takes \the [partner] deeper into their mouth.", + ) + else + improv = TRUE + if(improv) + // get confused about how to do the sex + message = pick( + "licks \the [partner].", + "looks a little unsure of where to lick \the [partner].", + "runs their tongue between \the [partner]'s legs.", + "kisses \the [partner]'s thigh.", + "tries their best with \the [partner].", + ) + else + var/improv = FALSE + switch(fucktarget) + if("vagina") + if(partner.has_vagina()) + message = pick( + "buries their face in \the [partner]'s pussy.", + "nuzzles \the [partner]'s wet sex.", + "finds their face caught between \the [partner]'s thighs.", + "kneels down between \the [partner]'s legs.", + "grips \the [partner]'s legs, pushing them apart.", + "sinks their face in between \the [partner]'s thighs.", + ) + else + improv = TRUE + if("penis") + if(partner.has_penis()) + message = pick( + "takes \the [partner]'s cock into their mouth.", + "wraps their lips around \the [partner]'s cock.", + "finds their face between \the [partner]'s thighs.", + "kneels down between \the [partner]'s legs.", + "grips \the [partner]'s legs, kissing at the tip of their cock.", + "goes down on \the [partner].", + ) + else + improv = TRUE + if(improv) + message = pick( + "begins to lick \the [partner].", + "starts kissing \the [partner]'s thigh.", + "sinks down between \the [partner]'s thighs.", + "briefly flashes a puzzled look from between \the [partner]'s legs.", + "looks unsure of how to handle \the [partner]'s lack of genitalia.", + "seems like they were expecting \the [partner] to have a cock or a pussy or ... something.", + ) + peepee = null + else + if(ishuman(partner)) + var/mob/living/carbon/human/pardner = partner + switch(fucktarget) + if("vagina") + peepee = pardner.getorganslot(ORGAN_SLOT_VAGINA) + if("penis") + peepee = pardner.getorganslot(ORGAN_SLOT_PENIS) + partner.set_is_fucking(src, CUM_TARGET_MOUTH, peepee) + + playlewdinteractionsound(get_turf(src), pick( 'sandcode/sound/interactions/bj1.ogg', + 'sandcode/sound/interactions/bj2.ogg', + 'sandcode/sound/interactions/bj3.ogg', + 'sandcode/sound/interactions/bj4.ogg', + 'sandcode/sound/interactions/bj5.ogg', + 'sandcode/sound/interactions/bj6.ogg', + 'sandcode/sound/interactions/bj7.ogg', + 'sandcode/sound/interactions/bj8.ogg', + 'sandcode/sound/interactions/bj9.ogg', + 'sandcode/sound/interactions/bj10.ogg', + 'sandcode/sound/interactions/bj11.ogg'), 50, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + partner.handle_post_sex(lust_increase, CUM_TARGET_MOUTH, src) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + lust_increase = NORMAL_LUST //RESET IT REE + +/mob/living/proc/do_facefuck(mob/living/partner, var/fucktarget = "penis") + var/message + var/obj/item/organ/genital/peepee = null + var/retaliation_message = FALSE + + if(is_fucking(partner, CUM_TARGET_MOUTH)) + var/improv = FALSE + switch(fucktarget) + if("vagina") + if(has_vagina()) + message = pick( + "grinds their pussy into \the [partner]'s face.", + "grips the back of \the [partner]'s head, forcing them onto their pussy.", + "rolls their pussy against \the [partner]'s tongue.", + "slides \the [partner]'s mouth between their legs.", + "looks \the [partner]'s in the eyes as their pussy presses into a waiting tongue.", + "sways their hips, pushing their sex into \the [partner]'s face.", + ) + if(partner.a_intent == INTENT_HARM) + // src.adjustBruteLoss(5) + retaliation_message = pick( + "looks deeply displeased to be there.", + "struggles to escape from between \the [src]'s thighs.", + ) + else + improv = TRUE + if("penis") + if(has_penis()) + message = pick( + "roughly fucks \the [partner]'s mouth.", + "forces their cock down \the [partner]'s throat.", + "pushes in against \the [partner]'s tongue until a tight gagging sound comes.", + "grips \the [partner]'s hair and draws them to the base of the cock.", + "looks \the [partner]'s in the eyes as their cock presses into a waiting tongue.", + "rolls their hips hard, sinking into \the [partner]'s mouth.", + ) + if(partner.a_intent == INTENT_HARM) + // src.adjustBruteLoss(5) + retaliation_message = pick( + "stares up from between \the [src]'s knees, trying to squirm away.", + "struggles to escape from between \the [src]'s legs.", + ) + else + improv = TRUE + if(improv) + message = pick( + "grinds against \the [partner]'s face.", + "feels \the [partner]'s face between bare legs.", + "pushes in against \the [partner]'s tongue.", + "grips \the [partner]'s hair, drawing them into the strangely sexless spot between their legs.", + "looks \the [partner]'s in the eyes as they're caught beneath two thighs.", + "rolls their hips hard against \the [partner]'s face.", + ) + if(partner.a_intent == INTENT_HARM) + // src.adjustBruteLoss(5) + retaliation_message = pick( + "stares up from between \the [src]'s knees, trying to squirm away.", + "struggles to escape from between \the [src]'s legs.", + ) + else + var/improv = FALSE + switch(fucktarget) + if("vagina") + if(has_vagina()) + message = "forces \the [partner]'s face into their pussy." + else + improv = TRUE + if("penis") + if(has_penis()) + if(is_fucking(partner, CUM_TARGET_THROAT)) + message = "retracts their cock from \the [partner]'s throat" + else + message = "shoves their cock into \the [partner]'s mouth" + else + improv = TRUE + if(improv) + message = "shoves their crotch into \the [partner]'s face." + else + if(ishuman(partner)) + var/mob/living/carbon/human/pardner = partner + switch(fucktarget) + if("vagina") + peepee = pardner.getorganslot(ORGAN_SLOT_VAGINA) + if("penis") + peepee = pardner.getorganslot(ORGAN_SLOT_PENIS) + set_is_fucking(partner , CUM_TARGET_MOUTH, peepee) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/oral1.ogg', + 'sandcode/sound/interactions/oral2.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + if(retaliation_message) + visible_message(message = "\The [partner] [retaliation_message]", ignored_mobs = get_unconsenting()) + handle_post_sex(NORMAL_LUST, CUM_TARGET_MOUTH, partner) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/thigh_smother(mob/living/partner, var/fucktarget = "penis") + + var/message + var/obj/item/organ/genital/peepee = null + var/lust_increase = 1 + + if(is_fucking(partner, THIGH_SMOTHERING)) + var/improv = FALSE + switch(fucktarget) + if("vagina") + if(has_vagina()) + message = pick(list( + "presses their weight down onto \the [partner]'s face, blocking their vision completely.", + "rides \the [partner]'s face, grinding their wet pussy all over it.")) + else + improv = TRUE + if("penis") + if(has_penis()) + message = pick(list("presses their weight down onto \the [partner]'s face, blocking their vision completely.", + "forces their dick and nutsack into \the [partner]'s face as they're stuck locked in between their thighs.", + "slips their cock into \the [partner]'s helpless mouth, keeping their shaft pressed hard into their face.")) + else + improv = TRUE + + if(improv) + message = "rubs their groin up and down \the [partner]'s face." + + else + var/improv = FALSE + switch(fucktarget) + if("vagina") + if(has_vagina()) + message = pick(list( + "clambers over \the [partner]'s face and pins them down with their thighs, their moist slit rubbing all over \the [partner]'s mouth and nose.", + "locks their legs around \the [partner]'s head before pulling it into their mound.")) + else + improv = TRUE + + if("penis") + if(has_penis()) + message = pick(list( + "clambers over \the [partner]'s face and pins them down with their thighs, then slowly inching closer and covering their eyes and nose with their leaking erection.", + "locks their legs around \the [partner]'s head before pulling it into their fat package, smothering them.")) + else + improv = TRUE + + if(improv) + message = "deviously locks their legs around \the [partner]'s head and smothers it in their thighs." + else + if(ishuman(partner)) + var/mob/living/carbon/human/pardner = partner + switch(fucktarget) + if("vagina") + peepee = pardner.getorganslot(ORGAN_SLOT_VAGINA) + if("penis") + peepee = pardner.getorganslot(ORGAN_SLOT_PENIS) + set_is_fucking(partner , THIGH_SMOTHERING, peepee) + + + + + var file = pick('sandcode/sound/interactions/bj10.ogg', + 'sandcode/sound/interactions/bj3.ogg', + 'sandcode/sound/interactions/foot_wet1.ogg', + 'sandcode/sound/interactions/foot_dry3.ogg') + + playlewdinteractionsound(loc, file, 70, 1, -1) + + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + + handle_post_sex(lust_increase, THIGH_SMOTHERING, partner) + + partner.dir = get_dir(partner,src) + + do_fucking_animation(get_dir(src, partner)) + + + + + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/oral1.ogg', + 'sandcode/sound/interactions/oral2.ogg'), 70, 1, -1) + + + handle_post_sex(NORMAL_LUST, CUM_TARGET_MOUTH, partner) + + partner.dir = get_dir(partner,src) + + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_throatfuck(mob/living/partner) + var/message + var/obj/item/organ/genital/peepee = null + var/retaliation_message = FALSE + + if(is_fucking(partner, CUM_TARGET_THROAT)) + message = "[pick( + "brutally shoves their cock into \the [partner]'s throat to make them gag.", + "chokes \the [partner] on their dick, going in balls deep.", + "slams in and out of \the [partner]'s mouth, their balls slapping off their face.")]" + if(rand(3)) + partner.emote("chokes on \The [src]") + if(prob(1) && istype(partner, /mob/living)) + var/mob/living/H = partner + H.adjustOxyLoss(5) + //add_logs(src, partner, "attacked", src) //cmon, it's 1 in 100. how can it spam logs + if(partner.a_intent == INTENT_HARM) + // src.adjustBruteLoss(5) + retaliation_message = pick( + "stares up from between \the [src]'s knees, trying to squirm away.", + "struggles to escape from between \the [src]'s legs.", + ) + else if(is_fucking(partner, CUM_TARGET_MOUTH)) + message = "thrusts deeper into \the [partner]'s mouth and down their throat." + if(ishuman(src)) + var/mob/living/carbon/human/coomer = partner + peepee = coomer.getorganslot(ORGAN_SLOT_PENIS) + set_is_fucking(partner , CUM_TARGET_THROAT, peepee) + else + message = "forces their dick deep down \the [partner]'s throat" + if(ishuman(src)) + var/mob/living/carbon/human/coomer = partner + peepee = coomer.getorganslot(ORGAN_SLOT_PENIS) + set_is_fucking(partner , CUM_TARGET_THROAT, peepee) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/oral1.ogg', + 'sandcode/sound/interactions/oral2.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + if(retaliation_message) + visible_message(message = "\The [partner] [retaliation_message]", ignored_mobs = get_unconsenting()) + handle_post_sex(NORMAL_LUST, CUM_TARGET_THROAT, partner) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/nut_face(var/mob/living/partner) + + var/message + + var/lust_increase = 1 + + if(is_fucking(partner, NUTS_TO_FACE)) + message = pick(list( + "grabs the back of [partner]'s head and pulls it into their crotch.", + "jams their nutsack right into [partner]'s face.", + "roughly grinds their fat nutsack into [partner]'s mouth.", + "pulls out their saliva-covered nuts from [partner]'s violated mouth and then wipes off the slime onto their face.")) + else + message = pick(list("wedges a digit into the side of [partner]'s jaw and pries it open before using their other hand to shove their whole nutsack inside!", "stands with their groin inches away from [partner]'s face, then thrusting their hips forward and smothering [partner]'s whole face with their heavy ballsack.")) + set_is_fucking(partner , NUTS_TO_FACE, getorganslot(ORGAN_SLOT_PENIS) ? getorganslot(ORGAN_SLOT_PENIS) : null) + + /*playlewdinteractionsound(loc, pick('sandcode/sound/interactions/nuts1.ogg', + 'sandcode/sound/interactions/nuts2.ogg', + 'sandcode/sound/interactions/nuts3.ogg', + 'sandcode/sound/interactions/nuts4.ogg'), 70, 1, -1)*/ //These files don't even exist but nobody noticed because double-quotes were used instead of single. + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + handle_post_sex(lust_increase, CUM_TARGET_MOUTH, partner) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_anal(mob/living/partner) + var/message + + if(is_fucking(partner, CUM_TARGET_ANUS)) + message = "[pick( + "thrusts in and out of \the [partner]'s ass.", + "pounds \the [partner]'s ass.", + "slams their hips up against \the [partner]'s ass hard.", + "goes balls deep into \the [partner]'s ass over and over again.")]" + else + message = "[pick( + "works their cock into \the [partner]'s asshole.", + "grabs the base of their twitching cock and presses the tip into \the [partner]'s asshole.", + "shoves their dick deep inside of \the [partner]'s ass, making their rear jiggle.")]" + set_is_fucking(partner, CUM_TARGET_ANUS, getorganslot(ORGAN_SLOT_PENIS) ? getorganslot(ORGAN_SLOT_PENIS) : null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/bang1.ogg', + 'sandcode/sound/interactions/bang2.ogg', + 'sandcode/sound/interactions/bang3.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + handle_post_sex(NORMAL_LUST, CUM_TARGET_ANUS, partner) + partner.handle_post_sex(NORMAL_LUST, null, src) + partner.dir = get_dir(src, partner) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_vaginal(mob/living/partner) + var/message + + if(is_fucking(partner, CUM_TARGET_VAGINA)) + message = "[pick( + "pounds \the [partner]'s pussy.", + "shoves their dick deep into \the [partner]'s pussy", + "thrusts in and out of \the [partner]'s cunt.", + "goes balls deep into \the [partner]'s pussy over and over again.")]" + else + message = "slides their cock into \the [partner]'s pussy." + set_is_fucking(partner, CUM_TARGET_VAGINA, getorganslot(ORGAN_SLOT_PENIS) ? getorganslot(ORGAN_SLOT_PENIS) : null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/champ1.ogg', + 'sandcode/sound/interactions/champ2.ogg'), 50, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + handle_post_sex(NORMAL_LUST, CUM_TARGET_VAGINA, partner) + partner.handle_post_sex(NORMAL_LUST, null, src) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_mount(mob/living/partner) + var/message + + if(partner.is_fucking(src, CUM_TARGET_VAGINA)) + message = "[pick("rides \the [partner]'s dick.", + "forces [partner]'s cock on their pussy")]" + else + message = "slides their pussy onto \the [partner]'s cock." + partner.set_is_fucking(src, CUM_TARGET_VAGINA, partner.getorganslot(ORGAN_SLOT_PENIS) ? partner.getorganslot(ORGAN_SLOT_PENIS) : null) + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/bang1.ogg', + 'sandcode/sound/interactions/bang2.ogg', + 'sandcode/sound/interactions/bang3.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + partner.handle_post_sex(NORMAL_LUST, CUM_TARGET_VAGINA, src) + handle_post_sex(NORMAL_LUST, null, partner) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_mountass(mob/living/partner) + var/message + + if(partner.is_fucking(src, CUM_TARGET_ANUS)) + message = "[pick("rides \the [partner]'s dick.", + "forces [partner]'s cock on their ass")]" + else + message = "lowers their ass onto \the [partner]'s cock." + partner.set_is_fucking(src, CUM_TARGET_ANUS, partner.getorganslot(ORGAN_SLOT_PENIS) ? partner.getorganslot(ORGAN_SLOT_PENIS) : null) + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/bang1.ogg', + 'sandcode/sound/interactions/bang2.ogg', + 'sandcode/sound/interactions/bang3.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + partner.handle_post_sex(NORMAL_LUST, CUM_TARGET_ANUS, src) + handle_post_sex(NORMAL_LUST, null, partner) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_tribadism(mob/living/partner) + var/message + + if(partner.is_fucking(src, CUM_TARGET_VAGINA)) + message = "[pick("grinds their pussy against \the [partner]'s cunt.", + "rubs their cunt against \the [partner]'s pussy.", + "thrusts against \the [partner]'s pussy.", + "humps \the [partner], their pussies grinding against each other.")]" + else + message = "presses their pussy into \the [partner]'s own." + partner.set_is_fucking(src, CUM_TARGET_VAGINA, partner.getorganslot(ORGAN_SLOT_VAGINA) ? partner.getorganslot(ORGAN_SLOT_VAGINA) : null) + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/squelch1.ogg', + 'sandcode/sound/interactions/squelch2.ogg', + 'sandcode/sound/interactions/squelch3.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + partner.handle_post_sex(NORMAL_LUST, CUM_TARGET_VAGINA, src) + handle_post_sex(NORMAL_LUST, null, partner) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_fingering(mob/living/partner) + visible_message(message = "\The [src] [pick("fingers \the [partner].", + "fingers \the [partner]'s pussy.", + "fingers \the [partner] hard.")]", ignored_mobs = get_unconsenting()) + playlewdinteractionsound(loc, 'sandcode/sound/interactions/champ_fingering.ogg', 50, 1, -1) + partner.handle_post_sex(NORMAL_LUST, null, src) + partner.dir = get_dir(partner, src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_fingerass(mob/living/partner) + visible_message(message = "\The [src] [pick("fingers \the [partner].", + "fingers \the [partner]'s asshole.", + "fingers \the [partner] hard.")]", ignored_mobs = get_unconsenting()) + playlewdinteractionsound(loc, 'sandcode/sound/interactions/champ_fingering.ogg', 50, 1, -1) + partner.handle_post_sex(NORMAL_LUST, null, src) + partner.dir = get_dir(partner, src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_rimjob(mob/living/partner) + visible_message(message = "\The [src] licks \the [partner]'s asshole.", ignored_mobs = get_unconsenting()) + playlewdinteractionsound(loc, 'sandcode/sound/interactions/champ_fingering.ogg', 50, 1, -1) + partner.handle_post_sex(NORMAL_LUST, null, src) + partner.dir = get_dir(src, partner) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_handjob(mob/living/partner) + var/message + + if(partner.is_fucking(src, CUM_TARGET_HAND)) + message = "[pick("jerks \the [partner] off.", + "works \the [partner]'s shaft.", + "wanks \the [partner]'s cock hard.")]" + else + message = "[pick("wraps their hand around \the [partner]'s cock.", + "starts playing with \the [partner]'s cock")]" + partner.set_is_fucking(src, CUM_TARGET_HAND, partner.getorganslot(ORGAN_SLOT_PENIS) ? partner.getorganslot(ORGAN_SLOT_PENIS) : null) + + playlewdinteractionsound(src, pick('sandcode/sound/interactions/bang1.ogg', + 'sandcode/sound/interactions/bang2.ogg', + 'sandcode/sound/interactions/bang3.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + partner.handle_post_sex(NORMAL_LUST, CUM_TARGET_HAND, src) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_breastfuck(mob/living/partner) + var/message + + if(is_fucking(partner, CUM_TARGET_BREASTS)) + message = "[pick("fucks \the [partner]'s' breasts.", + "grinds their cock between \the [partner]'s boobs.", + "thrusts into \the [partner]'s tits.", + "grabs \the [partner]'s breasts together and presses their dick between them.")]" + else + message = "pushes \the [partner]'s breasts together and presses their dick between them." + set_is_fucking(partner , CUM_TARGET_BREASTS, getorganslot(ORGAN_SLOT_PENIS) ? getorganslot(ORGAN_SLOT_PENIS) : null) + + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/bang1.ogg', + 'sandcode/sound/interactions/bang2.ogg', + 'sandcode/sound/interactions/bang3.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + handle_post_sex(NORMAL_LUST, CUM_TARGET_BREASTS, partner) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_mountface(mob/living/partner) + var/message + + if(is_fucking(partner, GRINDING_FACE_WITH_ANUS)) + message = "[pick("grinds their ass into \the [partner]'s face.", + "shoves their ass into \the [partner]'s face.")]" + else + message = "[pick( + "grabs the back of \the [partner]'s head and forces it into their asscheeks.", + "squats down and plants their ass right on \the [partner]'s face")]" + set_is_fucking(partner , GRINDING_FACE_WITH_ANUS, null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/squelch1.ogg', + 'sandcode/sound/interactions/squelch2.ogg', + 'sandcode/sound/interactions/squelch3.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + handle_post_sex(LOW_LUST, null, src) + partner.dir = get_dir(src, partner) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_lickfeet(mob/living/partner) + var/message + + if(partner.get_item_by_slot(SLOT_SHOES) != null) + message = "licks \the [partner]'s [partner.get_shoes()]." + else + message = "licks \the [partner]'s [partner.has_feet() == 1 ? "foot" : "feet"]." + + playlewdinteractionsound(loc, 'sandcode/sound/interactions/champ_fingering.ogg', 50, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + handle_post_sex(LOW_LUST, null, src) + partner.dir = get_dir(src, partner) + do_fucking_animation(get_dir(src, partner)) + +/*Grinding YOUR feet in TARGET's face*/ +/mob/living/proc/do_grindface(mob/living/partner) + var/message + + if(is_fucking(partner, GRINDING_FACE_WITH_FEET)) + if(src.get_item_by_slot(SLOT_SHOES) != null) + message = "[pick(list("grinds their [get_shoes()] into [partner]'s face.", + "presses their footwear down hard on [partner]'s face.", + "rubs off the dirt from their [get_shoes()] onto [partner]'s face."))]" + else + message = "[pick(list("grinds their bare feet into [partner]'s face.", + "deviously covers [partner]'s mouth and nose with their bare feet.", + "runs the soles of their bare feet against [partner]'s lips."))]" + + else if(is_fucking(partner, GRINDING_MOUTH_WITH_FEET)) + if(src.get_item_by_slot(SLOT_SHOES) != null) + message = "[pick(list("pulls their [get_shoes()] out of [partner]'s mouth and puts them on their face.", + "slowly retracts their [get_shoes()] from [partner]'s mouth, putting them on their face instead."))]" + else + message = "[pick(list("pulls their bare feet out of [partner]'s mouth and rests them on their face instead.", + "retracts their bare feet from [partner]'s mouth and grinds them into their face instead."))]" + + set_is_fucking(partner , GRINDING_FACE_WITH_FEET, null) + + else + if(src.get_item_by_slot(SLOT_SHOES) != null) + message = "[pick(list("plants their [get_shoes()] ontop of [partner]'s face.", + "rests their [get_shoes()] on [partner]'s face and presses down hard.", + "harshly places their [get_shoes()] atop [partner]'s face."))]" + else + message = "[pick(list("plants their bare feet ontop of [partner]'s face.", + "rests their feet on [partner]'s face, smothering them.", + "positions their bare feet atop [partner]'s face."))]" + + set_is_fucking(partner, GRINDING_FACE_WITH_FEET, null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/foot_dry1.ogg', + 'sandcode/sound/interactions/foot_dry2.ogg', + 'sandcode/sound/interactions/foot_dry3.ogg', + 'sandcode/sound/interactions/foot_dry4.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + partner.handle_post_sex(LOW_LUST, null, src) + partner.dir = get_dir(src, partner) + do_fucking_animation(get_dir(src, partner)) + +/*Grinding YOUR feet in TARGET's mouth*/ +/mob/living/proc/do_grindmouth(mob/living/partner) + var/message + + if(is_fucking(partner, GRINDING_MOUTH_WITH_FEET)) + if(src.get_item_by_slot(SLOT_SHOES) != null) + message = "[pick(list("roughly shoves their [get_shoes()] deeper into [partner]'s mouth.", + "harshly forces another inch of their [get_shoes()] into [partner]'s mouth.", + "presses their weight down, their [get_shoes()] prying deeper into [partner]'s mouth."))]" + else + message = "[pick(list("wiggles their toes deep inside [partner]'s mouth.", + "crams their barefeet down deeper into [partner]'s mouth, making them gag.", + "roughly grinds their feet on [partner]'s tongue."))]" + + else if(is_fucking(partner, GRINDING_FACE_WITH_FEET)) + if(src.get_item_by_slot(SLOT_SHOES) != null) + message = "[pick(list("decides to force their [get_shoes()] deep into [partner]'s mouth.", + "pressed the tip of their [get_shoes()] against [partner]'s lips and shoves inwards."))]" + else + message = "[pick(list("pries open [partner]'s mouth with their toes and shoves their bare foot in.", + "presses down their foot even harder, cramming their foot into [partner]'s mouth."))]" + + set_is_fucking(partner , GRINDING_MOUTH_WITH_FEET, null) + + else + if(src.get_item_by_slot(SLOT_SHOES) != null) + message = "[pick(list("readies themselves and in one swift motion, shoves their [get_shoes()] into [partner]'s mouth.", + "grinds the tip of their [get_shoes()] against [partner]'s mouth before pushing themselves in."))]" + else + message = "[pick(list("rubs their dirty bare feet across [partner]'s face before prying them into their muzzle.", + "forces their barefeet into [partner]'s mouth.", + "covers [partner]'s mouth and nose with their foot until they gasp for breath, then shoves both feet inside before they can react."))]" + set_is_fucking(partner , GRINDING_MOUTH_WITH_FEET, null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/foot_wet1.ogg', + 'sandcode/sound/interactions/foot_wet2.ogg', + 'sandcode/sound/interactions/foot_wet3.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + partner.handle_post_sex(LOW_LUST, null, src) + partner.dir = get_dir(src, partner) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_footfuck(mob/living/partner) + var/message + + if(is_fucking(src, CUM_TARGET_FEET)) + message = "[pick("fucks \the [partner]'s [partner.get_shoes() ? partner.get_shoes(TRUE) : pick("foot", "sole")].", + "rubs their dick on \the [partner]'s [partner.get_shoes() ? partner.get_shoes(TRUE) : pick("foot", "sole")].", + "grinds their cock on \the [partner]'s [partner.get_shoes() ? partner.get_shoes(TRUE) : pick("foot", "sole")].")]" + else + message = "[pick("positions their cock on \the [partner]'s [partner.get_shoes() ? partner.get_shoes(TRUE) : pick("foot", "sole")].", + "positions their cock on \the [partner]'s [partner.get_shoes() ? partner.get_shoes(TRUE) : pick("foot","sole")].", + "starts grinding their cock against \the [partner]'s [partner.get_shoes() ? partner.get_shoes(TRUE) : pick("foot", "sole")].")]" + set_is_fucking(src, CUM_TARGET_FEET, getorganslot(ORGAN_SLOT_PENIS) ? getorganslot(ORGAN_SLOT_PENIS) : null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/foot_dry1.ogg', + 'sandcode/sound/interactions/foot_dry3.ogg', + 'sandcode/sound/interactions/foot_wet1.ogg', + 'sandcode/sound/interactions/foot_wet2.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + handle_post_sex(NORMAL_LUST, CUM_TARGET_FEET, src) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_dfootfuck(mob/living/partner) + var/message + + if(is_fucking(src, CUM_TARGET_FEET)) + message = "[pick("fucks \the [partner]'s [partner.get_shoes() ? partner.get_shoes() : pick("feet", "soles")].", + "rubs their dick between \the [partner]'s [partner.get_shoes() ? partner.get_shoes() : pick("feet", "soles")].", + "thrusts their cock between \the [partner]'s [partner.get_shoes() ? partner.get_shoes() : pick("feet", "soles")].")]" + else + message = "[pick("positions their cock between \the [partner]'s [partner.get_shoes() ? partner.get_shoes() : pick("feet", "soles")].", + "starts grinding their cock against \the [partner]'s [partner.get_shoes() ? partner.get_shoes() : pick("feet", "soles")].", + "starts grinding their cock between \the [partner]'s [partner.get_shoes() ? partner.get_shoes() : pick("feet", "soles")].")]" + set_is_fucking(src, CUM_TARGET_FEET, getorganslot(ORGAN_SLOT_PENIS) ? getorganslot(ORGAN_SLOT_PENIS) : null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/foot_dry1.ogg', + 'sandcode/sound/interactions/foot_dry3.ogg', + 'sandcode/sound/interactions/foot_wet1.ogg', + 'sandcode/sound/interactions/foot_wet2.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + handle_post_sex(NORMAL_LUST, CUM_TARGET_FEET, src) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_vfootfuck(mob/living/partner) + var/message + + if(is_fucking(src, CUM_TARGET_FEET)) + message = "[pick("grinds their pussy against \the [partner]'s [partner.get_shoes() ? partner.get_shoes(TRUE) : pick("foot", "sole")].", + "rubs their clit on \the [partner]'s [partner.get_shoes() ? partner.get_shoes(TRUE) : pick("foot", "sole")].", + "ruts on \the [partner]'s [partner.get_shoes() ? partner.get_shoes(TRUE) : pick("foot", "sole")].")]" + else + message = "[pick("positions their vagina on \the [partner]'s [partner.get_shoes() ? partner.get_shoes(TRUE) : pick("foot", "sole")].", + "positions their clit on \the [partner]'s [partner.get_shoes() ? partner.get_shoes(TRUE) : pick("foot","sole")].", + "starts grinding their pussy against \the [partner]'s [partner.get_shoes() ? partner.get_shoes(TRUE) : pick("foot", "sole")].")]" + set_is_fucking(src, CUM_TARGET_FEET, getorganslot(ORGAN_SLOT_VAGINA) ? getorganslot(ORGAN_SLOT_VAGINA) : null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/foot_dry1.ogg', + 'sandcode/sound/interactions/foot_dry3.ogg', + 'sandcode/sound/interactions/foot_wet1.ogg', + 'sandcode/sound/interactions/foot_wet2.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + handle_post_sex(NORMAL_LUST, CUM_TARGET_FEET, src) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_footjob(mob/living/partner) + var/message + + if(partner.is_fucking(src, CUM_TARGET_FEET)) + message = "[pick("jerks \the [partner] off with their [get_shoes() ? get_shoes(TRUE) : pick("foot", "sole")].", + "rubs their [get_shoes() ? get_shoes(TRUE) : pick("foot", "sole", "toes")] on \the [partner]'s shaft.", + "works their [get_shoes() ? get_shoes(TRUE) : pick("foot", "sole")] up and down on \the [partner]'s cock.")]" + else + message = "[pick("[get_shoes() ? "positions their [get_shoes(TRUE)] on" :"positions their foot on"] \the [partner]'s cock.", + "starts playing around with \the [partner]'s cock, using their [get_shoes() ? get_shoes(TRUE) :"foot"].")]" + partner.set_is_fucking(src, CUM_TARGET_FEET, partner.getorganslot(ORGAN_SLOT_PENIS) ? partner.getorganslot(ORGAN_SLOT_PENIS) : null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/foot_dry1.ogg', + 'sandcode/sound/interactions/foot_dry3.ogg', + 'sandcode/sound/interactions/foot_wet1.ogg', + 'sandcode/sound/interactions/foot_wet2.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + partner.handle_post_sex(NORMAL_LUST, CUM_TARGET_FEET, src) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_dfootjob(mob/living/partner) + var/message + + if(partner.is_fucking(src, CUM_TARGET_FEET)) + message = "[pick("jerks \the [partner] off with their [get_shoes() ? get_shoes() : pick("feet", "soles")].", + "rubs their [get_shoes() ? get_shoes() : pick("feet", "soles")] on \the [partner]'s shaft.", + "rubs [get_shoes() ? "their [get_shoes()]" : "all of their toes"] on \the [partner]'s cock.", + "works their [get_shoes() ? get_shoes() : pick("feet", "soles")] up and down on \the [partner]'s cock.")]" + else + message = "[pick("[get_shoes() ? "wraps their [get_shoes()] around" : "wraps their [pick("feet", "soles")] around"] \the [partner]'s cock.", + "starts playing around with \the [partner]'s cock, using their [get_shoes() ? get_shoes() : "feet"].")]" + partner.set_is_fucking(src, CUM_TARGET_FEET, partner.getorganslot(ORGAN_SLOT_PENIS) ? partner.getorganslot(ORGAN_SLOT_PENIS) : null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/foot_dry1.ogg', + 'sandcode/sound/interactions/foot_dry3.ogg', + 'sandcode/sound/interactions/foot_wet1.ogg', + 'sandcode/sound/interactions/foot_wet2.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + partner.handle_post_sex(NORMAL_LUST, CUM_TARGET_FEET, src) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_footjob_v(mob/living/partner) + var/message + + if(partner.is_fucking(src, CUM_TARGET_FEET)) + message = "[pick("rubs \the [partner] clit with their [get_shoes() ? get_shoes(TRUE) : pick("foot", "sole")].", + "rubs their [get_shoes() ? get_shoes(TRUE) : pick("foot", "sole", "toes")] on \the [partner]'s coochie.", + "rubs their [get_shoes() ? get_shoes(TRUE) : pick("foot", "sole", "toes")] on \the [partner]'s vagina.", + "rubs their foot up and down on \the [partner]'s pussy.")]" + else + message = "[pick("[get_shoes() ? "positions their [get_shoes(TRUE)] on" : "positions their foot on"] \the [partner]'s pussy.", + "starts playing around with \the [partner]'s pussy, using their [get_shoes() ? get_shoes(TRUE) : "foot"].")]" + partner.set_is_fucking(src, CUM_TARGET_FEET, partner.getorganslot(ORGAN_SLOT_VAGINA) ? partner.getorganslot(ORGAN_SLOT_VAGINA) : null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/foot_dry1.ogg', + 'sandcode/sound/interactions/foot_dry3.ogg', + 'sandcode/sound/interactions/foot_wet1.ogg', + 'sandcode/sound/interactions/foot_wet2.ogg'), 70, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting()) + partner.handle_post_sex(NORMAL_LUST, CUM_TARGET_FEET, src) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/get_shoes(var/singular = FALSE) + var/obj/A = get_item_by_slot(SLOT_SHOES) + if(A) + var/txt = A.name + if(findtext (A.name,"the")) + txt = copytext(A.name, 5, length(A.name)+1) + if(singular) + txt = copytext(A.name, 5, length(A.name)) + return txt + else + if(singular) + txt = copytext(A.name, 1, length(A.name)) + return txt + +/mob/living/proc/handle_post_sex(amount, orifice, mob/living/partner) + if(stat != CONSCIOUS) + return + + if(amount) + add_lust(amount) + if(get_lust() >= lust_tolerance) + if(prob(10)) + to_chat(src, "You struggle to not orgasm!") + return + if(lust >= lust_tolerance*3) + cum(partner, orifice) + else + moan() + +/mob/living/proc/get_unconsenting(var/extreme = FALSE, var/list/ignored_mobs) + var/list/nope = list() + nope += ignored_mobs + for(var/mob/M in range(7, src)) + if(M.client) + var/client/cli = M.client + if(!(cli.prefs.toggles & VERB_CONSENT)) //Note: This probably could do with a specific preference + nope += M + else if(extreme && (cli.prefs.extremepref == "No")) + nope += M + else + nope += M + return nope + +//Yep, weird shit goes down here. +/mob/living/proc/do_eyefuck(mob/living/partner) + var/message + + if(is_fucking(partner, CUM_TARGET_EYES)) + message = "[pick( + "pounds into \the [partner]'s [has_eyes() ? "eye":"eyesocket"].", + "shoves their dick deep into \the [partner]'s skull", + "thrusts in and out of \the [partner]'s [has_eyes() ? "eye":"eyesocket"].", + "goes balls deep into \the [partner]'s cranium over and over again.")]" + var/client/cli = partner.client + var/mob/living/carbon/C = partner + if(cli && istype(C)) + if(cli.prefs.extremeharm != "No") + if(prob(15)) + C.bleed(2) + if(prob(25)) + C.adjustOrganLoss(ORGAN_SLOT_EYES,rand(3,7)) + C.adjustOrganLoss(ORGAN_SLOT_BRAIN, rand(3,7)) + else + message = "forcefully slides their cock inbetween \the [partner]'s [has_eyes() ? "eyelid":"eyesocket"]." + set_is_fucking(partner, CUM_TARGET_EYES, getorganslot(ORGAN_SLOT_PENIS) ? getorganslot(ORGAN_SLOT_PENIS) : null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/champ1.ogg', + 'sandcode/sound/interactions/champ2.ogg'), 50, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting(TRUE)) + handle_post_sex(NORMAL_LUST, CUM_TARGET_EYES, partner) + partner.handle_post_sex(LOW_LUST, null, src) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) + +/mob/living/proc/do_earfuck(mob/living/partner) + var/message + + if(is_fucking(partner, CUM_TARGET_EARS)) + message = "[pick( + "pounds into \the [partner]'s [has_eyes() ? "ear":"earsocker"].", + "shoves their dick deep into \the [partner]'s skull", + "thrusts in and out of \the [partner]'s [has_eyes() ? "ear":"eyesocket"].", + "goes balls deep into \the [partner]'s cranium over and over again.")]" + var/client/cli = partner.client + var/mob/living/carbon/C = partner + if(cli && istype(C)) + if(cli.prefs.extremeharm != "No") + if(prob(15)) + C.bleed(2) + if(prob(25)) + C.adjustOrganLoss(ORGAN_SLOT_EARS, rand(3,7)) + C.adjustOrganLoss(ORGAN_SLOT_BRAIN, rand(3,7)) + else + message = "forcefully slides their cock inside \the [partner]'s [has_ears() ? "ear":"earsocket"]." + set_is_fucking(partner, CUM_TARGET_EARS, getorganslot(ORGAN_SLOT_PENIS) ? getorganslot(ORGAN_SLOT_PENIS) : null) + + playlewdinteractionsound(loc, pick('sandcode/sound/interactions/champ1.ogg', + 'sandcode/sound/interactions/champ2.ogg'), 50, 1, -1) + visible_message(message = "\The [src] [message]", ignored_mobs = get_unconsenting(TRUE)) + handle_post_sex(NORMAL_LUST, CUM_TARGET_EARS, partner) + partner.handle_post_sex(LOW_LUST, null, src) + partner.dir = get_dir(partner,src) + do_fucking_animation(get_dir(src, partner)) +// diff --git a/sandcode/code/interactions/lewd/lewd_datums.dm b/sandcode/code/interactions/lewd/lewd_datums.dm new file mode 100644 index 0000000000..dc2066fb6f --- /dev/null +++ b/sandcode/code/interactions/lewd/lewd_datums.dm @@ -0,0 +1,598 @@ +/datum/interaction/lewd/kiss + command = "deepkiss" + description = "Kiss them deeply." + require_user_mouth = TRUE + require_target_mouth = TRUE + write_log_user = "kissed" + write_log_target = "was kissed by" + interaction_sound = null + max_distance = 1 + +/datum/interaction/lewd/kiss/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + if(user.get_lust() < 5) + user.set_lust(5) + if(target.get_lust() < 5) + target.set_lust(5) + +/datum/interaction/lewd/kiss/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(user.get_lust() >= 3) + user.visible_message("\The [user] gives an intense, lingering kiss to \the [target].") + else + user.visible_message("\The [user] kisses \the [target] deeply.") + +/datum/interaction/lewd/titgrope + command = "titgrope" + description = "Grope their breasts." + require_target_breasts = REQUIRE_ANY + write_log_user = "groped" + write_log_target = "was groped by" + interaction_sound = null + max_distance = 1 + +/datum/interaction/lewd/titgrope/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + +/datum/interaction/lewd/titgrope/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(user.a_intent == INTENT_HELP) + user.visible_message( + pick("\The [user] gently gropes \the [target]'s breast.", + "\The [user] softly squeezes \the [target]'s breasts.", + "\The [user] grips \the [target]'s breasts.", + "\The [user] runs a few fingers over \the [target]'s breast.", + "\The [user] delicately teases \the [target]'s nipple.", + "\The [user] traces a touch across \the [target]'s breast.")) + if(user.a_intent == INTENT_HARM) + user.visible_message( + pick("\The [user] aggressively gropes \the [target]'s breast.", + "\The [user] grabs \the [target]'s breasts.", + "\The [user] tightly squeezes \the [target]'s breasts.", + "\The [user] slaps at \the [target]'s breasts.", + "\The [user] gropes \the [target]'s breasts roughly.")) + if(prob(5 + target.get_lust())) + if(target.a_intent == INTENT_HELP) + user.visible_message( + pick("\The [target] shivers in arousal.", + "\The [target] moans quietly.", + "\The [target] breathes out a soft moan.", + "\The [target] gasps.", + "\The [target] shudders softly.", + "\The [target] trembles as hands run across bare skin.")) + if(target.get_lust() < 5) + target.set_lust(5) + if(target.a_intent == INTENT_DISARM) + if (target.restrained()) + user.visible_message( + pick("\The [target] twists playfully against the restraints.", + "\The [target] squirms away from [user]'s hand.", + "\The [target] slides back from [user]'s roaming hand.", + "\The [target] thrusts bare breasts forward into [user]'s hands.")) + else + user.visible_message( + pick("\The [target] playfully bats at [user]'s hand.", + "\The [target] squirms away from [user]'s hand.", + "\The [target] guides [user]'s hand across bare breasts.", + "\The [target] teasingly laces a few fingers over [user]'s knuckles.")) + if(target.get_lust() < 10) + target.add_lust(1) + if(target.a_intent == INTENT_GRAB) + user.visible_message( + pick("\The [target] grips [user]'s wrist tight.", + "\The [target] digs nails into [user]'s arm.", + "\The [target] grabs [user]'s wrist for a second.")) + if(target.a_intent == INTENT_HARM) + user.adjustBruteLoss(1) + user.visible_message( + pick("\The [target] pushes [user] roughly away.", + "\The [target] digs nails angrily into [user]'s arm.", + "\The [target] fiercely struggles against [user].", + "\The [target] claws [user]'s forearm, drawing blood.", + "\The [target] slaps [user]'s hand away.")) + return + +/datum/interaction/lewd/nipsuck + command = "nipsuck" + description = "Suck their nipples." + require_target_topless = TRUE + require_user_mouth = TRUE + write_log_user = "sucked nipples" + write_log_target = "had their nipples sucked by" + interaction_sound = null + max_distance = 1 + +/datum/interaction/lewd/nipsuck/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + if((user.a_intent == INTENT_HELP) || (user.a_intent == INTENT_DISARM)) + user.visible_message( + pick("\The [user] gently sucks on \the [target]'s [pick("nipple", "nipples")].", + "\The [user] gently nibs \the [target]'s [pick("nipple", "nipples")].", + "\The [user] licks \the [target]'s [pick("nipple", "nipples")].")) + if(target.has_breasts(REQUIRE_EXPOSED)) + var/modifier = 1 + var/obj/item/organ/genital/breasts/B = target.getorganslot(ORGAN_SLOT_BREASTS) + switch(B.size) + if("c" || "d" || "e") + modifier = 2 + if("f" || "g" || "h") + modifier = 3 + if("i") + modifier = 4 + if("j") + modifier = 5 + else + modifier = 1 + target.reagents.add_reagent(/datum/reagent/consumable/milk, rand(1,2 * modifier)) + + if(user.a_intent == INTENT_HARM) + user.visible_message( + pick("\The [user] bites \the [target]'s [pick("nipple", "nipples")].", + "\The [user] aggressively sucks \the [target]'s [pick("nipple", "nipples")].")) + if(target.has_breasts(REQUIRE_EXPOSED)) + var/modifier = 1 + var/obj/item/organ/genital/breasts/B = target.getorganslot(ORGAN_SLOT_BREASTS) + switch(B.size) + if("c" || "d" || "e") + modifier = 2 + if("f" || "g" || "h") + modifier = 3 + if("i") + modifier = 4 + if("j") + modifier = 5 + else + modifier = 1 + target.reagents.add_reagent(/datum/reagent/consumable/milk, rand(1,3 * modifier)) //aggressive sucking leads to high rewards + + if(user.a_intent == INTENT_GRAB) + user.visible_message( + pick("\The [user] sucks \the [target]'s [pick("nipple", "nipples")] intently.", + "\The [user] feasts \the [target]'s [pick("nipple", "nipples")].", + "\The [user] glomps \the [target]'s [pick("nipple", "nipples")].")) + if(target.has_breasts(REQUIRE_EXPOSED)) + var/modifier = 1 + var/obj/item/organ/genital/breasts/B = target.getorganslot(ORGAN_SLOT_BREASTS) + switch(B.size) + if("c" || "d" || "e") + modifier = 2 + if("f" || "g" || "h") + modifier = 3 + if("i") + modifier = 4 + if("j") + modifier = 5 + else + modifier = 1 + target.reagents.add_reagent(/datum/reagent/consumable/milk, rand(1,3 * modifier)) //aggressive sucking leads to high rewards + + if(prob(5 + target.get_lust())) + if(target.a_intent == INTENT_HELP) + if(!target.has_breasts()) + user.visible_message( + pick("\The [target] shivers in arousal.", + "\The [target] moans quietly.", + "\The [target] breathes out a soft moan.", + "\The [target] gasps.", + "\The [target] shudders softly.", + "\The [target] trembles as their chest gets molested.")) + else + user.visible_message( + pick("\The [target] shivers in arousal.", + "\The [target] moans quietly.", + "\The [target] breathes out a soft moan.", + "\The [target] gasps.", + "\The [target] shudders softly.", + "\The [target] trembles as their breasts get molested.", + "\The [target] quivers in arousal as \the [user] delights themselves on their milk.")) + if(target.get_lust() < 5) + target.set_lust(5) + if(target.a_intent == INTENT_DISARM) + if (target.restrained()) + if(!target.has_breasts()) + user.visible_message( + pick("\The [target] twists playfully against the restraints.", + "\The [target] squirms away from \the [user]'s mouth.", + "\The [target] slides back from \the [user]'s mouth.", + "\The [target] thrusts their bare chest forward into \the [user]'s mouth.")) + else + user.visible_message( + pick("\The [target] twists playfully against the restraints.", + "\The [target] squirms away from \the [user]'s mouth.", + "\The [target] slides back from \the [user]'s mouth.", + "\The [target] thrust their bare breasts forward into \the [user]'s mouth.")) + else + if(!target.has_breasts()) + user.visible_message( + pick("\The [target] playfully shoos away \the [user]'s head.", + "\The [target] squirms away from \the [user]'s mouth.", + "\The [target] holds \the [user]'s head against their chest.", + "\The [target] teasingly caresses \the [user]'s neck.")) + else + user.visible_message( + pick("\The [target] playfully shoos away \the [user]'s head.", + "\The [target] squirms away from \the [user]'s mouth.", + "\The [target] holds \the [user]'s head against their breast.", + "\The [target] teasingly caresses \the [user]'s neck.", + "\The [target] rubs their breasts against \the [user]'s head.")) + if(target.get_lust() < 10) + target.add_lust(1) + if(target.a_intent == INTENT_GRAB) + user.visible_message( + pick("\The [target] grips \the [user]'s head tight.", + "\The [target] digs nails into \the [user]'s scalp.", + "\The [target] grabs and shoves \the [user]'s head away.")) + if(target.a_intent == INTENT_HARM) + user.adjustBruteLoss(1) + user.visible_message( + pick("\The [target] slaps \the [user] away.", + "\The [target] scratches [user]'s face.", + "\The [target] fiercely struggles against [user].", + "\The [target] claws [user]'s face, drawing blood.", + "\The [target] elbows [user]'s mouth away.")) + target.dir = get_dir(target, user) + user.dir = get_dir(user, target) + playlewdinteractionsound(user.loc, pick('sandcode/sound/interactions/oral1.ogg', + 'sandcode/sound/interactions/oral2.ogg'), 70, 1, -1) + return + +/datum/interaction/lewd/oral + command = "suckvag" + description = "Go down on them." + require_user_mouth = TRUE + require_target_vagina = REQUIRE_EXPOSED + write_log_user = "gave head to" + write_log_target = "was given head by" + interaction_sound = null + user_not_tired = TRUE + max_distance = 0 + +/datum/interaction/lewd/oral/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_oral(target, "vagina") + +/datum/interaction/lewd/oral/blowjob + command = "suckcock" + description = "Suck them off." + require_target_vagina = REQUIRE_NONE + require_target_penis = REQUIRE_EXPOSED + target_not_tired = TRUE + +/datum/interaction/lewd/oral/blowjob/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_oral(target, "penis") + +/datum/interaction/lewd/fuck + command = "fuckvag" + description = "Fuck their pussy." + require_user_penis = REQUIRE_EXPOSED + require_target_vagina = REQUIRE_EXPOSED + write_log_user = "fucked" + write_log_target = "was fucked by" + interaction_sound = null + user_not_tired = TRUE + max_distance = 0 + +/datum/interaction/lewd/fuck/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_vaginal(target) + +/datum/interaction/lewd/fuck/anal + command = "fuckass" + description = "Fuck their ass." + require_target_vagina = REQUIRE_NONE + require_target_anus = REQUIRE_EXPOSED + user_not_tired = TRUE + +/datum/interaction/lewd/fuck/anal/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_anal(target) + +/datum/interaction/lewd/finger + command = "finger" + description = "Finger their pussy." + require_user_hands = REQUIRE_ANY + require_target_vagina = REQUIRE_EXPOSED + interaction_sound = null + user_not_tired = TRUE + max_distance = 0 + +/datum/interaction/lewd/finger/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_fingering(target) + +/datum/interaction/lewd/fingerass + command = "fingerm" + description = "Finger their ass." + interaction_sound = null + require_user_hands = REQUIRE_ANY + require_target_anus = REQUIRE_EXPOSED + user_not_tired = TRUE + max_distance = 0 + +/datum/interaction/lewd/fingerass/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_fingerass(target) + + +/datum/interaction/lewd/facefuck + command = "facefuck" + description = "Fuck their mouth." + interaction_sound = null + require_target_mouth = TRUE + user_not_tired = TRUE + require_user_penis = REQUIRE_EXPOSED + max_distance = 0 + +/datum/interaction/lewd/facefuck/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_facefuck(target) + +/datum/interaction/lewd/throatfuck + command = "throatfuck" + description = "Fuck their throat. | Does oxy damage." + interaction_sound = null + require_user_penis = REQUIRE_EXPOSED + require_target_mouth = TRUE + user_not_tired = TRUE + max_distance = 0 + +/datum/interaction/lewd/throatfuck/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_throatfuck(target) + +/datum/interaction/lewd/handjob + command = "handjob" + description = "Jerk them off." + interaction_sound = null + require_user_hands = TRUE + require_target_penis = REQUIRE_EXPOSED + target_not_tired = TRUE + max_distance = 1 + +/datum/interaction/lewd/handjob/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_handjob(target) + +/datum/interaction/lewd/breastfuck + command = "breastfuck" + description = "Fuck their breasts." + interaction_sound = null + require_user_penis = REQUIRE_EXPOSED + user_not_tired = TRUE + require_target_breasts = REQUIRE_EXPOSED + max_distance = 0 + +/datum/interaction/lewd/breastfuck/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_breastfuck(target) + +/datum/interaction/lewd/mount + command = "mount" + description = "Mount with your pussy." + interaction_sound = null + require_user_vagina = REQUIRE_EXPOSED + require_target_penis = REQUIRE_EXPOSED + user_not_tired = TRUE + target_not_tired = TRUE + max_distance = 0 + +/datum/interaction/lewd/mount/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_mount(target) + +/datum/interaction/lewd/mountass + command = "mountm" + description = "Mount with your ass." + interaction_sound = null + require_user_anus = REQUIRE_EXPOSED + require_target_penis = REQUIRE_EXPOSED + user_not_tired = TRUE + target_not_tired = TRUE + max_distance = 0 + +/datum/interaction/lewd/mountass/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_mountass(target) + +/datum/interaction/lewd/tribadism + command = "tribadism" + description = "Grind your pussy against theirs." + interaction_sound = null + require_target_vagina = REQUIRE_EXPOSED + require_user_vagina = REQUIRE_EXPOSED + user_not_tired = TRUE + max_distance = 0 + +/datum/interaction/lewd/tribadism/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_tribadism(target) + +/datum/interaction/lewd/rimjob + command = "rimjob" + description = "Lick their ass." + interaction_sound = null + require_user_mouth = TRUE + require_target_anus = REQUIRE_EXPOSED + user_not_tired = TRUE + max_distance = 0 + +/datum/interaction/lewd/rimjob/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_rimjob(target) + +/datum/interaction/lewd/mountface + command = "mountface" + description = "Ass to face." + interaction_sound = null + require_target_mouth = TRUE + require_user_anus = REQUIRE_EXPOSED + user_not_tired = TRUE + max_distance = 0 + +/datum/interaction/lewd/mountface/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_mountface(target) + +/datum/interaction/lewd/lickfeet + command = "lickfeet" + description = "Lick their feet." + interaction_sound = null + require_user_mouth = TRUE + require_target_feet = REQUIRE_ANY + require_target_num_feet = 1 + max_distance = 1 + +/datum/interaction/lewd/lickfeet/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_lickfeet(target) + +/datum/interaction/lewd/grindface + command = "grindface" + description = "Feet grind their face." + interaction_sound = null + require_target_mouth = TRUE + require_user_num_feet = 1 + require_user_feet = REQUIRE_ANY + max_distance = 0 + +/datum/interaction/lewd/grindface/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_grindface(target) + +/datum/interaction/lewd/grindmouth + command = "grindmouth" + description = "Feet grind their mouth." + interaction_sound = null + require_target_mouth = TRUE + require_user_num_feet = 1 + require_user_feet = REQUIRE_ANY + max_distance = 0 + +/datum/interaction/lewd/grindmouth/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_grindmouth(target) + +/datum/interaction/lewd/footfuck + command = "footfuck" + description = "Rub your cock on their foot." + interaction_sound = null + require_target_num_feet = 1 + require_target_feet = REQUIRE_ANY + require_user_penis = REQUIRE_EXPOSED + user_not_tired = TRUE + max_distance = 1 + +/datum/interaction/lewd/footfuck/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_footfuck(target) + +/datum/interaction/lewd/footfuck/double + command = "footfuckdouble" + description = "Rub your cock between their feet." + require_target_num_feet = 2 + +/datum/interaction/lewd/footfuck/double/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_dfootfuck(target) + +/datum/interaction/lewd/footjob + command = "footjob" + description = "Jerk them off with your foot." + interaction_sound = null + require_user_num_feet = 1 + require_user_feet = REQUIRE_ANY + require_target_penis = REQUIRE_EXPOSED + target_not_tired = TRUE + max_distance = 1 + +/datum/interaction/lewd/footjob/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_footjob(target) + +/datum/interaction/lewd/footjob/double + command = "footjob_double" + description = "Jerk them off with both of your feet." + require_user_num_feet = 2 + +/datum/interaction/lewd/footjob/double/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_dfootjob(target) + +/datum/interaction/lewd/footjob/vagina + command = "footjob_vaginal" + description = "Rub their vagina with your foot." + require_target_vagina = REQUIRE_EXPOSED + require_target_penis = REQUIRE_NONE + +/datum/interaction/lewd/footjob/vagina/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_footjob_v(target) + +/datum/interaction/lewd/thighs + command = "thigh_smother" + description = "Smother them." + max_distance = 0 + require_user_bottomless = TRUE + require_target_mouth = TRUE + interaction_sound = null + user_not_tired = TRUE + write_log_user = "thigh-trapped" + write_log_target = "was smothered by" + +/datum/interaction/lewd/thighs/display_interaction(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target) + user.thigh_smother(target) + +/datum/interaction/lewd/nuts + command = "nut_face" + description = "Nuts to face." + interaction_sound = null + require_user_balls = REQUIRE_EXPOSED + require_target_mouth = TRUE + max_distance = 0 + write_log_user = "make-them-suck-their-nuts" + write_log_target = "was made to suck nuts by" + +/datum/interaction/lewd/nuts/display_interaction(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target) + user.nut_face(target) + +/datum/interaction/lewd/nut_smack + command = "smack_nuts" + description = "Smack their nuts." + interaction_sound = 'sandcode/sound/interactions/slap.ogg' + simple_message = "USER slaps TARGET's nuts!" + require_target_balls = REQUIRE_EXPOSED + needs_physical_contact = TRUE + max_distance = 1 + write_log_user = "slapped-nuts" + write_log_target = "had their nuts slapped by" + +//Weird shit section +/datum/interaction/lewd/earfuck + command = "earfuck" + description = "Fuck their ear." + interaction_sound = null + simple_message = "USER penetrates TARGET's ear!" + require_user_penis = REQUIRE_EXPOSED + require_target_ears = REQUIRE_EXPOSED + max_distance = 1 + write_log_user = "earfucked" + write_log_target = "had their ear fucked by" + extreme = TRUE + +/datum/interaction/lewd/earfuck/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_earfuck(target) + +/datum/interaction/lewd/earfuck/earsocketfuck + command = "earsocketfuck" + description = "Fuck their earsocket." + interaction_sound = null + simple_message = "USER penetrates TARGET's earsocket!" + require_user_penis = REQUIRE_EXPOSED + require_target_earsockets = REQUIRE_EXPOSED + max_distance = 1 + write_log_user = "earsocket fucked" + write_log_target = "had their earsocket fucked by" + extreme = TRUE + +/datum/interaction/lewd/eyefuck + command = "eyefuck" + description = "Fuck their eye." + interaction_sound = null + simple_message = "USER penetrates TARGET's eye!" + require_user_penis = REQUIRE_EXPOSED + require_target_eyes = REQUIRE_EXPOSED + max_distance = 1 + write_log_user = "eyefucked" + write_log_target = "had their eye fucked by" + extreme = TRUE + +/datum/interaction/lewd/eyefuck/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + user.do_eyefuck(target) + +/datum/interaction/lewd/eyefuck/eyesocketfuck + command = "eyesocketfuck" + description = "Fuck their eyesocket." + interaction_sound = null + simple_message = "USER penetrates TARGET's eyesocket!" + require_user_penis = REQUIRE_EXPOSED + require_target_eyesockets = REQUIRE_EXPOSED + max_distance = 1 + write_log_user = "eyesocketfucked" + write_log_target = "had their eyesocket fucked by" + extreme = TRUE +// diff --git a/sandcode/code/interactions/lewd/lewd_interactions.dm b/sandcode/code/interactions/lewd/lewd_interactions.dm new file mode 100644 index 0000000000..3546070a8b --- /dev/null +++ b/sandcode/code/interactions/lewd/lewd_interactions.dm @@ -0,0 +1,583 @@ +// If I could have gotten away with using a tilde in the type path, I would have. +/datum/interaction/lewd + command = "assslap" + description = "Slap their ass." + simple_message = "USER slaps TARGET right on the ass!" + simple_style = "danger" + interaction_sound = 'sound/weapons/slap.ogg' + needs_physical_contact = TRUE + require_ooc_consent = TRUE + max_distance = 1 + + write_log_user = "ass-slapped" + write_log_target = "was ass-slapped by" + + var/user_not_tired + var/target_not_tired + //Avoid using these! + //Should only really use in case there are no related organs + //but you want the target or user to be topless/bottomless. + //Example: Nipple licking/sucking. + //Otherwise, simply use the "require" vars, which + //i have changed to actually check for the appropriate organs. + //This is better because it means that exposing a genital while still + //wearing something actually means you can do the s*x. + var/require_user_topless + var/require_target_topless + var/require_user_bottomless + var/require_target_bottomless + // + + //REQUIRE_NONE for doesn't require. + //REQUIRE_EXPOSED for requires exposed. + //REQUIRE_ANY for both exposed and unexposed. + //REQUIRE_UNEXPOSED for requires unexposed. + var/require_user_penis + var/require_user_anus + var/require_user_vagina + var/require_user_breasts + var/require_user_feet + var/require_user_balls + + //Different from the others above. Use the number of required feet. + var/require_user_num_feet + + //Same logic presented before + var/require_target_penis + var/require_target_anus + var/require_target_vagina + var/require_target_breasts + var/require_target_feet + var/require_target_balls + + var/require_target_num_feet + + //"just fucking kill me" variables + //also the same logic as before + var/extreme = FALSE //Boolean. Used to hide extreme shit from those who do not want it. + var/require_target_ears + var/require_target_earsockets + var/require_target_eyes + var/require_target_eyesockets + var/require_user_ears + var/require_user_earsockets + var/require_user_eyes + var/require_user_eyesockets + // + + var/user_refractory_cost + var/target_refractory_cost + +/datum/interaction/lewd/evaluate_user(mob/living/carbon/human/user, silent = TRUE) + if(..(user, silent)) + if(user_not_tired && user.get_refraction_dif()) + if(!silent) //bye spam + to_chat(user, "You're still exhausted from the last time. You need to wait [DisplayTimeText(user.get_refraction_dif(), TRUE)] until you can do that!") + return FALSE + + if(require_user_bottomless && !user.is_bottomless()) + if(!silent) + to_chat(user, "Your pants are in the way.") + return FALSE + + if(require_user_topless && !user.is_topless()) + if(!silent) + to_chat(user, "Your top is in the way.") + return FALSE + + if(require_user_penis) + switch(require_user_penis) + if(REQUIRE_EXPOSED) + if(!user.has_penis(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Your penis need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!user.has_penis(REQUIRE_ANY)) + if(!silent) + to_chat(user, "You don't have a penis.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!user.has_penis(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Your penis need to be unexposed.") + return FALSE + + if(require_user_balls) + switch(require_user_balls) + if(REQUIRE_EXPOSED) + if(!user.has_balls(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Your balls need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!user.has_balls(REQUIRE_ANY)) + if(!silent) + to_chat(user, "You don't have balls.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!user.has_balls(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Your balls need to be unexposed.") + return FALSE + + if(require_user_anus) + switch(require_user_anus) + if(REQUIRE_EXPOSED) + if(!user.has_anus(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Your anus need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!user.has_anus(REQUIRE_ANY)) + if(!silent) + to_chat(user, "You don't have an anus.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!user.has_anus(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Your anus need to be unexposed.") + return FALSE + + if(require_user_vagina) + switch(require_user_vagina) + if(REQUIRE_EXPOSED) + if(!user.has_vagina(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Your vagina need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!user.has_vagina(REQUIRE_ANY)) + if(!silent) + to_chat(user, "You don't have a vagina.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!user.has_vagina(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Your vagina need to be unexposed.") + return FALSE + + if(require_user_breasts) + switch(require_user_breasts) + if(REQUIRE_EXPOSED) + if(!user.has_breasts(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Your breasts need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!user.has_breasts(REQUIRE_ANY)) + if(!silent) + to_chat(user, "You don't have breasts.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!user.has_breasts(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Your breasts need to be unexposed.") + return FALSE + + if(require_user_feet) + switch(require_user_feet) + if(REQUIRE_EXPOSED) + if(!user.has_feet(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Your feet need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!user.has_feet(REQUIRE_ANY)) + if(!silent) + to_chat(user, "You don't have enough feet.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!user.has_feet(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Your feet need to be unexposed.") + return FALSE + + if(require_user_num_feet && (user.get_num_feet() < require_user_num_feet)) + if(!silent) + to_chat(user, "You don't have enough feet.") + return FALSE + + if(require_user_eyes) + switch(require_user_eyes) + if(REQUIRE_EXPOSED) + if(!user.has_eyes(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Your eyes need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!user.has_eyes(REQUIRE_ANY)) + if(!silent) + to_chat(user, "You don't have eyes.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!user.has_eyes(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Your eyes need to be unexposed.") + return FALSE + + if(require_user_eyesockets) + switch(require_user_eyesockets) + if(REQUIRE_EXPOSED) + if(!user.has_eyesockets(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Your eyesockets need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!user.has_eyesockets(REQUIRE_ANY)) + if(!silent) + to_chat(user, "You still have eyes.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!user.has_eyesockets(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Your eyesockets need to be unexposed.") + return FALSE + + if(require_user_ears) + switch(require_user_ears) + if(REQUIRE_EXPOSED) + if(!user.has_ears(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Your ears need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!user.has_ears(REQUIRE_ANY)) + if(!silent) + to_chat(user, "You don't have ears.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!user.has_ears(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Your ears need to be unexposed.") + return FALSE + + if(require_user_earsockets) + switch(require_user_earsockets) + if(REQUIRE_EXPOSED) + if(!user.has_earsockets(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Your earsockets need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!user.has_earsockets(REQUIRE_ANY)) + if(!silent) + to_chat(user, "You still have eyes.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!user.has_earsockets(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Your earsockets need to be unexposed.") + return FALSE + + if(extreme) + var/client/cli = user.client + if(cli) + if(cli.prefs.extremepref == "No") + if(!silent) + to_chat(user, "That's way too much for you.") + return FALSE + + if(require_ooc_consent) + if(user.client && user.client.prefs.toggles & VERB_CONSENT) + return TRUE + return FALSE + return FALSE + +/datum/interaction/lewd/evaluate_target(mob/living/carbon/human/user, mob/living/carbon/human/target, silent = TRUE) + if(..(user, target, silent)) + if(target_not_tired && target.get_refraction_dif()) + if(!silent) //same with this + to_chat(user, "They're still exhausted from the last time. They need to wait [DisplayTimeText(target.get_refraction_dif(), TRUE)] until you can do that!") + return FALSE + + if(require_target_penis) + switch(require_target_penis) + if(REQUIRE_EXPOSED) + if(!target.has_penis(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Their penis needs to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!target.has_penis(REQUIRE_ANY)) + if(!silent) + to_chat(user, "They don't have a penis.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!target.has_penis(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Their penis needs to be unexposed.") + return FALSE + + if(require_target_balls) + switch(require_target_balls) + if(REQUIRE_EXPOSED) + if(!target.has_balls(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Their balls need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!target.has_balls(REQUIRE_ANY)) + if(!silent) + to_chat(user, "They don't have balls.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!target.has_balls(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Their balls need to be unexposed.") + return FALSE + + if(require_target_anus) + switch(require_target_anus) + if(REQUIRE_EXPOSED) + if(!target.has_anus(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Their anus needs to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!target.has_anus(REQUIRE_ANY)) + if(!silent) + to_chat(user, "They don't have an anus.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!target.has_anus(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Their anus needs to be unexposed.") + return FALSE + + if(require_target_vagina) + switch(require_target_vagina) + if(REQUIRE_EXPOSED) + if(!target.has_vagina(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Their vagina needs to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!target.has_vagina(REQUIRE_ANY)) + if(!silent) + to_chat(user, "They don't have a vagina.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!target.has_vagina(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Their vagina needs to be unexposed.") + return FALSE + + if(require_target_breasts) + switch(require_target_breasts) + if(REQUIRE_EXPOSED) + if(!target.has_breasts(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Their breasts need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!target.has_breasts(REQUIRE_ANY)) + if(!silent) + to_chat(user, "They don't have breasts.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!target.has_breasts(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Their breasts need to be unexposed.") + return FALSE + + if(require_target_feet) + switch(require_target_feet) + if(REQUIRE_EXPOSED) + if(!target.has_feet(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Their feet need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!target.has_feet(REQUIRE_ANY)) + if(!silent) + to_chat(user, "They don't have enough feet.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!target.has_feet(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Their feet need to be unexposed.") + return FALSE + + if(require_target_num_feet && (user.get_num_feet() < require_target_num_feet)) + if(!silent) + to_chat(user, "They don't have enough feet.") + return FALSE + + if(require_target_eyes) + switch(require_target_eyes) + if(REQUIRE_EXPOSED) + if(!target.has_eyes(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Their eyes need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!target.has_eyes(REQUIRE_ANY)) + if(!silent) + to_chat(user, "They don't have eyes.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!target.has_eyes(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Their eyes need to be unexposed.") + return FALSE + + if(require_target_eyesockets) + switch(require_target_eyesockets) + if(REQUIRE_EXPOSED) + if(!target.has_eyesockets(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Their eyesockets need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!target.has_eyesockets(REQUIRE_ANY)) + if(!silent) + to_chat(user, "They still have eyes.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!target.has_eyesockets(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Their eyesockets need to be unexposed.") + return FALSE + + if(require_target_ears) + switch(require_target_ears) + if(REQUIRE_EXPOSED) + if(!target.has_ears(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Their ears need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!target.has_ears(REQUIRE_ANY)) + if(!silent) + to_chat(user, "They don't have ears.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!target.has_ears(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Their ears need to be unexposed.") + return FALSE + + if(require_target_earsockets) + switch(require_target_earsockets) + if(REQUIRE_EXPOSED) + if(!target.has_earsockets(REQUIRE_EXPOSED)) + if(!silent) + to_chat(user, "Their earsockets need to be exposed.") + return FALSE + if(REQUIRE_ANY) + if(!target.has_earsockets(REQUIRE_ANY)) + if(!silent) + to_chat(user, "They still have eyes.") + return FALSE + if(REQUIRE_UNEXPOSED) + if(!target.has_earsockets(REQUIRE_UNEXPOSED)) + if(!silent) + to_chat(user, "Their earsockets need to be unexposed.") + return FALSE + + if(require_target_bottomless && !target.is_bottomless()) + if(!silent) + to_chat(user, "Their clothes are in the way.") + return FALSE + + if(require_target_topless && !target.is_topless()) + if(!silent) + to_chat(user, "Their clothes are in the way.") + return FALSE + + if(extreme) + var/client/cli = target.client + if(cli) + if(target.client.prefs.extremepref == "No") + if(!silent) + to_chat(user, "For some reason, you don't want to do this to [target].") + return FALSE + + if(require_target_bottomless && !target.is_bottomless()) + if(!silent) + to_chat(user, "Their pants are in the way.") + return FALSE + + if(require_ooc_consent) + if(target.client && target.client.prefs.toggles & VERB_CONSENT) + return TRUE + return FALSE + return FALSE + +/datum/interaction/lewd/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(user_refractory_cost) + user.refractory_period = world.time + user_refractory_cost*10 + if(target_refractory_cost) + target.refractory_period = world.time + target_refractory_cost*10 + user.last_lewd_datum = src + if(user.cleartimer) + deltimer(user.cleartimer) + user.cleartimer = addtimer(CALLBACK(user, /mob/living/proc/clear_lewd_datum), 300, TIMER_STOPPABLE) + return ..() + +/datum/interaction/lewd/get_action_link_for(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(user.stat == DEAD) + to_chat(user, "You cannot do that while deceased!") + return + if(extreme) + return "EXTREME: [..()]" + return "LEWD: [..()]" + +/mob/living/carbon/human/list_interaction_attributes(var/mob/living/LM) + var/dat = ..() + if(get_refraction_dif()) + dat += "
...are sexually exhausted for the time being." + if(a_intent == INTENT_HELP) + dat += "
...are acting gentle." + else if (a_intent == INTENT_DISARM) + dat += "
...are acting playful." + else if (a_intent == INTENT_GRAB) + dat += "
...are acting rough." + else if(a_intent == INTENT_HARM) + dat += "
...are fighting anyone who comes near." + //Here comes the fucking weird shit. + if(client) + var/client/cli = client + var/client/ucli = LM.client + if(cli.prefs.extremepref != "No") + if(!ucli || (ucli.prefs.extremepref != "No")) + if(!get_item_by_slot(ITEM_SLOT_EARS)) + if(has_ears()) + dat += "
...have unprotected ears." + else + dat += "
...have a hole where their ears should be." + else + dat += "
...have covered ears." + if(!get_item_by_slot(ITEM_SLOT_EYES)) + if(has_eyes()) + dat += "
...have exposed eyes." + else + dat += "
...have exposed eyesockets." + else + dat += "
...have covered eyes." + // + if(is_topless() && is_bottomless()) + dat += "
...are naked." + else + if((is_topless() && !is_bottomless()) || (!is_topless() && is_bottomless())) + dat += "
...are partially clothed." + else + dat += "
...are clothed." + if(has_breasts(REQUIRE_EXPOSED)) + dat += "
...have breasts." + if(has_penis(REQUIRE_EXPOSED)) + dat += "
...have a penis." + if(has_balls(REQUIRE_EXPOSED)) + dat += "
...have a ballsack." + if(has_vagina(REQUIRE_EXPOSED)) + dat += "
...have a vagina." + if(has_anus(REQUIRE_EXPOSED)) + dat += "
...have an anus." + if(has_feet(REQUIRE_EXPOSED)) + switch(has_feet(REQUIRE_EXPOSED)) + if(2) + dat += "
...have a pair of feet." + if(1) + dat += "
...have a single foot." + return dat diff --git a/sandcode/code/interactions/lewd/lewd_species.dm b/sandcode/code/interactions/lewd/lewd_species.dm new file mode 100644 index 0000000000..59dd1b087a --- /dev/null +++ b/sandcode/code/interactions/lewd/lewd_species.dm @@ -0,0 +1,16 @@ +/datum/species + var/has_genitals = TRUE //if this goes to 2, blame coders + var/has_anus = TRUE //same with this + var/has_breasts = FALSE + +/datum/species/proc/has_penis(mob/living/carbon/human/H) + return has_genitals && H.has_penis + +/datum/species/proc/has_vagina(mob/living/carbon/human/H) + return has_genitals && H.has_vagina + +/datum/species/proc/has_anus(mob/living/carbon/human/H) + return has_anus + +/datum/species/proc/has_breasts(mob/living/carbon/human/H) + return has_breasts || (H.has_breasts) diff --git a/sandcode/code/interactions/lewd/objects.dm b/sandcode/code/interactions/lewd/objects.dm new file mode 100644 index 0000000000..f6308e6698 --- /dev/null +++ b/sandcode/code/interactions/lewd/objects.dm @@ -0,0 +1,161 @@ +//Dildo changes. +/obj/item/dildo + /*name = "dildo" + desc = "Hmmm, deal throw." + icon = 'honk/icons/obj/items/dildo.dmi' + icon_state = "dildo_map" + item_state = "dildo" + throwforce = 0 + force = 5 + w_class = WEIGHT_CLASS_SMALL + attack_verb = list("slammed", "bashed", "whipped")*/ + + var/hole = CUM_TARGET_VAGINA + + /*var/random_color = TRUE + var/static/list/dildo_colors = list( + "purple" = "#800080", + "darkviolet" = "#9400D3", + "red" = "#FF0000", + "dimgray" = "#696969", + "black" = "#000000", //dont kill me for this - lennuel + "white" = "#ffffff", //racial equality + "green" = "#008000", + "pink" = "#FFC0CB", + "lightpink" = "#ffc0cb", + "blue" = "#7282e5" + ) + +/obj/item/dildo/Initialize() + . = ..() + if(random_color) //random colors! + icon_state = "dildo" + var/our_color = pick(dildo_colors) + add_atom_colour(dildo_colors[our_color], FIXED_COLOUR_PRIORITY) + update_icon() + +/obj/item/dildo/update_icon() + if(!random_color) //icon override + return + cut_overlays() + var/mutable_appearance/base_overlay = mutable_appearance(icon, "dildo_base") + base_overlay.appearance_flags = RESET_COLOR + add_overlay(base_overlay) */ + +/obj/item/dildo/attack(mob/living/carbon/human/M, mob/living/carbon/human/user) + var/message = "" + if(istype(M, /mob/living/carbon/human) && user.zone_selected == BODY_ZONE_PRECISE_GROIN && M.is_bottomless()) + if(M.client && M.client.prefs) + if(M.client.prefs.toggles & VERB_CONSENT) + if(hole == CUM_TARGET_VAGINA && M.has_vagina()) + message = (user == M) ? pick("fucks their own pussy with \the [src]","shoves the [src] into their pussy", "jams the [src] into their pussy") : pick("fucks [M] right in the pussy with \the [src]", "jams \the [src] right into [M]'s pussy") + else if(hole == CUM_TARGET_ANUS && M.has_anus()) + message = (user == M) ? pick("fucks their own ass with \the [src]","shoves the [src] into their ass", "jams the [src] into their ass") : pick("fucks [M]'s asshole with \the [src]", "jams \the [src] into [M]'s ass") + if(message) + user.visible_message("[user] [message].") + M.handle_post_sex(5, null, user) + playsound(loc, pick('sandcode/sound/interactions/bang4.ogg', + 'sandcode/sound/interactions/bang5.ogg', + 'sandcode/sound/interactions/bang6.ogg'), 70, 1, -1) + else if(user.a_intent == INTENT_HARM) + return ..() + +/obj/item/dildo/attack_self(mob/living/carbon/human/user as mob) + if(hole == CUM_TARGET_VAGINA) + hole = CUM_TARGET_ANUS + else + hole = CUM_TARGET_VAGINA + to_chat(user, "Hmmm. Maybe we should put it in \a [hole]?") + +//reagent here +/*datum/reagent/consumable/cum // could probably be made /blood/consumable/cum to just inherit the DNA procs sometime + name = "cum" + id = "cum" + description = "Where you found this is your own business." + color = "#AAAAAA77" + taste_description = "something with a tang" + data = list("donor"=null,"viruses"=null,"donor_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null) + taste_mult = 2 + reagent_state = LIQUID + nutriment_factor = 0.5 * REAGENTS_METABOLISM + glass_icon_state = "glass_white" + glass_name = "glass of cream" + glass_desc = "Smells suspicious." + shot_glass_icon_state = "shotglasscream" + +/datum/reagent/consumable/cum/reaction_turf(turf/T, reac_volume) + if(!istype(T)) + return + if(reac_volume < 3) + return + + var/obj/effect/decal/cleanable/cum/S = locate() in T + if(!S) + S = new(T) + S.reagents.add_reagent("cum", reac_volume) + if(data["blood_DNA"]) + S.add_blood_DNA(list(data["blood_DNA"] = data["blood_type"])) //yes. STD + +//cleanable here + +/obj/effect/decal/cleanable/cum + name = "cum" + desc = "It's pie cream from a cream pie. Or not..." + gender = PLURAL + layer = ABOVE_NORMAL_TURF_LAYER + density = 0 + icon = 'honk/icons/effects/cum.dmi' + icon_state = "cum1" + random_icon_states = list("cum1", "cum3", "cum4", "cum5", "cum6", "cum7", "cum8", "cum9", "cum10", "cum11", "cum12") + mergeable_decal = TRUE + blood_state = null + bloodiness = null + //var/blood_DNA = list() + +/obj/effect/decal/cleanable/cum/Initialize() + . = ..() + dir = pick(1,2,4,8) + reagents.add_reagent("cum", rand(8,13)) + add_blood_DNA(list("Unknown DNA" = "O+"))*/ + +//begin redds code +/obj/item/dildo/cyborg + name = "F.I.S.T.R. Machine" + desc = "Fully Integrated Sexual Tension Relief Machine" + +/obj/item/dildo/cyborg/attack(mob/living/carbon/human/M, mob/living/carbon/human/user) + var/message = "" + if(istype(M, /mob/living/carbon/human) && M.is_bottomless()) + if(M.client && M.client.prefs) + if(M.client.prefs.toggles & VERB_CONSENT) + if(hole == CUM_TARGET_VAGINA && M.has_vagina()) + message = (user == M) ? pick("fucks their own pussy with \the [src]","shoves the [src] into their pussy", "jams the [src] into their pussy") : pick("fucks [M] right in the pussy with \the [src]", "jams \the [src] right into [M]'s pussy") + else if(hole == CUM_TARGET_ANUS && M.has_anus()) + message = (user == M) ? pick("fucks their own ass with \the [src]","shoves the [src] into their ass", "jams the [src] into their ass") : pick("fucks [M]'s asshole with \the [src]", "jams \the [src] into [M]'s ass") + if(message) + user.visible_message("[user] [message].") + M.handle_post_sex(5, null, user) + playsound(loc, pick('sandcode/sound/interactions/bang4.ogg', + 'sandcode/sound/interactions/bang5.ogg', + 'sandcode/sound/interactions/bang6.ogg'), 70, 1, -1) + else if(user.a_intent == INTENT_HARM) + return ..() +//end redds code + +/obj/item/pneumatic_cannon/dildo + color = "#FFC0CB" + name = "pneumatic cannon" + desc = "A pneumatic cannon with a picture of a bus printed on the side that resembles an A-shape." + automatic = TRUE + selfcharge = TRUE + gasPerThrow = 0 + checktank = FALSE + fire_mode = PCANNON_FIFO + throw_amount = 1 + maxWeightClass = 60 + var/static/list/dildo_typecache = typecacheof(/obj/item/dildo) + charge_type = /obj/item/dildo + +/obj/item/pneumatic_cannon/dildo/Initialize() + . = ..() + allowed_typecache = dildo_typecache diff --git a/sandcode/sound/interactions/bang1.ogg b/sandcode/sound/interactions/bang1.ogg new file mode 100644 index 0000000000..d0b8abafa7 Binary files /dev/null and b/sandcode/sound/interactions/bang1.ogg differ diff --git a/sandcode/sound/interactions/bang2.ogg b/sandcode/sound/interactions/bang2.ogg new file mode 100644 index 0000000000..ff07de9666 Binary files /dev/null and b/sandcode/sound/interactions/bang2.ogg differ diff --git a/sandcode/sound/interactions/bang3.ogg b/sandcode/sound/interactions/bang3.ogg new file mode 100644 index 0000000000..62cbe7cd63 Binary files /dev/null and b/sandcode/sound/interactions/bang3.ogg differ diff --git a/sandcode/sound/interactions/bang4.ogg b/sandcode/sound/interactions/bang4.ogg new file mode 100644 index 0000000000..4d9d9da805 Binary files /dev/null and b/sandcode/sound/interactions/bang4.ogg differ diff --git a/sandcode/sound/interactions/bang5.ogg b/sandcode/sound/interactions/bang5.ogg new file mode 100644 index 0000000000..184af6787c Binary files /dev/null and b/sandcode/sound/interactions/bang5.ogg differ diff --git a/sandcode/sound/interactions/bang6.ogg b/sandcode/sound/interactions/bang6.ogg new file mode 100644 index 0000000000..11c7c056e5 Binary files /dev/null and b/sandcode/sound/interactions/bang6.ogg differ diff --git a/sandcode/sound/interactions/bj1.ogg b/sandcode/sound/interactions/bj1.ogg new file mode 100644 index 0000000000..004d668f05 Binary files /dev/null and b/sandcode/sound/interactions/bj1.ogg differ diff --git a/sandcode/sound/interactions/bj10.ogg b/sandcode/sound/interactions/bj10.ogg new file mode 100644 index 0000000000..5d0d56e010 Binary files /dev/null and b/sandcode/sound/interactions/bj10.ogg differ diff --git a/sandcode/sound/interactions/bj11.ogg b/sandcode/sound/interactions/bj11.ogg new file mode 100644 index 0000000000..5335057b45 Binary files /dev/null and b/sandcode/sound/interactions/bj11.ogg differ diff --git a/sandcode/sound/interactions/bj2.ogg b/sandcode/sound/interactions/bj2.ogg new file mode 100644 index 0000000000..54c74dd5b4 Binary files /dev/null and b/sandcode/sound/interactions/bj2.ogg differ diff --git a/sandcode/sound/interactions/bj3.ogg b/sandcode/sound/interactions/bj3.ogg new file mode 100644 index 0000000000..f12481f0b7 Binary files /dev/null and b/sandcode/sound/interactions/bj3.ogg differ diff --git a/sandcode/sound/interactions/bj4.ogg b/sandcode/sound/interactions/bj4.ogg new file mode 100644 index 0000000000..35b036d3a5 Binary files /dev/null and b/sandcode/sound/interactions/bj4.ogg differ diff --git a/sandcode/sound/interactions/bj5.ogg b/sandcode/sound/interactions/bj5.ogg new file mode 100644 index 0000000000..1c33bee84e Binary files /dev/null and b/sandcode/sound/interactions/bj5.ogg differ diff --git a/sandcode/sound/interactions/bj6.ogg b/sandcode/sound/interactions/bj6.ogg new file mode 100644 index 0000000000..564e82bad4 Binary files /dev/null and b/sandcode/sound/interactions/bj6.ogg differ diff --git a/sandcode/sound/interactions/bj7.ogg b/sandcode/sound/interactions/bj7.ogg new file mode 100644 index 0000000000..a65d9cdc84 Binary files /dev/null and b/sandcode/sound/interactions/bj7.ogg differ diff --git a/sandcode/sound/interactions/bj8.ogg b/sandcode/sound/interactions/bj8.ogg new file mode 100644 index 0000000000..e88550afce Binary files /dev/null and b/sandcode/sound/interactions/bj8.ogg differ diff --git a/sandcode/sound/interactions/bj9.ogg b/sandcode/sound/interactions/bj9.ogg new file mode 100644 index 0000000000..2f4feb33ea Binary files /dev/null and b/sandcode/sound/interactions/bj9.ogg differ diff --git a/sandcode/sound/interactions/champ1.ogg b/sandcode/sound/interactions/champ1.ogg new file mode 100644 index 0000000000..07f2118f2a Binary files /dev/null and b/sandcode/sound/interactions/champ1.ogg differ diff --git a/sandcode/sound/interactions/champ2.ogg b/sandcode/sound/interactions/champ2.ogg new file mode 100644 index 0000000000..63a1e8f1b8 Binary files /dev/null and b/sandcode/sound/interactions/champ2.ogg differ diff --git a/sandcode/sound/interactions/champ_fingering.ogg b/sandcode/sound/interactions/champ_fingering.ogg new file mode 100644 index 0000000000..7e540c9703 Binary files /dev/null and b/sandcode/sound/interactions/champ_fingering.ogg differ diff --git a/sandcode/sound/interactions/clawcum1.ogg b/sandcode/sound/interactions/clawcum1.ogg new file mode 100644 index 0000000000..4313ad4ee1 Binary files /dev/null and b/sandcode/sound/interactions/clawcum1.ogg differ diff --git a/sandcode/sound/interactions/clawcum2.ogg b/sandcode/sound/interactions/clawcum2.ogg new file mode 100644 index 0000000000..8573ca2093 Binary files /dev/null and b/sandcode/sound/interactions/clawcum2.ogg differ diff --git a/sandcode/sound/interactions/final_f1.ogg b/sandcode/sound/interactions/final_f1.ogg new file mode 100644 index 0000000000..3453ec4f93 Binary files /dev/null and b/sandcode/sound/interactions/final_f1.ogg differ diff --git a/sandcode/sound/interactions/final_f2.ogg b/sandcode/sound/interactions/final_f2.ogg new file mode 100644 index 0000000000..a0ba60e0a6 Binary files /dev/null and b/sandcode/sound/interactions/final_f2.ogg differ diff --git a/sandcode/sound/interactions/final_f3.ogg b/sandcode/sound/interactions/final_f3.ogg new file mode 100644 index 0000000000..dd9070bd5b Binary files /dev/null and b/sandcode/sound/interactions/final_f3.ogg differ diff --git a/sandcode/sound/interactions/final_m1.ogg b/sandcode/sound/interactions/final_m1.ogg new file mode 100644 index 0000000000..5f7bf53993 Binary files /dev/null and b/sandcode/sound/interactions/final_m1.ogg differ diff --git a/sandcode/sound/interactions/final_m2.ogg b/sandcode/sound/interactions/final_m2.ogg new file mode 100644 index 0000000000..12ddb5d65b Binary files /dev/null and b/sandcode/sound/interactions/final_m2.ogg differ diff --git a/sandcode/sound/interactions/final_m3.ogg b/sandcode/sound/interactions/final_m3.ogg new file mode 100644 index 0000000000..148f6c46c3 Binary files /dev/null and b/sandcode/sound/interactions/final_m3.ogg differ diff --git a/sandcode/sound/interactions/final_m4.ogg b/sandcode/sound/interactions/final_m4.ogg new file mode 100644 index 0000000000..a812334091 Binary files /dev/null and b/sandcode/sound/interactions/final_m4.ogg differ diff --git a/sandcode/sound/interactions/final_m5.ogg b/sandcode/sound/interactions/final_m5.ogg new file mode 100644 index 0000000000..977d01e8ff Binary files /dev/null and b/sandcode/sound/interactions/final_m5.ogg differ diff --git a/sandcode/sound/interactions/foot_dry1.ogg b/sandcode/sound/interactions/foot_dry1.ogg new file mode 100644 index 0000000000..1ac7bc6693 Binary files /dev/null and b/sandcode/sound/interactions/foot_dry1.ogg differ diff --git a/sandcode/sound/interactions/foot_dry2.ogg b/sandcode/sound/interactions/foot_dry2.ogg new file mode 100644 index 0000000000..957e57591b Binary files /dev/null and b/sandcode/sound/interactions/foot_dry2.ogg differ diff --git a/sandcode/sound/interactions/foot_dry3.ogg b/sandcode/sound/interactions/foot_dry3.ogg new file mode 100644 index 0000000000..5b38d5ab85 Binary files /dev/null and b/sandcode/sound/interactions/foot_dry3.ogg differ diff --git a/sandcode/sound/interactions/foot_dry4.ogg b/sandcode/sound/interactions/foot_dry4.ogg new file mode 100644 index 0000000000..7f71feedff Binary files /dev/null and b/sandcode/sound/interactions/foot_dry4.ogg differ diff --git a/sandcode/sound/interactions/foot_wet1.ogg b/sandcode/sound/interactions/foot_wet1.ogg new file mode 100644 index 0000000000..b38a193226 Binary files /dev/null and b/sandcode/sound/interactions/foot_wet1.ogg differ diff --git a/sandcode/sound/interactions/foot_wet2.ogg b/sandcode/sound/interactions/foot_wet2.ogg new file mode 100644 index 0000000000..857906e8db Binary files /dev/null and b/sandcode/sound/interactions/foot_wet2.ogg differ diff --git a/sandcode/sound/interactions/foot_wet3.ogg b/sandcode/sound/interactions/foot_wet3.ogg new file mode 100644 index 0000000000..9a3b8c755f Binary files /dev/null and b/sandcode/sound/interactions/foot_wet3.ogg differ diff --git a/sandcode/sound/interactions/hug.ogg b/sandcode/sound/interactions/hug.ogg new file mode 100644 index 0000000000..68c2948bdc Binary files /dev/null and b/sandcode/sound/interactions/hug.ogg differ diff --git a/sandcode/sound/interactions/moan_f1.ogg b/sandcode/sound/interactions/moan_f1.ogg new file mode 100644 index 0000000000..836ecee5f8 Binary files /dev/null and b/sandcode/sound/interactions/moan_f1.ogg differ diff --git a/sandcode/sound/interactions/moan_f2.ogg b/sandcode/sound/interactions/moan_f2.ogg new file mode 100644 index 0000000000..fd2f267236 Binary files /dev/null and b/sandcode/sound/interactions/moan_f2.ogg differ diff --git a/sandcode/sound/interactions/moan_f3.ogg b/sandcode/sound/interactions/moan_f3.ogg new file mode 100644 index 0000000000..7e3a35b446 Binary files /dev/null and b/sandcode/sound/interactions/moan_f3.ogg differ diff --git a/sandcode/sound/interactions/moan_f4.ogg b/sandcode/sound/interactions/moan_f4.ogg new file mode 100644 index 0000000000..71ac8c9b85 Binary files /dev/null and b/sandcode/sound/interactions/moan_f4.ogg differ diff --git a/sandcode/sound/interactions/moan_f5.ogg b/sandcode/sound/interactions/moan_f5.ogg new file mode 100644 index 0000000000..8e3396a4ac Binary files /dev/null and b/sandcode/sound/interactions/moan_f5.ogg differ diff --git a/sandcode/sound/interactions/moan_f6.ogg b/sandcode/sound/interactions/moan_f6.ogg new file mode 100644 index 0000000000..cf6f129004 Binary files /dev/null and b/sandcode/sound/interactions/moan_f6.ogg differ diff --git a/sandcode/sound/interactions/moan_f7.ogg b/sandcode/sound/interactions/moan_f7.ogg new file mode 100644 index 0000000000..9eb7f73643 Binary files /dev/null and b/sandcode/sound/interactions/moan_f7.ogg differ diff --git a/sandcode/sound/interactions/moan_m0.ogg b/sandcode/sound/interactions/moan_m0.ogg new file mode 100644 index 0000000000..4f1ede0079 Binary files /dev/null and b/sandcode/sound/interactions/moan_m0.ogg differ diff --git a/sandcode/sound/interactions/moan_m1.ogg b/sandcode/sound/interactions/moan_m1.ogg new file mode 100644 index 0000000000..93e6d1410c Binary files /dev/null and b/sandcode/sound/interactions/moan_m1.ogg differ diff --git a/sandcode/sound/interactions/moan_m12.ogg b/sandcode/sound/interactions/moan_m12.ogg new file mode 100644 index 0000000000..cd9a9d1e3d Binary files /dev/null and b/sandcode/sound/interactions/moan_m12.ogg differ diff --git a/sandcode/sound/interactions/moan_m2.ogg b/sandcode/sound/interactions/moan_m2.ogg new file mode 100644 index 0000000000..3bae5e073c Binary files /dev/null and b/sandcode/sound/interactions/moan_m2.ogg differ diff --git a/sandcode/sound/interactions/moan_m3.ogg b/sandcode/sound/interactions/moan_m3.ogg new file mode 100644 index 0000000000..a37eefe4cd Binary files /dev/null and b/sandcode/sound/interactions/moan_m3.ogg differ diff --git a/sandcode/sound/interactions/moan_m4.ogg b/sandcode/sound/interactions/moan_m4.ogg new file mode 100644 index 0000000000..e8683ea526 Binary files /dev/null and b/sandcode/sound/interactions/moan_m4.ogg differ diff --git a/sandcode/sound/interactions/moan_m5.ogg b/sandcode/sound/interactions/moan_m5.ogg new file mode 100644 index 0000000000..2308558b66 Binary files /dev/null and b/sandcode/sound/interactions/moan_m5.ogg differ diff --git a/sandcode/sound/interactions/moan_m6.ogg b/sandcode/sound/interactions/moan_m6.ogg new file mode 100644 index 0000000000..0d60d7a74c Binary files /dev/null and b/sandcode/sound/interactions/moan_m6.ogg differ diff --git a/sandcode/sound/interactions/moan_m7.ogg b/sandcode/sound/interactions/moan_m7.ogg new file mode 100644 index 0000000000..4e74477570 Binary files /dev/null and b/sandcode/sound/interactions/moan_m7.ogg differ diff --git a/sandcode/sound/interactions/oral1.ogg b/sandcode/sound/interactions/oral1.ogg new file mode 100644 index 0000000000..332ccf3c73 Binary files /dev/null and b/sandcode/sound/interactions/oral1.ogg differ diff --git a/sandcode/sound/interactions/oral2.ogg b/sandcode/sound/interactions/oral2.ogg new file mode 100644 index 0000000000..fff69d370f Binary files /dev/null and b/sandcode/sound/interactions/oral2.ogg differ diff --git a/sandcode/sound/interactions/purr1.ogg b/sandcode/sound/interactions/purr1.ogg new file mode 100644 index 0000000000..e1cc6bd204 Binary files /dev/null and b/sandcode/sound/interactions/purr1.ogg differ diff --git a/sandcode/sound/interactions/purr2.ogg b/sandcode/sound/interactions/purr2.ogg new file mode 100644 index 0000000000..caa9ca2dcb Binary files /dev/null and b/sandcode/sound/interactions/purr2.ogg differ diff --git a/sandcode/sound/interactions/purr3.ogg b/sandcode/sound/interactions/purr3.ogg new file mode 100644 index 0000000000..f53100aec2 Binary files /dev/null and b/sandcode/sound/interactions/purr3.ogg differ diff --git a/sandcode/sound/interactions/slap.ogg b/sandcode/sound/interactions/slap.ogg new file mode 100644 index 0000000000..d8804bc6e9 Binary files /dev/null and b/sandcode/sound/interactions/slap.ogg differ diff --git a/sandcode/sound/interactions/squelch1.ogg b/sandcode/sound/interactions/squelch1.ogg new file mode 100644 index 0000000000..faf8d651bd Binary files /dev/null and b/sandcode/sound/interactions/squelch1.ogg differ diff --git a/sandcode/sound/interactions/squelch2.ogg b/sandcode/sound/interactions/squelch2.ogg new file mode 100644 index 0000000000..d4148c1981 Binary files /dev/null and b/sandcode/sound/interactions/squelch2.ogg differ diff --git a/sandcode/sound/interactions/squelch3.ogg b/sandcode/sound/interactions/squelch3.ogg new file mode 100644 index 0000000000..a9b4a7b6e2 Binary files /dev/null and b/sandcode/sound/interactions/squelch3.ogg differ diff --git a/sandcode/sound/interactions/swallow.ogg b/sandcode/sound/interactions/swallow.ogg new file mode 100644 index 0000000000..d58b2628d5 Binary files /dev/null and b/sandcode/sound/interactions/swallow.ogg differ diff --git a/sandcode/sound/interactions/under_moan_f1.ogg b/sandcode/sound/interactions/under_moan_f1.ogg new file mode 100644 index 0000000000..a3fd6b56d5 Binary files /dev/null and b/sandcode/sound/interactions/under_moan_f1.ogg differ diff --git a/sandcode/sound/interactions/under_moan_f2.ogg b/sandcode/sound/interactions/under_moan_f2.ogg new file mode 100644 index 0000000000..5232ccd4b9 Binary files /dev/null and b/sandcode/sound/interactions/under_moan_f2.ogg differ diff --git a/sandcode/sound/interactions/under_moan_f3.ogg b/sandcode/sound/interactions/under_moan_f3.ogg new file mode 100644 index 0000000000..d3b651ab81 Binary files /dev/null and b/sandcode/sound/interactions/under_moan_f3.ogg differ diff --git a/sandcode/sound/interactions/under_moan_f4.ogg b/sandcode/sound/interactions/under_moan_f4.ogg new file mode 100644 index 0000000000..1c697450d0 Binary files /dev/null and b/sandcode/sound/interactions/under_moan_f4.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 199dc96003..b1b1c88604 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3544,4 +3544,13 @@ #include "sandcode\code\modules\telescience\telesci.dm" #include "sandcode\code\modules\telescience\telesci_computer.dm" #include "sandcode\code\modules\uplink\uplink_items.dm" +#include "sandcode\code\interactions\_interaction.dm" +#include "sandcode\code\interactions\interaction_definitions.dm" +#include "sandcode\code\interactions\interaction_interface.dm" +#include "sandcode\code\interactions\interaction_mob.dm" +#include "sandcode\code\interactions\lewd\_lewd.dm" +#include "sandcode\code\interactions\lewd\lewd_datums.dm" +#include "sandcode\code\interactions\lewd\lewd_interactions.dm" +#include "sandcode\code\interactions\lewd\lewd_species.dm" +#include "sandcode\code\interactions\lewd\objects.dm" // END_INCLUDE