diff --git a/code/__DEFINES/~~~splurt_defines/interactions.dm b/code/__DEFINES/~~~splurt_defines/interactions.dm new file mode 100644 index 0000000000000..8381fd905df9a --- /dev/null +++ b/code/__DEFINES/~~~splurt_defines/interactions.dm @@ -0,0 +1,53 @@ +/// Extra interaction checks. + +// A person needs to have mouth available to do this interaction +#define INTERACTION_REQUIRE_SELF_MOUTH "self_mouth" +#define INTERACTION_REQUIRE_TARGET_MOUTH "target_mouth" + +// A person needs to have their top clothes removed to do this interaction +#define INTERACTION_REQUIRE_SELF_TOPLESS "self_topless" +#define INTERACTION_REQUIRE_TARGET_TOPLESS "target_topless" + +// A person needs to have their bottom clothes removed to do this interaction +#define INTERACTION_REQUIRE_SELF_BOTTOMLESS "self_bottomless" +#define INTERACTION_REQUIRE_TARGET_BOTTOMLESS "target_bottomless" + +// A person needs to have their feet exposed to do this interaction +#define INTERACTION_REQUIRE_SELF_FEET "self_feet" +#define INTERACTION_REQUIRE_TARGET_FEET "target_feet" + +/// Climax definitions + +//Climaxing genitals +#define CLIMAX_VAGINA "vagina" +#define CLIMAX_PENIS "penis" +#define CLIMAX_BOTH "both" + +//Climaxing positions +#define CLIMAX_POSITION_USER "climax_user" +#define CLIMAX_POSITION_TARGET "climax_target" + +//Climaxing targets (use these if you're not using organ slots) +#define CLIMAX_TARGET_MOUTH "mouth" +#define CLIMAX_TARGET_SHEATH "sheath" + +// Extreme types +#define INTERACTION_EXTREME (1<<0) +#define INTERACTION_HARMFUL (1<<1) // If you use this on an interaction, add INTERACTION_EXTREME to it as well +#define INTERACTION_UNHOLY (1<<2) + +// Interaction categories +#define INTERACTION_CAT_LEWD "lewd" +#define INTERACTION_CAT_EXTREME "extreme" +#define INTERACTION_CAT_HARMFUL "harmful" +#define INTERACTION_CAT_UNHOLY "unholy" + +// Additional details sent to the interaction menu +#define INTERACTION_FILLS_CONTAINERS list( \ + "info" = "You can fill a container if you have it in your active hand or are pulling it", \ + "icon" = "flask", \ + "color" = "transparent" \ + ) + +// Interaction flags (used for logic but normally not sent to the interaction menu) +#define INTERACTION_OVERRIDE_FLUID_TRANSFER (1<<0) diff --git a/code/__DEFINES/~~~splurt_defines/subsystems.dm b/code/__DEFINES/~~~splurt_defines/subsystems.dm new file mode 100644 index 0000000000000..c581df65ca656 --- /dev/null +++ b/code/__DEFINES/~~~splurt_defines/subsystems.dm @@ -0,0 +1,6 @@ +/* + * # INIT_ORDER_INTERACTIONS + * Used by the Interactions subsystems, used to set it's own position in the queue. + * This puts this last on priority, very far from other subsystems, +*/ +#define INIT_ORDER_INTERACTIONS -150 diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm index 5af3f7b842275..4c43381d46e22 100644 --- a/code/controllers/configuration/configuration.dm +++ b/code/controllers/configuration/configuration.dm @@ -105,7 +105,7 @@ load_whitelist() // SKYRAT EDIT ADDITION START - populate_interaction_instances() + //populate_interaction_instances() //SPLURT EDIT REMOVAL - Interactions remove_erp_things() // SKYRAT EDIT ADDITION END diff --git a/modular_skyrat/master_files/code/modules/mob/living/examine_tgui.dm b/modular_skyrat/master_files/code/modules/mob/living/examine_tgui.dm index ec0dd5b9b4edc..5d7a578287510 100644 --- a/modular_skyrat/master_files/code/modules/mob/living/examine_tgui.dm +++ b/modular_skyrat/master_files/code/modules/mob/living/examine_tgui.dm @@ -66,10 +66,20 @@ var/e_prefs_v = preferences.read_preference(/datum/preference/choiced/erp_status_v) var/e_prefs_nc = preferences.read_preference(/datum/preference/choiced/erp_status_nc) var/e_prefs_mechanical = preferences.read_preference(/datum/preference/choiced/erp_status_mechanics) + // SPLURT EDIT ADDITION - Interaction preferences + var/e_prefs_extm = preferences.read_preference(/datum/preference/choiced/erp_status_extm) + var/e_prefs_extmharm = preferences.read_preference(/datum/preference/choiced/erp_status_extmharm) + var/e_prefs_unholy = preferences.read_preference(/datum/preference/choiced/erp_status_unholy) + // SPLURT EDIT END ooc_notes += "ERP: [e_prefs]\n" ooc_notes += "Hypnosis: [e_prefs_hypno]\n" ooc_notes += "Vore: [e_prefs_v]\n" ooc_notes += "Non-Con: [e_prefs_nc]\n" + // SPLURT EDIT ADDITION - Interaction preferences + ooc_notes += "Extreme ERP verbs: [e_prefs_extm]\n" + ooc_notes += "Harmful ERP verbs: [e_prefs_extmharm]\n" + ooc_notes += "Unholy ERP verbs: [e_prefs_unholy]\n" + // SPLURT EDIT END ooc_notes += "ERP Mechanics: [e_prefs_mechanical]\n" ooc_notes += "\n" diff --git a/modular_skyrat/modules/interaction_menu/code/interaction_component.dm b/modular_skyrat/modules/interaction_menu/code/interaction_component.dm index b6122decd070e..42f54025a2d88 100644 --- a/modular_skyrat/modules/interaction_menu/code/interaction_component.dm +++ b/modular_skyrat/modules/interaction_menu/code/interaction_component.dm @@ -21,13 +21,31 @@ /datum/component/interactable/proc/build_interactions_list() interactions = list() - for(var/iterating_interaction_id in GLOB.interaction_instances) - var/datum/interaction/interaction = GLOB.interaction_instances[iterating_interaction_id] + //SPLURT EDIT - Interactions subsystem + if(!SSinteractions) + return // Can't continue, no subsystem + for(var/iterating_interaction_id in SSinteractions.interactions) + var/datum/interaction/interaction = SSinteractions.interactions[iterating_interaction_id] + //SPLURT EDIT END if(interaction.lewd) if(!self.client?.prefs?.read_preference(/datum/preference/toggle/erp)) continue + // SPLURT EDIT ADDITION - Interaction preferences + if(interaction.unsafe_types & INTERACTION_EXTREME) + if(self.client?.prefs?.read_preference(/datum/preference/choiced/erp_status_extm) == "No") + continue + if(interaction.unsafe_types & INTERACTION_HARMFUL) + if(self.client?.prefs?.read_preference(/datum/preference/choiced/erp_status_extmharm) == "No") + continue + if(interaction.unsafe_types & INTERACTION_UNHOLY) + if(self.client?.prefs?.read_preference(/datum/preference/choiced/erp_status_unholy) == "No") + continue + // SPLURT EDIT END + /* + SPLURT EDIT REMOVAL - Interactions if(interaction.sexuality != "" && interaction.sexuality != self.client?.prefs?.read_preference(/datum/preference/choiced/erp_sexuality)) continue + */ interactions.Add(interaction) /datum/component/interactable/RegisterWithParent() @@ -143,11 +161,11 @@ if(params["interaction"]) var/interaction_id = params["interaction"] - if(GLOB.interaction_instances[interaction_id]) + if(SSinteractions.interactions[interaction_id]) var/mob/living/carbon/human/user = locate(params["userref"]) - if(!can_interact(GLOB.interaction_instances[interaction_id], user)) + if(!can_interact(SSinteractions.interactions[interaction_id], user)) return FALSE - GLOB.interaction_instances[interaction_id].act(user, locate(params["selfref"])) + SSinteractions.interactions[interaction_id].act(user, locate(params["selfref"])) var/datum/component/interactable/interaction_component = user.GetComponent(/datum/component/interactable) interaction_component.interact_last = world.time interact_next = interaction_component.interact_last + INTERACTION_COOLDOWN diff --git a/modular_skyrat/modules/interaction_menu/code/interaction_datum.dm b/modular_skyrat/modules/interaction_menu/code/interaction_datum.dm index 7826e8d285047..bc5744f6094e7 100644 --- a/modular_skyrat/modules/interaction_menu/code/interaction_datum.dm +++ b/modular_skyrat/modules/interaction_menu/code/interaction_datum.dm @@ -1,5 +1,7 @@ +/* SPLURT EDIT REMOVAL - Interactions subsystem GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction) +*/ /datum/interaction /// The name to be displayed in the interaction menu for this interaction @@ -48,9 +50,12 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction) var/list/interaction_requires = list() /// What color should the interaction button be? var/color = "blue" + /* SPLURT EDIT REMOVAL - Interactions /// What sexuality preference do we display for. var/sexuality = "" + */ +/* SPLURT EDIT REMOVAL - Interactions - Refractored in modular /datum/interaction/proc/allow_act(mob/living/carbon/human/user, mob/living/carbon/human/target) if(target == user && usage == INTERACTION_OTHER) return FALSE @@ -82,6 +87,7 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction) else CRASH("Unimplemented interaction requirement '[requirement]'") return TRUE +*/ //SPLURT EDIT END /datum/interaction/proc/act(mob/living/carbon/human/user, mob/living/carbon/human/target) if(!allow_act(user, target)) @@ -115,16 +121,24 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction) message_admins("Deprecated sound handling for '[name]'. Correct format is a list with one entry. This message will only show once.") sound_possible = list(sound_possible) sound_cache = pick(sound_possible) + //SPLURT EDIT - Interactions - For some reason the original code didn't send any sound + /* for(var/mob/mob in view(sound_range, user)) SEND_SOUND(sound_cache, mob) - + */ + conditional_pref_sound(user, sound_cache, 80, TRUE, falloff_distance = sound_range, pref_to_check = /datum/preference/toggle/erp/sounds) + //SPLURT EDIT END if(lewd) - user.adjust_pleasure(user_pleasure) + user.adjust_pleasure(user_pleasure * (target.dna.features["sexual_potency"] || 1), target, src, CLIMAX_POSITION_USER) //SPLURT EDIT - Interactions user.adjust_arousal(user_arousal) - user.adjust_pain(user_pain) - target.adjust_pleasure(target_pleasure) - target.adjust_arousal(target_arousal) - target.adjust_pain(target_pain) + user.adjust_pain(user_pain, target, src, CLIMAX_POSITION_USER) //SPLURT EDIT - Interactions + if(usage == INTERACTION_OTHER) //SPLURT EDIT - Interactions + target.adjust_pleasure(target_pleasure * (user.dna.features["sexual_potency"] || 1), user, src, CLIMAX_POSITION_TARGET) //SPLURT EDIT - Interactions + target.adjust_arousal(target_arousal) + target.adjust_pain(target_pain, user, src, CLIMAX_POSITION_TARGET) //SPLURT EDIT - Interactions + + post_interaction(user, target) //SPLURT EDIT - Interactions + return TRUE /datum/interaction/proc/load_from_json(path) var/fpath = path @@ -157,7 +171,7 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction) target_pleasure = sanitize_integer(json["target_pleasure"], 0, 100, 0) target_pain = sanitize_integer(json["target_pain"], 0, 100, 0) lewd = sanitize_integer(json["lewd"], 0, 1, 0) - sexuality = sanitize_text(json["sexuality"]) + // sexuality = sanitize_text(json["sexuality"]) //SPLURT EDIT REMOVAL - Interactions return TRUE /datum/interaction/proc/json_save(path) @@ -187,7 +201,7 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction) "target_pleasure" = target_pleasure, "target_pain" = target_pain, "lewd" = lewd, - "sexuality" = sexuality, + // "sexuality" = sexuality, //SPLURT EDIT REMOVAL - Interactions ) var/file = file(fpath) WRITE_FILE(file, json_encode(json)) @@ -201,7 +215,11 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction) /proc/populate_interaction_instances() for(var/spath in subtypesof(/datum/interaction)) var/datum/interaction/interaction = new spath() - GLOB.interaction_instances[interaction.name] = interaction + //SPLURT EDIT - Interactions + if(interaction.name == /datum/interaction::name || interaction.description == /datum/interaction::description) + continue + //SPLURT EDIT END + SSinteractions.interactions[interaction.name] = interaction //SPLURT EDIT - Interactions subsystem - Original: GLOB.interaction_instances[interaction.name] = interaction populate_interaction_jsons(INTERACTION_JSON_FOLDER) /proc/populate_interaction_jsons(directory) @@ -214,7 +232,7 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction) continue var/datum/interaction/interaction = new() if(interaction.load_from_json(directory + file)) - GLOB.interaction_instances[interaction.name] = interaction + SSinteractions.interactions[interaction.name] = interaction //SPLURT EDIT - Interactions subsystem - Original: GLOB.interaction_instances[interaction.name] = interaction else message_admins("Error loading interaction from file: '[directory + file]'. Inform coders.") /proc/populate_interaction_jsons_master(path) @@ -225,7 +243,7 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction) var/list/json = json_load(file) for(var/iname in json) - if(GLOB.interaction_instances[iname]) + if(SSinteractions.interactions[iname]) //SPLURT EDIT - Interactions subsystem - Original: if(GLOB.interaction_instances[iname]) message_admins("Interaction Master '[path]' contained a duplicate interaction! '[iname]'") continue @@ -257,9 +275,9 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction) interaction.target_pleasure = sanitize_integer(ijson["target_pleasure"], 0, 100, 0) interaction.target_pain = sanitize_integer(ijson["target_pain"], 0, 100, 0) interaction.lewd = sanitize_integer(ijson["lewd"], 0, 1, 0) - interaction.sexuality = sanitize_text(ijson["sexuality"]) + // interaction.sexuality = sanitize_text(ijson["sexuality"]) //SPLURT EDIT REMOVAL - Interactions - GLOB.interaction_instances[iname] = interaction + SSinteractions.interactions[iname] = interaction //SPLURT EDIT - Interactions subsystem - Original: GLOB.interaction_instances[iname] = interaction ADMIN_VERB(reload_interactions, R_DEBUG, "Reload Interactions", "Force reload interactions.", ADMIN_CATEGORY_DEBUG) - populate_interaction_instances() + SSinteractions.prepare_interactions() //SPLURT EDIT - Interactions subsystem - Original: populate_interaction_instances() diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/climax.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/climax.dm index e980f1c9c0f65..c652a792e5f43 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/climax.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/climax.dm @@ -1,6 +1,10 @@ +//SPLURT EDIT REMOVAL BEGIN - Interactions - Moved climax defines to global defines +/* #define CLIMAX_VAGINA "Vagina" #define CLIMAX_PENIS "Penis" #define CLIMAX_BOTH "Both" +*/ +//SPLURT EDIT REMOVAL END #define CLIMAX_ON_FLOOR "On the floor" #define CLIMAX_IN_OR_ON "Climax in or on someone" @@ -9,7 +13,7 @@ /// Used to prevent nightmare scenarios. var/refractory_period -/mob/living/carbon/human/proc/climax(manual = TRUE) +/mob/living/carbon/human/proc/climax(manual = TRUE, mob/living/carbon/human/partner, datum/interaction/climax_interaction, interaction_position) if (CONFIG_GET(flag/disable_erp_preferences)) return @@ -39,7 +43,10 @@ else if(has_penis()) genitals.Add(CLIMAX_PENIS) climax_choice = tgui_alert(src, "You are climaxing, choose which genitalia to climax with.", "Genitalia Preference!", genitals) - + //SPLURT EDIT ADDITION BEGIN - Interactions + else if(climax_interaction?.cum_genital[interaction_position]) + climax_choice = climax_interaction.cum_genital[interaction_position] + //SPLURT EDIT ADDITION END switch(gender) if(MALE) conditional_pref_sound(get_turf(src), pick('modular_skyrat/modules/modular_items/lewd_items/sounds/final_m1.ogg', @@ -81,7 +88,7 @@ if(interactable_inrange_humans.len) buttons += CLIMAX_IN_OR_ON - var/penis_climax_choice = tgui_alert(src, "Choose where to shoot your load.", "Load preference!", buttons) + var/penis_climax_choice = climax_interaction && !manual ? CLIMAX_IN_OR_ON : tgui_alert(src, "Choose where to shoot your load.", "Load preference!", buttons) //SPLURT EDIT CHANGE - Interactions var/create_cum_decal = FALSE @@ -91,13 +98,13 @@ span_userlove("You shoot string after string of hot cum, hitting the floor!")) else - var/target_choice = tgui_input_list(src, "Choose a person to cum in or on.", "Choose target!", interactable_inrange_humans) + var/target_choice = climax_interaction && !manual ? partner?.name : tgui_input_list(src, "Choose a person to cum in or on.", "Choose target!", interactable_inrange_humans) //SPLURT EDIT CHANGE - Interactions if(!target_choice) create_cum_decal = TRUE visible_message(span_userlove("[src] shoots [self_their] sticky load onto the floor!"), \ span_userlove("You shoot string after string of hot cum, hitting the floor!")) else - var/mob/living/carbon/human/target_human = interactable_inrange_humans[target_choice] + var/mob/living/carbon/human/target_human = climax_interaction && !manual && partner ? partner : interactable_inrange_humans[target_choice] //SPLURT EDIT CHANGE - Interactions var/target_human_them = target_human.p_them() var/list/target_buttons = list() @@ -107,16 +114,29 @@ if(target_human.has_vagina(REQUIRE_GENITAL_EXPOSED)) target_buttons += ORGAN_SLOT_VAGINA if(target_human.has_anus(REQUIRE_GENITAL_EXPOSED)) - target_buttons += "asshole" + target_buttons += ORGAN_SLOT_ANUS //SPLURT EDIT CHANGE - Interactions - Changed asshole to anus for consistency if(target_human.has_penis(REQUIRE_GENITAL_EXPOSED)) + target_buttons += ORGAN_SLOT_PENIS var/obj/item/organ/external/genital/penis/other_penis = target_human.get_organ_slot(ORGAN_SLOT_PENIS) if(other_penis.sheath != "None") target_buttons += "sheath" target_buttons += "On [target_human_them]" - var/climax_into_choice = tgui_input_list(src, "Where on or in [target_human] do you wish to cum?", "Final frontier!", target_buttons) + //SPLURT EDIT CHANGE BEGIN - Interactions + var/climax_into_choice + var/interaction_inside = partner?.get_organ_slot(climax_interaction?.cum_target[interaction_position]) || target_buttons.Find(climax_interaction?.cum_target[interaction_position]) + + if(climax_interaction && !manual && interaction_inside) + climax_into_choice = climax_interaction.cum_target[interaction_position] + else if(manual) + climax_into_choice = tgui_input_list(src, "Where on or in [target_human] do you wish to cum?", "Final frontier!", target_buttons) + else + climax_into_choice = "On [target_human_them]" - if(!climax_into_choice) + if(climax_interaction && !manual && climax_interaction.show_climax(src, target_human, interaction_position)) + create_cum_decal = !interaction_inside + else if(!climax_into_choice) + //SPLURT EDIT CHANGE END create_cum_decal = TRUE visible_message(span_userlove("[src] shoots their sticky load onto the floor!"), \ span_userlove("You shoot string after string of hot cum, hitting the floor!")) @@ -130,9 +150,33 @@ to_chat(target_human, span_userlove("Your [climax_into_choice] fills with warm cum as [src] shoots [self_their] load into it.")) var/obj/item/organ/external/genital/testicles/testicles = get_organ_slot(ORGAN_SLOT_TESTICLES) - testicles.transfer_internal_fluid(null, testicles.internal_fluid_count * 0.6) // yep. we are sending semen to nullspace - if(create_cum_decal) - add_cum_splatter_floor(get_turf(src)) + //SPLURT EDIT CHANGE BEGIN - Interactions + if(!(climax_interaction?.interaction_modifier_flags & INTERACTION_OVERRIDE_FLUID_TRANSFER)) + if(create_cum_decal) + if(HAS_TRAIT(src, TRAIT_MESSY)) + // Transfer reagents to the turf using liquids system + var/datum/reagents/R = new(testicles.internal_fluid_maximum) + testicles.transfer_internal_fluid(R, testicles.internal_fluid_count * 0.6) + if(partner && partner != src) + // Get turf between src and partner for directional splatter + var/turf/T = get_turf(partner) + T.add_liquid_from_reagents(R, FALSE, 1, get_turf(src), partner) + else + var/turf/T = get_turf(src) + T.add_liquid_from_reagents(R, FALSE, 1) + qdel(R) + else + testicles.transfer_internal_fluid(null, testicles.internal_fluid_count * 0.6) + add_cum_splatter_floor(get_turf(src)) + else if(partner) + // Transfer reagents directly to partner + var/datum/reagents/R = new(testicles.internal_fluid_maximum) + testicles.transfer_internal_fluid(R, testicles.internal_fluid_count * 0.6) + R.trans_to(partner, R.total_volume) + qdel(R) + else + testicles.transfer_internal_fluid(null, testicles.internal_fluid_count * 0.6) + //SPLURT EDIT CHANGE END try_lewd_autoemote("moan") if(climax_choice == CLIMAX_PENIS) @@ -144,22 +188,122 @@ if(climax_choice == CLIMAX_VAGINA || climax_choice == CLIMAX_BOTH) var/obj/item/organ/external/genital/vagina/vagina = get_organ_slot(ORGAN_SLOT_VAGINA) - if(is_bottomless() || vagina.visibility_preference == GENITAL_ALWAYS_SHOW) - visible_message(span_userlove("[src] twitches and moans as [p_they()] climax from their vagina!"), span_userlove("You twitch and moan as you climax from your vagina!")) - add_cum_splatter_floor(get_turf(src), female = TRUE) - else + //SPLURT EDIT CHANGE BEGIN - Interactions + if(!is_bottomless() && vagina.visibility_preference != GENITAL_ALWAYS_SHOW) visible_message(span_userlove("[src] cums in [self_their] underwear from [self_their] vagina!"), \ - span_userlove("You cum in your underwear from your vagina! Eww.")) + span_userlove("You cum in your underwear from your vagina! Eww.")) self_orgasm = TRUE + else + var/list/interactable_inrange_humans = list() + + for(var/mob/living/carbon/human/iterating_human in (view(1, src) - src)) + interactable_inrange_humans[iterating_human.name] = iterating_human + + var/list/buttons = list(CLIMAX_ON_FLOOR) + if(interactable_inrange_humans.len) + buttons += CLIMAX_IN_OR_ON + + var/vagina_climax_choice = climax_interaction && !manual ? CLIMAX_IN_OR_ON : tgui_alert(src, "Choose where to squirt.", "Squirt preference!", buttons) + + var/create_cum_decal = FALSE + + if(!vagina_climax_choice || vagina_climax_choice == CLIMAX_ON_FLOOR) + create_cum_decal = TRUE + visible_message(span_userlove("[src] twitches and moans as [p_they()] squirt on the floor!"), \ + span_userlove("You twitch and moan as you squirt on the floor!")) + + else + var/target_choice = climax_interaction && !manual ? partner.name : tgui_input_list(src, "Choose who to squirt on.", "Choose target!", interactable_inrange_humans) + if(!target_choice) + create_cum_decal = TRUE + visible_message(span_userlove("[src] twitches and moans as [p_they()] squirt on the floor!"), \ + span_userlove("You twitch and moan as you squirt on the floor!")) + else + var/mob/living/carbon/human/target_human = climax_interaction && !manual ? partner : interactable_inrange_humans[target_choice] + var/target_human_them = target_human.p_them() + + var/list/target_buttons = list() + + if(!target_human.wear_mask) + target_buttons += "mouth" + if(target_human.has_vagina(REQUIRE_GENITAL_EXPOSED)) + target_buttons += ORGAN_SLOT_VAGINA + if(target_human.has_anus(REQUIRE_GENITAL_EXPOSED)) + target_buttons += ORGAN_SLOT_ANUS + if(target_human.has_penis(REQUIRE_GENITAL_EXPOSED)) + target_buttons += ORGAN_SLOT_PENIS + var/obj/item/organ/external/genital/penis/other_penis = target_human.get_organ_slot(ORGAN_SLOT_PENIS) + if(other_penis.sheath != "None") + target_buttons += "sheath" + target_buttons += "On [target_human_them]" + + var/climax_into_choice + var/interaction_inside = partner?.get_organ_slot(climax_interaction?.cum_target[interaction_position]) || target_buttons.Find(climax_interaction?.cum_target[interaction_position]) + + if(climax_interaction && !manual && interaction_inside) + climax_into_choice = climax_interaction.cum_target[interaction_position] + else if(manual) + climax_into_choice = tgui_input_list(src, "Where on or in [target_human] do you wish to squirt?", "Final frontier!", target_buttons) + else + climax_into_choice = "On [target_human_them]" + + if(climax_interaction && !manual && climax_interaction.show_climax(src, target_human, interaction_position)) + create_cum_decal = !interaction_inside + else if(!climax_into_choice) + create_cum_decal = TRUE + visible_message(span_userlove("[src] squirts on the floor!"), \ + span_userlove("You squirt on the floor!")) + else if(climax_into_choice == "On [target_human_them]") + create_cum_decal = TRUE + visible_message(span_userlove("[src] squirts all over [target_human]!"), \ + span_userlove("You squirt all over [target_human]!")) + else + visible_message(span_userlove("[src] squirts into [target_human]'s [climax_into_choice]!"), \ + span_userlove("You squirt into [target_human]'s [climax_into_choice]!")) + to_chat(target_human, span_userlove("Your [climax_into_choice] fills with [src]'s fluids.")) + + if(!(climax_interaction?.interaction_modifier_flags & INTERACTION_OVERRIDE_FLUID_TRANSFER)) + if(create_cum_decal) + if(HAS_TRAIT(src, TRAIT_MESSY)) + var/datum/reagents/R = new(vagina.internal_fluid_maximum) + vagina.transfer_internal_fluid(R, vagina.internal_fluid_count) + if(partner && partner != src) + var/turf/T = get_turf(partner) + T.add_liquid_from_reagents(R, FALSE, 1, get_turf(src), partner) + else + var/turf/T = get_turf(src) + T.add_liquid_from_reagents(R, FALSE, 1) + qdel(R) + else + vagina.transfer_internal_fluid(null, vagina.internal_fluid_count) + add_cum_splatter_floor(get_turf(src), female = TRUE) + else if(partner) + var/datum/reagents/R = new(vagina.internal_fluid_maximum) + vagina.transfer_internal_fluid(R, vagina.internal_fluid_count) + R.trans_to(partner, R.total_volume) + qdel(R) + else + vagina.transfer_internal_fluid(null, vagina.internal_fluid_count) + //SPLURT EDIT CHANGE END apply_status_effect(/datum/status_effect/climax) apply_status_effect(/datum/status_effect/climax_cooldown) if(self_orgasm) add_mood_event("orgasm", /datum/mood_event/climaxself) + + // SPLURT EDIT ADDITION BEGIN - Interactions + if(climax_interaction && !manual) + climax_interaction.post_climax(src, partner, interaction_position) + //SPLURT EDIT ADDITION END return TRUE +//SPLURT EDIT REMOVAL BEGIN - Interactions - Moved climax defines to global defines +/* #undef CLIMAX_VAGINA #undef CLIMAX_PENIS #undef CLIMAX_BOTH +*/ +//SPLURT EDIT REMOVAL END + #undef CLIMAX_ON_FLOOR #undef CLIMAX_IN_OR_ON diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pain.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pain.dm index e0d14bf01a21b..8cad6ca5087b7 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pain.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pain.dm @@ -5,7 +5,7 @@ /mob/living/proc/adjust_pain(change_amount = 0) return -/mob/living/carbon/human/adjust_pain(change_amount = 0) +/mob/living/carbon/human/adjust_pain(change_amount = 0, mob/living/carbon/human/partner, datum/interaction/interaction, position) if(stat >= DEAD || !client?.prefs?.read_preference(/datum/preference/toggle/erp)) return @@ -23,6 +23,6 @@ else if(HAS_TRAIT(src, TRAIT_MASOCHISM)) if(change_amount > 0) adjust_arousal(change_amount) - adjust_pleasure(change_amount / 2) + adjust_pleasure(change_amount / 2, partner, interaction, position) pain = clamp(pain + change_amount, AROUSAL_MINIMUM, AROUSAL_LIMIT) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pleasure.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pleasure.dm index bcda8503d219c..3d667dc29da0e 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pleasure.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pleasure.dm @@ -1,8 +1,8 @@ -/mob/living/carbon/human/proc/adjust_pleasure(pleas = 0) +/mob/living/carbon/human/proc/adjust_pleasure(pleas = 0, mob/living/carbon/human/partner, datum/interaction/interaction, position) if(stat >= DEAD || !client?.prefs?.read_preference(/datum/preference/toggle/erp)) return - pleasure = clamp(pleasure + pleas, AROUSAL_MINIMUM, AROUSAL_LIMIT) + pleasure = clamp(pleasure + pleas, AROUSAL_MINIMUM, AROUSAL_LIMIT * (dna.features["lust_tolerance"] || 1)) // SPLURT EDIT - Lust tolerance - if(pleasure >= AROUSAL_AUTO_CLIMAX_THRESHOLD) // lets cum - climax(manual = FALSE) + if(pleasure >= AROUSAL_AUTO_CLIMAX_THRESHOLD * (dna.features["lust_tolerance"] || 1)) // lets cum // SPLURT EDIT - Lust tolerance + climax(manual = FALSE, partner = partner, climax_interaction = interaction, interaction_position = position) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/pentacamphor.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/pentacamphor.dm index 8816143570445..114c3c3f4f8ee 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/pentacamphor.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/pentacamphor.dm @@ -25,7 +25,7 @@ to_chat(exposed_mob, span_notice("Your mind is free. Your thoughts are pure and innocent once more.")) REMOVE_TRAIT(exposed_mob, TRAIT_BIMBO, TRAIT_LEWDCHEM) return - if(!HAS_TRAIT(exposed_mob, TRAIT_NEVERBONER)) + if(!HAS_TRAIT(exposed_mob, TRAIT_NEVERBONER) && !exposed_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/forced_neverboner)) // SPLURT EDIT - Added a preference to force neverboner to_chat(exposed_mob, span_notice("You feel like you'll never feel aroused again...")) ADD_TRAIT(exposed_mob, TRAIT_NEVERBONER, TRAIT_LEWDCHEM) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm index 86b0ca58ab70e..6388a81de2ccf 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm @@ -36,6 +36,7 @@ /mob/living/carbon/human/proc/is_head_uncovered() return (head?.body_parts_covered & HEAD) +/* SPLURT EDIT REMOVAL - Interactions - Refractored in modular /// Returns true if the human has an accessible penis for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines. /mob/living/carbon/human/proc/has_penis(required_state = REQUIRE_GENITAL_ANY) var/obj/item/organ/external/genital/genital = get_organ_slot(ORGAN_SLOT_PENIS) @@ -117,6 +118,7 @@ return genital.visibility_preference != GENITAL_ALWAYS_SHOW && !is_bottomless() else return TRUE +*/ //SPLURT EDIT END /// Returns true if the human has a accessible feet for the parameter, returning the number of feet the human has if they do. Accepts any of the `REQUIRE_GENITAL_` defines. /mob/living/carbon/human/proc/has_arms(required_state = REQUIRE_GENITAL_ANY) diff --git a/modular_zubbers/code/modules/client/verbs/character_directory.dm b/modular_zubbers/code/modules/client/verbs/character_directory.dm index 9e90850dae17d..ffd2d32b1e8d7 100644 --- a/modular_zubbers/code/modules/client/verbs/character_directory.dm +++ b/modular_zubbers/code/modules/client/verbs/character_directory.dm @@ -192,6 +192,11 @@ GLOBAL_DATUM(character_directory, /datum/character_directory) var/vore = "Ask" var/hypno = "Ask" var/noncon = "Ask" + // SPLURT EDIT START: INTERACTION PANEL + var/extreme = "Ask" + var/extremeharm = "Ask" + var/unholy = "Ask" + // SPLURT EDIT END: INTERACTION PANEL var/character_ad = "" var/exploitable = "" var/ref = REF(mob) @@ -235,6 +240,11 @@ GLOBAL_DATUM(character_directory, /datum/character_directory) noncon = READ_PREFS(mob, choiced/erp_status_nc) character_ad = READ_PREFS(mob, text/character_ad) ooc_notes = READ_PREFS(mob, text/ooc_notes) + // SPLURT EDIT START: INTERACTION PANEL + extreme = READ_PREFS(mob, choiced/erp_status_extm) + extremeharm = READ_PREFS(mob, choiced/erp_status_extmharm) + unholy = READ_PREFS(mob, choiced/erp_status_unholy) + // SPLURT EDIT END: INTERACTION PANEL //If the user is an antagonist or Observer, we want them to be able to see exploitables in the Directory. if(user.mind?.has_antag_datum(/datum/antagonist) || isobserver(user)) if(exploitable == EXPLOITABLE_DEFAULT_TEXT) @@ -254,6 +264,11 @@ GLOBAL_DATUM(character_directory, /datum/character_directory) "vore" = vore, "hypno" = hypno, "noncon" = noncon, + // SPLURT EDIT START: INTERACTION PANEL + "extreme" = extreme, + "extremeharm" = extremeharm, + "unholy" = unholy, + // SPLURT EDIT END: INTERACTION PANEL "exploitable" = exploitable, "character_ad" = character_ad, "flavor_text" = flavor_text, diff --git a/modular_zzplurt/code/controllers/subsystem/interactions.dm b/modular_zzplurt/code/controllers/subsystem/interactions.dm new file mode 100644 index 0000000000000..5904a1ae7fed5 --- /dev/null +++ b/modular_zzplurt/code/controllers/subsystem/interactions.dm @@ -0,0 +1,52 @@ +SUBSYSTEM_DEF(interactions) + name = "Interactions" + flags = SS_NO_FIRE + init_order = INIT_ORDER_INTERACTIONS + var/list/datum/interaction/interactions + VAR_PROTECTED/list/blacklisted_mobs = list( + /mob/dead, + /mob/dview, + /mob/camera, + /mob/living/basic/pet, + /mob/living/basic/cockroach, + /mob/living/basic/butterfly, + /mob/living/basic/chick, + /mob/living/basic/chicken, + /mob/living/basic/cow, + /mob/living/basic/crab, + /mob/living/basic/kiwi, + /mob/living/basic/parrot, + /mob/living/basic/sloth, + /mob/living/basic/goat + ) + VAR_PROTECTED/initialized_blacklist + +/datum/controller/subsystem/interactions/Initialize() + prepare_interactions() + prepare_blacklisted_mobs() + . = ..() + var/extra_info = " Loaded [LAZYLEN(interactions)] interactions!" + to_chat(world, span_boldannounce(extra_info)) + log_config(extra_info) + +/datum/controller/subsystem/interactions/stat_entry(msg) + msg += "|🖐:[LAZYLEN(interactions)]|" + msg += "🚫👨:[LAZYLEN(blacklisted_mobs)]" + return ..() + +/datum/controller/subsystem/interactions/proc/prepare_interactions() + QDEL_LIST_ASSOC_VAL(interactions) + QDEL_NULL(interactions) + interactions = list() + populate_interaction_instances() + +/datum/controller/subsystem/interactions/proc/prepare_blacklisted_mobs() + blacklisted_mobs = typecacheof(blacklisted_mobs) + initialized_blacklist = TRUE + +/datum/controller/subsystem/interactions/proc/is_blacklisted(mob/living/creature) + if(!creature || !initialized_blacklist) + return TRUE + if(is_type_in_typecache(creature, blacklisted_mobs)) + return TRUE + return FALSE diff --git a/modular_zzplurt/code/datums/quirks/neutral_quirks/messy.dm b/modular_zzplurt/code/datums/quirks/neutral_quirks/messy.dm index dcd68645b844b..c34c20da269d6 100644 --- a/modular_zzplurt/code/datums/quirks/neutral_quirks/messy.dm +++ b/modular_zzplurt/code/datums/quirks/neutral_quirks/messy.dm @@ -11,4 +11,3 @@ mail_goodies = list ( /obj/item/mop = 1 // Clean this mess up! ) - hidden_quirk = TRUE diff --git a/modular_zzplurt/code/modules/client/preferences/erp_preferences.dm b/modular_zzplurt/code/modules/client/preferences/erp_preferences.dm new file mode 100644 index 0000000000000..cf20deef15c49 --- /dev/null +++ b/modular_zzplurt/code/modules/client/preferences/erp_preferences.dm @@ -0,0 +1,152 @@ +/datum/preference/choiced/erp_status_extm + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "erp_status_pref_extm" + +/datum/preference/choiced/erp_status_extm/init_possible_values() + return list("Yes - Switch", "Yes - Dom", "Yes - Sub", "Yes", "Ask (L)OOC", "Check OOC Notes", "No") + +/datum/preference/choiced/erp_status_extm/create_default_value() + return "No" + +/datum/preference/choiced/erp_status_extm/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + if(CONFIG_GET(flag/disable_erp_preferences)) + return FALSE + + return preferences.read_preference(/datum/preference/toggle/master_erp_preferences) + +/datum/preference/choiced/erp_status_extm/deserialize(input, datum/preferences/preferences) + if(CONFIG_GET(flag/disable_erp_preferences)) + return "No" + if(!preferences.read_preference(/datum/preference/toggle/master_erp_preferences)) + return "No" + . = ..() + +/datum/preference/choiced/erp_status_extm/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences) + return FALSE + +/datum/preference/choiced/erp_status_extmharm + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "erp_status_pref_extmharm" + +/datum/preference/choiced/erp_status_extmharm/init_possible_values() + return list("Yes - Switch", "Yes - Dom", "Yes - Sub", "Yes", "Ask (L)OOC", "Check OOC Notes", "No") + +/datum/preference/choiced/erp_status_extmharm/create_default_value() + return "No" + +/datum/preference/choiced/erp_status_extmharm/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + if(CONFIG_GET(flag/disable_erp_preferences)) + return FALSE + + return preferences.read_preference(/datum/preference/toggle/master_erp_preferences) && preferences.read_preference(/datum/preference/choiced/erp_status_extm) != "No" + +/datum/preference/choiced/erp_status_extmharm/deserialize(input, datum/preferences/preferences) + if(CONFIG_GET(flag/disable_erp_preferences)) + return "No" + if(!preferences.read_preference(/datum/preference/toggle/master_erp_preferences) || !(preferences.read_preference(/datum/preference/choiced/erp_status_extm) != "No")) + return "No" + . = ..() + +/datum/preference/choiced/erp_status_extmharm/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences) + return FALSE + +/datum/preference/choiced/erp_status_unholy + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "erp_status_pref_unholy" + +/datum/preference/choiced/erp_status_unholy/init_possible_values() + return list("Yes - Switch", "Yes - Dom", "Yes - Sub", "Yes", "Ask (L)OOC", "Check OOC Notes", "No") + +/datum/preference/choiced/erp_status_unholy/create_default_value() + return "No" + +/datum/preference/choiced/erp_status_unholy/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + if(CONFIG_GET(flag/disable_erp_preferences)) + return FALSE + + return preferences.read_preference(/datum/preference/toggle/master_erp_preferences) + +/datum/preference/choiced/erp_status_unholy/deserialize(input, datum/preferences/preferences) + if(CONFIG_GET(flag/disable_erp_preferences)) + return "No" + if(!preferences.read_preference(/datum/preference/toggle/master_erp_preferences)) + return "No" + . = ..() + +/datum/preference/choiced/erp_status_unholy/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences) + return FALSE + +/datum/preference/numeric/erp_lust_tolerance + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_key = "erp_lust_tolerance_pref" + savefile_identifier = PREFERENCE_CHARACTER + + minimum = 0.5 + maximum = 2.0 + step = 0.01 + +/datum/preference/numeric/erp_lust_tolerance/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["lust_tolerance"] = value + +/datum/preference/numeric/erp_lust_tolerance/create_informed_default_value(datum/preferences/preferences) + return 1.0 + +/datum/preference/numeric/erp_lust_tolerance/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + if(CONFIG_GET(flag/disable_erp_preferences)) + return FALSE + + return preferences.read_preference(/datum/preference/toggle/master_erp_preferences) + +/datum/preference/numeric/erp_sexual_potency + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_key = "erp_sexual_potency_pref" + savefile_identifier = PREFERENCE_CHARACTER + + minimum = 0.5 + maximum = 2.0 + step = 0.01 + +/datum/preference/numeric/erp_sexual_potency/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["sexual_potency"] = value + +/datum/preference/numeric/erp_sexual_potency/create_informed_default_value(datum/preferences/preferences) + return 1.0 + +/datum/preference/numeric/erp_sexual_potency/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + if(CONFIG_GET(flag/disable_erp_preferences)) + return FALSE + + return preferences.read_preference(/datum/preference/toggle/master_erp_preferences) + +/datum/preference/toggle/erp/butt_enlargement + savefile_key = "butt_enlargement_pref" + +/datum/preference/toggle/erp/butt_shrinkage + savefile_key = "butt_shrinkage_pref" + +/datum/preference/toggle/erp/belly_enlargement + savefile_key = "belly_enlargement_pref" + +/datum/preference/toggle/erp/belly_shrinkage + savefile_key = "belly_shrinkage_pref" + +/datum/preference/toggle/erp/forced_neverboner + savefile_key = "forced_neverboner_pref" diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interaction_datum.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interaction_datum.dm new file mode 100644 index 0000000000000..31f446d60a06f --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interaction_datum.dm @@ -0,0 +1,260 @@ +#define isclownjob(A) (is_species(A, /datum/species/human) && A?.mind?.assigned_role?.title == "Clown") + +/datum/interaction + /// Which genital the interaction cums with (must be penis, vagina or both) + var/list/cum_genital = list(CLIMAX_POSITION_USER = null, CLIMAX_POSITION_TARGET = null) + /// Where on the partner the interaction cums in? (It must match a genital's ORGAN_SLOT, mouth or sheath) + var/list/cum_target = list(CLIMAX_POSITION_USER = null, CLIMAX_POSITION_TARGET = null) + /// override of the text to display when the interaction cums (use this if you're not using a cum_target) + var/list/cum_message_text_overrides = list(CLIMAX_POSITION_USER = list(), CLIMAX_POSITION_TARGET = list()) + /// override of the self message to display when the interaction cums (use this if you're not using a cum_target) + var/list/cum_self_text_overrides = list(CLIMAX_POSITION_USER = list(), CLIMAX_POSITION_TARGET = list()) + /// override of the text to display to the partner when the interaction cums (use this if you're not using a cum_target) + var/list/cum_partner_text_overrides = list(CLIMAX_POSITION_USER = list(), CLIMAX_POSITION_TARGET = list()) + /// Is the interaction considered extreme/harmful/unholy? + var/unsafe_types = NONE + /// Additional details to display in the interaction menu and used in some interaction logic + var/list/additional_details = list() + /// Interaction modifier flags, edits how the interaction works from its original definition + var/interaction_modifier_flags = NONE + + /// List of clown-specific messages for different genital types + var/static/list/clown_genital_messages = list( + ORGAN_SLOT_VAGINA = list( + "\The %OWNER%'s clussy honks[pick(" loudly", "")]!", + "\The %OWNER%'s pussy squeaks[pick(" loudly", "")]!" + ), + ORGAN_SLOT_PENIS = list( + "\The %OWNER%'s cock honks[pick(" loudly", "")]!", + "\The %OWNER%'s penis squeaks[pick(" loudly", "")]!" + ), + ORGAN_SLOT_ANUS = list( + "\The %OWNER%'s fun hole honks[pick(" loudly", "")]!", + "\The %OWNER%'s ass squeaks[pick(" loudly", "")]!" + ), + ORGAN_SLOT_BREASTS = list( + "\The %OWNER%'s honkers produce a loud squeak!", + "\The %OWNER%'s breasts honk[pick(" loudly", "")]!" + ), + ORGAN_SLOT_MOUTH = list( + "\The %OWNER%'s mouth honks[pick(" loudly", "")]!", + "\The %OWNER%'s throat squeaks[pick(" loudly", "")]!" + ) + ) + +/datum/interaction/New() + cum_message_text_overrides[CLIMAX_POSITION_USER] = sanitize_islist(cum_message_text_overrides[CLIMAX_POSITION_USER], list()) + cum_self_text_overrides[CLIMAX_POSITION_USER] = sanitize_islist(cum_self_text_overrides[CLIMAX_POSITION_USER], list()) + cum_partner_text_overrides[CLIMAX_POSITION_USER] = sanitize_islist(cum_partner_text_overrides[CLIMAX_POSITION_USER], list()) + cum_message_text_overrides[CLIMAX_POSITION_TARGET] = sanitize_islist(cum_message_text_overrides[CLIMAX_POSITION_TARGET], list()) + cum_self_text_overrides[CLIMAX_POSITION_TARGET] = sanitize_islist(cum_self_text_overrides[CLIMAX_POSITION_TARGET], list()) + cum_partner_text_overrides[CLIMAX_POSITION_TARGET] = sanitize_islist(cum_partner_text_overrides[CLIMAX_POSITION_TARGET], list()) + . = ..() + +/datum/interaction/load_from_json(path) + . = ..() + if(!.) + return FALSE + + var/list/unsafe_flags = list( + "extreme" = INTERACTION_EXTREME, + "extremeharm" = INTERACTION_EXTREME | INTERACTION_HARMFUL, + "unholy" = INTERACTION_UNHOLY, + ) + + var/list/json = json_decode(file2text(path)) + cum_genital[CLIMAX_POSITION_USER] = sanitize_text(json["cum_genital_user"]) + cum_genital[CLIMAX_POSITION_TARGET] = sanitize_text(json["cum_genital_target"]) + cum_target[CLIMAX_POSITION_USER] = sanitize_text(json["cum_target_user"]) + cum_target[CLIMAX_POSITION_TARGET] = sanitize_text(json["cum_target_target"]) + cum_message_text_overrides[CLIMAX_POSITION_USER] = sanitize_islist(json["cum_message_text_overrides_user"], list()) + cum_message_text_overrides[CLIMAX_POSITION_TARGET] = sanitize_islist(json["cum_message_text_overrides_target"], list()) + cum_self_text_overrides[CLIMAX_POSITION_USER] = sanitize_islist(json["cum_self_text_overrides_user"], list()) + cum_self_text_overrides[CLIMAX_POSITION_TARGET] = sanitize_islist(json["cum_self_text_overrides_target"], list()) + cum_partner_text_overrides[CLIMAX_POSITION_USER] = sanitize_islist(json["cum_partner_text_overrides_user"], list()) + cum_partner_text_overrides[CLIMAX_POSITION_TARGET] = sanitize_islist(json["cum_partner_text_overrides_target"], list()) + + var/list/unsafe_list = sanitize_islist(json["unsafe_types"], list()) + for(var/unsafe_type in unsafe_list) + unsafe_types |= unsafe_flags[unsafe_type] + +/datum/interaction/json_save(path) + . = ..() + if(!.) + return FALSE + + var/file = file(path) + var/list/json = json_decode(file2text(path)) + + json["cum_genital_user"] = cum_genital[CLIMAX_POSITION_USER] + json["cum_genital_target"] = cum_genital[CLIMAX_POSITION_TARGET] + json["cum_target_user"] = cum_target[CLIMAX_POSITION_USER] + json["cum_target_target"] = cum_target[CLIMAX_POSITION_TARGET] + json["cum_message_text_overrides_user"] = cum_message_text_overrides[CLIMAX_POSITION_USER] + json["cum_message_text_overrides_target"] = cum_message_text_overrides[CLIMAX_POSITION_TARGET] + json["cum_self_text_overrides_user"] = cum_self_text_overrides[CLIMAX_POSITION_USER] + json["cum_self_text_overrides_target"] = cum_self_text_overrides[CLIMAX_POSITION_TARGET] + json["cum_partner_text_overrides_user"] = cum_partner_text_overrides[CLIMAX_POSITION_USER] + json["cum_partner_text_overrides_target"] = cum_partner_text_overrides[CLIMAX_POSITION_TARGET] + + var/list/unsafe_flags = list() + if(unsafe_types & INTERACTION_EXTREME) + unsafe_flags += "extreme" + if(unsafe_types & INTERACTION_HARMFUL) + unsafe_flags += "extremeharm" + if(unsafe_types & INTERACTION_UNHOLY) + unsafe_flags += "unholy" + json["unsafe_types"] = unsafe_flags + + WRITE_FILE(file, json_encode(json)) + return TRUE + + +/datum/interaction/proc/allow_act(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(target == user && usage == INTERACTION_OTHER) + return FALSE + + if(unsafe_types & INTERACTION_EXTREME) + if(!(user.client?.prefs?.read_preference(/datum/preference/choiced/erp_status_extm) != "No") || !(target.client?.prefs?.read_preference(/datum/preference/choiced/erp_status_extm) != "No")) + return FALSE + if(unsafe_types & INTERACTION_HARMFUL) + if(!(user.client?.prefs?.read_preference(/datum/preference/choiced/erp_status_extmharm) != "No") || !(target.client?.prefs?.read_preference(/datum/preference/choiced/erp_status_extmharm) != "No")) + return FALSE + if(unsafe_types & INTERACTION_UNHOLY) + if(!(user.client?.prefs?.read_preference(/datum/preference/choiced/erp_status_unholy) != "No") || !(target.client?.prefs?.read_preference(/datum/preference/choiced/erp_status_unholy) != "No")) + return FALSE + + if(user_required_parts.len) + for(var/slot in user_required_parts) + if(!user.has_genital(LAZYACCESS(user_required_parts, slot) || REQUIRE_GENITAL_EXPOSED, slot)) + return FALSE + + if(target_required_parts.len) + for(var/slot in target_required_parts) + if(!target.has_genital(LAZYACCESS(target_required_parts, slot) || REQUIRE_GENITAL_EXPOSED, slot)) + return FALSE + + for(var/requirement in interaction_requires) + switch(requirement) + if(INTERACTION_REQUIRE_SELF_HAND) + if(!user.get_active_hand()) + return FALSE + if(INTERACTION_REQUIRE_TARGET_HAND) + if(!target.get_active_hand()) + return FALSE + if(INTERACTION_REQUIRE_SELF_MOUTH) + if(!user.get_bodypart(BODY_ZONE_PRECISE_MOUTH) || user.is_mouth_covered()) + return FALSE + if(INTERACTION_REQUIRE_TARGET_MOUTH) + if(!target.get_bodypart(BODY_ZONE_PRECISE_MOUTH) || target.is_mouth_covered()) + return FALSE + if(INTERACTION_REQUIRE_SELF_TOPLESS) + if(!user.is_topless()) + return FALSE + if(INTERACTION_REQUIRE_TARGET_TOPLESS) + if(!target.is_topless()) + return FALSE + if(INTERACTION_REQUIRE_SELF_BOTTOMLESS) + if(!user.is_bottomless()) + return FALSE + if(INTERACTION_REQUIRE_TARGET_BOTTOMLESS) + if(!target.is_bottomless()) + return FALSE + if(INTERACTION_REQUIRE_SELF_FEET) + if(!(user.has_feet() >= (LAZYACCESS(user_required_parts, INTERACTION_REQUIRE_SELF_FEET) || 2))) //We prolly don't need to care if it's exposed or not + return FALSE + if(INTERACTION_REQUIRE_TARGET_FEET) + if(!(target.has_feet() >= (LAZYACCESS(target_required_parts, INTERACTION_REQUIRE_TARGET_FEET) || 2))) + return FALSE + else + CRASH("Unimplemented interaction requirement '[requirement]'") + return TRUE + +/// Called when the interaction is performed +/datum/interaction/proc/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + handle_clown_interaction(user, target) + return + +/// Handles clown-specific interaction effects +/datum/interaction/proc/handle_clown_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + if(!prob(50)) // 50% chance for honk effects + return + + var/is_user_clown = isclownjob(user) + var/is_target_clown = target ? isclownjob(target) : FALSE + if(!is_user_clown && !is_target_clown) + return + + // Handle user's genitals if they're a clown + if(is_user_clown) + for(var/genital_slot in user_required_parts) + if(clown_genital_messages[genital_slot]) + var/message = pick(clown_genital_messages[genital_slot]) + message = replacetext(message, "%OWNER%", user) + user.visible_message(span_lewd(message)) + playsound(user, 'sound/items/bikehorn.ogg', 40, TRUE, -1) + + // Handle target's genitals if they're a clown + if(is_target_clown && target) + for(var/genital_slot in target_required_parts) + if(clown_genital_messages[genital_slot]) + var/message = pick(clown_genital_messages[genital_slot]) + message = replacetext(message, "%OWNER%", target) + target.visible_message(span_lewd(message)) + playsound(target, 'sound/items/bikehorn.ogg', 40, TRUE, -1) + +// Called when either the user or target is cumming from the interaction, makes the interaction text +/datum/interaction/proc/show_climax(mob/living/carbon/human/cumming, mob/living/carbon/human/came_in, position) + var/override_check = length(cum_message_text_overrides[position]) && length(cum_self_text_overrides[position]) && (length(cum_partner_text_overrides[position]) || usage == INTERACTION_SELF) + if(!override_check) + return FALSE + + var/cumming_their = cumming.p_their() + var/cumming_them = cumming.p_them() + var/came_in_them = came_in.p_them() + var/came_in_their = came_in.p_their() + var/genital_used = cum_genital[position] + var/hole_used = cum_target[position] + + if(override_check) + var/message = pick(cum_message_text_overrides[position]) + message = replacetext(message, "%CUMMING%", "[cumming]") + message = replacetext(message, "%CUMMING_THEIR%", "[cumming_their]") + message = replacetext(message, "%CUMMING_THEM%", "[cumming_them]") + message = replacetext(message, "%CAME_IN%", "[came_in]") + message = replacetext(message, "%CAME_IN_THEIR%", "[came_in_their]") + message = replacetext(message, "%CAME_IN_THEM%", "[came_in_them]") + message = replacetext(message, "%CUM_GENITAL%", "[genital_used]") + message = replacetext(message, "%CUM_TARGET%", "[hole_used]") + + var/self_message = pick(cum_self_text_overrides[position]) + self_message = replacetext(self_message, "%CUMMING%", "[cumming]") + self_message = replacetext(self_message, "%CUMMING_THEIR%", "[cumming_their]") + self_message = replacetext(self_message, "%CUMMING_THEM%", "[cumming_them]") + self_message = replacetext(self_message, "%CAME_IN%", "[came_in]") + self_message = replacetext(self_message, "%CAME_IN_THEIR%", "[came_in_their]") + self_message = replacetext(self_message, "%CAME_IN_THEM%", "[came_in_them]") + self_message = replacetext(self_message, "%CUM_GENITAL%", "[genital_used]") + self_message = replacetext(self_message, "%CUM_TARGET%", "[hole_used]") + + cumming.visible_message(span_userlove(message), span_userlove(self_message)) + + if(usage == INTERACTION_OTHER) + var/partner_message = pick(cum_partner_text_overrides[position]) + partner_message = replacetext(partner_message, "%CUMMING%", "[cumming]") + partner_message = replacetext(partner_message, "%CUMMING_THEIR%", "[cumming_their]") + partner_message = replacetext(partner_message, "%CUMMING_THEM%", "[cumming_them]") + partner_message = replacetext(partner_message, "%CAME_IN%", "[came_in]") + partner_message = replacetext(partner_message, "%CAME_IN_THEIR%", "[came_in_their]") + partner_message = replacetext(partner_message, "%CAME_IN_THEM%", "[came_in_them]") + partner_message = replacetext(partner_message, "%CUM_GENITAL%", "[genital_used]") + partner_message = replacetext(partner_message, "%CUM_TARGET%", "[hole_used]") + + to_chat(came_in, span_userlove(partner_message)) + return TRUE + +/// Called after either the user or target cums from the interaction +/datum/interaction/proc/post_climax(mob/living/carbon/human/cumming, mob/living/carbon/human/came_in, position) + return + +#undef isclownjob diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/_lewd.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/_lewd.dm new file mode 100644 index 0000000000000..68a5e6cc52786 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/_lewd.dm @@ -0,0 +1,4 @@ +/datum/interaction/lewd + lewd = TRUE + color = "pink" + category = INTERACTION_CAT_LEWD diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/armpit.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/armpit.dm new file mode 100644 index 0000000000000..ee33860a7acee --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/armpit.dm @@ -0,0 +1,162 @@ +/datum/interaction/lewd/armpit_fuck + name = "Armpit Fuck" + description = "Fuck their armpit." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_TOPLESS) + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_USER = list( + "cums all over %TARGET%'s armpit", + "shoots their load into %TARGET%'s pit", + "covers %TARGET%'s underarm in cum" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_USER = list( + "You cum all over %TARGET%'s armpit", + "You shoot your load into %TARGET%'s pit", + "You cover %TARGET%'s underarm in cum" + )) + cum_partner_text_overrides = list(CLIMAX_POSITION_USER = list( + "%USER% cums all over your armpit", + "%USER% shoots their load into your pit", + "%USER% covers your underarm in cum" + )) + message = list( + "fucks %TARGET%'s armpit", + "slides their cock into %TARGET%'s underarm", + "thrusts into %TARGET%'s pit", + "pounds %TARGET%'s armpit" + ) + user_messages = list( + "You feel %TARGET%'s warm pit around your cock", + "The softness of %TARGET%'s armpit feels good against your shaft", + "%TARGET%'s underarm squeezes your cock nicely" + ) + target_messages = list( + "You feel %USER%'s cock rubbing in your armpit", + "%USER%'s shaft slides against your underarm", + "The warmth of %USER%'s cock presses into your pit" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 3 + target_pleasure = 0 + user_arousal = 5 + target_arousal = 2 + +/datum/interaction/lewd/armpit_lick + name = "Lick Armpit" + description = "Lick their armpit." + interaction_requires = list( + INTERACTION_REQUIRE_SELF_MOUTH, + INTERACTION_REQUIRE_TARGET_TOPLESS + ) + message = list( + "licks %TARGET%'s armpit", + "runs their tongue along %TARGET%'s underarm", + "tastes %TARGET%'s pit", + "plants their face in %TARGET%'s armpit" + ) + user_messages = list( + "You taste %TARGET%'s armpit", + "The scent of %TARGET%'s pit fills your nose", + "You savor the taste of %TARGET%'s underarm" + ) + target_messages = list( + "You feel %USER%'s tongue in your armpit", + "%USER%'s wet tongue slides across your pit", + "The warmth of %USER%'s mouth tingles your underarm" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ_fingering.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 3 + target_arousal = 3 + +/datum/interaction/lewd/armpit_smother + name = "Armpit Smother" + description = "Press your armpit against their face." + interaction_requires = list( + INTERACTION_REQUIRE_TARGET_MOUTH, + INTERACTION_REQUIRE_SELF_TOPLESS + ) + message = list( + "presses their armpit against %TARGET%'s face", + "smothers %TARGET%'s face with their pit", + "forces %TARGET%'s face into their underarm", + "pins %TARGET%'s head under their arm" + ) + user_messages = list( + "You feel %TARGET%'s face pressed into your pit", + "You hold %TARGET%'s head against your underarm", + "You keep %TARGET%'s face buried in your armpit" + ) + target_messages = list( + "Your face is pressed into %USER%'s armpit", + "%USER%'s underarm smothers your face", + "Your nose fills with the scent of %USER%'s pit" + ) + sound_range = 1 + sound_use = FALSE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 3 + target_arousal = 3 + +/datum/interaction/lewd/armpit_pitjob + name = "Give Pitjob" + description = "Jerk them off with your armpit." + interaction_requires = list( + INTERACTION_REQUIRE_SELF_TOPLESS + ) + target_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_TARGET = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_TARGET = list( + "cums all over %USER%'s armpit", + "shoots their load into %USER%'s pit", + "covers %USER%'s underarm in cum" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_TARGET = list( + "%TARGET% cums all over your armpit", + "%TARGET% shoots their load into your pit", + "%TARGET% covers your underarm in cum" + )) + cum_partner_text_overrides = list(CLIMAX_POSITION_TARGET = list( + "You cum all over %USER%'s armpit", + "You shoot your load into %USER%'s pit", + "You cover %USER%'s underarm in cum" + )) + message = list( + "works %TARGET%'s cock with their armpit", + "squeezes %TARGET%'s shaft between their arm and chest", + "jerks %TARGET% off with their pit", + "pleasures %TARGET%'s cock with their underarm" + ) + user_messages = list( + "You feel %TARGET%'s cock throb in your armpit", + "The warmth of %TARGET%'s shaft fills your pit", + "You squeeze %TARGET%'s cock with your underarm" + ) + target_messages = list( + "%USER%'s warm pit strokes your cock", + "Your shaft slides between %USER%'s arm and chest", + "The softness of %USER%'s armpit feels amazing" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 3 + user_arousal = 2 + target_arousal = 5 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/belly.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/belly.dm new file mode 100644 index 0000000000000..8ba48f644d0b9 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/belly.dm @@ -0,0 +1,117 @@ +/datum/interaction/lewd/bellyfuck + name = "Bellyfuck" + description = "Fuck their belly." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_TOPLESS) + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_USER = list( + "cums all over %TARGET%'s belly", + "shoots their load onto %TARGET%'s stomach", + "covers %TARGET%'s navel in cum" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_USER = list( + "You cum all over %TARGET%'s belly", + "You shoot your load onto %TARGET%'s stomach", + "You cover %TARGET%'s navel in cum" + )) + cum_partner_text_overrides = list(CLIMAX_POSITION_USER = list( + "%USER% cums all over your belly", + "%USER% shoots their load onto your stomach", + "%USER% covers your navel in cum" + )) + message = list( + "rubs their cock against %TARGET%'s belly", + "fucks %TARGET%'s navel", + "grinds their cock on %TARGET%'s stomach", + "thrusts against %TARGET%'s belly" + ) + user_messages = list( + "You feel %TARGET%'s warm skin against your cock", + "The softness of %TARGET%'s belly feels good against your shaft", + "%TARGET%'s belly feels amazing against your cock" + ) + target_messages = list( + "You feel %USER%'s cock rubbing against your belly", + "%USER%'s shaft slides across your stomach", + "The warmth of %USER%'s cock presses against your navel" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 3 + target_pleasure = 0 + user_arousal = 5 + target_arousal = 2 + +/datum/interaction/lewd/nuzzle_belly + name = "Nuzzle Belly" + description = "Nuzzle their belly." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_TOPLESS) + message = list( + "nuzzles %TARGET%'s belly", + "rubs their face against %TARGET%'s stomach", + "presses their cheek to %TARGET%'s navel", + "snuggles against %TARGET%'s tummy" + ) + user_messages = list( + "You feel %TARGET%'s warm skin against your face", + "The softness of %TARGET%'s belly feels nice against your cheek", + "%TARGET%'s stomach is warm and inviting" + ) + target_messages = list( + "You feel %USER%'s face nuzzling your belly", + "%USER%'s cheek rubs softly against your stomach", + "The warmth of %USER%'s face presses against your navel" + ) + sound_range = 1 + sound_use = FALSE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 2 + target_arousal = 3 + +/datum/interaction/lewd/deflate_belly + name = "Deflate Belly" + description = "Deflate belly." + user_required_parts = list(ORGAN_SLOT_BELLY = REQUIRE_GENITAL_ANY) + usage = INTERACTION_SELF + message = list( + "deflates their belly", + "lets out air from their belly", + "makes their belly smaller" + ) + sound_range = 1 + sound_use = FALSE + user_pleasure = 0 + user_arousal = 0 + +/datum/interaction/lewd/deflate_belly/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + var/obj/item/organ/external/genital/belly/gut = user.get_organ_slot(ORGAN_SLOT_BELLY) + if(gut) + gut.set_size(gut.genital_size - 1) + +/datum/interaction/lewd/inflate_belly + name = "Inflate Belly" + description = "Inflate belly." + user_required_parts = list(ORGAN_SLOT_BELLY = REQUIRE_GENITAL_ANY) + usage = INTERACTION_SELF + message = list( + "inflates their belly", + "makes their belly bigger", + "expands their belly" + ) + sound_range = 1 + sound_use = FALSE + user_pleasure = 0 + user_arousal = 0 + +/datum/interaction/lewd/inflate_belly/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + var/obj/item/organ/external/genital/belly/gut = user.get_organ_slot(ORGAN_SLOT_BELLY) + if(gut) + gut.set_size(gut.genital_size + 1) diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/breasts.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/breasts.dm new file mode 100644 index 0000000000000..da54fc38ed2ee --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/breasts.dm @@ -0,0 +1,266 @@ +/datum/interaction/lewd/breastfeed + name = "Breastfeed" + description = "Breastfeed them." + user_required_parts = list(ORGAN_SLOT_BREASTS = REQUIRE_GENITAL_EXPOSED) + interaction_requires = list(INTERACTION_REQUIRE_TARGET_MOUTH) + message = list( + "pushes their breasts against %TARGET%'s mouth, squirting their warm %MILK% into their mouth.", + "fills %TARGET%'s mouth with warm, sweet %MILK% as they squeeze their boobs, panting.", + "lets a large stream of their own abundant %MILK% coat the back of %TARGET%'s throat." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/oral1.ogg', + 'modular_zzplurt/sound/interactions/oral2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 2 + user_arousal = 3 + target_pleasure = 0 + target_arousal = 2 + +/datum/interaction/lewd/breastfeed/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/obj/item/organ/external/genital/breasts/breasts = user.get_organ_slot(ORGAN_SLOT_BREASTS) + if(!breasts?.internal_fluid_datum) + return + + var/datum/reagent/milk = find_reagent_object_from_type(breasts.internal_fluid_datum) + var/list/original_messages = message.Copy() + var/chosen_message = pick(message) + chosen_message = replacetext(chosen_message, "%MILK%", lowertext(milk.name)) + message = list(chosen_message) + . = ..() + message = original_messages + +/datum/interaction/lewd/breastfeed/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + var/obj/item/organ/external/genital/breasts/breasts = user.get_organ_slot(ORGAN_SLOT_BREASTS) + if(breasts?.internal_fluid_datum) + // Calculate milk amount based on how full the breasts are (0.5 to 2 multiplier) + var/milk_multiplier = 0.5 + if(breasts.internal_fluid_maximum > 0) + milk_multiplier = 0.5 + (1.5 * (breasts.internal_fluid_count / breasts.internal_fluid_maximum)) + + var/transfer_amount = rand(1, 3 * milk_multiplier) + var/datum/reagents/R = new(breasts.internal_fluid_maximum) + breasts.transfer_internal_fluid(R, transfer_amount) + R.trans_to(target, R.total_volume) + qdel(R) + +/datum/interaction/lewd/titgrope + name = "Grope Breasts" + description = "Grope their breasts." + interaction_requires = list(INTERACTION_REQUIRE_SELF_HAND) + target_required_parts = list(ORGAN_SLOT_BREASTS = REQUIRE_GENITAL_ANY) + additional_details = list(INTERACTION_FILLS_CONTAINERS) + message = list( + "gently gropes %TARGET%'s breast.", + "softly squeezes %TARGET%'s breasts.", + "grips %TARGET%'s breasts.", + "runs a few fingers over %TARGET%'s breast.", + "delicately teases %TARGET%'s nipple.", + "traces a touch across %TARGET%'s breast." + ) + sound_possible = list('modular_zzplurt/sound/interactions/squelch1.ogg') + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + user_arousal = 2 + target_pleasure = 3 + target_arousal = 5 + +/datum/interaction/lewd/titgrope/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/obj/item/reagent_containers/liquid_container + var/list/original_messages = message.Copy() + + // Check for container + var/obj/item/cached_item = user.get_active_held_item() + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + else + cached_item = user.pulling + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + + if(liquid_container) + message = list("milks %TARGET%'s breasts into \the [liquid_container].") + . = ..() + message = original_messages + return + + // Handle different intents + switch(resolve_intent_name(user.combat_mode)) + if("harm") + message = list( + "aggressively gropes %TARGET%'s breast.", + "grabs %TARGET%'s breasts.", + "tightly squeezes %TARGET%'s breasts.", + "slaps at %TARGET%'s breasts.", + "gropes %TARGET%'s breasts roughly." + ) + if("disarm") + message = list( + "playfully bats at %TARGET%'s breasts.", + "teasingly gropes %TARGET%'s breasts.", + "playfully squeezes %TARGET%'s breasts.", + "mischievously fondles %TARGET%'s breasts.", + "impishly teases %TARGET%'s nipples." + ) + if("grab") + message = list( + "firmly grips %TARGET%'s breasts.", + "possessively gropes %TARGET%'s breasts.", + "eagerly kneads %TARGET%'s breasts.", + "roughly fondles %TARGET%'s breasts.", + "greedily squeezes %TARGET%'s breasts." + ) + . = ..() + message = original_messages + +/datum/interaction/lewd/titgrope/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + if(interaction_modifier_flags & INTERACTION_OVERRIDE_FLUID_TRANSFER) + var/obj/item/reagent_containers/liquid_container + + var/obj/item/cached_item = user.get_active_held_item() + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + else + cached_item = user.pulling + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + + if(liquid_container) + var/obj/item/organ/external/genital/breasts/breasts = target.get_organ_slot(ORGAN_SLOT_BREASTS) + if(breasts?.internal_fluid_datum) + // Calculate milk amount based on how full the breasts are (0.5 to 2 multiplier) + var/milk_multiplier = 0.5 + if(breasts.internal_fluid_maximum > 0) + milk_multiplier = 0.5 + (1.5 * (breasts.internal_fluid_count / breasts.internal_fluid_maximum)) + + var/transfer_amount = rand(1, 3 * milk_multiplier) + var/datum/reagents/R = new(breasts.internal_fluid_maximum) + breasts.transfer_internal_fluid(R, transfer_amount) + R.trans_to(liquid_container, R.total_volume) + qdel(R) + + // Handle arousal effects based on intent + var/intent = resolve_intent_name(user.combat_mode) + if(intent != "harm" && prob(5 + target.arousal)) + var/list/arousal_messages + switch(intent) + if("help") + arousal_messages = list( + "%TARGET% shivers in arousal.", + "%TARGET% moans quietly.", + "%TARGET% breathes out a soft moan.", + "%TARGET% gasps.", + "%TARGET% shudders softly.", + "%TARGET% trembles as hands run across bare skin." + ) + if("disarm") + arousal_messages = list( + "%TARGET% playfully squirms.", + "%TARGET% lets out a teasing giggle.", + "%TARGET% bites their lip.", + "%TARGET% wiggles teasingly.", + "%TARGET% gives a flirtatious gasp." + ) + if("grab") + arousal_messages = list( + "%TARGET% moans eagerly.", + "%TARGET% presses into the touch.", + "%TARGET% lets out a wanting groan.", + "%TARGET% quivers with excitement.", + "%TARGET% shivers with anticipation." + ) + + if(arousal_messages) + var/target_message = list(pick(arousal_messages)) + target.visible_message(span_lewd(replacetext(target_message, "%TARGET%", target))) + +/datum/interaction/lewd/breastsmother + name = "Breast Smother" + description = "Smother them with your breasts." + interaction_requires = list( + INTERACTION_REQUIRE_TARGET_MOUTH + ) + user_required_parts = list(ORGAN_SLOT_BREASTS = REQUIRE_GENITAL_EXPOSED) + message = list( + "presses their breasts against %TARGET%'s face", + "smothers %TARGET%'s face with their tits", + "forces %TARGET%'s face between their breasts", + "pins %TARGET%'s head between their boobs" + ) + user_messages = list( + "You feel %TARGET%'s face pressed between your breasts", + "You hold %TARGET%'s head against your chest", + "You keep %TARGET%'s face buried in your cleavage" + ) + target_messages = list( + "Your face is pressed between %USER%'s breasts", + "%USER%'s tits smother your face", + "Your vision is filled with %USER%'s cleavage" + ) + sound_range = 1 + sound_use = FALSE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 3 + target_arousal = 3 + +/datum/interaction/lewd/breastsmother/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + if(prob((user.dna.features["sexual_potency"] * 5) + 10)) + target.adjustOxyLoss(2) + target.adjust_arousal(5) + user.adjust_arousal(8) + +/datum/interaction/lewd/do_boobjob + name = "Give Boobjob" + description = "Give them a boobjob." + target_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + user_required_parts = list(ORGAN_SLOT_BREASTS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_TARGET = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_TARGET = list( + "cums all over %USER%'s breasts", + "shoots their load onto %USER%'s tits", + "covers %USER%'s chest in cum" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_TARGET = list( + "%TARGET% cums all over your breasts", + "%TARGET% shoots their load onto your tits", + "%TARGET% covers your chest in cum" + )) + cum_partner_text_overrides = list(CLIMAX_POSITION_TARGET = list( + "You cum all over %USER%'s breasts", + "You shoot your load onto %USER%'s tits", + "You cover %USER%'s chest in cum" + )) + message = list( + "wraps their breasts around %TARGET%'s cock", + "works %TARGET%'s shaft between their tits", + "pleasures %TARGET% with their breasts", + "squeezes their breasts around %TARGET%'s cock" + ) + user_messages = list( + "You feel %TARGET%'s cock throbbing between your breasts", + "The warmth of %TARGET%'s shaft feels nice between your tits", + "You squeeze your breasts around %TARGET%'s cock" + ) + target_messages = list( + "%USER%'s soft breasts squeeze your cock", + "Your shaft slides between %USER%'s tits", + "The softness of %USER%'s breasts feels amazing" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 4 + user_arousal = 4 + target_arousal = 6 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/facefuck.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/facefuck.dm new file mode 100644 index 0000000000000..59307abf62fd4 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/facefuck.dm @@ -0,0 +1,82 @@ +/datum/interaction/lewd/facefuck_vagina + name = "Facefuck (Vagina)" + description = "Grind your pussy against their face." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_MOUTH) + user_required_parts = list(ORGAN_SLOT_VAGINA = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_VAGINA) + cum_target = list(CLIMAX_POSITION_USER = CLIMAX_TARGET_MOUTH) + message = list( + "grinds their pussy into %TARGET%'s face.", + "grips the back of %TARGET%'s head, forcing them onto their pussy.", + "rolls their pussy against %TARGET%'s tongue.", + "slides %TARGET%'s mouth between their legs.", + "looks %TARGET% in the eyes as their pussy presses into a waiting tongue.", + "sways their hips, pushing their sex into %TARGET%'s face." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/oral1.ogg', + 'modular_zzplurt/sound/interactions/oral2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 5 + user_arousal = 8 + target_pleasure = 0 + target_arousal = 3 + +/datum/interaction/lewd/facefuck_penis + name = "Facefuck (Penis)" + description = "Fuck their mouth with your cock." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_MOUTH) + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_target = list(CLIMAX_POSITION_USER = CLIMAX_TARGET_MOUTH) + message = list( + "roughly fucks %TARGET%'s mouth.", + "forces their cock down %TARGET%'s throat.", + "pushes in against %TARGET%'s tongue until a tight gagging sound comes.", + "grips %TARGET%'s hair and draws them to the base of their cock.", + "looks %TARGET% in the eyes as their cock presses into a waiting tongue.", + "rolls their hips hard, sinking into %TARGET%'s mouth." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/oral1.ogg', + 'modular_zzplurt/sound/interactions/oral2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 5 + target_pleasure = 0 + user_arousal = 8 + target_arousal = 3 + +/datum/interaction/lewd/throatfuck + name = "Throatfuck" + description = "Fuck their throat. (Warning: Causes oxygen damage)" + interaction_requires = list(INTERACTION_REQUIRE_TARGET_MOUTH) + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_target = list(CLIMAX_POSITION_USER = CLIMAX_TARGET_MOUTH) + message = list( + "brutally shoves their cock into %TARGET%'s throat to make them gag.", + "chokes %TARGET% on their cock, going in balls deep.", + "slams in and out of %TARGET%'s mouth, their balls slapping off their face." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/oral1.ogg', + 'modular_zzplurt/sound/interactions/oral2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 7 + target_pleasure = 0 + user_arousal = 10 + target_arousal = 2 + target_pain = 5 + +/datum/interaction/lewd/throatfuck/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + var/stat_before = target.stat + target.adjustOxyLoss(3) + if(target.stat == UNCONSCIOUS && stat_before != UNCONSCIOUS) + message = list("%TARGET% passes out on %USER%'s cock.") diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/feet.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/feet.dm new file mode 100644 index 0000000000000..eb6186bba72ab --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/feet.dm @@ -0,0 +1,228 @@ +/datum/interaction/lewd/grindface + name = "Grind Face" + description = "Feet grind their face." + interaction_requires = list(INTERACTION_REQUIRE_SELF_FEET, INTERACTION_REQUIRE_TARGET_MOUTH) + message = list( + "grinds their %FEET% into %TARGET%'s face.", + "presses their %FEET% down hard on %TARGET%'s face.", + "rubs off the dirt from their %FEET% onto %TARGET%'s face.", + "plants their %FEET% ontop of %TARGET%'s face.", + "rests their %FEET% on %TARGET%'s face and presses down hard.", + "harshly places their %FEET% atop %TARGET%'s face." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/foot_dry1.ogg', + 'modular_zzplurt/sound/interactions/foot_dry2.ogg', + 'modular_zzplurt/sound/interactions/foot_dry3.ogg', + 'modular_zzplurt/sound/interactions/foot_dry4.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 3 + target_arousal = 3 + +/datum/interaction/lewd/grindface/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/list/original_messages = message.Copy() + // Get shoes or barefoot text + var/obj/item/clothing/shoes/worn_shoes = user.get_item_by_slot(ITEM_SLOT_FEET) + var/feet_text = worn_shoes?.name || pick("bare feet", "soles") + + var/chosen_message = pick(message) + chosen_message = replacetext(chosen_message, "%FEET%", feet_text) + message = list(chosen_message) + . = ..() + message = original_messages + +/datum/interaction/lewd/grindmouth + name = "Grind Mouth" + description = "Feet grind their mouth." + interaction_requires = list(INTERACTION_REQUIRE_SELF_FEET, INTERACTION_REQUIRE_TARGET_MOUTH) + message = list( + "roughly shoves their %FEET% deeper into %TARGET%'s mouth.", + "harshly forces another inch of their %FEET% into %TARGET%'s mouth.", + "presses their weight down, their %FEET% prying deeper into %TARGET%'s mouth.", + "forces their %FEET% deep into %TARGET%'s mouth.", + "presses the tip of their %FEET% against %TARGET%'s lips and shoves inwards.", + "readies themselves and in one swift motion, shoves their %FEET% into %TARGET%'s mouth." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/foot_wet1.ogg', + 'modular_zzplurt/sound/interactions/foot_wet2.ogg', + 'modular_zzplurt/sound/interactions/foot_wet3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 3 + target_arousal = 3 + +/datum/interaction/lewd/grindmouth/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/list/original_messages = message.Copy() + var/obj/item/clothing/shoes/worn_shoes = user.get_item_by_slot(ITEM_SLOT_FEET) + var/feet_text = worn_shoes?.name || pick("bare feet", "toes", "soles") + + var/chosen_message = pick(message) + chosen_message = replacetext(chosen_message, "%FEET%", feet_text) + message = list(chosen_message) + . = ..() + message = original_messages + +/datum/interaction/lewd/footjob + name = "Footjob" + description = "Jerk them off with your foot." + interaction_requires = list(INTERACTION_REQUIRE_SELF_FEET) + target_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_TARGET = CLIMAX_PENIS) + cum_target = list(CLIMAX_POSITION_TARGET = null) + message = list( + "jerks %TARGET% off with their %FEET%.", + "rubs their %FEET% on %TARGET%'s shaft.", + "works their %FEET% up and down on %TARGET%'s cock." + ) + cum_message_text_overrides = list( + CLIMAX_POSITION_TARGET = list( + "cums all over %USER%'s %FEET%.", + "covers %USER%'s %FEET% in cum.", + "shoots their load onto %USER%'s %FEET%." + ) + ) + cum_self_text_overrides = list( + CLIMAX_POSITION_TARGET = list( + "you cum all over %USER%'s %FEET%.", + "you cover %USER%'s %FEET% in cum.", + "you shoot your load onto %USER%'s %FEET%." + ) + ) + cum_partner_text_overrides = list( + CLIMAX_POSITION_TARGET = list( + "%TARGET% cums all over your %FEET%.", + "%TARGET% covers your %FEET% in cum.", + "%TARGET% shoots their load onto your %FEET%." + ) + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/foot_dry1.ogg', + 'modular_zzplurt/sound/interactions/foot_dry3.ogg', + 'modular_zzplurt/sound/interactions/foot_wet1.ogg', + 'modular_zzplurt/sound/interactions/foot_wet2.ogg' + ) + sound_range = 1 + sound_use = TRUE + target_pleasure = 4 + user_pleasure = 0 + user_arousal = 3 + target_arousal = 6 + +/datum/interaction/lewd/footjob/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/list/original_messages = message.Copy() + var/obj/item/clothing/shoes/worn_shoes = user.get_item_by_slot(ITEM_SLOT_FEET) + var/feet_text = worn_shoes?.name || pick("foot", "sole") + + var/chosen_message = pick(message) + chosen_message = replacetext(chosen_message, "%FEET%", feet_text) + message = list(chosen_message) + . = ..() + message = original_messages + +/datum/interaction/lewd/footjob/show_climax(mob/living/carbon/human/cumming, mob/living/carbon/human/came_in, position) + var/obj/item/clothing/shoes/worn_shoes = cumming.get_item_by_slot(ITEM_SLOT_FEET) + var/feet_text = worn_shoes?.name || pick("foot", "sole") + + // Store original lists, with null checks + var/list/original_message_overrides = cum_message_text_overrides[position] + var/list/original_self_overrides = cum_self_text_overrides[position] + var/list/original_partner_overrides = cum_partner_text_overrides[position] + original_message_overrides = original_message_overrides?.Copy() + original_self_overrides = original_self_overrides?.Copy() + original_partner_overrides = original_partner_overrides?.Copy() + + // Pick and modify one message from each list + var/message_override = replacetext(pick(cum_message_text_overrides[position]), "%FEET%", feet_text) + var/self_override = replacetext(pick(cum_self_text_overrides[position]), "%FEET%", feet_text) + var/partner_override = replacetext(pick(cum_partner_text_overrides[position]), "%FEET%", feet_text) + + // Set single message lists + cum_message_text_overrides[position] = list(message_override) + cum_self_text_overrides[position] = list(self_override) + cum_partner_text_overrides[position] = list(partner_override) + + . = ..() + + // Restore original lists + cum_message_text_overrides[position] = original_message_overrides + cum_self_text_overrides[position] = original_self_overrides + cum_partner_text_overrides[position] = original_partner_overrides + +/datum/interaction/lewd/footjob/double + name = "Double Footjob" + description = "Jerk them off with both of your feet." + interaction_requires = list(INTERACTION_REQUIRE_SELF_FEET) + message = list( + "jerks %TARGET% off with their %FEET%.", + "rubs their %FEET% on %TARGET%'s shaft.", + "works their %FEET% up and down on %TARGET%'s cock." + ) + user_pleasure = 0 + target_pleasure = 5 + user_arousal = 4 + target_arousal = 7 + +/datum/interaction/lewd/footjob/double/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/list/original_messages = message.Copy() + var/obj/item/clothing/shoes/worn_shoes = user.get_item_by_slot(ITEM_SLOT_FEET) + var/feet_text = worn_shoes?.name || "feet" + + var/chosen_message = pick(message) + chosen_message = replacetext(chosen_message, "%FEET%", feet_text) + message = list(chosen_message) + . = ..() + message = original_messages + +/datum/interaction/lewd/footjob/vagina + name = "Vaginal Footjob" + description = "Rub their vagina with your foot." + target_required_parts = list(ORGAN_SLOT_VAGINA = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_TARGET = CLIMAX_VAGINA) + cum_target = list(CLIMAX_POSITION_TARGET = null) + message = list( + "rubs %TARGET%'s clit with their %FEET%.", + "rubs their %FEET% on %TARGET%'s coochie.", + "rubs their %FEET% on %TARGET%'s pussy.", + "rubs their foot up and down on %TARGET%'s pussy." + ) + cum_message_text_overrides = list( + CLIMAX_POSITION_TARGET = list( + "squirts all over %USER%'s %FEET%.", + "orgasms on %USER%'s %FEET%.", + "coats %USER%'s %FEET% with their juices." + ) + ) + cum_self_text_overrides = list( + CLIMAX_POSITION_TARGET = list( + "you squirt all over %USER%'s %FEET%.", + "you orgasm on %USER%'s %FEET%.", + "you coat %USER%'s %FEET% with your juices." + ) + ) + cum_partner_text_overrides = list( + CLIMAX_POSITION_TARGET = list( + "%TARGET% squirts all over your %FEET%.", + "%TARGET% orgasms on your %FEET%.", + "%TARGET% coats your %FEET% with their juices." + ) + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/foot_dry1.ogg', + 'modular_zzplurt/sound/interactions/foot_dry3.ogg', + 'modular_zzplurt/sound/interactions/foot_wet1.ogg', + 'modular_zzplurt/sound/interactions/foot_wet2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 4 + target_pleasure = 0 + user_arousal = 6 + target_arousal = 2 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/finger.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/finger.dm new file mode 100644 index 0000000000000..0866197407a4e --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/finger.dm @@ -0,0 +1,88 @@ +/datum/interaction/lewd/finger + name = "Finger Pussy" + description = "Finger their pussy." + interaction_requires = list(INTERACTION_REQUIRE_SELF_HAND) + target_required_parts = list(ORGAN_SLOT_VAGINA = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_TARGET = CLIMAX_VAGINA) + cum_target = list(CLIMAX_POSITION_TARGET = null) + additional_details = list(INTERACTION_FILLS_CONTAINERS) + message = list( + "fingers %TARGET%", + "fingers %TARGET%'s pussy", + "fingers %TARGET% hard" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ_fingering.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 3 + user_arousal = 3 + target_arousal = 5 + target_pain = 0 + +/datum/interaction/lewd/finger/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/list/original_messages = message.Copy() + var/obj/item/reagent_containers/liquid_container + + var/obj/item/cached_item = user.get_active_held_item() + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + else + cached_item = user.pulling + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + + if(liquid_container) + interaction_modifier_flags |= INTERACTION_OVERRIDE_FLUID_TRANSFER + message = list( + "fingers %TARGET% over \the [liquid_container]", + "fingers %TARGET%'s pussy above \the [liquid_container]", + "fingers %TARGET% hard while holding \the [liquid_container]" + ) + . = ..() + message = original_messages + interaction_modifier_flags &= ~INTERACTION_OVERRIDE_FLUID_TRANSFER + +/datum/interaction/lewd/finger/post_climax(mob/living/carbon/human/cumming, mob/living/carbon/human/came_in, position) + if(interaction_modifier_flags & INTERACTION_OVERRIDE_FLUID_TRANSFER) + var/obj/item/reagent_containers/liquid_container + + var/obj/item/cached_item = came_in.get_active_held_item() + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + else + cached_item = came_in.pulling + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + + if(liquid_container) + var/obj/item/organ/external/genital/vagina/vagina = cumming.get_organ_slot(ORGAN_SLOT_VAGINA) + if(vagina) + vagina.transfer_internal_fluid(liquid_container.reagents, vagina.internal_fluid_count) + + . = ..() + +/datum/interaction/lewd/fingerass + name = "Finger Ass" + description = "Finger their ass." + interaction_requires = list(INTERACTION_REQUIRE_SELF_HAND) + target_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_TARGET = CLIMAX_BOTH) + message = list( + "fingers %TARGET%'s ass", + "fingers %TARGET%'s asshole", + "fingers %TARGET% hard" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ_fingering.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 3 + user_arousal = 3 + target_arousal = 5 + target_pain = 2 + diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/frotting.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/frotting.dm new file mode 100644 index 0000000000000..40389df1dc2ae --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/frotting.dm @@ -0,0 +1,80 @@ +/datum/interaction/lewd/frotting + name = "Frotting" + description = "Rub your penis against theirs." + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + target_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS, CLIMAX_POSITION_TARGET = CLIMAX_PENIS) + cum_target = list(CLIMAX_POSITION_USER = null, CLIMAX_POSITION_TARGET = null) + cum_message_text_overrides = list( + CLIMAX_POSITION_USER = list( + "cums all over %TARGET%'s cock.", + "shoots their load onto %TARGET%'s shaft.", + "covers %TARGET%'s penis with their cum." + ), + CLIMAX_POSITION_TARGET = list( + "cums all over %USER%'s cock.", + "shoots their load onto %USER%'s shaft.", + "covers %USER%'s penis with their cum." + ) + ) + cum_self_text_overrides = list( + CLIMAX_POSITION_USER = list( + "you cum all over %TARGET%'s cock.", + "you shoot your load onto %TARGET%'s shaft.", + "you cover %TARGET%'s penis with your cum." + ), + CLIMAX_POSITION_TARGET = list( + "you cum all over %USER%'s cock.", + "you shoot your load onto %USER%'s shaft.", + "you cover %USER%'s penis with your cum." + ) + ) + cum_partner_text_overrides = list( + CLIMAX_POSITION_USER = list( + "%USER% cums all over your cock.", + "%USER% shoots their load onto your shaft.", + "%USER% covers your penis with their cum." + ), + CLIMAX_POSITION_TARGET = list( + "%TARGET% cums all over your cock.", + "%TARGET% shoots their load onto your shaft.", + "%TARGET% covers your penis with their cum." + ) + ) + message = list( + "rubs their cock against %TARGET%'s.", + "grinds their shaft against %TARGET%'s penis.", + "presses their dick against %TARGET%'s member.", + "frotts against %TARGET%'s cock." + ) + sound_use = TRUE + sound_range = 1 + user_pleasure = 6 + target_pleasure = 6 + user_arousal = 10 + target_arousal = 10 + +/datum/interaction/lewd/tribadism + name = "Tribadism" + description = "Grind your pussy against theirs." + user_required_parts = list(ORGAN_SLOT_VAGINA = REQUIRE_GENITAL_EXPOSED) + target_required_parts = list(ORGAN_SLOT_VAGINA = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_VAGINA, CLIMAX_POSITION_TARGET = CLIMAX_VAGINA) + cum_target = list(CLIMAX_POSITION_USER = ORGAN_SLOT_VAGINA, CLIMAX_POSITION_TARGET = ORGAN_SLOT_VAGINA) + message = list( + "grinds their pussy against %TARGET%'s cunt.", + "rubs their cunt against %TARGET%'s pussy.", + "thrusts against %TARGET%'s pussy.", + "humps %TARGET%, their pussies grinding against each other." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/squelch1.ogg', + 'modular_zzplurt/sound/interactions/squelch2.ogg', + 'modular_zzplurt/sound/interactions/squelch3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 6 + target_pleasure = 6 + user_arousal = 10 + target_arousal = 10 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/fuck.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/fuck.dm new file mode 100644 index 0000000000000..22dd398464be6 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/fuck.dm @@ -0,0 +1,239 @@ +/datum/interaction/lewd/fuck + name = "Fuck" + description = "Fuck their pussy." + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + target_required_parts = list(ORGAN_SLOT_VAGINA = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS, CLIMAX_POSITION_TARGET = CLIMAX_VAGINA) + cum_target = list(CLIMAX_POSITION_USER = ORGAN_SLOT_VAGINA, CLIMAX_POSITION_TARGET = ORGAN_SLOT_PENIS) + message = list( + "pounds %TARGET%'s pussy.", + "shoves their cock deep into %TARGET%'s pussy.", + "thrusts in and out of %TARGET%'s cunt.", + "goes balls deep into %TARGET%'s pussy over and over again." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ1.ogg', + 'modular_zzplurt/sound/interactions/champ2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 8 + target_pleasure = 8 + user_arousal = 12 + target_arousal = 12 + +/datum/interaction/lewd/fuck/anal + name = "Anal Fuck" + description = "Fuck their ass." + target_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS, CLIMAX_POSITION_TARGET = CLIMAX_BOTH) + cum_target = list(CLIMAX_POSITION_USER = ORGAN_SLOT_ANUS, CLIMAX_POSITION_TARGET = ORGAN_SLOT_PENIS) + message = list( + "thrusts in and out of %TARGET%'s ass.", + "pounds %TARGET%'s ass.", + "slams their hips up against %TARGET%'s ass hard.", + "goes balls deep into %TARGET%'s ass over and over again." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_use = TRUE + user_pleasure = 8 + target_pleasure = 4 + user_arousal = 12 + target_arousal = 8 + target_pain = 3 + +/datum/interaction/lewd/breastfuck + name = "Breast Fuck" + description = "Fuck their breasts." + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + target_required_parts = list(ORGAN_SLOT_BREASTS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_target = list(CLIMAX_POSITION_USER = ORGAN_SLOT_BREASTS) + message = list( + "fucks %TARGET%'s breasts.", + "grinds their cock between %TARGET%'s boobs.", + "thrusts into %TARGET%'s tits.", + "grabs %TARGET%'s breasts together and presses their cock between them." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 4 + target_pleasure = 0 + user_arousal = 8 + target_arousal = 3 + +/datum/interaction/lewd/footfuck + name = "Foot Fuck" + description = "Rub your cock on their foot." + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + interaction_requires = list(INTERACTION_REQUIRE_TARGET_FEET) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_message_text_overrides = list( + CLIMAX_POSITION_USER = list( + "cums all over %TARGET%'s foot.", + "shoots their load on %TARGET%'s sole.", + "covers %TARGET%'s toes in cum." + ), + CLIMAX_POSITION_TARGET = list() + ) + cum_self_text_overrides = list( + CLIMAX_POSITION_USER = list( + "you cum all over %TARGET%'s foot.", + "you shoot your load on %TARGET%'s sole.", + "you cover %TARGET%'s toes in cum." + ), + CLIMAX_POSITION_TARGET = list() + ) + cum_partner_text_overrides = list( + CLIMAX_POSITION_USER = list( + "%USER% cums all over your foot.", + "%USER% shoots their load on your sole.", + "%USER% covers your toes in cum." + ), + CLIMAX_POSITION_TARGET = list() + ) + message = list( + "fucks %TARGET%'s foot.", + "rubs their cock on %TARGET%'s foot.", + "grinds their cock on %TARGET%'s foot." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/foot_dry1.ogg', + 'modular_zzplurt/sound/interactions/foot_dry3.ogg', + 'modular_zzplurt/sound/interactions/foot_wet1.ogg', + 'modular_zzplurt/sound/interactions/foot_wet2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 3 + target_pleasure = 0 + user_arousal = 6 + target_arousal = 2 + +/datum/interaction/lewd/footfuck/double + name = "Double Foot Fuck" + description = "Rub your cock between their feet." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_FEET) + cum_message_text_overrides = list( + CLIMAX_POSITION_USER = list( + "cums all over %TARGET%'s feet.", + "shoots their load on %TARGET%'s soles.", + "covers %TARGET%'s toes in cum." + ), + CLIMAX_POSITION_TARGET = list() + ) + cum_self_text_overrides = list( + CLIMAX_POSITION_USER = list( + "you cum all over %TARGET%'s feet.", + "you shoot your load on %TARGET%'s soles.", + "you cover %TARGET%'s toes in cum." + ), + CLIMAX_POSITION_TARGET = list() + ) + cum_partner_text_overrides = list( + CLIMAX_POSITION_USER = list( + "%USER% cums all over your feet.", + "%USER% shoots their load on your soles.", + "%USER% covers your toes in cum." + ), + CLIMAX_POSITION_TARGET = list() + ) + message = list( + "fucks %TARGET%'s feet.", + "rubs their cock between %TARGET%'s feet.", + "thrusts their cock between %TARGET%'s feet.", + "grinds their cock between %TARGET%'s feet." + ) + user_arousal = 15 + target_arousal = 5 + +/datum/interaction/lewd/footfuck/vag + name = "Vaginal Foot Grind" + description = "Rub your vagina on their foot." + user_required_parts = list(ORGAN_SLOT_VAGINA = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_VAGINA) + cum_message_text_overrides = list( + CLIMAX_POSITION_USER = list( + "squirts all over %TARGET%'s foot.", + "orgasms on %TARGET%'s sole.", + "coats %TARGET%'s toes with their juices." + ), + CLIMAX_POSITION_TARGET = list() + ) + cum_self_text_overrides = list( + CLIMAX_POSITION_USER = list( + "you squirt all over %TARGET%'s foot.", + "you orgasm on %TARGET%'s sole.", + "you coat %TARGET%'s toes with your juices." + ), + CLIMAX_POSITION_TARGET = list() + ) + cum_partner_text_overrides = list( + CLIMAX_POSITION_USER = list( + "%USER% squirts all over your foot.", + "%USER% orgasms on your sole.", + "%USER% coats your toes with their juices." + ), + CLIMAX_POSITION_TARGET = list() + ) + message = list( + "grinds their pussy against %TARGET%'s foot.", + "rubs their clit on %TARGET%'s foot.", + "ruts on %TARGET%'s foot." + ) + sound_use = TRUE + user_pleasure = 15 + target_pleasure = 0 + user_arousal = 20 + target_arousal = 5 + +/datum/interaction/lewd/cockfuck + name = "Cockfuck" + description = "Fuck their cock." + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + target_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list( + CLIMAX_POSITION_USER = CLIMAX_PENIS, + CLIMAX_POSITION_TARGET = CLIMAX_PENIS + ) + cum_target = list( + CLIMAX_POSITION_USER = ORGAN_SLOT_PENIS, + CLIMAX_POSITION_TARGET = ORGAN_SLOT_PENIS + ) + message = list( + "pushes their cock into %TARGET%'s urethra", + "penetrates %TARGET%'s cock with their own", + "thrusts deep into %TARGET%'s cockhole", + "fucks %TARGET%'s cock from the inside" + ) + user_messages = list( + "You feel %TARGET%'s cock squeezing around yours", + "The warmth of %TARGET%'s urethra envelops your shaft", + "%TARGET%'s cock tightens around yours as you thrust deeper" + ) + target_messages = list( + "You feel %USER%'s cock stretching your urethra", + "%USER%'s shaft pushes deep inside your cock", + "The warmth of %USER%'s cock fills your shaft from within" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 6 + target_pleasure = 6 + user_arousal = 8 + target_arousal = 8 + target_pain = 4 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/grope.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/grope.dm new file mode 100644 index 0000000000000..d8cda5250d6a2 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/grope.dm @@ -0,0 +1,26 @@ +/datum/interaction/lewd/grope_ass + name = "Grope Ass" + description = "Grope their ass." + interaction_requires = list(INTERACTION_REQUIRE_SELF_HAND) + message = list( + "gropes %TARGET%'s ass", + "squeezes %TARGET%'s butt", + "fondles %TARGET%'s rear", + "grabs %TARGET%'s ass cheeks" + ) + user_messages = list( + "You feel %TARGET%'s soft ass in your hand", + "The firmness of %TARGET%'s butt feels nice in your palm", + "You squeeze %TARGET%'s plump rear" + ) + target_messages = list( + "You feel %USER%'s hand groping your ass", + "%USER%'s fingers squeeze your butt cheeks", + "The warmth of %USER%'s palm presses against your rear" + ) + sound_range = 1 + sound_use = FALSE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 3 + target_arousal = 3 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/handjob.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/handjob.dm new file mode 100644 index 0000000000000..8975f374a09de --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/handjob.dm @@ -0,0 +1,129 @@ +/datum/interaction/lewd/handjob + name = "Handjob" + description = "Jerk them off." + interaction_requires = list(INTERACTION_REQUIRE_SELF_HAND) + target_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_TARGET = CLIMAX_PENIS) + cum_target = list(CLIMAX_POSITION_TARGET = null) + additional_details = list(INTERACTION_FILLS_CONTAINERS) + message = list( + "jerks %TARGET% off", + "works %TARGET%'s shaft", + "wanks %TARGET%'s cock hard" + ) + cum_message_text_overrides = list( + CLIMAX_POSITION_TARGET = list( + "cums all over %USER%'s hand.", + "shoots their load onto %USER%'s palm.", + "covers %USER%'s fingers in cum." + ) + ) + cum_self_text_overrides = list( + CLIMAX_POSITION_TARGET = list( + "you cum all over %USER%'s hand.", + "you shoot your load onto %USER%'s palm.", + "you cover %USER%'s fingers in cum." + ) + ) + cum_partner_text_overrides = list( + CLIMAX_POSITION_TARGET = list( + "%TARGET% cums all over your hand.", + "%TARGET% shoots their load onto your palm.", + "%TARGET% covers your fingers in cum." + ) + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + user_arousal = 3 + target_pleasure = 4 + target_arousal = 6 + +/datum/interaction/lewd/handjob/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/obj/item/reagent_containers/liquid_container + + // Check active hand first + var/obj/item/cached_item = user.get_active_held_item() + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + else + // Check if pulling a container + cached_item = user.pulling + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + + // Add container text to message if needed + if(liquid_container) + var/list/original_messages = message.Copy() + var/chosen_message = pick(message) + message = list("[chosen_message] over \the [liquid_container]") + interaction_modifier_flags |= INTERACTION_OVERRIDE_FLUID_TRANSFER + . = ..() + interaction_modifier_flags &= ~INTERACTION_OVERRIDE_FLUID_TRANSFER + message = original_messages + else + . = ..() + +/datum/interaction/lewd/handjob/show_climax(mob/living/carbon/human/cumming, mob/living/carbon/human/came_in, position) + if(interaction_modifier_flags & INTERACTION_OVERRIDE_FLUID_TRANSFER) + var/obj/item/reagent_containers/liquid_container + + // Check active hand first + var/obj/item/cached_item = came_in.get_active_held_item() + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + else + // Check if pulling a container + cached_item = came_in.pulling + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + + if(liquid_container) + // Store original lists, with null checks + var/list/original_message_overrides = cum_message_text_overrides[position] + var/list/original_self_overrides = cum_self_text_overrides[position] + var/list/original_partner_overrides = cum_partner_text_overrides[position] + original_message_overrides = original_message_overrides?.Copy() + original_self_overrides = original_self_overrides?.Copy() + original_partner_overrides = original_partner_overrides?.Copy() + + // Set container-specific messages + cum_message_text_overrides[position] = list("cums into \the [liquid_container].") + cum_self_text_overrides[position] = list("you cum into \the [liquid_container].") + cum_partner_text_overrides[position] = list("%TARGET% cums into \the [liquid_container].") + + . = ..() + + // Restore original messages + cum_message_text_overrides[position] = original_message_overrides + cum_self_text_overrides[position] = original_self_overrides + cum_partner_text_overrides[position] = original_partner_overrides + return + + . = ..() + +/datum/interaction/lewd/handjob/post_climax(mob/living/carbon/human/cumming, mob/living/carbon/human/came_in, position) + if(interaction_modifier_flags & INTERACTION_OVERRIDE_FLUID_TRANSFER) + var/obj/item/reagent_containers/liquid_container + + // Check active hand first + var/obj/item/cached_item = came_in.get_active_held_item() + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + else + // Check if pulling a container + cached_item = came_in.pulling + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + + if(liquid_container) + var/obj/item/organ/external/genital/testicles/testicles = cumming.get_organ_slot(ORGAN_SLOT_TESTICLES) + if(testicles) + testicles.transfer_internal_fluid(liquid_container.reagents, testicles.internal_fluid_count) + + . = ..() diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/harmful/_extreme.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/harmful/_extreme.dm new file mode 100644 index 0000000000000..a01855b024220 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/harmful/_extreme.dm @@ -0,0 +1,8 @@ +/datum/interaction/lewd/extreme + unsafe_types = INTERACTION_EXTREME + color = "red" + category = INTERACTION_CAT_EXTREME + +/datum/interaction/lewd/extreme/harmful + unsafe_types = INTERACTION_EXTREME | INTERACTION_HARMFUL + category = INTERACTION_CAT_HARMFUL diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/harmful/earfuck.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/harmful/earfuck.dm new file mode 100644 index 0000000000000..6b1d6630b3b1c --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/harmful/earfuck.dm @@ -0,0 +1,90 @@ +/datum/interaction/lewd/extreme/harmful/earfuck + name = "Earfuck" + description = "Fuck their ear." + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_USER = list( + "cums deep into %TARGET%'s ear", + "shoots their load into %TARGET%'s ear canal", + "fills %TARGET%'s ear with their cum" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_USER = list( + "You cum deep into %TARGET%'s ear", + "You shoot your load into %TARGET%'s ear canal", + "You fill %TARGET%'s ear with your cum" + )) + cum_partner_text_overrides = list(CLIMAX_POSITION_USER = list( + "%USER% cums deep into your ear", + "%USER% shoots their load into your ear canal", + "%USER% fills your ear with their cum" + )) + message = list( + "pounds into %TARGET%'s ear.", + "shoves their cock deep into %TARGET%'s skull", + "thrusts in and out of %TARGET%'s ear.", + "goes balls deep into %TARGET%'s cranium over and over again." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ1.ogg', + 'modular_zzplurt/sound/interactions/champ2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 5 + target_pleasure = 0 + user_arousal = 8 + target_arousal = 0 + target_pain = 15 + +/datum/interaction/lewd/extreme/harmful/earfuck/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + if(prob(15)) + target.bleed(2) + if(prob(25)) + target.adjustOrganLoss(ORGAN_SLOT_EARS, rand(3,7)) + target.adjustOrganLoss(ORGAN_SLOT_BRAIN, rand(3,7)) + +/datum/interaction/lewd/extreme/harmful/earsocketfuck + name = "Earsocketfuck" + description = "Fuck their earsocket." + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_USER = list( + "cums deep into %TARGET%'s empty ear socket", + "shoots their load into %TARGET%'s skull", + "fills %TARGET%'s ear socket with their cum" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_USER = list( + "You cum deep into %TARGET%'s empty ear socket", + "You shoot your load into %TARGET%'s skull", + "You fill %TARGET%'s ear socket with your cum" + )) + cum_partner_text_overrides = list(CLIMAX_POSITION_USER = list( + "%USER% cums deep into your empty ear socket", + "%USER% shoots their load into your skull", + "%USER% fills your ear socket with their cum" + )) + message = list( + "pounds into %TARGET%'s earsocket.", + "shoves their cock deep into %TARGET%'s skull", + "thrusts in and out of %TARGET%'s earsocket.", + "goes balls deep into %TARGET%'s cranium over and over again." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ1.ogg', + 'modular_zzplurt/sound/interactions/champ2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 5 + target_pleasure = 0 + user_arousal = 8 + target_arousal = 0 + target_pain = 15 + +/datum/interaction/lewd/extreme/harmful/earsocketfuck/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + if(prob(15)) + target.bleed(2) + if(prob(25)) + target.adjustOrganLoss(ORGAN_SLOT_BRAIN, rand(3,7)) diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/harmful/eyefuck.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/harmful/eyefuck.dm new file mode 100644 index 0000000000000..0e3a0d2dad8f7 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/harmful/eyefuck.dm @@ -0,0 +1,90 @@ +/datum/interaction/lewd/extreme/harmful/eyefuck + name = "Eyefuck" + description = "Fuck their eye." + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_USER = list( + "cums deep into %TARGET%'s eye", + "shoots their load into %TARGET%'s eye socket", + "fills %TARGET%'s eye with their cum" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_USER = list( + "You cum deep into %TARGET%'s eye", + "You shoot your load into %TARGET%'s eye socket", + "You fill %TARGET%'s eye with your cum" + )) + cum_partner_text_overrides = list(CLIMAX_POSITION_USER = list( + "%USER% cums deep into your eye", + "%USER% shoots their load into your eye socket", + "%USER% fills your eye with their cum" + )) + message = list( + "pounds into %TARGET%'s eye.", + "shoves their cock deep into %TARGET%'s skull", + "thrusts in and out of %TARGET%'s eye.", + "goes balls deep into %TARGET%'s cranium over and over again." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ1.ogg', + 'modular_zzplurt/sound/interactions/champ2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 5 + target_pleasure = 0 + user_arousal = 8 + target_arousal = 0 + target_pain = 15 + +/datum/interaction/lewd/extreme/harmful/eyefuck/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + if(prob(15)) + target.bleed(2) + if(prob(25)) + target.adjustOrganLoss(ORGAN_SLOT_EYES, rand(3,7)) + target.adjustOrganLoss(ORGAN_SLOT_BRAIN, rand(3,7)) + +/datum/interaction/lewd/extreme/harmful/eyesocketfuck + name = "Eyesocketfuck" + description = "Fuck their eyesocket." + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_USER = list( + "cums deep into %TARGET%'s empty eye socket", + "shoots their load into %TARGET%'s skull", + "fills %TARGET%'s eye socket with their cum" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_USER = list( + "You cum deep into %TARGET%'s empty eye socket", + "You shoot your load into %TARGET%'s skull", + "You fill %TARGET%'s eye socket with your cum" + )) + cum_partner_text_overrides = list(CLIMAX_POSITION_USER = list( + "%USER% cums deep into your empty eye socket", + "%USER% shoots their load into your skull", + "%USER% fills your eye socket with their cum" + )) + message = list( + "pounds into %TARGET%'s eyesocket.", + "shoves their cock deep into %TARGET%'s skull", + "thrusts in and out of %TARGET%'s eyesocket.", + "goes balls deep into %TARGET%'s cranium over and over again." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ1.ogg', + 'modular_zzplurt/sound/interactions/champ2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 5 + target_pleasure = 0 + user_arousal = 8 + target_arousal = 0 + target_pain = 15 + +/datum/interaction/lewd/extreme/harmful/eyesocketfuck/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + if(prob(15)) + target.bleed(2) + if(prob(25)) + target.adjustOrganLoss(ORGAN_SLOT_BRAIN, rand(3,7)) diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/kiss.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/kiss.dm new file mode 100644 index 0000000000000..58277fef34ab8 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/kiss.dm @@ -0,0 +1,46 @@ +/datum/interaction/lewd/kiss + name = "Kiss" + description = "Kiss them deeply." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH, INTERACTION_REQUIRE_TARGET_MOUTH) + message = list( + "gives an intense, lingering kiss to %TARGET%.", + "kisses %TARGET% deeply.", + "slides their tongue into %TARGET%'s mouth.", + "presses their lips against %TARGET%'s.", + "gives %TARGET% a passionate kiss." + ) + user_messages = list( + "You feel %TARGET%'s warm lips against yours.", + "Your tongue dances with %TARGET%'s.", + "The taste of %TARGET%'s mouth lingers on your lips." + ) + target_messages = list( + "%USER%'s tongue explores your mouth.", + "You feel %USER%'s lips press against yours.", + "The warmth of %USER%'s kiss sends shivers down your spine." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/kiss1.ogg', + 'modular_zzplurt/sound/interactions/kiss2.ogg', + 'modular_zzplurt/sound/interactions/kiss3.ogg', + 'modular_zzplurt/sound/interactions/kiss4.ogg', + 'modular_zzplurt/sound/interactions/kiss5.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 2 + target_arousal = 2 + +/datum/interaction/lewd/kiss/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + + // Check if user has TRAIT_KISS_SLUT and increase their lust + if(HAS_TRAIT(user, TRAIT_KISS_SLUT)) + user.adjust_pleasure(10, target, interaction = src, position = CLIMAX_POSITION_USER) + user.adjust_arousal(10) + // Check if target has TRAIT_KISS_SLUT and increase their lust + if(HAS_TRAIT(target, TRAIT_KISS_SLUT)) + target.adjust_pleasure(10, user, interaction = src, position = CLIMAX_POSITION_TARGET) + target.adjust_arousal(10) diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/lick.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/lick.dm new file mode 100644 index 0000000000000..eb92dc12eda69 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/lick.dm @@ -0,0 +1,128 @@ +/datum/interaction/lewd/rimjob + name = "Rim" + description = "Lick their ass." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH) + target_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_TARGET = CLIMAX_BOTH) + message = list( + "licks %TARGET%'s asshole.", + "rims %TARGET% deeply.", + "buries their tongue in %TARGET%'s ass.", + "presses their tongue against %TARGET%'s pucker.", + "gives %TARGET%'s ass a passionate licking." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ_fingering.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 4 + user_arousal = 3 + target_arousal = 6 + +/datum/interaction/lewd/lickfeet + name = "Lick Feet" + description = "Lick their feet." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH, INTERACTION_REQUIRE_TARGET_FEET) + message = list( + "licks %TARGET%'s bare feet.", + "runs their tongue along %TARGET%'s soles.", + "laps at %TARGET%'s toes.", + "tastes %TARGET%'s bare feet." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ_fingering.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 3 + target_arousal = 3 + +/datum/interaction/lewd/lickfeet/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/list/original_messages = message.Copy() + var/obj/item/clothing/shoes/shoes = target.get_item_by_slot(ITEM_SLOT_FEET) + + if(shoes) + message = list( + "licks %TARGET%'s [shoes.name].", + "runs their tongue over %TARGET%'s [shoes.name].", + "drags their tongue across %TARGET%'s [shoes.name].", + "tastes %TARGET%'s [shoes.name]." + ) + . = ..() + message = original_messages + +/datum/interaction/lewd/lick_sweat + name = "Lick Sweat" + description = "Lick their sweat." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH) + message = list( + "licks the sweat off %TARGET%'s skin", + "tastes %TARGET%'s salty sweat", + "runs their tongue along %TARGET%'s sweaty body", + "savors the taste of %TARGET%'s perspiration" + ) + user_messages = list( + "You taste %TARGET%'s salty sweat", + "The tang of %TARGET%'s sweat fills your mouth", + "You savor the salty taste of %TARGET%'s skin" + ) + target_messages = list( + "You feel %USER%'s tongue licking your sweat", + "%USER%'s wet tongue slides across your sweaty skin", + "The warmth of %USER%'s mouth tingles against your damp skin" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ_fingering.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 3 + target_arousal = 3 + +/datum/interaction/lewd/lick_nuts + name = "Lick Balls" + description = "Lick their balls." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH) + target_required_parts = list(ORGAN_SLOT_TESTICLES = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_TARGET = CLIMAX_PENIS) + message = list( + "licks %TARGET%'s balls", + "sucks on %TARGET%'s testicles", + "tongues %TARGET%'s ballsack", + "worships %TARGET%'s balls with their tongue" + ) + user_messages = list( + "You feel %TARGET%'s balls against your tongue", + "The taste of %TARGET%'s sack fills your mouth", + "You lavish attention on %TARGET%'s balls" + ) + target_messages = list( + "%USER%'s tongue works over your balls", + "You feel %USER%'s hot mouth on your sack", + "The warmth of %USER%'s tongue makes your balls tingle" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bj1.ogg', + 'modular_zzplurt/sound/interactions/bj2.ogg', + 'modular_zzplurt/sound/interactions/bj3.ogg', + 'modular_zzplurt/sound/interactions/bj4.ogg', + 'modular_zzplurt/sound/interactions/bj5.ogg', + 'modular_zzplurt/sound/interactions/bj6.ogg', + 'modular_zzplurt/sound/interactions/bj7.ogg', + 'modular_zzplurt/sound/interactions/bj8.ogg', + 'modular_zzplurt/sound/interactions/bj9.ogg', + 'modular_zzplurt/sound/interactions/bj10.ogg', + 'modular_zzplurt/sound/interactions/bj11.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 3 + user_arousal = 3 + target_arousal = 5 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/misc.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/misc.dm new file mode 100644 index 0000000000000..717a6c356bd27 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/misc.dm @@ -0,0 +1,19 @@ +/datum/interaction/lewd/clothesplosion + name = "Clothesplosion" + description = "Explode out of your clothes." + usage = INTERACTION_SELF + message = list( + "bursts out of their clothes!", + "explodes out of their outfit!", + "dramatically tears free of their garments!" + ) + sound_range = 1 + sound_use = FALSE + user_pleasure = 0 + user_arousal = 0 + +/datum/interaction/lewd/clothesplosion/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + if(!istype(user)) + return + user.clothing_burst(FALSE) diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/mount.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/mount.dm new file mode 100644 index 0000000000000..c1a6f28602dc0 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/mount.dm @@ -0,0 +1,73 @@ +/datum/interaction/lewd/mount_vagina + name = "Mount (Vagina)" + description = "Mount them with your pussy." + user_required_parts = list(ORGAN_SLOT_VAGINA = REQUIRE_GENITAL_EXPOSED) + target_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_VAGINA, CLIMAX_POSITION_TARGET = CLIMAX_PENIS) + cum_target = list(CLIMAX_POSITION_USER = ORGAN_SLOT_PENIS, CLIMAX_POSITION_TARGET = ORGAN_SLOT_VAGINA) + message = list( + "rides %TARGET%'s cock.", + "forces %TARGET%'s cock into their pussy.", + "slides their pussy onto %TARGET%'s cock.", + "impales themself on %TARGET%'s cock." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 7 + target_pleasure = 7 + user_arousal = 10 + target_arousal = 10 + +/datum/interaction/lewd/mount_anus + name = "Mount (Anus)" + description = "Mount them with your ass." + user_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_EXPOSED) + target_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_BOTH, CLIMAX_POSITION_TARGET = CLIMAX_PENIS) + cum_target = list(CLIMAX_POSITION_USER = null, CLIMAX_POSITION_TARGET = ORGAN_SLOT_ANUS) + message = list( + "rides %TARGET%'s cock with their ass.", + "forces %TARGET%'s cock into their ass.", + "slides their ass onto %TARGET%'s cock.", + "impales their ass on %TARGET%'s cock." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 4 + target_pleasure = 7 + user_arousal = 8 + target_arousal = 10 + user_pain = 3 + +/datum/interaction/lewd/mount_face + name = "Mount Face" + description = "Sit on their face." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_MOUTH) + user_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_EXPOSED) + message = list( + "grinds their ass into %TARGET%'s face.", + "shoves their ass into %TARGET%'s face.", + "plants their ass right on %TARGET%'s face.", + "grabs the back of %TARGET%'s head and forces it into their asscheeks." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/squelch1.ogg', + 'modular_zzplurt/sound/interactions/squelch2.ogg', + 'modular_zzplurt/sound/interactions/squelch3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 3 + target_pleasure = 0 + user_arousal = 5 + target_arousal = 3 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/nipples.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/nipples.dm new file mode 100644 index 0000000000000..c84ea062f150e --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/nipples.dm @@ -0,0 +1,52 @@ +/datum/interaction/lewd/nipplefuck + name = "Nipplefuck" + description = "Fuck their nipple." + interaction_requires = list( + INTERACTION_REQUIRE_TARGET_TOPLESS + ) + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + target_required_parts = list(ORGAN_SLOT_BREASTS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_USER = list( + "cums all over %TARGET%'s nipple", + "shoots their load into %TARGET%'s breast", + "fills %TARGET%'s nipple with cum" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_USER = list( + "You cum all over %TARGET%'s nipple", + "You shoot your load into %TARGET%'s breast", + "You fill %TARGET%'s nipple with cum" + )) + cum_partner_text_overrides = list(CLIMAX_POSITION_USER = list( + "%USER% cums all over your nipple", + "%USER% shoots their load into your breast", + "%USER% fills your nipple with cum" + )) + message = list( + "fucks %TARGET%'s nipple", + "slides their cock into %TARGET%'s breast", + "pounds %TARGET%'s nipple", + "thrusts deep into %TARGET%'s nipple" + ) + user_messages = list( + "You feel %TARGET%'s nipple squeezing your cock", + "The warmth of %TARGET%'s breast envelops your shaft", + "%TARGET%'s nipple feels amazing around your cock" + ) + target_messages = list( + "You feel %USER%'s cock stretching your nipple", + "%USER%'s shaft pushes deep into your breast", + "The warmth of %USER%'s cock fills your nipple" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 4 + target_pleasure = 2 + user_arousal = 6 + target_arousal = 4 + target_pain = 2 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/nipsuck.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/nipsuck.dm new file mode 100644 index 0000000000000..688d0c8eba783 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/nipsuck.dm @@ -0,0 +1,100 @@ +/datum/interaction/lewd/nipsuck + name = "Suck Nipples" + description = "Suck their nipples." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH) + target_required_parts = list(ORGAN_SLOT_BREASTS = REQUIRE_GENITAL_EXPOSED) + message = list( + "gently sucks on %TARGET%'s nipple.", + "gently nibs %TARGET%'s nipple.", + "licks %TARGET%'s nipple." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/oral1.ogg', + 'modular_zzplurt/sound/interactions/oral2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + user_arousal = 2 + target_pleasure = 3 + target_arousal = 5 + +/datum/interaction/lewd/nipsuck/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/list/original_messages = message.Copy() + + // Handle different intents + switch(resolve_intent_name(user.combat_mode)) + if("harm") + message = list( + "bites %TARGET%'s nipple.", + "aggressively sucks %TARGET%'s nipple." + ) + target_pleasure = 4 // Aggressive sucking has higher rewards + target_arousal = 5 + if("disarm") + message = list( + "playfully nibbles %TARGET%'s nipple.", + "teasingly sucks %TARGET%'s nipple.", + "gently bites %TARGET%'s nipple." + ) + if("grab") + message = list( + "sucks %TARGET%'s nipple intently.", + "feasts on %TARGET%'s nipple.", + "glomps %TARGET%'s nipple." + ) + target_pleasure = 4 // Intent sucking has higher rewards + target_arousal = 5 + . = ..() + message = original_messages + +/datum/interaction/lewd/nipsuck/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + var/obj/item/organ/external/genital/breasts/breasts = target.get_organ_slot(ORGAN_SLOT_BREASTS) + if(breasts?.internal_fluid_datum) + // Calculate milk amount based on how full the breasts are (0.5 to 2 multiplier) + var/milk_multiplier = 0.5 + if(breasts.internal_fluid_maximum > 0) + milk_multiplier = 0.5 + (1.5 * (breasts.internal_fluid_count / breasts.internal_fluid_maximum)) + + var/transfer_amount = rand(1, 2 * milk_multiplier) + var/intent = resolve_intent_name(user.combat_mode) + if(intent == "harm" || intent == "grab") + transfer_amount = rand(1, 3 * milk_multiplier) // More aggressive sucking gets more milk + + var/datum/reagents/R = new(breasts.internal_fluid_maximum) + breasts.transfer_internal_fluid(R, transfer_amount) + R.trans_to(user, R.total_volume) + qdel(R) + + if(!user.combat_mode && prob(5 + target.arousal)) + var/list/arousal_messages + switch(resolve_intent_name(user.combat_mode)) + if("help") + arousal_messages = list( + "%TARGET% shivers in arousal.", + "%TARGET% moans quietly.", + "%TARGET% breathes out a soft moan.", + "%TARGET% gasps.", + "%TARGET% shudders softly.", + "%TARGET% trembles as their chest gets molested." + ) + if("disarm") + arousal_messages = list( + "%TARGET% playfully squirms.", + "%TARGET% wiggles teasingly.", + "%TARGET% lets out a playful moan.", + "%TARGET% bites their lip.", + "%TARGET% squirms from the teasing." + ) + if("grab") + arousal_messages = list( + "%TARGET% moans eagerly.", + "%TARGET% presses their chest forward.", + "%TARGET% lets out a wanting groan.", + "%TARGET% quivers with excitement.", + "%TARGET% shivers with anticipation." + ) + + if(arousal_messages) + message = list(pick(arousal_messages)) diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/nuts.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/nuts.dm new file mode 100644 index 0000000000000..801e23ce255ff --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/nuts.dm @@ -0,0 +1,54 @@ +/datum/interaction/lewd/nuts + name = "Nuts to Face" + description = "Put your balls in their face." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_MOUTH) + user_required_parts = list(ORGAN_SLOT_TESTICLES = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_target = list(CLIMAX_POSITION_USER = CLIMAX_TARGET_MOUTH) + message = list( + "grabs the back of %TARGET%'s head and pulls it into their crotch.", + "jams their nutsack right into %TARGET%'s face.", + "roughly grinds their fat nutsack into %TARGET%'s mouth.", + "pulls out their saliva-covered nuts from %TARGET%'s violated mouth and then wipes off the slime onto their face.", + "wedges a digit into the side of %TARGET%'s jaw and pries it open before using their other hand to shove their whole nutsack inside!", + "stands with their groin inches away from %TARGET%'s face, then thrusting their hips forward and smothering %TARGET%'s whole face with their heavy ballsack." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/oral1.ogg', + 'modular_zzplurt/sound/interactions/oral2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 3 + target_pleasure = 0 + user_arousal = 5 + target_arousal = 2 + +/datum/interaction/lewd/nut_smack + name = "Smack Nuts" + description = "Smack their nuts." + interaction_requires = list(INTERACTION_REQUIRE_SELF_HAND) + target_required_parts = list(ORGAN_SLOT_TESTICLES = REQUIRE_GENITAL_EXPOSED) + message = list( + "smacks %TARGET%'s nuts!", + "slaps %TARGET%'s balls!", + "gives %TARGET%'s testicles a slap!", + "whacks %TARGET% right in the nuts!" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/slap.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = -10 + target_pain = 15 + user_arousal = 2 + target_arousal = 0 + +/datum/interaction/lewd/nut_smack/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/original_pleasure = target_pleasure + if(HAS_TRAIT(target, TRAIT_MASOCHISM)) + target_pleasure = abs(original_pleasure) * 1.5 // Masochists get 50% more pleasure from the pain + . = ..() + target_pleasure = original_pleasure diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/oral.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/oral.dm new file mode 100644 index 0000000000000..85f633f6a6b5e --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/oral.dm @@ -0,0 +1,96 @@ +/datum/interaction/lewd/oral_vagina + name = "Perform Oral" + description = "Go down on them." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH) + target_required_parts = list(ORGAN_SLOT_VAGINA = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_TARGET = CLIMAX_VAGINA) + cum_target = list(CLIMAX_POSITION_TARGET = CLIMAX_TARGET_MOUTH) + message = list( + "buries their face in %TARGET%'s pussy.", + "nuzzles %TARGET%'s wet sex.", + "finds their face caught between %TARGET%'s thighs.", + "kneels down between %TARGET%'s legs.", + "grips %TARGET%'s legs, pushing them apart.", + "sinks their face in between %TARGET%'s thighs." + ) + user_messages = list( + "You feel %TARGET%'s warm wetness against your face.", + "The scent of %TARGET%'s arousal fills your senses.", + "You press your tongue deeper into %TARGET%'s folds." + ) + target_messages = list( + "%USER%'s tongue explores your pussy.", + "You feel %USER%'s hot breath against your sex.", + "The warmth of %USER%'s mouth sends shivers up your spine." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bj1.ogg', + 'modular_zzplurt/sound/interactions/bj2.ogg', + 'modular_zzplurt/sound/interactions/bj3.ogg', + 'modular_zzplurt/sound/interactions/bj4.ogg', + 'modular_zzplurt/sound/interactions/bj5.ogg', + 'modular_zzplurt/sound/interactions/bj6.ogg', + 'modular_zzplurt/sound/interactions/bj7.ogg', + 'modular_zzplurt/sound/interactions/bj8.ogg', + 'modular_zzplurt/sound/interactions/bj9.ogg', + 'modular_zzplurt/sound/interactions/bj10.ogg', + 'modular_zzplurt/sound/interactions/bj11.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 5 + user_arousal = 3 + target_arousal = 7 + +/datum/interaction/lewd/oral_penis + name = "Suck Cock" + description = "Suck them off." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH) + target_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_TARGET = CLIMAX_PENIS) + cum_target = list(CLIMAX_POSITION_TARGET = CLIMAX_TARGET_MOUTH) + message = list( + "takes %TARGET%'s cock into their mouth.", + "wraps their lips around %TARGET%'s cock.", + "finds their face between %TARGET%'s thighs.", + "kneels down between %TARGET%'s legs.", + "grips %TARGET%'s legs, kissing at the tip of their cock.", + "goes down on %TARGET%." + ) + user_messages = list( + "You feel %TARGET%'s cock throb in your mouth.", + "The taste of %TARGET%'s precum lingers on your tongue.", + "You take %TARGET% deeper into your throat." + ) + target_messages = list( + "%USER%'s tongue swirls around your cock.", + "You feel %USER%'s hot mouth envelope you.", + "The warmth of %USER%'s throat makes you twitch." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bj1.ogg', + 'modular_zzplurt/sound/interactions/bj2.ogg', + 'modular_zzplurt/sound/interactions/bj3.ogg', + 'modular_zzplurt/sound/interactions/bj4.ogg', + 'modular_zzplurt/sound/interactions/bj5.ogg', + 'modular_zzplurt/sound/interactions/bj6.ogg', + 'modular_zzplurt/sound/interactions/bj7.ogg', + 'modular_zzplurt/sound/interactions/bj8.ogg', + 'modular_zzplurt/sound/interactions/bj9.ogg', + 'modular_zzplurt/sound/interactions/bj10.ogg', + 'modular_zzplurt/sound/interactions/bj11.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 5 + user_arousal = 3 + target_arousal = 7 + +/datum/interaction/lewd/oral_penis/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + if(prob((target.dna.features["sexual_potency"] * 10) + 15)) + user.adjustOxyLoss(3) + target.adjust_arousal(10) + target.adjust_pleasure(10, user, interaction = src, position = CLIMAX_POSITION_TARGET) diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/self/breasts.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/self/breasts.dm new file mode 100644 index 0000000000000..24500f85f3221 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/self/breasts.dm @@ -0,0 +1,128 @@ +/datum/interaction/lewd/titgrope_self + name = "Grope Breasts (self)" + description = "Grope your own breasts." + interaction_requires = list(INTERACTION_REQUIRE_SELF_HAND) + user_required_parts = list(ORGAN_SLOT_BREASTS = REQUIRE_GENITAL_ANY) + usage = INTERACTION_SELF + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_BOTH) + additional_details = list(INTERACTION_FILLS_CONTAINERS) + message = list( + "gently gropes their breast", + "softly squeezes their breasts", + "grips their breasts", + "runs a few fingers over their breast", + "delicately teases their nipple", + "traces a touch across their breast" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/squelch1.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 2 + user_arousal = 3 + +/datum/interaction/lewd/titgrope_self/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + if(prob(5 + user.arousal)) + user.visible_message(span_lewd("\The [user] [pick( + "shivers in arousal.", + "moans quietly.", + "breathes out a soft moan.", + "gasps.", + "shudders softly.", + "trembles as their hands run across bare skin.")]")) + + var/obj/item/reagent_containers/liquid_container + var/obj/item/cached_item = user.get_active_held_item() + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + else + cached_item = user.pulling + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + + if(liquid_container) + var/obj/item/organ/external/genital/breasts/breasts = user.get_organ_slot(ORGAN_SLOT_BREASTS) + if(breasts?.internal_fluid_datum) + // Calculate milk amount based on how full the breasts are (0.5 to 2 multiplier) + var/milk_multiplier = 0.5 + if(breasts.internal_fluid_maximum > 0) + milk_multiplier = 0.5 + (1.5 * (breasts.internal_fluid_count / breasts.internal_fluid_maximum)) + + var/transfer_amount = rand(1, 3 * milk_multiplier) + var/datum/reagents/R = new(breasts.internal_fluid_maximum) + breasts.transfer_internal_fluid(R, transfer_amount) + R.trans_to(liquid_container, R.total_volume) + qdel(R) + +/datum/interaction/lewd/self_nipsuck + name = "Suck Nipples (self)" + description = "Suck your own nipples." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH) + user_required_parts = list(ORGAN_SLOT_BREASTS = REQUIRE_GENITAL_EXPOSED) + usage = INTERACTION_SELF + message = list( + "brings their own milk tanks to their mouth and sucks deeply into them", + "takes a big sip of their own fresh milk", + "fills their own mouth with a big gulp of their warm milk" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/oral1.ogg', + 'modular_zzplurt/sound/interactions/oral2.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 3 + user_arousal = 5 + +/datum/interaction/lewd/self_nipsuck/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + var/obj/item/organ/external/genital/breasts/breasts = user.get_organ_slot(ORGAN_SLOT_BREASTS) + if(breasts?.internal_fluid_datum) + // Calculate milk amount based on how full the breasts are (0.5 to 2 multiplier) + var/milk_multiplier = 0.5 + if(breasts.internal_fluid_maximum > 0) + milk_multiplier = 0.5 + (1.5 * (breasts.internal_fluid_count / breasts.internal_fluid_maximum)) + + var/transfer_amount = rand(1, 3 * milk_multiplier) + var/datum/reagents/R = new(breasts.internal_fluid_maximum) + breasts.transfer_internal_fluid(R, transfer_amount) + R.trans_to(user, R.total_volume) + qdel(R) + +/datum/interaction/lewd/breastfuck_self + name = "Breastfuck (self)" + description = "Fuck your own breasts." + interaction_requires = list(INTERACTION_REQUIRE_SELF_HAND) + user_required_parts = list( + ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED, + ORGAN_SLOT_BREASTS = REQUIRE_GENITAL_EXPOSED + ) + usage = INTERACTION_SELF + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_USER = list( + "cums all over their own breasts", + "shoots their load onto their tits", + "covers their breasts in cum" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_USER = list( + "You cum all over your own breasts", + "You shoot your load onto your tits", + "You cover your breasts in cum" + )) + message = list( + "fucks their own breasts", + "slides their cock between their breasts", + "thrusts between their tits", + "pleasures themself with their breasts" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 4 + user_arousal = 6 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/self/finger.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/self/finger.dm new file mode 100644 index 0000000000000..f8b5ff5777151 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/self/finger.dm @@ -0,0 +1,92 @@ +/datum/interaction/lewd/finger_self_vagina + name = "Finger Pussy (self)" + description = "Finger your own pussy." + interaction_requires = list(INTERACTION_REQUIRE_SELF_HAND) + user_required_parts = list(ORGAN_SLOT_VAGINA = REQUIRE_GENITAL_EXPOSED) + usage = INTERACTION_SELF + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_VAGINA) + cum_message_text_overrides = list(CLIMAX_POSITION_USER = list( + "cums hard on their fingers", + "shudders as they cum on their hand", + "fingers themself to climax" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_USER = list( + "You cum hard on your fingers", + "You shudder as you cum on your hand", + "You finger yourself to climax" + )) + additional_details = list(INTERACTION_FILLS_CONTAINERS) + message = list( + "fingers their pussy deep", + "fingers their pussy", + "plays with their pussy", + "fingers their own pussy hard" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ_fingering.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 4 + user_arousal = 6 + +/datum/interaction/lewd/finger_self_vagina/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/obj/item/reagent_containers/liquid_container + + var/obj/item/cached_item = user.get_active_held_item() + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + else + cached_item = user.pulling + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + + if(liquid_container) + var/list/original_messages = message.Copy() + var/chosen_message = pick(message) + message = list("[chosen_message] over \the [liquid_container]") + interaction_modifier_flags |= INTERACTION_OVERRIDE_FLUID_TRANSFER + . = ..() + interaction_modifier_flags &= ~INTERACTION_OVERRIDE_FLUID_TRANSFER + message = original_messages + else + . = ..() + +/datum/interaction/lewd/finger_self_vagina/post_climax(mob/living/carbon/human/user, mob/living/carbon/human/target, position) + var/obj/item/reagent_containers/liquid_container + var/obj/item/cached_item = user.get_active_held_item() + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + else + cached_item = user.pulling + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + + if(liquid_container) + var/obj/item/organ/external/genital/vagina/vagina = user.get_organ_slot(ORGAN_SLOT_VAGINA) + if(vagina?.internal_fluid_datum) + var/datum/reagents/R = new(vagina.internal_fluid_maximum) + vagina.transfer_internal_fluid(R, vagina.internal_fluid_count) + R.trans_to(liquid_container, R.total_volume) + qdel(R) + . = ..() + +/datum/interaction/lewd/finger_self_anus + name = "Finger Ass (self)" + description = "Finger your own ass." + interaction_requires = list(INTERACTION_REQUIRE_SELF_HAND) + user_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_EXPOSED) + usage = INTERACTION_SELF + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_BOTH) + message = list( + "fingers themself", + "fingers their asshole", + "fingers themself hard" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/champ_fingering.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 3 + user_arousal = 5 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/self/jack.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/self/jack.dm new file mode 100644 index 0000000000000..fa251a5f69d58 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/self/jack.dm @@ -0,0 +1,74 @@ +/datum/interaction/lewd/jack_self + name = "Jack Off (self)" + description = "Jerk yourself off." + interaction_requires = list(INTERACTION_REQUIRE_SELF_HAND) + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + usage = INTERACTION_SELF + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_USER = list( + "cums hard on their hand", + "shoots their load onto their fingers", + "ejaculates onto their palm" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_USER = list( + "You cum hard on your hand", + "You shoot your load onto your fingers", + "You ejaculate onto your palm" + )) + additional_details = list(INTERACTION_FILLS_CONTAINERS) + message = list( + "jerks themself off", + "works their shaft", + "strokes their cock", + "wanks their cock hard" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 4 + user_arousal = 6 + +/datum/interaction/lewd/jack_self/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/obj/item/reagent_containers/liquid_container + + var/obj/item/cached_item = user.get_active_held_item() + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + else + cached_item = user.pulling + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + + if(liquid_container) + var/list/original_messages = message.Copy() + var/chosen_message = pick(message) + message = list("[chosen_message] over \the [liquid_container]") + interaction_modifier_flags |= INTERACTION_OVERRIDE_FLUID_TRANSFER + . = ..() + interaction_modifier_flags &= ~INTERACTION_OVERRIDE_FLUID_TRANSFER + message = original_messages + else + . = ..() + +/datum/interaction/lewd/jack_self/post_climax(mob/living/carbon/human/user, mob/living/carbon/human/target, position) + var/obj/item/reagent_containers/liquid_container + var/obj/item/cached_item = user.get_active_held_item() + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + else + cached_item = user.pulling + if(istype(cached_item, /obj/item/reagent_containers)) + liquid_container = cached_item + + if(liquid_container) + var/obj/item/organ/external/genital/penis/penis = user.get_organ_slot(ORGAN_SLOT_PENIS) + if(penis?.internal_fluid_datum) + var/datum/reagents/R = new(penis.internal_fluid_maximum) + penis.transfer_internal_fluid(R, penis.internal_fluid_count) + R.trans_to(liquid_container, R.total_volume) + qdel(R) + . = ..() diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/self/oral.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/self/oral.dm new file mode 100644 index 0000000000000..2334b8b588212 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/self/oral.dm @@ -0,0 +1,63 @@ +/datum/interaction/lewd/oral_vagina_self + name = "Lick pussy (Self)" + description = "Lick your own pussy." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH) + user_required_parts = list(ORGAN_SLOT_VAGINA = REQUIRE_GENITAL_EXPOSED) + usage = INTERACTION_SELF + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_VAGINA) + cum_target = list(CLIMAX_POSITION_USER = CLIMAX_TARGET_MOUTH) + message = list( + "licks their own pussy.", + "pleasures themself with their tongue.", + "performs oral on themself.", + "licks their own clit eagerly." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bj1.ogg', + 'modular_zzplurt/sound/interactions/bj2.ogg', + 'modular_zzplurt/sound/interactions/bj3.ogg', + 'modular_zzplurt/sound/interactions/bj4.ogg', + 'modular_zzplurt/sound/interactions/bj5.ogg', + 'modular_zzplurt/sound/interactions/bj6.ogg', + 'modular_zzplurt/sound/interactions/bj7.ogg', + 'modular_zzplurt/sound/interactions/bj8.ogg', + 'modular_zzplurt/sound/interactions/bj9.ogg', + 'modular_zzplurt/sound/interactions/bj10.ogg', + 'modular_zzplurt/sound/interactions/bj11.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 5 + user_arousal = 7 + +/datum/interaction/lewd/oral_self + name = "Selfsuck" + description = "Suck yourself off." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH) + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + usage = INTERACTION_SELF + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_target = list(CLIMAX_POSITION_USER = CLIMAX_TARGET_MOUTH) + message = list( + "wraps their lips around their own cock.", + "pleasures themself with their mouth.", + "sucks their own cock eagerly.", + "gives themself head." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bj1.ogg', + 'modular_zzplurt/sound/interactions/bj2.ogg', + 'modular_zzplurt/sound/interactions/bj3.ogg', + 'modular_zzplurt/sound/interactions/bj4.ogg', + 'modular_zzplurt/sound/interactions/bj5.ogg', + 'modular_zzplurt/sound/interactions/bj6.ogg', + 'modular_zzplurt/sound/interactions/bj7.ogg', + 'modular_zzplurt/sound/interactions/bj8.ogg', + 'modular_zzplurt/sound/interactions/bj9.ogg', + 'modular_zzplurt/sound/interactions/bj10.ogg', + 'modular_zzplurt/sound/interactions/bj11.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 5 + user_arousal = 7 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/slap.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/slap.dm new file mode 100644 index 0000000000000..5b77abe03558e --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/slap.dm @@ -0,0 +1,28 @@ +/datum/interaction/lewd/slap + name = "Slap Ass" + description = "Slap their ass." + interaction_requires = list(INTERACTION_REQUIRE_SELF_HAND) + target_required_parts = list(ORGAN_SLOT_BUTT = REQUIRE_GENITAL_EXPOSED) + message = list( + "slaps %TARGET% right on the ass!", + "spanks %TARGET%'s ass!", + "gives %TARGET%'s behind a good smack!", + "lands a stinging slap on %TARGET%'s butt!" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/slap.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 0 + target_pain = 10 + user_arousal = 2 + target_arousal = 0 + +/datum/interaction/lewd/slap/act(mob/living/carbon/human/user, mob/living/carbon/human/target) + var/original_pleasure = target_pleasure + if(HAS_TRAIT(target, TRAIT_MASOCHISM)) + target_pleasure = 2 + . = ..() + target_pleasure = original_pleasure diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/thighs.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/thighs.dm new file mode 100644 index 0000000000000..4027e902bb843 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/thighs.dm @@ -0,0 +1,147 @@ +/datum/interaction/lewd/thighs_penis + name = "Thigh Smother (Penis)" + description = "Smother them with your penis." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_MOUTH) + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_target = list(CLIMAX_POSITION_USER = CLIMAX_TARGET_MOUTH) + message = list( + "presses their weight down onto %TARGET%'s face, blocking their vision completely.", + "forces their cock into %TARGET%'s face as they're stuck locked between their thighs.", + "slips their cock into %TARGET%'s helpless mouth, keeping their shaft pressed hard into their face." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bj10.ogg', + 'modular_zzplurt/sound/interactions/bj3.ogg', + 'modular_zzplurt/sound/interactions/foot_wet1.ogg', + 'modular_zzplurt/sound/interactions/foot_dry3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 4 + target_pleasure = 0 + user_arousal = 6 + target_arousal = 2 + +/datum/interaction/lewd/thighs_vagina + name = "Thigh Smother (Vagina)" + description = "Smother them with your pussy." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_MOUTH) + user_required_parts = list(ORGAN_SLOT_VAGINA = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_VAGINA) + cum_target = list(CLIMAX_POSITION_USER = CLIMAX_TARGET_MOUTH) + message = list( + "presses their weight down onto %TARGET%'s face, blocking their vision completely.", + "rides %TARGET%'s face, grinding their wet pussy all over it.", + "grinds their pussy into %TARGET%'s face." + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bj10.ogg', + 'modular_zzplurt/sound/interactions/bj3.ogg', + 'modular_zzplurt/sound/interactions/foot_wet1.ogg', + 'modular_zzplurt/sound/interactions/foot_dry3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 4 + target_pleasure = 0 + user_arousal = 6 + target_arousal = 2 + +/datum/interaction/lewd/thighfuck + name = "Thighfuck" + description = "Fuck their thighs." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_BOTTOMLESS) + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_USER = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_USER = list( + "cums all over %TARGET%'s thighs", + "shoots their load onto %TARGET%'s legs", + "covers %TARGET%'s thighs in cum" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_USER = list( + "You cum all over %TARGET%'s thighs", + "You shoot your load onto %TARGET%'s legs", + "You cover %TARGET%'s thighs in cum" + )) + cum_partner_text_overrides = list(CLIMAX_POSITION_USER = list( + "%USER% cums all over your thighs", + "%USER% shoots their load onto your legs", + "%USER% covers your thighs in cum" + )) + message = list( + "fucks %TARGET%'s thighs", + "slides their cock between %TARGET%'s legs", + "thrusts between %TARGET%'s thighs", + "pounds against %TARGET%'s legs" + ) + user_messages = list( + "You feel %TARGET%'s thighs squeezing your cock", + "The warmth between %TARGET%'s legs feels amazing", + "%TARGET%'s soft thighs feel great around your shaft" + ) + target_messages = list( + "You feel %USER%'s cock sliding between your thighs", + "%USER%'s shaft rubs between your legs", + "The warmth of %USER%'s cock presses against your thighs" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 4 + target_pleasure = 0 + user_arousal = 6 + target_arousal = 4 + +/datum/interaction/lewd/thighjob + name = "Give Thighjob" + description = "Pleasure them with your thighs." + interaction_requires = list(INTERACTION_REQUIRE_SELF_BOTTOMLESS) + target_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list(CLIMAX_POSITION_TARGET = CLIMAX_PENIS) + cum_message_text_overrides = list(CLIMAX_POSITION_TARGET = list( + "cums all over %USER%'s thighs", + "shoots their load onto %USER%'s legs", + "covers %USER%'s thighs in cum" + )) + cum_self_text_overrides = list(CLIMAX_POSITION_TARGET = list( + "%TARGET% cums all over your thighs", + "%TARGET% shoots their load onto your legs", + "%TARGET% covers your thighs in cum" + )) + cum_partner_text_overrides = list(CLIMAX_POSITION_TARGET = list( + "You cum all over %USER%'s thighs", + "You shoot your load onto %USER%'s legs", + "You cover %USER%'s thighs in cum" + )) + message = list( + "squeezes %TARGET%'s cock between their thighs", + "works %TARGET%'s shaft with their legs", + "pleasures %TARGET% with their thighs", + "rubs %TARGET%'s cock between their legs" + ) + user_messages = list( + "You feel %TARGET%'s cock throbbing between your thighs", + "The warmth of %TARGET%'s shaft feels nice between your legs", + "You squeeze %TARGET%'s cock with your thighs" + ) + target_messages = list( + "%USER%'s warm thighs squeeze your cock", + "Your shaft slides between %USER%'s legs", + "The softness of %USER%'s thighs feels amazing" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 4 + user_arousal = 4 + target_arousal = 6 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/unholy/_unholy.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/unholy/_unholy.dm new file mode 100644 index 0000000000000..57edf6214a4c7 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/unholy/_unholy.dm @@ -0,0 +1,4 @@ +/datum/interaction/lewd/unholy + unsafe_types = INTERACTION_UNHOLY + color = "brown" + category = INTERACTION_CAT_UNHOLY diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/unholy/fart.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/unholy/fart.dm new file mode 100644 index 0000000000000..733d447c72b8f --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/unholy/fart.dm @@ -0,0 +1,151 @@ +/datum/interaction/lewd/unholy/facefart + name = "Face Fart" + description = "Fart on their face." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_MOUTH) + user_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_ANY) + message = list( + "presses their ass against %TARGET%'s face and lets out a fart", + "releases gas right into %TARGET%'s face", + "lets loose a nasty fart on %TARGET%'s face", + "forces %TARGET% to smell their flatulence" + ) + user_messages = list( + "You feel relief as you release gas in %TARGET%'s face", + "You let out a fart right on %TARGET%'s face", + "You make %TARGET% smell your gas" + ) + target_messages = list( + "%USER% releases their gas right in your face", + "You're forced to smell %USER%'s fart", + "%USER%'s flatulence fills your nostrils" + ) + sound_possible = list() + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 2 + target_arousal = 2 + +/datum/interaction/lewd/unholy/facefart/New() + sound_possible = GLOB.brap_noises // GLOB.brap_noises: expected a constant expression + . = ..() + +/datum/interaction/lewd/unholy/crotchfart + name = "Crotch Fart" + description = "Fart on their crotch." + user_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_ANY) + message = list( + "presses their ass against %TARGET%'s crotch and lets out a fart", + "releases gas all over %TARGET%'s groin", + "lets loose a nasty fart on %TARGET%'s genitals", + "forces %TARGET% to feel their flatulence" + ) + user_messages = list( + "You feel relief as you release gas on %TARGET%'s crotch", + "You let out a fart right on %TARGET%'s groin", + "You make %TARGET% feel your gas" + ) + target_messages = list( + "%USER% releases their gas right on your crotch", + "You feel %USER%'s fart on your groin", + "%USER%'s flatulence washes over your genitals" + ) + sound_possible = list() + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 2 + user_arousal = 3 + target_arousal = 4 + +/datum/interaction/lewd/unholy/crotchfart/New() + sound_possible = GLOB.brap_noises // GLOB.brap_noises: expected a constant expression + . = ..() + +/datum/interaction/lewd/unholy/fartfuck + name = "Fart Fuck" + description = "Fuck their ass + fart." + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + target_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list( + CLIMAX_POSITION_USER = CLIMAX_PENIS, + CLIMAX_POSITION_TARGET = CLIMAX_BOTH + ) + cum_target = list( + CLIMAX_POSITION_USER = ORGAN_SLOT_ANUS + ) + message = list( + "pounds %TARGET%'s ass while letting out farts", + "releases gas while fucking %TARGET%'s hole", + "farts with each thrust into %TARGET%'s ass", + "fills %TARGET% with their flatulence while fucking them" + ) + user_messages = list( + "You feel relief as you release gas while fucking %TARGET%", + "You let out farts as you pound %TARGET%'s ass", + "You make %TARGET% feel your gas inside them" + ) + target_messages = list( + "%USER% releases their gas inside your ass", + "You feel %USER%'s farts filling you up", + "%USER%'s flatulence mingles with their thrusts" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_possible = list() + sound_range = 1 + sound_use = TRUE + user_pleasure = 6 + target_pleasure = 4 + user_arousal = 8 + target_arousal = 6 + +/datum/interaction/lewd/unholy/fartfuck/New() + sound_possible = GLOB.brap_noises // GLOB.brap_noises: expected a constant expression + . = ..() + +/datum/interaction/lewd/unholy/fartfuck/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + conditional_pref_sound(user, pick('modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg'), 80, TRUE, falloff_distance = sound_range, pref_to_check = /datum/preference/toggle/erp/sounds) + +/datum/interaction/lewd/unholy/suck_fart + name = "Suck Fart" + description = "Suck the farts out of their asshole." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH) + target_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list( + CLIMAX_POSITION_TARGET = CLIMAX_BOTH + ) + message = list( + "presses their face between %TARGET%'s asscheeks and inhales their farts", + "sucks the gas right out of %TARGET%'s ass", + "breathes in %TARGET%'s flatulence deeply", + "puts their mouth on %TARGET%'s hole to taste their gas" + ) + user_messages = list( + "You inhale %TARGET%'s farts directly from the source", + "You suck the gas from %TARGET%'s asshole", + "You taste %TARGET%'s flatulence on your tongue" + ) + target_messages = list( + "%USER% sucks your farts right out of your ass", + "You feel %USER%'s mouth pulling gas from your hole", + "%USER% inhales your flatulence eagerly" + ) + sound_possible = list() + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 3 + user_arousal = 4 + target_arousal = 5 + +/datum/interaction/lewd/unholy/suck_fart/New() + sound_possible = GLOB.brap_noises // GLOB.brap_noises: expected a constant expression + . = ..() diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/unholy/piss.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/unholy/piss.dm new file mode 100644 index 0000000000000..f536e6a2d0444 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/unholy/piss.dm @@ -0,0 +1,56 @@ +/datum/interaction/lewd/unholy/piss_over + name = "Piss Over" + description = "Piss all over them." + interaction_requires = list(INTERACTION_REQUIRE_SELF_BOTTOMLESS) + message = list( + "relieves themselves all over %TARGET%", + "marks their territory on %TARGET%", + "releases their bladder onto %TARGET%", + "pisses all over %TARGET%" + ) + user_messages = list( + "You feel relief as you release onto %TARGET%", + "You empty your bladder on %TARGET%", + "You mark %TARGET% with your urine" + ) + target_messages = list( + "%USER% pisses all over you", + "You feel %USER%'s warm urine splash on you", + "%USER% marks you as their territory" + ) + sound_range = 1 + sound_use = FALSE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 2 + target_arousal = 2 + +/datum/interaction/lewd/unholy/piss_mouth + name = "Piss Mouth" + description = "Piss inside their mouth." + interaction_requires = list( + INTERACTION_REQUIRE_SELF_BOTTOMLESS, + INTERACTION_REQUIRE_TARGET_MOUTH + ) + message = list( + "relieves themselves into %TARGET%'s mouth", + "fills %TARGET%'s mouth with piss", + "releases their bladder down %TARGET%'s throat", + "uses %TARGET%'s mouth as their urinal" + ) + user_messages = list( + "You feel relief as you release into %TARGET%'s mouth", + "You empty your bladder down %TARGET%'s throat", + "You make %TARGET% drink your piss" + ) + target_messages = list( + "%USER% pisses right into your mouth", + "You're forced to swallow %USER%'s urine", + "%USER% uses your mouth as their urinal" + ) + sound_range = 1 + sound_use = FALSE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 3 + target_arousal = 3 diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/unholy/scat.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/unholy/scat.dm new file mode 100644 index 0000000000000..2efb7ef956d58 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/interactions/lewd/unholy/scat.dm @@ -0,0 +1,150 @@ +/datum/interaction/lewd/unholy/faceshit + name = "Face Shit" + description = "Shit on their face." + interaction_requires = list(INTERACTION_REQUIRE_TARGET_MOUTH) + user_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_ANY) + message = list( + "squats over %TARGET%'s face and releases their bowels", + "defecates right onto %TARGET%'s face", + "lets loose their filth on %TARGET%'s face", + "forces %TARGET% to experience their mess" + ) + user_messages = list( + "You feel relief as you release onto %TARGET%'s face", + "You empty your bowels on %TARGET%'s face", + "You make %TARGET% deal with your mess" + ) + target_messages = list( + "%USER% releases their filth right on your face", + "You're forced to experience %USER%'s mess", + "%USER%'s waste covers your face" + ) + sound_possible = list() + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 2 + target_arousal = 2 + +/datum/interaction/lewd/unholy/faceshit/New() + sound_possible = GLOB.brap_noises // GLOB.brap_noises: expected a constant expression + . = ..() + +/datum/interaction/lewd/unholy/crotchshit + name = "Crotch Shit" + description = "Shit on their crotch." + user_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_ANY) + message = list( + "squats over %TARGET%'s crotch and releases their bowels", + "defecates all over %TARGET%'s groin", + "lets loose their filth on %TARGET%'s genitals", + "forces %TARGET% to feel their mess" + ) + user_messages = list( + "You feel relief as you release onto %TARGET%'s crotch", + "You empty your bowels on %TARGET%'s groin", + "You make %TARGET% deal with your mess" + ) + target_messages = list( + "%USER% releases their filth right on your crotch", + "You feel %USER%'s mess on your groin", + "%USER%'s waste covers your genitals" + ) + sound_possible = list() + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 0 + user_arousal = 2 + target_arousal = 2 + +/datum/interaction/lewd/unholy/crotchshit/New() + sound_possible = GLOB.brap_noises // GLOB.brap_noises: expected a constant expression + . = ..() + +/datum/interaction/lewd/unholy/shitfuck + name = "Shit Fuck" + description = "Fuck their ass + shit." + user_required_parts = list(ORGAN_SLOT_PENIS = REQUIRE_GENITAL_EXPOSED) + target_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list( + CLIMAX_POSITION_USER = CLIMAX_PENIS, + CLIMAX_POSITION_TARGET = CLIMAX_BOTH + ) + cum_target = list( + CLIMAX_POSITION_USER = ORGAN_SLOT_ANUS + ) + message = list( + "pounds %TARGET%'s ass while they release their bowels", + "fucks %TARGET%'s hole as they defecate", + "thrusts into %TARGET%'s ass as they make a mess", + "fills %TARGET% with their cock while they empty themselves" + ) + user_messages = list( + "You feel %TARGET% releasing as you fuck them", + "You pound %TARGET%'s ass as they make a mess", + "You make %TARGET% feel full while they empty themselves" + ) + target_messages = list( + "You release your bowels as %USER% fucks you", + "You feel %USER%'s cock while you make a mess", + "You empty yourself as %USER% fills you up" + ) + sound_possible = list( + 'modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg' + ) + sound_range = 1 + sound_use = TRUE + user_pleasure = 6 + target_pleasure = 4 + user_arousal = 8 + target_arousal = 6 + +/datum/interaction/lewd/unholy/shitfuck/New() + sound_possible = GLOB.brap_noises // GLOB.brap_noises: expected a constant expression + . = ..() + +/datum/interaction/lewd/unholy/shitfuck/post_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target) + . = ..() + conditional_pref_sound(user, pick('modular_zzplurt/sound/interactions/bang1.ogg', + 'modular_zzplurt/sound/interactions/bang2.ogg', + 'modular_zzplurt/sound/interactions/bang3.ogg'), 80, TRUE, falloff_distance = sound_range, pref_to_check = /datum/preference/toggle/erp/sounds) + +/datum/interaction/lewd/unholy/suck_shit + name = "Suck Shit" + description = "Suck the shit out of their asshole." + interaction_requires = list(INTERACTION_REQUIRE_SELF_MOUTH) + target_required_parts = list(ORGAN_SLOT_ANUS = REQUIRE_GENITAL_EXPOSED) + cum_genital = list( + CLIMAX_POSITION_TARGET = CLIMAX_BOTH + ) + message = list( + "presses their face between %TARGET%'s asscheeks and sucks out their filth", + "eagerly consumes %TARGET%'s mess directly from the source", + "feeds on %TARGET%'s waste eagerly", + "puts their mouth on %TARGET%'s hole to taste their mess" + ) + user_messages = list( + "You consume %TARGET%'s waste directly from the source", + "You suck the filth from %TARGET%'s asshole", + "You taste %TARGET%'s mess on your tongue" + ) + target_messages = list( + "%USER% sucks your waste right out of your ass", + "You feel %USER%'s mouth pulling your mess from your hole", + "%USER% eagerly consumes your waste" + ) + sound_possible = list() + sound_range = 1 + sound_use = TRUE + user_pleasure = 0 + target_pleasure = 3 + user_arousal = 4 + target_arousal = 5 + +/datum/interaction/lewd/unholy/suck_shit/New() + sound_possible = GLOB.brap_noises // GLOB.brap_noises: expected a constant expression + . = ..() diff --git a/modular_zzplurt/code/modules/lewd/interaction_menu/lewd_helpers/human.dm b/modular_zzplurt/code/modules/lewd/interaction_menu/lewd_helpers/human.dm new file mode 100644 index 0000000000000..984f8d644cacb --- /dev/null +++ b/modular_zzplurt/code/modules/lewd/interaction_menu/lewd_helpers/human.dm @@ -0,0 +1,46 @@ + +/// Helper proc that checks if a human has a genital of a specific type and exposure state +/mob/living/carbon/human/proc/has_genital(required_state = REQUIRE_GENITAL_ANY, genital_slot) + var/obj/item/organ/external/genital/genital = get_organ_slot(genital_slot) + if(!genital) + return FALSE + + switch(required_state) + if(REQUIRE_GENITAL_ANY) + return TRUE + if(REQUIRE_GENITAL_EXPOSED) + return genital.is_exposed() + if(REQUIRE_GENITAL_UNEXPOSED) + return !genital.is_exposed() + else + return TRUE + +/// Returns true if the human has an accessible penis for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines. +/mob/living/carbon/human/proc/has_penis(required_state = REQUIRE_GENITAL_ANY) + return has_genital(required_state, ORGAN_SLOT_PENIS) + +/// Returns true if the human has accessible balls for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines. +/mob/living/carbon/human/proc/has_balls(required_state = REQUIRE_GENITAL_ANY) + return has_genital(required_state, ORGAN_SLOT_TESTICLES) + +/// Returns true if the human has an accessible vagina for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines. +/mob/living/carbon/human/proc/has_vagina(required_state = REQUIRE_GENITAL_ANY) + return has_genital(required_state, ORGAN_SLOT_VAGINA) + +/// Returns true if the human has accessible breasts for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines. +/mob/living/carbon/human/proc/has_breasts(required_state = REQUIRE_GENITAL_ANY) + return has_genital(required_state, ORGAN_SLOT_BREASTS) + +/// Returns true if the human has an accessible anus for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines. +/mob/living/carbon/human/proc/has_anus(required_state = REQUIRE_GENITAL_ANY) + if(issilicon(src)) + return TRUE + return has_genital(required_state, ORGAN_SLOT_ANUS) + +/// Returns true if the human has an accessible butt for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines. +/mob/living/carbon/human/proc/has_butt(required_state = REQUIRE_GENITAL_ANY) + return has_genital(required_state, ORGAN_SLOT_BUTT) + +/// Returns true if the human has an accessible belly for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines. +/mob/living/carbon/human/proc/has_belly(required_state = REQUIRE_GENITAL_ANY) + return has_genital(required_state, ORGAN_SLOT_BELLY) diff --git a/modular_zzplurt/code/modules/lewd_chemistry/reagents/_aphrodisiac.dm b/modular_zzplurt/code/modules/lewd_chemistry/reagents/_aphrodisiac.dm new file mode 100644 index 0000000000000..36db711263d01 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd_chemistry/reagents/_aphrodisiac.dm @@ -0,0 +1,195 @@ +/datum/reagent/drug/aphrodisiac + /// Largest size the chem can make a mob's butt + var/butt_max_size = 8 + /// How much the butt is increased in size each time it's run + var/butt_size_increase_step = 1 + /// Smallest size the chem can make a mob's butt + var/butt_minimum_size = 1 + /// How much to reduce the size of the butt each time it's run + var/butt_size_reduction_step = 1 + + /// Largest size the chem can make a mob's belly + var/belly_max_size = 7 + /// How much the belly is increased in size each time it's run + var/belly_size_increase_step = 1 + /// Smallest size the chem can make a mob's belly + var/belly_minimum_size = 1 + /// How much to reduce the size of the belly each time it's run + var/belly_size_reduction_step = 1 + + // Blud didn't even undefine the indexes in nonmodular code 💀💀💀 + #define GENITAL_BELLY 6 + #define GENITAL_BUTT 7 + +/** +* Handle belly growth +* +* exposed_mob - the mob being affected by the reagent +* suppress_chat - whether or not to display a message in chat +* mob_belly - the belly to cause to grow +*/ +/datum/reagent/drug/aphrodisiac/proc/grow_belly(mob/living/carbon/human/exposed_mob, suppress_chat = FALSE, obj/item/organ/external/genital/belly/mob_belly = exposed_mob?.get_organ_slot(ORGAN_SLOT_BELLY)) + if(!mob_belly) + return + + if(!exposed_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/belly_enlargement)) + return + + enlargement_amount += enlarger_increase_step + + if(enlargement_amount >= enlargement_threshold) + if(mob_belly?.genital_size >= belly_max_size) + return + + mob_belly.genital_size = min(mob_belly.genital_size + belly_size_increase_step, belly_max_size) + update_appearance(exposed_mob, mob_belly) + enlargement_amount = 0 + + growth_to_chat(exposed_mob, mob_belly, suppress_chat) + + // Damage from being too big for clothes + if((mob_belly?.genital_size >= (belly_max_size - 2)) && (exposed_mob.w_uniform || exposed_mob.wear_suit)) + if(prob(damage_chance)) + to_chat(exposed_mob, span_danger("Your belly strains against your clothes!")) + exposed_mob.adjustOxyLoss(5) + exposed_mob.apply_damage(1, BRUTE, exposed_mob.get_bodypart(BODY_ZONE_CHEST)) + +/** +* Handle butt growth +* +* exposed_mob - the mob being affected by the reagent +* suppress_chat - whether or not to display a message in chat +* mob_butt - the butt to cause to grow +*/ +/datum/reagent/drug/aphrodisiac/proc/grow_butt(mob/living/carbon/human/exposed_mob, suppress_chat = FALSE, obj/item/organ/external/genital/butt/mob_butt = exposed_mob?.get_organ_slot(ORGAN_SLOT_BUTT)) + if(!mob_butt) + return + + if(!exposed_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/butt_enlargement)) + return + + enlargement_amount += enlarger_increase_step + + if(enlargement_amount >= enlargement_threshold) + if(mob_butt?.genital_size >= butt_max_size) + return + + mob_butt.genital_size = min(mob_butt.genital_size + butt_size_increase_step, butt_max_size) + update_appearance(exposed_mob, mob_butt) + enlargement_amount = 0 + + growth_to_chat(exposed_mob, mob_butt, suppress_chat) + + // Damage from being too big for clothes + if((mob_butt?.genital_size >= (butt_max_size - 2)) && (exposed_mob.w_uniform || exposed_mob.wear_suit)) + if(prob(damage_chance)) + to_chat(exposed_mob, span_danger("Your bottom strains against your clothes!")) + exposed_mob.apply_damage(1, BRUTE, exposed_mob.get_bodypart(BODY_ZONE_PRECISE_GROIN)) + +/** +* Handle belly creation +* +* exposed_mob - the mob being affected by the reagent +* suppress_chat - whether or not to display a message in chat +* mob_belly - the mob's belly +*/ +/datum/reagent/drug/aphrodisiac/proc/create_belly(mob/living/carbon/human/exposed_mob, suppress_chat = FALSE, obj/item/organ/external/genital/belly/mob_belly = exposed_mob?.get_organ_slot(ORGAN_SLOT_BELLY)) + if(mob_belly) + return + + if(!exposed_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/new_genitalia_growth)) + return + + var/obj/item/organ/external/genital/belly/new_belly = new + new_belly.build_from_dna(exposed_mob.dna, ORGAN_SLOT_BELLY) + new_belly.Insert(exposed_mob, 0, FALSE) + new_belly.genital_size = belly_minimum_size + update_appearance(exposed_mob, new_belly) + + if(!suppress_chat) + to_chat(exposed_mob, span_purple("Your midsection feels warm as it begins to expand outward.")) + +/** +* Handle butt creation +* +* exposed_mob - the mob being affected by the reagent +* suppress_chat - whether or not to display a message in chat +* mob_butt - the mob's butt +*/ +/datum/reagent/drug/aphrodisiac/proc/create_butt(mob/living/carbon/human/exposed_mob, suppress_chat = FALSE, obj/item/organ/external/genital/butt/mob_butt = exposed_mob?.get_organ_slot(ORGAN_SLOT_BUTT)) + if(mob_butt) + return + + if(!exposed_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/new_genitalia_growth)) + return + + var/obj/item/organ/external/genital/butt/new_butt = new + new_butt.build_from_dna(exposed_mob.dna, ORGAN_SLOT_BUTT) + new_butt.Insert(exposed_mob, 0, FALSE) + new_butt.genital_size = butt_minimum_size + update_appearance(exposed_mob, new_butt) + + if(!suppress_chat) + to_chat(exposed_mob, span_purple("Your bottom feels warm as it begins to expand outward.")) + +/** +* Handle belly shrinkage +* +* exposed_mob - the mob being affected by the reagent +* suppress_chat - whether or not to display a message in chat +* mob_belly - the belly to shrink +* message - the message to send to chat +*/ +/datum/reagent/drug/aphrodisiac/proc/shrink_belly(mob/living/carbon/human/exposed_mob, suppress_chat = FALSE, obj/item/organ/external/genital/belly/mob_belly = exposed_mob?.get_organ_slot(ORGAN_SLOT_BELLY)) + if(!mob_belly) + return + + if(!exposed_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/belly_shrinkage)) + return + + if(mob_belly.genital_size > belly_minimum_size) + mob_belly.genital_size = max(mob_belly.genital_size - belly_size_reduction_step, belly_minimum_size) + update_appearance(exposed_mob, mob_belly) + + else if(mob_belly.genital_size == belly_minimum_size) + var/message = "Your belly has completely flattened out." + remove_genital(exposed_mob, mob_belly, suppress_chat, message) + +/** +* Handle butt shrinkage +* +* exposed_mob - the mob being affected by the reagent +* suppress_chat - whether or not to display a message in chat +* mob_butt - the butt to shrink +* message - the message to send to chat +*/ +/datum/reagent/drug/aphrodisiac/proc/shrink_butt(mob/living/carbon/human/exposed_mob, suppress_chat = FALSE, obj/item/organ/external/genital/butt/mob_butt = exposed_mob?.get_organ_slot(ORGAN_SLOT_BUTT)) + if(!mob_butt) + return + + if(!exposed_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/butt_shrinkage)) + return + + if(mob_butt.genital_size > butt_minimum_size) + mob_butt.genital_size = max(mob_butt.genital_size - butt_size_reduction_step, butt_minimum_size) + update_appearance(exposed_mob, mob_butt) + + else if(mob_butt.genital_size == butt_minimum_size) + var/message = "Your bottom has completely flattened out." + remove_genital(exposed_mob, mob_butt, suppress_chat, message) + +/** +* Handle shrinkage of genitals +* +* exposed_mob - the mob being affected by the reagent +* suppress_chat - whether or not to display a message in chat +* genitals_to_shrink - the genitals to shrink +*/ +/datum/reagent/drug/aphrodisiac/shrink_genitals(mob/living/carbon/human/exposed_mob, suppress_chat, list/genitals_to_shrink) + . = ..() + for(var/mob_genital in genitals_to_shrink) + switch(mob_genital) + if(GENITAL_BELLY) + shrink_belly(exposed_mob, suppress_chat) + if(GENITAL_BUTT) + shrink_butt(exposed_mob, suppress_chat) diff --git a/modular_zzplurt/code/modules/lewd_chemistry/reagents/belly_enlarger.dm b/modular_zzplurt/code/modules/lewd_chemistry/reagents/belly_enlarger.dm new file mode 100644 index 0000000000000..571241d817dd4 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd_chemistry/reagents/belly_enlarger.dm @@ -0,0 +1,78 @@ +/* +* BELLY ENLARGEMENT +* Normal function increases the player's belly size +* If the player's belly is near or at the maximum size and they're wearing clothing, they press against the player's clothes and causes brute damage +*/ + +/datum/reagent/drug/aphrodisiac/belly_enlarger + name = "belladine nectar" + description = "A volatile collodial mixture that encourages abdominal expansion via a potent gastric mix." + color = "#01ff63" + taste_description = "blueberry gum" + overdose_threshold = 17 + metabolization_rate = 0.25 + life_pref_datum = /datum/preference/toggle/erp/belly_enlargement + overdose_pref_datum = /datum/preference/toggle/erp + + /// Words for the belly when huge + var/static/list/words_for_bigger = list( + "huge", + "massive", + "distended", + "bloated", + "swollen", + "rather large", + "jiggly", + "hefty" + ) + + /// Wording chosen to expand the belly, shown only to the mob + var/static/list/action_text_list = list( + "expands outward to ", + "swells out to ", + "begins to bloat, growing to ", + "suddenly distends to ", + "inflates to " + ) + + /// Wording chosen to be seen by other mobs + var/static/list/public_action_text_list = list( + "expands outward.", + "seems to grow larger.", + "appears more distended than before.", + "suddenly swells up." + ) + +/datum/reagent/drug/aphrodisiac/belly_enlarger/life_effects(mob/living/carbon/human/exposed_mob) + if(!ishuman(exposed_mob)) + return + + var/obj/item/organ/external/genital/belly/mob_belly = exposed_mob.get_organ_slot(ORGAN_SLOT_BELLY) + + // Create belly if they don't have one + if(!mob_belly) + create_belly(exposed_mob) + return + + // Grow existing belly + grow_belly(exposed_mob) + +/datum/reagent/drug/aphrodisiac/belly_enlarger/growth_to_chat(mob/living/carbon/human/exposed_mob, obj/item/organ/external/genital/belly/mob_belly) + if(!mob_belly) + return + + if(mob_belly.genital_size >= (belly_max_size - 2)) + exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(words_for_bigger)] belly [pick(public_action_text_list)]")) + to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger)] belly [pick(action_text_list)]size [mob_belly.genital_size].")) + else + exposed_mob.visible_message(span_notice("[exposed_mob]'s belly [pick(public_action_text_list)]")) + to_chat(exposed_mob, span_purple("Your belly [pick(action_text_list)]size [mob_belly.genital_size].")) + +/datum/chemical_reaction/belly_enlarger + results = list(/datum/reagent/drug/aphrodisiac/belly_enlarger = 8) + required_reagents = list(/datum/reagent/medicine/salglu_solution = 1, + /datum/reagent/consumable/nutriment = 3, + /datum/reagent/silicon = 2, + /datum/reagent/drug/aphrodisiac/crocin = 2) + mix_message = "the reaction gives off a sweet aroma." + erp_reaction = TRUE diff --git a/modular_zzplurt/code/modules/lewd_chemistry/reagents/butt_enlarger.dm b/modular_zzplurt/code/modules/lewd_chemistry/reagents/butt_enlarger.dm new file mode 100644 index 0000000000000..30d8fbbbedcbc --- /dev/null +++ b/modular_zzplurt/code/modules/lewd_chemistry/reagents/butt_enlarger.dm @@ -0,0 +1,77 @@ +/* +* BUTT ENLARGEMENT +* Normal function increases the player's butt size +* If the player's butt is near or at the maximum size and they're wearing clothing, they press against the player's clothes and causes brute damage +*/ + +/datum/reagent/drug/aphrodisiac/butt_enlarger + name = "denbu tincture" + description = "A mixture of natural vitamins and valentines plant extract, causing butt enlargement in humanoids." + color = "#e8ff1b" + taste_description = "butter with a sweet aftertaste" + overdose_threshold = 17 + metabolization_rate = 0.25 + life_pref_datum = /datum/preference/toggle/erp/butt_enlargement + overdose_pref_datum = /datum/preference/toggle/erp + + /// Words for the butt when huge + var/static/list/words_for_bigger = list( + "huge", + "massive", + "jiggly", + "hefty", + "plump", + "thick", + "bubbling" + ) + + /// Wording chosen to expand the butt, shown only to the mob + var/static/list/action_text_list = list( + "expands outward to ", + "grows out to ", + "begins to plump up, growing to ", + "suddenly expands to ", + "swells out to " + ) + + /// Wording chosen to be seen by other mobs + var/static/list/public_action_text_list = list( + "expands outward.", + "seems to grow larger.", + "appears more plump than before.", + "suddenly swells up." + ) + +/datum/reagent/drug/aphrodisiac/butt_enlarger/life_effects(mob/living/carbon/human/exposed_mob) + if(!ishuman(exposed_mob)) + return + + var/obj/item/organ/external/genital/butt/mob_butt = exposed_mob.get_organ_slot(ORGAN_SLOT_BUTT) + + // Create butt if they don't have one + if(!mob_butt) + create_butt(exposed_mob) + return + + // Grow existing butt + grow_butt(exposed_mob) + +/datum/reagent/drug/aphrodisiac/butt_enlarger/growth_to_chat(mob/living/carbon/human/exposed_mob, obj/item/organ/external/genital/butt/mob_butt) + if(!mob_butt) + return + + if(mob_butt.genital_size >= (butt_max_size - 2)) + exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(words_for_bigger)] bottom [pick(public_action_text_list)]")) + to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger)] bottom [pick(action_text_list)]size [mob_butt.genital_size].")) + else + exposed_mob.visible_message(span_notice("[exposed_mob]'s bottom [pick(public_action_text_list)]")) + to_chat(exposed_mob, span_purple("Your bottom [pick(action_text_list)]size [mob_butt.genital_size].")) + +/datum/chemical_reaction/butt_enlarger + results = list(/datum/reagent/drug/aphrodisiac/butt_enlarger = 8) + required_reagents = list(/datum/reagent/medicine/salglu_solution = 1, + /datum/reagent/consumable/sugar = 2, + /datum/reagent/silicon = 3, + /datum/reagent/drug/aphrodisiac/crocin = 2) + mix_message = "the reaction gives off a sweet buttery aroma." + erp_reaction = TRUE diff --git a/modular_zzplurt/code/modules/lewd_chemistry/reagents/camphor.dm b/modular_zzplurt/code/modules/lewd_chemistry/reagents/camphor.dm new file mode 100644 index 0000000000000..7f6ce8bcbdba7 --- /dev/null +++ b/modular_zzplurt/code/modules/lewd_chemistry/reagents/camphor.dm @@ -0,0 +1,32 @@ +/datum/reagent/drug/aphrodisiac/camphor/overdose_effects(mob/living/carbon/human/exposed_mob) + . = ..() // Call parent to handle base genital resets + var/modified_genitals = FALSE + + if(exposed_mob.get_organ_slot(ORGAN_SLOT_BELLY)) + var/obj/item/organ/external/genital/belly/mob_belly = exposed_mob.get_organ_slot(ORGAN_SLOT_BELLY) + var/original_belly_size = exposed_mob.client?.prefs.read_preference(/datum/preference/numeric/belly_size) + if(original_belly_size) + if(mob_belly?.genital_size > original_belly_size) + mob_belly.genital_size -= belly_size_reduction_step + mob_belly.update_sprite_suffix() + modified_genitals = TRUE + if(mob_belly?.genital_size < original_belly_size) + mob_belly.genital_size += belly_size_increase_step + mob_belly.update_sprite_suffix() + modified_genitals = TRUE + + if(exposed_mob.get_organ_slot(ORGAN_SLOT_BUTT)) + var/obj/item/organ/external/genital/butt/mob_butt = exposed_mob.get_organ_slot(ORGAN_SLOT_BUTT) + var/original_butt_size = exposed_mob.client?.prefs.read_preference(/datum/preference/numeric/butt_size) + if(original_butt_size) + if(mob_butt?.genital_size > original_butt_size) + mob_butt.genital_size -= butt_size_reduction_step + mob_butt.update_sprite_suffix() + modified_genitals = TRUE + if(mob_butt?.genital_size < original_butt_size) + mob_butt.genital_size += butt_size_increase_step + mob_butt.update_sprite_suffix() + modified_genitals = TRUE + + if(modified_genitals) + exposed_mob.update_body() diff --git a/modular_zzplurt/code/modules/reagents/chemistry/reagents.dm b/modular_zzplurt/code/modules/reagents/chemistry/reagents.dm deleted file mode 100644 index 05a65ce5c1060..0000000000000 --- a/modular_zzplurt/code/modules/reagents/chemistry/reagents.dm +++ /dev/null @@ -1,95 +0,0 @@ -//Own stuff - -//Belly inflator yes -/datum/reagent/belly_inflator - name = "Belladine nectar" - description = "It will give you the lewdest tummy ache you've ever had" - color = "#01ff63" - taste_description = "blueberry gum" - overdose_threshold = 17 - -/datum/reagent/belly_inflator/on_mob_life(mob/living/carbon/M) - . = ..() - if(!ishuman(M)) - return ..() - - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/genital/belly/B = H.get_organ_slot(ORGAN_SLOT_BELLY) - if(!B) - B = new - B.build_from_dna(H.dna, ORGAN_SLOT_BELLY) - B.Insert(M) - to_chat(M, span_warning("You feel your midsection get warmer... bubbling softly as it seems to start distending")) - M.reagents.remove_reagent(type, 5) - - //If they have, increase size. - if(B.genital_size < 16) //just in case - B.set_size(B.genital_size + 0.1) - ..() - -/datum/reagent/GEsmaller_hypo - name = "Super Antacid" - color = "#fca3d4" - taste_description = "Milky strawberries" - description = "A medicine used to treat a patient's heavily bloated stomach" - metabolization_rate = 0.5 - -/datum/reagent/GEsmaller_hypo/on_mob_life(mob/living/carbon/M) - var/obj/item/organ/external/genital/belly/B = M.get_organ_slot(ORGAN_SLOT_BELLY) - if(!B) - return ..() - var/optimal_size = M.dna.features["belly_size"] - if(!optimal_size)//Fast fix for those who don't want it. - B.set_size(B.genital_size-0.2) - else if(B.genital_size > optimal_size) - B.set_size(B.genital_size-0.1) - else if(B.genital_size < optimal_size) - B.set_size(B.genital_size + 0.1) - return ..() - - -///Ass enhancer -/datum/reagent/butt_enlarger - name = "Denbu Tincture" //on Hyper it was 'Denbu Draft' but this makes it more consistent with the rectifying chemical down below. - description = "A mixture of natural vitamins and valentines plant extract, causing butt enlargement in humanoids." - color = "#e8ff1b" - taste_description = "butter with a sweet aftertaste" //pass me the butter, OM NOM - overdose_threshold = 17 - -/datum/reagent/butt_enlarger/on_mob_life(mob/living/carbon/M) //Increases butt size - if(!ishuman(M)) - return ..() - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/genital/butt/B =M.get_organ_slot(ORGAN_SLOT_BUTT) - if(!B) //If they don't have a butt. Give them one! - B = new - B.build_from_dna(H.dna, ORGAN_SLOT_BUTT) - B.Insert(M) - to_chat(M, "Your ass cheeks bulge outwards and feel more plush.") - M.reagents.remove_reagent(type, 5) - - //If they have, increase size. - if(B.genital_size < BUTT_MAX_SIZE) //just in case - B.set_size(B.genital_size + 0.05) - ..() - -/datum/reagent/AEsmaller_hypo //"BEsmaller" already exists so using "AE" instead, A is for ass. - name = "Rectify tincture" - color = "#e8ff1b" - taste_description = "butter" - description = "A medicine used to treat organomegaly in a patient's ass." - metabolization_rate = 0.5 - -/datum/reagent/AEsmaller_hypo/on_mob_life(mob/living/carbon/M) - var/obj/item/organ/external/genital/butt/B = M.get_organ_slot(ORGAN_SLOT_BUTT) - if(!B) - return ..() - - var/optimal_size = M.dna.features["butt_size"] - if(!optimal_size)//Fast fix for those who don't want it. - B.set_size(B.genital_size -0.2) - else if(B.genital_size > optimal_size) - B.set_size(B.genital_size - 0.1) - else if(B.genital_size < optimal_size) - B.set_size(B.genital_size + 0.1) - return ..() diff --git a/modular_zzplurt/sound/interactions/bang1.ogg b/modular_zzplurt/sound/interactions/bang1.ogg new file mode 100644 index 0000000000000..d0b8abafa7d60 Binary files /dev/null and b/modular_zzplurt/sound/interactions/bang1.ogg differ diff --git a/modular_zzplurt/sound/interactions/bang2.ogg b/modular_zzplurt/sound/interactions/bang2.ogg new file mode 100644 index 0000000000000..ff07de9666502 Binary files /dev/null and b/modular_zzplurt/sound/interactions/bang2.ogg differ diff --git a/modular_zzplurt/sound/interactions/bang3.ogg b/modular_zzplurt/sound/interactions/bang3.ogg new file mode 100644 index 0000000000000..62cbe7cd633a4 Binary files /dev/null and b/modular_zzplurt/sound/interactions/bang3.ogg differ diff --git a/modular_zzplurt/sound/interactions/bang4.ogg b/modular_zzplurt/sound/interactions/bang4.ogg new file mode 100644 index 0000000000000..4d9d9da8051ce Binary files /dev/null and b/modular_zzplurt/sound/interactions/bang4.ogg differ diff --git a/modular_zzplurt/sound/interactions/bang5.ogg b/modular_zzplurt/sound/interactions/bang5.ogg new file mode 100644 index 0000000000000..184af6787cb11 Binary files /dev/null and b/modular_zzplurt/sound/interactions/bang5.ogg differ diff --git a/modular_zzplurt/sound/interactions/bang6.ogg b/modular_zzplurt/sound/interactions/bang6.ogg new file mode 100644 index 0000000000000..11c7c056e5b27 Binary files /dev/null and b/modular_zzplurt/sound/interactions/bang6.ogg differ diff --git a/modular_zzplurt/sound/interactions/bj1.ogg b/modular_zzplurt/sound/interactions/bj1.ogg new file mode 100644 index 0000000000000..004d668f05266 Binary files /dev/null and b/modular_zzplurt/sound/interactions/bj1.ogg differ diff --git a/modular_zzplurt/sound/interactions/bj10.ogg b/modular_zzplurt/sound/interactions/bj10.ogg new file mode 100644 index 0000000000000..5d0d56e010f63 Binary files /dev/null and b/modular_zzplurt/sound/interactions/bj10.ogg differ diff --git a/modular_zzplurt/sound/interactions/bj11.ogg b/modular_zzplurt/sound/interactions/bj11.ogg new file mode 100644 index 0000000000000..5335057b45580 Binary files /dev/null and b/modular_zzplurt/sound/interactions/bj11.ogg differ diff --git a/modular_zzplurt/sound/interactions/bj2.ogg b/modular_zzplurt/sound/interactions/bj2.ogg new file mode 100644 index 0000000000000..54c74dd5b463c Binary files /dev/null and b/modular_zzplurt/sound/interactions/bj2.ogg differ diff --git a/modular_zzplurt/sound/interactions/bj3.ogg b/modular_zzplurt/sound/interactions/bj3.ogg new file mode 100644 index 0000000000000..f12481f0b7e01 Binary files /dev/null and b/modular_zzplurt/sound/interactions/bj3.ogg differ diff --git a/modular_zzplurt/sound/interactions/bj4.ogg b/modular_zzplurt/sound/interactions/bj4.ogg new file mode 100644 index 0000000000000..35b036d3a5646 Binary files /dev/null and b/modular_zzplurt/sound/interactions/bj4.ogg differ diff --git a/modular_zzplurt/sound/interactions/bj5.ogg b/modular_zzplurt/sound/interactions/bj5.ogg new file mode 100644 index 0000000000000..1c33bee84efc1 Binary files /dev/null and b/modular_zzplurt/sound/interactions/bj5.ogg differ diff --git a/modular_zzplurt/sound/interactions/bj6.ogg b/modular_zzplurt/sound/interactions/bj6.ogg new file mode 100644 index 0000000000000..564e82bad4cfd Binary files /dev/null and b/modular_zzplurt/sound/interactions/bj6.ogg differ diff --git a/modular_zzplurt/sound/interactions/bj7.ogg b/modular_zzplurt/sound/interactions/bj7.ogg new file mode 100644 index 0000000000000..a65d9cdc845ae Binary files /dev/null and b/modular_zzplurt/sound/interactions/bj7.ogg differ diff --git a/modular_zzplurt/sound/interactions/bj8.ogg b/modular_zzplurt/sound/interactions/bj8.ogg new file mode 100644 index 0000000000000..e88550afcecdd Binary files /dev/null and b/modular_zzplurt/sound/interactions/bj8.ogg differ diff --git a/modular_zzplurt/sound/interactions/bj9.ogg b/modular_zzplurt/sound/interactions/bj9.ogg new file mode 100644 index 0000000000000..2f4feb33ea8a9 Binary files /dev/null and b/modular_zzplurt/sound/interactions/bj9.ogg differ diff --git a/modular_zzplurt/sound/interactions/champ1.ogg b/modular_zzplurt/sound/interactions/champ1.ogg new file mode 100644 index 0000000000000..07f2118f2a45a Binary files /dev/null and b/modular_zzplurt/sound/interactions/champ1.ogg differ diff --git a/modular_zzplurt/sound/interactions/champ2.ogg b/modular_zzplurt/sound/interactions/champ2.ogg new file mode 100644 index 0000000000000..63a1e8f1b85e4 Binary files /dev/null and b/modular_zzplurt/sound/interactions/champ2.ogg differ diff --git a/modular_zzplurt/sound/interactions/champ_fingering.ogg b/modular_zzplurt/sound/interactions/champ_fingering.ogg new file mode 100644 index 0000000000000..7e540c970378d Binary files /dev/null and b/modular_zzplurt/sound/interactions/champ_fingering.ogg differ diff --git a/modular_zzplurt/sound/interactions/clawcum1.ogg b/modular_zzplurt/sound/interactions/clawcum1.ogg new file mode 100644 index 0000000000000..4313ad4ee1a08 Binary files /dev/null and b/modular_zzplurt/sound/interactions/clawcum1.ogg differ diff --git a/modular_zzplurt/sound/interactions/clawcum2.ogg b/modular_zzplurt/sound/interactions/clawcum2.ogg new file mode 100644 index 0000000000000..8573ca2093165 Binary files /dev/null and b/modular_zzplurt/sound/interactions/clawcum2.ogg differ diff --git a/modular_zzplurt/sound/interactions/final_f1.ogg b/modular_zzplurt/sound/interactions/final_f1.ogg new file mode 100644 index 0000000000000..3453ec4f9355c Binary files /dev/null and b/modular_zzplurt/sound/interactions/final_f1.ogg differ diff --git a/modular_zzplurt/sound/interactions/final_f2.ogg b/modular_zzplurt/sound/interactions/final_f2.ogg new file mode 100644 index 0000000000000..a0ba60e0a6ab7 Binary files /dev/null and b/modular_zzplurt/sound/interactions/final_f2.ogg differ diff --git a/modular_zzplurt/sound/interactions/final_f3.ogg b/modular_zzplurt/sound/interactions/final_f3.ogg new file mode 100644 index 0000000000000..dd9070bd5b407 Binary files /dev/null and b/modular_zzplurt/sound/interactions/final_f3.ogg differ diff --git a/modular_zzplurt/sound/interactions/final_m1.ogg b/modular_zzplurt/sound/interactions/final_m1.ogg new file mode 100644 index 0000000000000..6ef14a3d2d468 Binary files /dev/null and b/modular_zzplurt/sound/interactions/final_m1.ogg differ diff --git a/modular_zzplurt/sound/interactions/final_m2.ogg b/modular_zzplurt/sound/interactions/final_m2.ogg new file mode 100644 index 0000000000000..128d5f933b821 Binary files /dev/null and b/modular_zzplurt/sound/interactions/final_m2.ogg differ diff --git a/modular_zzplurt/sound/interactions/final_m3.ogg b/modular_zzplurt/sound/interactions/final_m3.ogg new file mode 100644 index 0000000000000..b57d0f4382882 Binary files /dev/null and b/modular_zzplurt/sound/interactions/final_m3.ogg differ diff --git a/modular_zzplurt/sound/interactions/final_m4.ogg b/modular_zzplurt/sound/interactions/final_m4.ogg new file mode 100644 index 0000000000000..128d5f933b821 Binary files /dev/null and b/modular_zzplurt/sound/interactions/final_m4.ogg differ diff --git a/modular_zzplurt/sound/interactions/final_m5.ogg b/modular_zzplurt/sound/interactions/final_m5.ogg new file mode 100644 index 0000000000000..b57d0f4382882 Binary files /dev/null and b/modular_zzplurt/sound/interactions/final_m5.ogg differ diff --git a/modular_zzplurt/sound/interactions/foot_dry1.ogg b/modular_zzplurt/sound/interactions/foot_dry1.ogg new file mode 100644 index 0000000000000..1ac7bc66936ff Binary files /dev/null and b/modular_zzplurt/sound/interactions/foot_dry1.ogg differ diff --git a/modular_zzplurt/sound/interactions/foot_dry2.ogg b/modular_zzplurt/sound/interactions/foot_dry2.ogg new file mode 100644 index 0000000000000..957e57591b740 Binary files /dev/null and b/modular_zzplurt/sound/interactions/foot_dry2.ogg differ diff --git a/modular_zzplurt/sound/interactions/foot_dry3.ogg b/modular_zzplurt/sound/interactions/foot_dry3.ogg new file mode 100644 index 0000000000000..5b38d5ab85710 Binary files /dev/null and b/modular_zzplurt/sound/interactions/foot_dry3.ogg differ diff --git a/modular_zzplurt/sound/interactions/foot_dry4.ogg b/modular_zzplurt/sound/interactions/foot_dry4.ogg new file mode 100644 index 0000000000000..7f71feedffa77 Binary files /dev/null and b/modular_zzplurt/sound/interactions/foot_dry4.ogg differ diff --git a/modular_zzplurt/sound/interactions/foot_wet1.ogg b/modular_zzplurt/sound/interactions/foot_wet1.ogg new file mode 100644 index 0000000000000..b38a1932262c1 Binary files /dev/null and b/modular_zzplurt/sound/interactions/foot_wet1.ogg differ diff --git a/modular_zzplurt/sound/interactions/foot_wet2.ogg b/modular_zzplurt/sound/interactions/foot_wet2.ogg new file mode 100644 index 0000000000000..857906e8dbcfd Binary files /dev/null and b/modular_zzplurt/sound/interactions/foot_wet2.ogg differ diff --git a/modular_zzplurt/sound/interactions/foot_wet3.ogg b/modular_zzplurt/sound/interactions/foot_wet3.ogg new file mode 100644 index 0000000000000..9a3b8c755fc88 Binary files /dev/null and b/modular_zzplurt/sound/interactions/foot_wet3.ogg differ diff --git a/modular_zzplurt/sound/interactions/hug.ogg b/modular_zzplurt/sound/interactions/hug.ogg new file mode 100644 index 0000000000000..68c2948bdc03f Binary files /dev/null and b/modular_zzplurt/sound/interactions/hug.ogg differ diff --git a/modular_zzplurt/sound/interactions/kiss1.ogg b/modular_zzplurt/sound/interactions/kiss1.ogg new file mode 100644 index 0000000000000..f4bd734338373 Binary files /dev/null and b/modular_zzplurt/sound/interactions/kiss1.ogg differ diff --git a/modular_zzplurt/sound/interactions/kiss2.ogg b/modular_zzplurt/sound/interactions/kiss2.ogg new file mode 100644 index 0000000000000..18f12d6f04a9e Binary files /dev/null and b/modular_zzplurt/sound/interactions/kiss2.ogg differ diff --git a/modular_zzplurt/sound/interactions/kiss3.ogg b/modular_zzplurt/sound/interactions/kiss3.ogg new file mode 100644 index 0000000000000..0f89617f002b6 Binary files /dev/null and b/modular_zzplurt/sound/interactions/kiss3.ogg differ diff --git a/modular_zzplurt/sound/interactions/kiss4.ogg b/modular_zzplurt/sound/interactions/kiss4.ogg new file mode 100644 index 0000000000000..a7c47dd8af7b4 Binary files /dev/null and b/modular_zzplurt/sound/interactions/kiss4.ogg differ diff --git a/modular_zzplurt/sound/interactions/kiss5.ogg b/modular_zzplurt/sound/interactions/kiss5.ogg new file mode 100644 index 0000000000000..31a48c4d62278 Binary files /dev/null and b/modular_zzplurt/sound/interactions/kiss5.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_f1.ogg b/modular_zzplurt/sound/interactions/moan_f1.ogg new file mode 100644 index 0000000000000..5f1ceff6fed64 Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_f1.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_f2.ogg b/modular_zzplurt/sound/interactions/moan_f2.ogg new file mode 100644 index 0000000000000..22160295fc695 Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_f2.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_f3.ogg b/modular_zzplurt/sound/interactions/moan_f3.ogg new file mode 100644 index 0000000000000..9654b020e9230 Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_f3.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_f4.ogg b/modular_zzplurt/sound/interactions/moan_f4.ogg new file mode 100644 index 0000000000000..6fcb314058f3a Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_f4.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_f5.ogg b/modular_zzplurt/sound/interactions/moan_f5.ogg new file mode 100644 index 0000000000000..7b38319610823 Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_f5.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_f6.ogg b/modular_zzplurt/sound/interactions/moan_f6.ogg new file mode 100644 index 0000000000000..c3b368e3a8b9d Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_f6.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_f7.ogg b/modular_zzplurt/sound/interactions/moan_f7.ogg new file mode 100644 index 0000000000000..09d3bb3fafbad Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_f7.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_m0.ogg b/modular_zzplurt/sound/interactions/moan_m0.ogg new file mode 100644 index 0000000000000..4f1ede0079c34 Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_m0.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_m1.ogg b/modular_zzplurt/sound/interactions/moan_m1.ogg new file mode 100644 index 0000000000000..6ef14a3d2d468 Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_m1.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_m12.ogg b/modular_zzplurt/sound/interactions/moan_m12.ogg new file mode 100644 index 0000000000000..cd9a9d1e3d7ee Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_m12.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_m2.ogg b/modular_zzplurt/sound/interactions/moan_m2.ogg new file mode 100644 index 0000000000000..128d5f933b821 Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_m2.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_m3.ogg b/modular_zzplurt/sound/interactions/moan_m3.ogg new file mode 100644 index 0000000000000..b57d0f4382882 Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_m3.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_m4.ogg b/modular_zzplurt/sound/interactions/moan_m4.ogg new file mode 100644 index 0000000000000..e8683ea52611d Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_m4.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_m5.ogg b/modular_zzplurt/sound/interactions/moan_m5.ogg new file mode 100644 index 0000000000000..2308558b66997 Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_m5.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_m6.ogg b/modular_zzplurt/sound/interactions/moan_m6.ogg new file mode 100644 index 0000000000000..0d60d7a74ccac Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_m6.ogg differ diff --git a/modular_zzplurt/sound/interactions/moan_m7.ogg b/modular_zzplurt/sound/interactions/moan_m7.ogg new file mode 100644 index 0000000000000..4e744775702d1 Binary files /dev/null and b/modular_zzplurt/sound/interactions/moan_m7.ogg differ diff --git a/modular_zzplurt/sound/interactions/oral1.ogg b/modular_zzplurt/sound/interactions/oral1.ogg new file mode 100644 index 0000000000000..332ccf3c7302d Binary files /dev/null and b/modular_zzplurt/sound/interactions/oral1.ogg differ diff --git a/modular_zzplurt/sound/interactions/oral2.ogg b/modular_zzplurt/sound/interactions/oral2.ogg new file mode 100644 index 0000000000000..fff69d370f247 Binary files /dev/null and b/modular_zzplurt/sound/interactions/oral2.ogg differ diff --git a/modular_zzplurt/sound/interactions/purr1.ogg b/modular_zzplurt/sound/interactions/purr1.ogg new file mode 100644 index 0000000000000..e1cc6bd20495c Binary files /dev/null and b/modular_zzplurt/sound/interactions/purr1.ogg differ diff --git a/modular_zzplurt/sound/interactions/purr2.ogg b/modular_zzplurt/sound/interactions/purr2.ogg new file mode 100644 index 0000000000000..caa9ca2dcbfd3 Binary files /dev/null and b/modular_zzplurt/sound/interactions/purr2.ogg differ diff --git a/modular_zzplurt/sound/interactions/purr3.ogg b/modular_zzplurt/sound/interactions/purr3.ogg new file mode 100644 index 0000000000000..f53100aec24f9 Binary files /dev/null and b/modular_zzplurt/sound/interactions/purr3.ogg differ diff --git a/modular_zzplurt/sound/interactions/slap.ogg b/modular_zzplurt/sound/interactions/slap.ogg new file mode 100644 index 0000000000000..d8804bc6e91a8 Binary files /dev/null and b/modular_zzplurt/sound/interactions/slap.ogg differ diff --git a/modular_zzplurt/sound/interactions/squelch1.ogg b/modular_zzplurt/sound/interactions/squelch1.ogg new file mode 100644 index 0000000000000..faf8d651bd1f4 Binary files /dev/null and b/modular_zzplurt/sound/interactions/squelch1.ogg differ diff --git a/modular_zzplurt/sound/interactions/squelch2.ogg b/modular_zzplurt/sound/interactions/squelch2.ogg new file mode 100644 index 0000000000000..d4148c198101e Binary files /dev/null and b/modular_zzplurt/sound/interactions/squelch2.ogg differ diff --git a/modular_zzplurt/sound/interactions/squelch3.ogg b/modular_zzplurt/sound/interactions/squelch3.ogg new file mode 100644 index 0000000000000..a9b4a7b6e2770 Binary files /dev/null and b/modular_zzplurt/sound/interactions/squelch3.ogg differ diff --git a/modular_zzplurt/sound/interactions/swallow.ogg b/modular_zzplurt/sound/interactions/swallow.ogg new file mode 100644 index 0000000000000..d58b2628d55b4 Binary files /dev/null and b/modular_zzplurt/sound/interactions/swallow.ogg differ diff --git a/modular_zzplurt/sound/interactions/under_moan_f1.ogg b/modular_zzplurt/sound/interactions/under_moan_f1.ogg new file mode 100644 index 0000000000000..a3fd6b56d5df8 Binary files /dev/null and b/modular_zzplurt/sound/interactions/under_moan_f1.ogg differ diff --git a/modular_zzplurt/sound/interactions/under_moan_f2.ogg b/modular_zzplurt/sound/interactions/under_moan_f2.ogg new file mode 100644 index 0000000000000..5232ccd4b9cf2 Binary files /dev/null and b/modular_zzplurt/sound/interactions/under_moan_f2.ogg differ diff --git a/modular_zzplurt/sound/interactions/under_moan_f3.ogg b/modular_zzplurt/sound/interactions/under_moan_f3.ogg new file mode 100644 index 0000000000000..d3b651ab8197a Binary files /dev/null and b/modular_zzplurt/sound/interactions/under_moan_f3.ogg differ diff --git a/modular_zzplurt/sound/interactions/under_moan_f4.ogg b/modular_zzplurt/sound/interactions/under_moan_f4.ogg new file mode 100644 index 0000000000000..1c697450d08ba Binary files /dev/null and b/modular_zzplurt/sound/interactions/under_moan_f4.ogg differ diff --git a/tgstation.dme b/tgstation.dme index cfa5a7c1d8581..e535f96c100b9 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -520,6 +520,7 @@ #include "code\__DEFINES\~~~splurt_defines\DNA.dm" #include "code\__DEFINES\~~~splurt_defines\hud.dm" #include "code\__DEFINES\~~~splurt_defines\intents.dm" +#include "code\__DEFINES\~~~splurt_defines\interactions.dm" #include "code\__DEFINES\~~~splurt_defines\inventory.dm" #include "code\__DEFINES\~~~splurt_defines\keybinding.dm" #include "code\__DEFINES\~~~splurt_defines\mobs.dm" @@ -527,6 +528,7 @@ #include "code\__DEFINES\~~~splurt_defines\spans.dm" #include "code\__DEFINES\~~~splurt_defines\species_clothing_paths.dm" #include "code\__DEFINES\~~~splurt_defines\strippable.dm" +#include "code\__DEFINES\~~~splurt_defines\subsystems.dm" #include "code\__DEFINES\~~~splurt_defines\thirst.dm" #include "code\__DEFINES\~~~splurt_defines\underwear.dm" #include "code\__DEFINES\~~~splurt_defines\dcs\signals.dm" @@ -9470,6 +9472,7 @@ #include "modular_zzplurt\code\controllers\configuration\entries\discord.dm" #include "modular_zzplurt\code\controllers\configuration\entries\fetish_content.dm" #include "modular_zzplurt\code\controllers\subsystem\discord.dm" +#include "modular_zzplurt\code\controllers\subsystem\interactions.dm" #include "modular_zzplurt\code\datums\dna.dm" #include "modular_zzplurt\code\datums\outfit.dm" #include "modular_zzplurt\code\datums\sprite_accessories.dm" @@ -9552,6 +9555,7 @@ #include "modular_zzplurt\code\modules\client\client_procs.dm" #include "modular_zzplurt\code\modules\client\preferences\body_size.dm" #include "modular_zzplurt\code\modules\client\preferences\clothing.dm" +#include "modular_zzplurt\code\modules\client\preferences\erp_preferences.dm" #include "modular_zzplurt\code\modules\client\preferences\genitals.dm" #include "modular_zzplurt\code\modules\client\preferences\mutant_parts.dm" #include "modular_zzplurt\code\modules\client\preferences\player_panel.dm" @@ -9575,6 +9579,44 @@ #include "modular_zzplurt\code\modules\hud\screen_objects.dm" #include "modular_zzplurt\code\modules\language\_language_holder.dm" #include "modular_zzplurt\code\modules\language\buzzwords.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interaction_datum.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\_lewd.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\armpit.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\belly.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\breasts.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\facefuck.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\feet.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\finger.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\frotting.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\fuck.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\grope.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\handjob.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\kiss.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\lick.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\misc.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\mount.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\nipples.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\nipsuck.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\nuts.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\oral.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\slap.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\thighs.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\harmful\_extreme.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\harmful\earfuck.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\harmful\eyefuck.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\self\breasts.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\self\finger.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\self\jack.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\self\oral.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\unholy\_unholy.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\unholy\fart.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\unholy\piss.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\interactions\lewd\unholy\scat.dm" +#include "modular_zzplurt\code\modules\lewd\interaction_menu\lewd_helpers\human.dm" +#include "modular_zzplurt\code\modules\lewd_chemistry\reagents\_aphrodisiac.dm" +#include "modular_zzplurt\code\modules\lewd_chemistry\reagents\belly_enlarger.dm" +#include "modular_zzplurt\code\modules\lewd_chemistry\reagents\butt_enlarger.dm" +#include "modular_zzplurt\code\modules\lewd_chemistry\reagents\camphor.dm" #include "modular_zzplurt\code\modules\mob\mob.dm" #include "modular_zzplurt\code\modules\mob\mob_defines.dm" #include "modular_zzplurt\code\modules\mob\mob_update_icons.dm" @@ -9621,7 +9663,6 @@ #include "modular_zzplurt\code\modules\modular_items\lewd_items\code\lewd_chemistry\reagents\cum.dm" #include "modular_zzplurt\code\modules\movespeed\modifiers\components.dm" #include "modular_zzplurt\code\modules\movespeed\modifiers\innate.dm" -#include "modular_zzplurt\code\modules\reagents\chemistry\reagents.dm" #include "modular_zzplurt\code\modules\reagents\chemistry\machinery\chem_dispenser.dm" #include "modular_zzplurt\code\modules\reagents\chemistry\reagents\food_reagents.dm" #include "modular_zzplurt\code\modules\reagents\chemistry\reagents\other_reagents.dm" diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/splurt/genitals.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/splurt/genitals.tsx index 563f94534fee7..059f3e1b74305 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/splurt/genitals.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/splurt/genitals.tsx @@ -1,6 +1,7 @@ import { CheckboxInput, Feature, + FeatureChoiced, FeatureChoicedServerData, FeatureNumberInput, FeatureNumeric, @@ -107,3 +108,32 @@ export const belly_emissive: Feature = { description: 'Emissive parts glow in the dark.', component: FeatureTriBoolInput, }; + +export const erp_status_pref_extm: FeatureChoiced = { + name: 'Extreme ERP verbs', + component: FeatureDropdownInput, +}; + +export const erp_status_pref_extmharm: FeatureChoiced = { + name: 'Harmful ERP verbs', + component: FeatureDropdownInput, +}; + +export const erp_status_pref_unholy: FeatureChoiced = { + name: 'Unholy ERP verbs', + component: FeatureDropdownInput, +}; + +export const erp_lust_tolerance_pref: FeatureNumeric = { + name: 'Lust tolerance multiplier', + description: + 'Set your lust tolerance multiplier. \n(0.5 = half tolerance, 2 = double tolerance)', + component: FeatureNumberInput, +}; + +export const erp_sexual_potency_pref: FeatureNumeric = { + name: 'Sexual potency multiplier', + description: + 'Set your sexual potency multiplier. \n(0.5 = half potency, 2 = double potency)', + component: FeatureNumberInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/splurt/erp_preferences.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/splurt/erp_preferences.tsx new file mode 100644 index 0000000000000..f551924fb794b --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/splurt/erp_preferences.tsx @@ -0,0 +1,41 @@ +import { CheckboxInput, FeatureToggle } from '../../base'; + +export const butt_enlargement_pref: FeatureToggle = { + name: 'Butt enlargement', + category: 'ERP', + description: + 'Determines if you wish to receive the effects of butt enlargement chemicals.', + component: CheckboxInput, +}; + +export const butt_shrinkage_pref: FeatureToggle = { + name: 'Butt shrinkage', + category: 'ERP', + description: + 'Determines if you wish to receive the effects of butt shrinkage chemicals.', + component: CheckboxInput, +}; + +export const belly_enlargement_pref: FeatureToggle = { + name: 'Belly enlargement', + category: 'ERP', + description: + 'Determines if you wish to receive the effects of belly enlargement chemicals.', + component: CheckboxInput, +}; + +export const belly_shrinkage_pref: FeatureToggle = { + name: 'Belly shrinkage', + category: 'ERP', + description: + 'Determines if you wish to receive the effects of belly shrinkage chemicals.', + component: CheckboxInput, +}; + +export const forced_neverboner_pref: FeatureToggle = { + name: 'Forced neverboner', + category: 'ERP', + description: + 'Determines if you wish to receive the effects of forced neverboner chemicals.', + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/ZubbersCharacterDirectory.jsx b/tgui/packages/tgui/interfaces/ZubbersCharacterDirectory.jsx index 80f61aa885d3d..53b1246bbf077 100644 --- a/tgui/packages/tgui/interfaces/ZubbersCharacterDirectory.jsx +++ b/tgui/packages/tgui/interfaces/ZubbersCharacterDirectory.jsx @@ -236,6 +236,30 @@ const CharacterDirectoryList = (props) => { > Noncon + + Extreme + + + Extreme Harm + + + Unholy + Advert