From ad48e8a05c348e8901013ecc9e53a0148e303330 Mon Sep 17 00:00:00 2001
From: Enric Gabriel <43283559+SandPoot@users.noreply.github.com>
Date: Sat, 4 Jul 2020 17:04:58 -0300
Subject: [PATCH] the interactions (#20)
---
code/__DEFINES/preferences.dm | 2 +
code/__HELPERS/_cit_helpers.dm | 10 +-
code/modules/client/preferences.dm | 75 +-
.../mob/living/carbon/human/examine.dm | 6 +
.../modules/client/preferences_savefile.dm | 20 +
sandcode/code/datums/shuttles.dm | 6 +-
sandcode/code/interactions/_interaction.dm | 183 ++
.../interactions/interaction_definitions.dm | 71 +
.../interactions/interaction_interface.dm | 65 +
sandcode/code/interactions/interaction_mob.dm | 50 +
sandcode/code/interactions/lewd/_lewd.dm | 1689 +++++++++++++++++
.../code/interactions/lewd/lewd_datums.dm | 598 ++++++
.../interactions/lewd/lewd_interactions.dm | 583 ++++++
.../code/interactions/lewd/lewd_species.dm | 16 +
sandcode/code/interactions/lewd/objects.dm | 161 ++
sandcode/sound/interactions/bang1.ogg | Bin 0 -> 11155 bytes
sandcode/sound/interactions/bang2.ogg | Bin 0 -> 16988 bytes
sandcode/sound/interactions/bang3.ogg | Bin 0 -> 15764 bytes
sandcode/sound/interactions/bang4.ogg | Bin 0 -> 16937 bytes
sandcode/sound/interactions/bang5.ogg | Bin 0 -> 14468 bytes
sandcode/sound/interactions/bang6.ogg | Bin 0 -> 17243 bytes
sandcode/sound/interactions/bj1.ogg | Bin 0 -> 69287 bytes
sandcode/sound/interactions/bj10.ogg | Bin 0 -> 17808 bytes
sandcode/sound/interactions/bj11.ogg | Bin 0 -> 19284 bytes
sandcode/sound/interactions/bj2.ogg | Bin 0 -> 27620 bytes
sandcode/sound/interactions/bj3.ogg | Bin 0 -> 23313 bytes
sandcode/sound/interactions/bj4.ogg | Bin 0 -> 29257 bytes
sandcode/sound/interactions/bj5.ogg | Bin 0 -> 15407 bytes
sandcode/sound/interactions/bj6.ogg | Bin 0 -> 21739 bytes
sandcode/sound/interactions/bj7.ogg | Bin 0 -> 25063 bytes
sandcode/sound/interactions/bj8.ogg | Bin 0 -> 20834 bytes
sandcode/sound/interactions/bj9.ogg | Bin 0 -> 23987 bytes
sandcode/sound/interactions/champ1.ogg | Bin 0 -> 18868 bytes
sandcode/sound/interactions/champ2.ogg | Bin 0 -> 19303 bytes
.../sound/interactions/champ_fingering.ogg | Bin 0 -> 25107 bytes
sandcode/sound/interactions/clawcum1.ogg | Bin 0 -> 22886 bytes
sandcode/sound/interactions/clawcum2.ogg | Bin 0 -> 27077 bytes
sandcode/sound/interactions/final_f1.ogg | Bin 0 -> 17355 bytes
sandcode/sound/interactions/final_f2.ogg | Bin 0 -> 31021 bytes
sandcode/sound/interactions/final_f3.ogg | Bin 0 -> 21472 bytes
sandcode/sound/interactions/final_m1.ogg | Bin 0 -> 56356 bytes
sandcode/sound/interactions/final_m2.ogg | Bin 0 -> 57923 bytes
sandcode/sound/interactions/final_m3.ogg | Bin 0 -> 53781 bytes
sandcode/sound/interactions/final_m4.ogg | Bin 0 -> 65361 bytes
sandcode/sound/interactions/final_m5.ogg | Bin 0 -> 64486 bytes
sandcode/sound/interactions/foot_dry1.ogg | Bin 0 -> 11882 bytes
sandcode/sound/interactions/foot_dry2.ogg | Bin 0 -> 10688 bytes
sandcode/sound/interactions/foot_dry3.ogg | Bin 0 -> 15499 bytes
sandcode/sound/interactions/foot_dry4.ogg | Bin 0 -> 11221 bytes
sandcode/sound/interactions/foot_wet1.ogg | Bin 0 -> 12170 bytes
sandcode/sound/interactions/foot_wet2.ogg | Bin 0 -> 8586 bytes
sandcode/sound/interactions/foot_wet3.ogg | Bin 0 -> 9043 bytes
sandcode/sound/interactions/hug.ogg | Bin 0 -> 8834 bytes
sandcode/sound/interactions/moan_f1.ogg | Bin 0 -> 10610 bytes
sandcode/sound/interactions/moan_f2.ogg | Bin 0 -> 10482 bytes
sandcode/sound/interactions/moan_f3.ogg | Bin 0 -> 7898 bytes
sandcode/sound/interactions/moan_f4.ogg | Bin 0 -> 8755 bytes
sandcode/sound/interactions/moan_f5.ogg | Bin 0 -> 9046 bytes
sandcode/sound/interactions/moan_f6.ogg | Bin 0 -> 10566 bytes
sandcode/sound/interactions/moan_f7.ogg | Bin 0 -> 7549 bytes
sandcode/sound/interactions/moan_m0.ogg | Bin 0 -> 11144 bytes
sandcode/sound/interactions/moan_m1.ogg | Bin 0 -> 19064 bytes
sandcode/sound/interactions/moan_m12.ogg | Bin 0 -> 12632 bytes
sandcode/sound/interactions/moan_m2.ogg | Bin 0 -> 18737 bytes
sandcode/sound/interactions/moan_m3.ogg | Bin 0 -> 19650 bytes
sandcode/sound/interactions/moan_m4.ogg | Bin 0 -> 14872 bytes
sandcode/sound/interactions/moan_m5.ogg | Bin 0 -> 13625 bytes
sandcode/sound/interactions/moan_m6.ogg | Bin 0 -> 13264 bytes
sandcode/sound/interactions/moan_m7.ogg | Bin 0 -> 10288 bytes
sandcode/sound/interactions/oral1.ogg | Bin 0 -> 21941 bytes
sandcode/sound/interactions/oral2.ogg | Bin 0 -> 12814 bytes
sandcode/sound/interactions/purr1.ogg | Bin 0 -> 34965 bytes
sandcode/sound/interactions/purr2.ogg | Bin 0 -> 316658 bytes
sandcode/sound/interactions/purr3.ogg | Bin 0 -> 147158 bytes
sandcode/sound/interactions/slap.ogg | Bin 0 -> 7827 bytes
sandcode/sound/interactions/squelch1.ogg | Bin 0 -> 9956 bytes
sandcode/sound/interactions/squelch2.ogg | Bin 0 -> 9389 bytes
sandcode/sound/interactions/squelch3.ogg | Bin 0 -> 18754 bytes
sandcode/sound/interactions/swallow.ogg | Bin 0 -> 19756 bytes
sandcode/sound/interactions/under_moan_f1.ogg | Bin 0 -> 14759 bytes
sandcode/sound/interactions/under_moan_f2.ogg | Bin 0 -> 12484 bytes
sandcode/sound/interactions/under_moan_f3.ogg | Bin 0 -> 12661 bytes
sandcode/sound/interactions/under_moan_f4.ogg | Bin 0 -> 10331 bytes
tgstation.dme | 9 +
84 files changed, 3537 insertions(+), 7 deletions(-)
create mode 100644 sandcode/code/interactions/_interaction.dm
create mode 100644 sandcode/code/interactions/interaction_definitions.dm
create mode 100644 sandcode/code/interactions/interaction_interface.dm
create mode 100644 sandcode/code/interactions/interaction_mob.dm
create mode 100644 sandcode/code/interactions/lewd/_lewd.dm
create mode 100644 sandcode/code/interactions/lewd/lewd_datums.dm
create mode 100644 sandcode/code/interactions/lewd/lewd_interactions.dm
create mode 100644 sandcode/code/interactions/lewd/lewd_species.dm
create mode 100644 sandcode/code/interactions/lewd/objects.dm
create mode 100644 sandcode/sound/interactions/bang1.ogg
create mode 100644 sandcode/sound/interactions/bang2.ogg
create mode 100644 sandcode/sound/interactions/bang3.ogg
create mode 100644 sandcode/sound/interactions/bang4.ogg
create mode 100644 sandcode/sound/interactions/bang5.ogg
create mode 100644 sandcode/sound/interactions/bang6.ogg
create mode 100644 sandcode/sound/interactions/bj1.ogg
create mode 100644 sandcode/sound/interactions/bj10.ogg
create mode 100644 sandcode/sound/interactions/bj11.ogg
create mode 100644 sandcode/sound/interactions/bj2.ogg
create mode 100644 sandcode/sound/interactions/bj3.ogg
create mode 100644 sandcode/sound/interactions/bj4.ogg
create mode 100644 sandcode/sound/interactions/bj5.ogg
create mode 100644 sandcode/sound/interactions/bj6.ogg
create mode 100644 sandcode/sound/interactions/bj7.ogg
create mode 100644 sandcode/sound/interactions/bj8.ogg
create mode 100644 sandcode/sound/interactions/bj9.ogg
create mode 100644 sandcode/sound/interactions/champ1.ogg
create mode 100644 sandcode/sound/interactions/champ2.ogg
create mode 100644 sandcode/sound/interactions/champ_fingering.ogg
create mode 100644 sandcode/sound/interactions/clawcum1.ogg
create mode 100644 sandcode/sound/interactions/clawcum2.ogg
create mode 100644 sandcode/sound/interactions/final_f1.ogg
create mode 100644 sandcode/sound/interactions/final_f2.ogg
create mode 100644 sandcode/sound/interactions/final_f3.ogg
create mode 100644 sandcode/sound/interactions/final_m1.ogg
create mode 100644 sandcode/sound/interactions/final_m2.ogg
create mode 100644 sandcode/sound/interactions/final_m3.ogg
create mode 100644 sandcode/sound/interactions/final_m4.ogg
create mode 100644 sandcode/sound/interactions/final_m5.ogg
create mode 100644 sandcode/sound/interactions/foot_dry1.ogg
create mode 100644 sandcode/sound/interactions/foot_dry2.ogg
create mode 100644 sandcode/sound/interactions/foot_dry3.ogg
create mode 100644 sandcode/sound/interactions/foot_dry4.ogg
create mode 100644 sandcode/sound/interactions/foot_wet1.ogg
create mode 100644 sandcode/sound/interactions/foot_wet2.ogg
create mode 100644 sandcode/sound/interactions/foot_wet3.ogg
create mode 100644 sandcode/sound/interactions/hug.ogg
create mode 100644 sandcode/sound/interactions/moan_f1.ogg
create mode 100644 sandcode/sound/interactions/moan_f2.ogg
create mode 100644 sandcode/sound/interactions/moan_f3.ogg
create mode 100644 sandcode/sound/interactions/moan_f4.ogg
create mode 100644 sandcode/sound/interactions/moan_f5.ogg
create mode 100644 sandcode/sound/interactions/moan_f6.ogg
create mode 100644 sandcode/sound/interactions/moan_f7.ogg
create mode 100644 sandcode/sound/interactions/moan_m0.ogg
create mode 100644 sandcode/sound/interactions/moan_m1.ogg
create mode 100644 sandcode/sound/interactions/moan_m12.ogg
create mode 100644 sandcode/sound/interactions/moan_m2.ogg
create mode 100644 sandcode/sound/interactions/moan_m3.ogg
create mode 100644 sandcode/sound/interactions/moan_m4.ogg
create mode 100644 sandcode/sound/interactions/moan_m5.ogg
create mode 100644 sandcode/sound/interactions/moan_m6.ogg
create mode 100644 sandcode/sound/interactions/moan_m7.ogg
create mode 100644 sandcode/sound/interactions/oral1.ogg
create mode 100644 sandcode/sound/interactions/oral2.ogg
create mode 100644 sandcode/sound/interactions/purr1.ogg
create mode 100644 sandcode/sound/interactions/purr2.ogg
create mode 100644 sandcode/sound/interactions/purr3.ogg
create mode 100644 sandcode/sound/interactions/slap.ogg
create mode 100644 sandcode/sound/interactions/squelch1.ogg
create mode 100644 sandcode/sound/interactions/squelch2.ogg
create mode 100644 sandcode/sound/interactions/squelch3.ogg
create mode 100644 sandcode/sound/interactions/swallow.ogg
create mode 100644 sandcode/sound/interactions/under_moan_f1.ogg
create mode 100644 sandcode/sound/interactions/under_moan_f2.ogg
create mode 100644 sandcode/sound/interactions/under_moan_f3.ogg
create mode 100644 sandcode/sound/interactions/under_moan_f4.ogg
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 0000000000000000000000000000000000000000..d0b8abafa7d60fb3066e717bd719916670f27500
GIT binary patch
literal 11155
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
qf;iOzK3FA{|2{3oI&iF4qO(EkRN`^pdi
literal 0
HcmV?d00001
diff --git a/sandcode/sound/interactions/bang2.ogg b/sandcode/sound/interactions/bang2.ogg
new file mode 100644
index 0000000000000000000000000000000000000000..ff07de966650207df3f9bc7f28ae7b1b07321a16
GIT binary patch
literal 16988
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
bR4P`KZD$zddg042lj{i&Km6c>4`}~4aT9D(
literal 0
HcmV?d00001
diff --git a/sandcode/sound/interactions/bang4.ogg b/sandcode/sound/interactions/bang4.ogg
new file mode 100644
index 0000000000000000000000000000000000000000..4d9d9da8051ce8cd228decdc02f55347952bcc5d
GIT binary patch
literal 16937
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