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 += "
"
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 += " | "
+ 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 += "
"
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