From 6ff379d144bf0740386a7b0e743f141245069384 Mon Sep 17 00:00:00 2001 From: Comrade Niobe <126028983+ComradeNiobe@users.noreply.github.com> Date: Thu, 1 May 2025 17:30:57 -0400 Subject: [PATCH 01/13] Port Sexcon from Azure This PR ports Sexcon from Azure Peak with heavy adjustments, namely decalization of sex actions. Not everything has been adapted, so still WIP, but it does work! --- maps/shaded_hills/shaded_hills.dm | 2 + mods/content/sexcon/_sexcon.dm | 84 +++ mods/content/sexcon/_sexcon.dme | 10 + mods/content/sexcon/datum/sex_action.dm | 44 ++ .../datum/sex_actions/deviant/facesitting.dm | 45 ++ .../datum/sex_actions/deviant/footjob.dm | 42 ++ .../sex_actions/deviant/force_thighjob.dm | 37 ++ .../datum/sex_actions/deviant/frotting.dm | 40 ++ .../datum/sex_actions/deviant/rub_body.dm | 33 + .../datum/sex_actions/deviant/scissoring.dm | 40 ++ .../datum/sex_actions/deviant/thighjob.dm | 33 + .../datum/sex_actions/deviant/titjob.dm | 37 ++ .../datum/sex_actions/deviant/tonguebath.dm | 34 + .../datum/sex_actions/force/force_blowjob.dm | 50 ++ .../sex_actions/force/force_crotch_nuzzle.dm | 35 + .../sex_actions/force/force_cunnilingus.dm | 43 ++ .../sex_actions/force/force_foot_lick.dm | 31 + .../datum/sex_actions/force/force_footjob.dm | 38 ++ .../sex_actions/force/force_nuzzle_armpit.dm | 35 + .../datum/sex_actions/force/force_rimming.dm | 39 ++ .../sex_actions/force/force_suck_nipples.dm | 53 ++ .../sex_actions/masturbate/masturbate_anus.dm | 32 + .../masturbate/masturbate_breasts.dm | 35 + .../masturbate/masturbate_other_anus.dm | 33 + .../masturbate/masturbate_other_breasts.dm | 36 ++ .../masturbate/masturbate_other_penis.dm | 38 ++ .../masturbate/masturbate_other_vagina.dm | 38 ++ .../masturbate_other_vagina_finger.dm | 38 ++ .../masturbate/masturbate_penis.dm | 40 ++ .../masturbate/masturbate_penis_over.dm | 43 ++ .../masturbate/masturbate_vagina.dm | 37 ++ .../masturbate/masturbate_vagina_finger.dm | 37 ++ .../sexcon/datum/sex_actions/oral/blowjob.dm | 44 ++ .../datum/sex_actions/oral/crotch_nuzzle.dm | 34 + .../datum/sex_actions/oral/cunnilingus.dm | 41 ++ .../datum/sex_actions/oral/foot_lick.dm | 30 + .../sexcon/datum/sex_actions/oral/kissing.dm | 36 ++ .../datum/sex_actions/oral/nuzzle_armpit.dm | 26 + .../sexcon/datum/sex_actions/oral/rimming.dm | 35 + .../datum/sex_actions/oral/suck_balls.dm | 39 ++ .../datum/sex_actions/oral/suck_nipples.dm | 48 ++ .../datum/sex_actions/sex/anal_ride_sex.dm | 51 ++ .../sexcon/datum/sex_actions/sex/anal_sex.dm | 52 ++ .../datum/sex_actions/sex/throat_sex.dm | 53 ++ .../datum/sex_actions/sex/vaginal_ride_sex.dm | 57 ++ .../datum/sex_actions/sex/vaginal_sex.dm | 64 ++ .../sexcon/datum/sex_actions/toy/toy_anal.dm | 39 ++ .../datum/sex_actions/toy/toy_helpers.dm | 9 + .../sexcon/datum/sex_actions/toy/toy_oral.dm | 36 ++ .../datum/sex_actions/toy/toy_other_anal.dm | 38 ++ .../datum/sex_actions/toy/toy_other_oral.dm | 35 + .../sex_actions/toy/toy_other_vaginal.dm | 42 ++ .../datum/sex_actions/toy/toy_vaginal.dm | 43 ++ mods/content/sexcon/datum/sexcon.dm | 603 ++++++++++++++++++ mods/content/sexcon/datum/sexcon_helpers.dm | 83 +++ mods/content/sexcon/sounds/mat/end.ogg | Bin 0 -> 11581 bytes mods/content/sexcon/sounds/mat/endin.ogg | Bin 0 -> 4889 bytes mods/content/sexcon/sounds/mat/endout.ogg | Bin 0 -> 8255 bytes mods/content/sexcon/sounds/mat/fap.ogg | Bin 0 -> 4292 bytes mods/content/sexcon/sounds/mat/fingering.ogg | Bin 0 -> 25107 bytes .../sexcon/sounds/mat/girlmouth (1).ogg | Bin 0 -> 9249 bytes .../sexcon/sounds/mat/girlmouth (2).ogg | Bin 0 -> 7317 bytes .../sexcon/sounds/mat/guymouth (1).ogg | Bin 0 -> 6525 bytes .../sexcon/sounds/mat/guymouth (2).ogg | Bin 0 -> 5528 bytes .../sexcon/sounds/mat/guymouth (3).ogg | Bin 0 -> 6702 bytes .../sexcon/sounds/mat/guymouth (4).ogg | Bin 0 -> 6643 bytes .../sexcon/sounds/mat/guymouth (5).ogg | Bin 0 -> 6000 bytes mods/content/sexcon/sounds/mat/insert (1).ogg | Bin 0 -> 5066 bytes mods/content/sexcon/sounds/mat/insert (2).ogg | Bin 0 -> 7156 bytes .../sexcon/sounds/mat/mouthend (1).ogg | Bin 0 -> 7227 bytes .../sexcon/sounds/mat/mouthend (2).ogg | Bin 0 -> 8036 bytes mods/content/sexcon/sounds/mat/segso.ogg | Bin 0 -> 5862 bytes .../voices/female/sex/horny1loop (1).ogg | Bin 0 -> 318703 bytes .../voices/female/sex/horny1loop (2).ogg | Bin 0 -> 248208 bytes .../voices/female/sex/horny2loop (1).ogg | Bin 0 -> 283642 bytes .../voices/female/sex/horny2loop (2).ogg | Bin 0 -> 245969 bytes .../voices/female/sex/horny3loop (1).ogg | Bin 0 -> 307764 bytes .../voices/female/sex/horny3loop (2).ogg | Bin 0 -> 275096 bytes .../sounds/voices/female/sex/sex (1).ogg | Bin 0 -> 5644 bytes .../sounds/voices/female/sex/sex (2).ogg | Bin 0 -> 7964 bytes .../sounds/voices/female/sex/sex (3).ogg | Bin 0 -> 7976 bytes .../sounds/voices/female/sex/sex (4).ogg | Bin 0 -> 8043 bytes .../sounds/voices/female/sex/sex (5).ogg | Bin 0 -> 14734 bytes .../sounds/voices/female/sex/sex (6).ogg | Bin 0 -> 7439 bytes .../sounds/voices/female/sex/sex (7).ogg | Bin 0 -> 7321 bytes .../sounds/voices/female/sex/sex (8).ogg | Bin 0 -> 5952 bytes .../sounds/voices/female/sex/sexlight (1).ogg | Bin 0 -> 10610 bytes .../sounds/voices/female/sex/sexlight (2).ogg | Bin 0 -> 10482 bytes .../sounds/voices/female/sex/sexlight (3).ogg | Bin 0 -> 7898 bytes .../sounds/voices/female/sex/sexlight (4).ogg | Bin 0 -> 8755 bytes .../sounds/voices/female/sex/sexlight (5).ogg | Bin 0 -> 9046 bytes .../sounds/voices/female/sex/sexlight (6).ogg | Bin 0 -> 10566 bytes .../sounds/voices/female/sex/sexlight (7).ogg | Bin 0 -> 7549 bytes 93 files changed, 2750 insertions(+) create mode 100644 mods/content/sexcon/_sexcon.dm create mode 100644 mods/content/sexcon/_sexcon.dme create mode 100644 mods/content/sexcon/datum/sex_action.dm create mode 100644 mods/content/sexcon/datum/sex_actions/deviant/facesitting.dm create mode 100644 mods/content/sexcon/datum/sex_actions/deviant/footjob.dm create mode 100644 mods/content/sexcon/datum/sex_actions/deviant/force_thighjob.dm create mode 100644 mods/content/sexcon/datum/sex_actions/deviant/frotting.dm create mode 100644 mods/content/sexcon/datum/sex_actions/deviant/rub_body.dm create mode 100644 mods/content/sexcon/datum/sex_actions/deviant/scissoring.dm create mode 100644 mods/content/sexcon/datum/sex_actions/deviant/thighjob.dm create mode 100644 mods/content/sexcon/datum/sex_actions/deviant/titjob.dm create mode 100644 mods/content/sexcon/datum/sex_actions/deviant/tonguebath.dm create mode 100644 mods/content/sexcon/datum/sex_actions/force/force_blowjob.dm create mode 100644 mods/content/sexcon/datum/sex_actions/force/force_crotch_nuzzle.dm create mode 100644 mods/content/sexcon/datum/sex_actions/force/force_cunnilingus.dm create mode 100644 mods/content/sexcon/datum/sex_actions/force/force_foot_lick.dm create mode 100644 mods/content/sexcon/datum/sex_actions/force/force_footjob.dm create mode 100644 mods/content/sexcon/datum/sex_actions/force/force_nuzzle_armpit.dm create mode 100644 mods/content/sexcon/datum/sex_actions/force/force_rimming.dm create mode 100644 mods/content/sexcon/datum/sex_actions/force/force_suck_nipples.dm create mode 100644 mods/content/sexcon/datum/sex_actions/masturbate/masturbate_anus.dm create mode 100644 mods/content/sexcon/datum/sex_actions/masturbate/masturbate_breasts.dm create mode 100644 mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_anus.dm create mode 100644 mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_breasts.dm create mode 100644 mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_penis.dm create mode 100644 mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_vagina.dm create mode 100644 mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_vagina_finger.dm create mode 100644 mods/content/sexcon/datum/sex_actions/masturbate/masturbate_penis.dm create mode 100644 mods/content/sexcon/datum/sex_actions/masturbate/masturbate_penis_over.dm create mode 100644 mods/content/sexcon/datum/sex_actions/masturbate/masturbate_vagina.dm create mode 100644 mods/content/sexcon/datum/sex_actions/masturbate/masturbate_vagina_finger.dm create mode 100644 mods/content/sexcon/datum/sex_actions/oral/blowjob.dm create mode 100644 mods/content/sexcon/datum/sex_actions/oral/crotch_nuzzle.dm create mode 100644 mods/content/sexcon/datum/sex_actions/oral/cunnilingus.dm create mode 100644 mods/content/sexcon/datum/sex_actions/oral/foot_lick.dm create mode 100644 mods/content/sexcon/datum/sex_actions/oral/kissing.dm create mode 100644 mods/content/sexcon/datum/sex_actions/oral/nuzzle_armpit.dm create mode 100644 mods/content/sexcon/datum/sex_actions/oral/rimming.dm create mode 100644 mods/content/sexcon/datum/sex_actions/oral/suck_balls.dm create mode 100644 mods/content/sexcon/datum/sex_actions/oral/suck_nipples.dm create mode 100644 mods/content/sexcon/datum/sex_actions/sex/anal_ride_sex.dm create mode 100644 mods/content/sexcon/datum/sex_actions/sex/anal_sex.dm create mode 100644 mods/content/sexcon/datum/sex_actions/sex/throat_sex.dm create mode 100644 mods/content/sexcon/datum/sex_actions/sex/vaginal_ride_sex.dm create mode 100644 mods/content/sexcon/datum/sex_actions/sex/vaginal_sex.dm create mode 100644 mods/content/sexcon/datum/sex_actions/toy/toy_anal.dm create mode 100644 mods/content/sexcon/datum/sex_actions/toy/toy_helpers.dm create mode 100644 mods/content/sexcon/datum/sex_actions/toy/toy_oral.dm create mode 100644 mods/content/sexcon/datum/sex_actions/toy/toy_other_anal.dm create mode 100644 mods/content/sexcon/datum/sex_actions/toy/toy_other_oral.dm create mode 100644 mods/content/sexcon/datum/sex_actions/toy/toy_other_vaginal.dm create mode 100644 mods/content/sexcon/datum/sex_actions/toy/toy_vaginal.dm create mode 100644 mods/content/sexcon/datum/sexcon.dm create mode 100644 mods/content/sexcon/datum/sexcon_helpers.dm create mode 100644 mods/content/sexcon/sounds/mat/end.ogg create mode 100644 mods/content/sexcon/sounds/mat/endin.ogg create mode 100644 mods/content/sexcon/sounds/mat/endout.ogg create mode 100644 mods/content/sexcon/sounds/mat/fap.ogg create mode 100644 mods/content/sexcon/sounds/mat/fingering.ogg create mode 100644 mods/content/sexcon/sounds/mat/girlmouth (1).ogg create mode 100644 mods/content/sexcon/sounds/mat/girlmouth (2).ogg create mode 100644 mods/content/sexcon/sounds/mat/guymouth (1).ogg create mode 100644 mods/content/sexcon/sounds/mat/guymouth (2).ogg create mode 100644 mods/content/sexcon/sounds/mat/guymouth (3).ogg create mode 100644 mods/content/sexcon/sounds/mat/guymouth (4).ogg create mode 100644 mods/content/sexcon/sounds/mat/guymouth (5).ogg create mode 100644 mods/content/sexcon/sounds/mat/insert (1).ogg create mode 100644 mods/content/sexcon/sounds/mat/insert (2).ogg create mode 100644 mods/content/sexcon/sounds/mat/mouthend (1).ogg create mode 100644 mods/content/sexcon/sounds/mat/mouthend (2).ogg create mode 100644 mods/content/sexcon/sounds/mat/segso.ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/horny1loop (1).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/horny1loop (2).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/horny2loop (1).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/horny2loop (2).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/horny3loop (1).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/horny3loop (2).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sex (1).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sex (2).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sex (3).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sex (4).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sex (5).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sex (6).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sex (7).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sex (8).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sexlight (1).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sexlight (2).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sexlight (3).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sexlight (4).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sexlight (5).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sexlight (6).ogg create mode 100644 mods/content/sexcon/sounds/voices/female/sex/sexlight (7).ogg diff --git a/maps/shaded_hills/shaded_hills.dm b/maps/shaded_hills/shaded_hills.dm index 0d7f2b5f7708..84610e9ba533 100644 --- a/maps/shaded_hills/shaded_hills.dm +++ b/maps/shaded_hills/shaded_hills.dm @@ -9,6 +9,8 @@ #include "../../mods/content/fantasy/_fantasy.dme" #include "../../mods/content/blacksmithy/_blacksmithy.dme" + #include "../../mods/content/sexcon/_sexcon.dme" + #include "areas/_areas.dm" #include "areas/downlands.dm" #include "areas/grassland.dm" diff --git a/mods/content/sexcon/_sexcon.dm b/mods/content/sexcon/_sexcon.dm new file mode 100644 index 000000000000..cf8f2675c9e3 --- /dev/null +++ b/mods/content/sexcon/_sexcon.dm @@ -0,0 +1,84 @@ +/decl/modpack/sexcon + name = "Sex Controller Content" + +/decl/config/toggle/sexcon + uid = "toggle_sexcon" + desc = "Remove the # to enable sex mechanics." + +/decl/config/toggle/sexcon/Initialize() + // Init decls repository for sex actions. + decls_repository.get_decls_of_subtype(/decl/sex_action) + return ..() + +/datum/client_preference/sexable + description = "Sex Mechanics" + key = "SEXCON_TOGGLE" + options = list(PREF_NO, PREF_YES) + +/// DEFINES /// + +#define SEX_ACTION(sex_action_type) RESOLVE_TO_DECL(sex_action_type) + +#define MAX_AROUSAL 150 +#define PASSIVE_EJAC_THRESHOLD 108 +#define ACTIVE_EJAC_THRESHOLD 100 +#define SEX_MAX_CHARGE 300 +#define CHARGE_FOR_CLIMAX 100 +#define AROUSAL_HARD_ON_THRESHOLD 20 +#define CHARGE_RECHARGE_RATE (CHARGE_FOR_CLIMAX / (5 MINUTES)) +#define AROUSAL_TIME_TO_UNHORNY (5 SECONDS) +#define SPENT_AROUSAL_RATE (3 / (1 SECONDS)) +#define IMPOTENT_AROUSAL_LOSS_RATE (3 / (1 SECONDS)) + +#define AROUSAL_HIGH_UNHORNY_RATE (1.5 / (1 SECONDS)) +#define AROUSAL_MID_UNHORNY_RATE (0.4 / (1 SECONDS)) +#define AROUSAL_LOW_UNHORNY_RATE (0.2 / (1 SECONDS)) + +#define MOAN_COOLDOWN 3 SECONDS +#define PAIN_COOLDOWN 6 SECONDS + +#define SEX_SPEED_LOW 1 +#define SEX_SPEED_MID 2 +#define SEX_SPEED_HIGH 3 +#define SEX_SPEED_EXTREME 4 + +#define SEX_SPEED_MIN 1 +#define SEX_SPEED_MAX 4 + +#define SEX_FORCE_LOW 1 +#define SEX_FORCE_MID 2 +#define SEX_FORCE_HIGH 3 +#define SEX_FORCE_EXTREME 4 + +#define SEX_FORCE_MIN 1 +#define SEX_FORCE_MAX 4 + +#define BLUEBALLS_GAIN_THRESHOLD 40 +#define BLUEBALLS_LOOSE_THRESHOLD 35 + +#define PAIN_MILD_EFFECT 10 +#define PAIN_MED_EFFECT 20 +#define PAIN_HIGH_EFFECT 30 +#define PAIN_MINIMUM_FOR_DAMAGE PAIN_MED_EFFECT +#define PAIN_DAMAGE_DIVISOR 50 + +// SEX SOUND LISTS // +var/global/list/sex_moan_heavy = list( + 'mods/content/sexcon/sounds/voices/female/sex/sex (1).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sex (2).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sex (3).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sex (4).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sex (5).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sex (6).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sex (7).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sex (8).ogg' +) +var/global/list/sex_moan_light = list( + 'mods/content/sexcon/sounds/voices/female/sex/sexlight (1).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sexlight (2).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sexlight (3).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sexlight (4).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sexlight (5).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sexlight (6).ogg', + 'mods/content/sexcon/sounds/voices/female/sex/sexlight (7).ogg' +) \ No newline at end of file diff --git a/mods/content/sexcon/_sexcon.dme b/mods/content/sexcon/_sexcon.dme new file mode 100644 index 000000000000..38ca3f62c6df --- /dev/null +++ b/mods/content/sexcon/_sexcon.dme @@ -0,0 +1,10 @@ +#ifndef MODPACK_SEXCON +#define MODPACK_SEXCON +// BEGIN_INCLUDE +#include "_sexcon.dm" +#include "datum\sex_action.dm" +#include "datum\sexcon_helpers.dm" +#include "datum\sexcon.dm" +#include "datum\sex_actions\oral\kissing.dm" +// END_INCLUDE +#endif diff --git a/mods/content/sexcon/datum/sex_action.dm b/mods/content/sexcon/datum/sex_action.dm new file mode 100644 index 000000000000..e5bbf7c9ea0d --- /dev/null +++ b/mods/content/sexcon/datum/sex_action.dm @@ -0,0 +1,44 @@ +/decl/sex_action + decl_flags = DECL_FLAG_MANDATORY_UID // uid = sexcon_[category]_[action] + abstract_type = /decl/sex_action + var/name = "Zodomize" + /// Time to do the act, modified by up to 2.5x speed by the speed toggle + var/do_time = 3.3 SECONDS + /// Whether the act is continous and will be done on repeat + var/continous = TRUE + /// Stamina cost per action, modified by up to 2.5x cost by the force toggle + var/stamina_cost = 0.5 + /// Whether the action requires both participants to be on the same tile + var/check_same_tile = TRUE + /// Whether the same tile check can be bypassed by an aggro grab on the person + var/aggro_grab_instead_same_tile = TRUE + /// Whether the action is forbidden from being done while incapacitated (stun, handcuffed) + var/check_incapacitated = TRUE + /// Whether the action requires an aggressive grab on the victim + var/require_grab = FALSE + /// If a grab is required, this is the required state of it + var/required_grab_state = /decl/grab/normal/aggressive // ? This is obsoleted by the danger_grab flag. + +/decl/sex_action/proc/can_perform(mob/living/human/user, mob/living/human/target) + return TRUE + +/decl/sex_action/proc/on_start(mob/living/human/user, mob/living/human/target) + SHOULD_CALL_PARENT(TRUE) + + var/decl/pronouns/pronouns = user.get_pronouns() + return pronouns + +/decl/sex_action/proc/on_perform(mob/living/human/user, mob/living/human/target) + return + +/decl/sex_action/proc/on_finish(mob/living/human/user, mob/living/human/target) + SHOULD_CALL_PARENT(TRUE) + + var/decl/pronouns/pronouns = user.get_pronouns() + return pronouns + +/decl/sex_action/proc/is_finished(mob/living/human/user, mob/living/human/target) + return FALSE + +/decl/sex_action/proc/shows_on_menu(mob/living/human/user, mob/living/human/target) + return TRUE diff --git a/mods/content/sexcon/datum/sex_actions/deviant/facesitting.dm b/mods/content/sexcon/datum/sex_actions/deviant/facesitting.dm new file mode 100644 index 000000000000..f0beea2b4a07 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/deviant/facesitting.dm @@ -0,0 +1,45 @@ +/decl/sex_action/facesitting + name = "Sit on their face" + +/decl/sex_action/facesitting/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/facesitting/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + // Need to stand up + if(user.resting) + return FALSE + // Target can't stand up + if(!target.resting) + return FALSE + return TRUE + +/decl/sex_action/facesitting/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] sits [user.p_their()] butt on [target]'s face!")) + +/decl/sex_action/facesitting/on_perform(mob/living/human/user, mob/living/human/target) + var/verbstring = pick(list("rubs", "smushes", "forces")) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] [verbstring] [user.p_their()] butt against [target] face.")) + target.make_sucking_noise() + + user.sexcon.perform_sex_action(user, 1, 3, TRUE) + user.sexcon.handle_passive_ejaculation() + + user.sexcon.perform_deepthroat_oxyloss(target, 1.3) + user.sexcon.perform_sex_action(target, 0, 2, FALSE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/facesitting/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] gets off [target]'s face.")) + +/decl/sex_action/facesitting/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/deviant/footjob.dm b/mods/content/sexcon/datum/sex_actions/deviant/footjob.dm new file mode 100644 index 000000000000..a02b95f81111 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/deviant/footjob.dm @@ -0,0 +1,42 @@ +/decl/sex_action/footjob + name = "Jerk them off with feet" + check_same_tile = FALSE + +/decl/sex_action/footjob/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/footjob/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_L_FOOT)) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_R_FOOT)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/footjob/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] puts [user.p_their()] feet on [target]'s cock...")) + +/decl/sex_action/footjob/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] jerks [target]'s cock with [user.p_their()] feet...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(target, 2, 4, TRUE) + + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/footjob/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [user.p_their()] feet off [target]'s cock...")) + +/decl/sex_action/footjob/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/deviant/force_thighjob.dm b/mods/content/sexcon/datum/sex_actions/deviant/force_thighjob.dm new file mode 100644 index 000000000000..aabcfa57b60b --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/deviant/force_thighjob.dm @@ -0,0 +1,37 @@ +/decl/sex_action/force_thighjob + name = "Jerk them off with thighs" + +/decl/sex_action/force_thighjob/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/force_thighjob/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/force_thighjob/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] moves [user.p_their()] thighs between [target]'s cock...")) + +/decl/sex_action/force_thighjob/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] jerks [target]'s cock with [user.p_their()] thighs...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(target, 2, 4, TRUE) + + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/force_thighjob/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops jerking [target] off with [user.p_their()] thighs...")) + +/decl/sex_action/force_thighjob/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/deviant/frotting.dm b/mods/content/sexcon/datum/sex_actions/deviant/frotting.dm new file mode 100644 index 000000000000..851e7334b88f --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/deviant/frotting.dm @@ -0,0 +1,40 @@ +/decl/sex_action/frotting + name = "Frot them" + +/decl/sex_action/frotting/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return + return TRUE + +/decl/sex_action/frotting/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/frotting/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] shoves [user.p_their()] cock against [target]'s own!")) + +/decl/sex_action/frotting/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] frots cocks together with [target].")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 20, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 1, 4, TRUE) + user.sexcon.handle_passive_ejaculation() + + user.sexcon.perform_sex_action(target, 1, 4, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/frotting/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] lets go of both their cocks.")) diff --git a/mods/content/sexcon/datum/sex_actions/deviant/rub_body.dm b/mods/content/sexcon/datum/sex_actions/deviant/rub_body.dm new file mode 100644 index 000000000000..10683a57f38e --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/deviant/rub_body.dm @@ -0,0 +1,33 @@ +/decl/sex_action/rub_body + name = "Rub their body" + check_same_tile = FALSE + +/decl/sex_action/rub_body/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/rub_body/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_CHEST, TRUE)) + return FALSE + return TRUE + +/decl/sex_action/rub_body/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] places [user.p_their()] hands onto [target]...")) + +/decl/sex_action/rub_body/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] rubs [target]'s body...")) + user.make_sucking_noise() + + user.sexcon.perform_sex_action(target, 0.5, 0, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/rub_body/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops rubbing [target]'s body ...")) + +/decl/sex_action/rub_body/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/deviant/scissoring.dm b/mods/content/sexcon/datum/sex_actions/deviant/scissoring.dm new file mode 100644 index 000000000000..26b0a5816049 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/deviant/scissoring.dm @@ -0,0 +1,40 @@ +/decl/sex_action/scissoring + name = "Scissor them" + +/decl/sex_action/scissoring/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_VAGINA)) + return + if(!target.getorganslot(ORGAN_SLOT_VAGINA)) + return + return TRUE + +/decl/sex_action/scissoring/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/scissoring/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] spreads [user.p_their()] legs and aligns [user.p_their()] cunt against [target]'s own!")) + +/decl/sex_action/scissoring/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] scissors with [target]'s cunt.")) + playsound(target, 'mods/content/sexcon/sounds/mat/segso.ogg', 50, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 1, 4, TRUE) + user.sexcon.handle_passive_ejaculation() + + user.sexcon.perform_sex_action(target, 1, 4, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/scissoring/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops scissoring with [target].")) diff --git a/mods/content/sexcon/datum/sex_actions/deviant/thighjob.dm b/mods/content/sexcon/datum/sex_actions/deviant/thighjob.dm new file mode 100644 index 000000000000..4391575fee15 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/deviant/thighjob.dm @@ -0,0 +1,33 @@ +/decl/sex_action/thighjob + name = "Use their thighs to get off" + +/decl/sex_action/thighjob/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return + return TRUE + +/decl/sex_action/thighjob/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/thighjob/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] grabs [target]'s thighs and shoves [user.p_their()] cock inbetween!")) + +/decl/sex_action/thighjob/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] fucks [target]'s thighs.")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 20, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 4, TRUE) + user.sexcon.handle_passive_ejaculation() + +/decl/sex_action/thighjob/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [user.p_their()] cock out from inbetween [target]'s thighs.")) diff --git a/mods/content/sexcon/datum/sex_actions/deviant/titjob.dm b/mods/content/sexcon/datum/sex_actions/deviant/titjob.dm new file mode 100644 index 000000000000..a3e40bc1cf2d --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/deviant/titjob.dm @@ -0,0 +1,37 @@ +/decl/sex_action/titjob + name = "Use their tits to get off" + +/decl/sex_action/titjob/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return + if(!target.getorganslot(ORGAN_SLOT_BREASTS)) + return + return TRUE + +/decl/sex_action/titjob/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_CHEST)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_BREASTS)) + return FALSE + return TRUE + +/decl/sex_action/titjob/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] grabs [target]'s tits and shoves [user.p_their()] cock inbetween!")) + +/decl/sex_action/titjob/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] fucks [target]'s tits.")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 20, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 4, TRUE) + user.sexcon.handle_passive_ejaculation() + +/decl/sex_action/titjob/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [user.p_their()] cock out from inbetween [target]'s tits.")) diff --git a/mods/content/sexcon/datum/sex_actions/deviant/tonguebath.dm b/mods/content/sexcon/datum/sex_actions/deviant/tonguebath.dm new file mode 100644 index 000000000000..a6b1c3fadb2c --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/deviant/tonguebath.dm @@ -0,0 +1,34 @@ +/decl/sex_action/tonguebath + name = "Bathe with tongue" + +/decl/sex_action/tonguebath/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/tonguebath/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN)) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + return TRUE + +/decl/sex_action/tonguebath/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] sticks [user.p_their()] tongue out, getting close to [target]...")) + +/decl/sex_action/tonguebath/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] bathes [target]'s body with [user.p_their()] tongue...")) + user.make_sucking_noise() + + user.sexcon.perform_sex_action(target, 0.5, 0, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/tonguebath/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops bathing [target]'s body ...")) + +/decl/sex_action/tonguebath/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/force/force_blowjob.dm b/mods/content/sexcon/datum/sex_actions/force/force_blowjob.dm new file mode 100644 index 000000000000..dbadaae63746 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/force/force_blowjob.dm @@ -0,0 +1,50 @@ +/decl/sex_action/force_blowjob + name = "Force them to suck" + require_grab = TRUE + stamina_cost = 1.0 + +/decl/sex_action/force_blowjob/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/force_blowjob/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + if(!user.sexcon.can_use_penis()) + return + return TRUE + +/decl/sex_action/force_blowjob/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] forces [target]'s head down to swallow and suck on [user.p_their()] cock!")) + playsound(target, list('mods/content/sexcon/sounds/mat/insert (1).ogg','mods/content/sexcon/sounds/mat/insert (2).ogg'), 20, TRUE, ignore_walls = FALSE) + +/decl/sex_action/force_blowjob/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] forces [target] to suck [user.p_their()] cock.")) + target.make_sucking_noise() + + user.sexcon.perform_sex_action(user, 2, 4, TRUE) + if(user.sexcon.check_active_ejaculation()) + user.visible_message(span_love("cums into [target]'s throat!")) + user.sexcon.cum_into() + + user.sexcon.perform_sex_action(target, 0, 7, FALSE) + if(!user.sexcon.considered_limp()) + user.sexcon.perform_deepthroat_oxyloss(target, 1.3) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/force_blowjob/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [user.p_their()] cock out of [target]'s throat.")) + +/decl/sex_action/force_blowjob/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/force/force_crotch_nuzzle.dm b/mods/content/sexcon/datum/sex_actions/force/force_crotch_nuzzle.dm new file mode 100644 index 000000000000..193d89b6078e --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/force/force_crotch_nuzzle.dm @@ -0,0 +1,35 @@ +/decl/sex_action/force_crotch_nuzzle + name = "Force them to nuzzle" + require_grab = TRUE + stamina_cost = 1.0 + +/decl/sex_action/force_crotch_nuzzle/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/force_crotch_nuzzle/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + return TRUE + +/decl/sex_action/force_crotch_nuzzle/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] forces [target]'s head against [user.p_their()] crotch!")) + +/decl/sex_action/force_crotch_nuzzle/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] forces [target] to nuzzle [user.p_their()] crotch.")) + + user.sexcon.perform_sex_action(user, 0.5, 0, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/force_crotch_nuzzle/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [target]'s head away from [user.p_their()] crotch.")) + +/decl/sex_action/force_crotch_nuzzle/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/force/force_cunnilingus.dm b/mods/content/sexcon/datum/sex_actions/force/force_cunnilingus.dm new file mode 100644 index 000000000000..3af379788a4a --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/force/force_cunnilingus.dm @@ -0,0 +1,43 @@ +/decl/sex_action/force_cunnilingus + name = "Force them to suck" + require_grab = TRUE + stamina_cost = 1.0 + +/decl/sex_action/force_cunnilingus/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/force_cunnilingus/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/force_cunnilingus/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] forces [target]'s head against [user.p_their()] cunt!")) + +/decl/sex_action/force_cunnilingus/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] forces [target] to suck [user.p_their()] cunt.")) + target.make_sucking_noise() + + user.sexcon.perform_sex_action(user, 2, 4, TRUE) + user.sexcon.handle_passive_ejaculation() + + user.sexcon.perform_sex_action(target, 0, 2, FALSE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/force_cunnilingus/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [target]'s head away.")) + +/decl/sex_action/force_cunnilingus/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/force/force_foot_lick.dm b/mods/content/sexcon/datum/sex_actions/force/force_foot_lick.dm new file mode 100644 index 000000000000..8612edf0ff7c --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/force/force_foot_lick.dm @@ -0,0 +1,31 @@ +/decl/sex_action/force_foot_lick + name = "Force them to lick your feet" + check_same_tile = FALSE + require_grab = TRUE + stamina_cost = 1.0 + +/decl/sex_action/force_foot_lick/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/force_foot_lick/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_L_FOOT)) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_R_FOOT)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + return TRUE + +/decl/sex_action/force_foot_lick/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] shoves [user.p_their()] feet against [target]'s head!")) + +/decl/sex_action/force_foot_lick/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] forces [target] to lick [user.p_their()] feet.")) + target.make_sucking_noise() + +/decl/sex_action/force_foot_lick/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [user.p_their()] feet away from [target]'s head.")) diff --git a/mods/content/sexcon/datum/sex_actions/force/force_footjob.dm b/mods/content/sexcon/datum/sex_actions/force/force_footjob.dm new file mode 100644 index 000000000000..5a693f03f39a --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/force/force_footjob.dm @@ -0,0 +1,38 @@ +/decl/sex_action/force_footjob + name = "Use their feet to get off" + check_same_tile = FALSE + require_grab = TRUE + stamina_cost = 1.0 + +/decl/sex_action/force_footjob/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + //if(!user.getorganslot(ORGAN_SLOT_PENIS)) + // return + return TRUE + +/decl/sex_action/force_footjob/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_L_FOOT)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_R_FOOT)) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return + return TRUE + +/decl/sex_action/force_footjob/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] grabs [target]'s feet and clamps them around [user.p_their()] cock!")) + +/decl/sex_action/force_footjob/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] uses [target] feet to jerk off.")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 20, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 4, TRUE) + user.sexcon.handle_passive_ejaculation() + +/decl/sex_action/force_footjob/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [user.p_their()] cock out from inbetween [target]'s feet.")) diff --git a/mods/content/sexcon/datum/sex_actions/force/force_nuzzle_armpit.dm b/mods/content/sexcon/datum/sex_actions/force/force_nuzzle_armpit.dm new file mode 100644 index 000000000000..021378d3ef15 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/force/force_nuzzle_armpit.dm @@ -0,0 +1,35 @@ +/decl/sex_action/force_armpit_nuzzle + name = "Force them against armpit" + require_grab = TRUE + stamina_cost = 1.0 + +/decl/sex_action/force_armpit_nuzzle/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/force_armpit_nuzzle/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_CHEST, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + return TRUE + +/decl/sex_action/force_armpit_nuzzle/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] forces [target]'s head against [user.p_their()] armpit!")) + +/decl/sex_action/force_armpit_nuzzle/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] forces [target] to nuzzle [user.p_their()] armpit.")) + + user.sexcon.perform_sex_action(user, 0.5, 0, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/force_armpit_nuzzle/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [target]'s head away from [user.p_their()] armpit.")) + +/decl/sex_action/force_armpit_nuzzle/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/force/force_rimming.dm b/mods/content/sexcon/datum/sex_actions/force/force_rimming.dm new file mode 100644 index 000000000000..f576de702310 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/force/force_rimming.dm @@ -0,0 +1,39 @@ +/decl/sex_action/force_rimming + name = "Force them to rim you" + require_grab = TRUE + stamina_cost = 1.0 + +/decl/sex_action/force_rimming/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/force_rimming/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + return TRUE + +/decl/sex_action/force_rimming/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] shoves [target]'s head against [user.p_their()] butt!")) + +/decl/sex_action/force_rimming/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] forces [target] to rim [user.p_their()] butt.")) + target.make_sucking_noise() + + user.sexcon.perform_sex_action(user, 2, 4, TRUE) + user.sexcon.handle_passive_ejaculation() + + user.sexcon.perform_sex_action(target, 0, 2, FALSE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/force_rimming/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [target]'s head away from [user.p_their()] butt.")) + +/decl/sex_action/force_rimming/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/force/force_suck_nipples.dm b/mods/content/sexcon/datum/sex_actions/force/force_suck_nipples.dm new file mode 100644 index 000000000000..ae90e73b8957 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/force/force_suck_nipples.dm @@ -0,0 +1,53 @@ +/decl/sex_action/force_suck_nipples + name = "Force them to suck nipples" + require_grab = TRUE + stamina_cost = 1.0 + +/decl/sex_action/force_suck_nipples/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_BREASTS)) + return FALSE + return TRUE + +/decl/sex_action/force_suck_nipples/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_CHEST, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_BREASTS)) + return FALSE + return TRUE + +/decl/sex_action/force_suck_nipples/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] forces [target]'s head down to swallow and suck on [user.p_their()] nipples!")) + playsound(target, list('mods/content/sexcon/sounds/mat/insert (1).ogg','mods/content/sexcon/sounds/mat/insert (2).ogg'), 20, TRUE, ignore_walls = FALSE) + +/decl/sex_action/force_suck_nipples/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] forces [target] to suck [user.p_their()] nipples.")) + target.make_sucking_noise() + + user.sexcon.perform_sex_action(user, 2, 4, TRUE) + + user.sexcon.perform_sex_action(target, 0, 7, FALSE) + if(!user.sexcon.considered_limp()) + user.sexcon.perform_deepthroat_oxyloss(target, 0.6) + target.sexcon.handle_passive_ejaculation() + + var/obj/item/organ/breasts/breasts = user.getorganslot(ORGAN_SLOT_BREASTS) + var/milk_to_add = min(max(breasts.breast_size, 1), breasts.milk_stored) + if(breasts.lactating && milk_to_add > 0 && prob(25)) + target.reagents.add_reagent(/datum/reagent/consumable/milk, milk_to_add) + breasts.milk_stored -= milk_to_add + to_chat(target, span_notice("I can taste milk.")) + to_chat(user, span_notice("I can feel milk leak from my buds.")) + +/decl/sex_action/force_suck_nipples/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [user.p_their()] nipples out of [target]'s mouth.")) + +/decl/sex_action/force_suck_nipples/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_anus.dm b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_anus.dm new file mode 100644 index 000000000000..11f1ec6b2fa5 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_anus.dm @@ -0,0 +1,32 @@ +/decl/sex_action/masturbate_anus + name = "Finger butt" + +/decl/sex_action/masturbate_anus/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user != target) + return FALSE + return TRUE + +/decl/sex_action/masturbate_anus/can_perform(mob/living/human/user, mob/living/human/target) + if(user != target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_anus/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts fingering [user.p_their()] butt...")) + +/decl/sex_action/masturbate_anus/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] fingers [user.p_their()] butt...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 6, TRUE) + user.sexcon.handle_passive_ejaculation() + +/decl/sex_action/masturbate_anus/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops fingering [user.p_their()] butt.")) + +/decl/sex_action/masturbate_anus/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_breasts.dm b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_breasts.dm new file mode 100644 index 000000000000..5ba7b2868ae6 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_breasts.dm @@ -0,0 +1,35 @@ +/decl/sex_action/masturbate_breasts + name = "Rub breasts" + +/decl/sex_action/masturbate_breasts/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user != target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_BREASTS)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_breasts/can_perform(mob/living/user, mob/living/target) + if(user != target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_CHEST, TRUE)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_BREASTS)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_breasts/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts rubbing [user.p_their()] breasts...")) + +/decl/sex_action/masturbate_breasts/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] fondles [user.p_their()] breasts...")) + + user.sexcon.perform_sex_action(user, 1, 4, TRUE) + user.sexcon.handle_passive_ejaculation() + +/decl/sex_action/masturbate_breasts/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops fondling [user.p_their()] breasts.")) + +/decl/sex_action/masturbate_breasts/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_anus.dm b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_anus.dm new file mode 100644 index 000000000000..a04ce6632d64 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_anus.dm @@ -0,0 +1,33 @@ +/decl/sex_action/masturbate_other_anus + name = "Finger their butt" + check_same_tile = FALSE + +/decl/sex_action/masturbate_other_anus/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/masturbate_other_anus/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_other_anus/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts fingering [target]'s butt...")) + +/decl/sex_action/masturbate_other_anus/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] fingers [target]'s butt...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(target, 2, 6, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/masturbate_other_anus/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops fingering [target]'s butt.")) + +/decl/sex_action/masturbate_other_anus/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_breasts.dm b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_breasts.dm new file mode 100644 index 000000000000..ebc3f30da0dd --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_breasts.dm @@ -0,0 +1,36 @@ +/decl/sex_action/masturbate_other_breasts + name = "Rub their breasts" + check_same_tile = FALSE + +/decl/sex_action/masturbate_other_breasts/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_BREASTS)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_other_breasts/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_CHEST, TRUE)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_BREASTS)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_other_breasts/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts rubbing [target]'s breasts...")) + +/decl/sex_action/masturbate_other_breasts/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] fondles [target]'s breasts...")) + + user.sexcon.perform_sex_action(target, 1, 4, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/masturbate_other_breasts/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops stroking [target]'s breasts.")) + +/decl/sex_action/masturbate_other_breasts/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_penis.dm b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_penis.dm new file mode 100644 index 000000000000..b10ccca1ef74 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_penis.dm @@ -0,0 +1,38 @@ +/decl/sex_action/masturbate_penis_other + name = "Jerk them off" + check_same_tile = FALSE + +/decl/sex_action/masturbate_penis_other/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_penis_other/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_penis_other/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts jerking [target]'s off...")) + +/decl/sex_action/masturbate_penis_other/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] jerks [target]'s cock off...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(target, 2, 0, TRUE) + + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/masturbate_penis_other/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops jerking [target]'s off.")) + +/decl/sex_action/masturbate_penis_other/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_vagina.dm b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_vagina.dm new file mode 100644 index 000000000000..4716785a3d98 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_vagina.dm @@ -0,0 +1,38 @@ +/decl/sex_action/masturbate_other_vagina + name = "Stroke their clit" + check_same_tile = FALSE + +/decl/sex_action/masturbate_other_vagina/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_other_vagina/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_other_vagina/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts stroking [target]'s clit...")) + +/decl/sex_action/masturbate_other_vagina/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] strokes [target]'s clit...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(target, 2, 4, TRUE) + + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/masturbate_other_vagina/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops stroking [target]'s clit.")) + +/decl/sex_action/masturbate_other_vagina/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_vagina_finger.dm b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_vagina_finger.dm new file mode 100644 index 000000000000..4c636eabfea7 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_other_vagina_finger.dm @@ -0,0 +1,38 @@ +/decl/sex_action/masturbate_other_vagina_finger + name = "Finger their pussy" + check_same_tile = FALSE + +/decl/sex_action/masturbate_other_vagina_finger/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_other_vagina_finger/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_other_vagina_finger/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts fingering [target]'s [pick("slit","cunt","pussy","snatch")]...")) + +/decl/sex_action/masturbate_other_vagina_finger/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] fingers [target]'s [pick("slit","cunt","pussy","snatch")]...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(target, 2, 4, TRUE) + + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/masturbate_other_vagina_finger/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops fingering [target]'s [pick("slit","cunt","pussy","snatch")].")) + +/decl/sex_action/masturbate_other_vagina_finger/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_penis.dm b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_penis.dm new file mode 100644 index 000000000000..93b926d77d42 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_penis.dm @@ -0,0 +1,40 @@ +/decl/sex_action/masturbate_penis + name = "Jerk off" + +/decl/sex_action/masturbate_penis/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user != target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_penis/can_perform(mob/living/human/user, mob/living/human/target) + if(user != target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + if(!user.sexcon.can_use_penis()) + return + return TRUE + +/decl/sex_action/masturbate_penis/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts jerking off...")) + +/decl/sex_action/masturbate_penis/on_perform(mob/living/human/user, mob/living/human/target) + var/chosen_verb = pick(list("jerks [user.p_their()] cock", "strokes [user.p_their()] cock", "masturbates", "jerks off")) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] [chosen_verb]...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 0, TRUE) + + user.sexcon.handle_passive_ejaculation() + +/decl/sex_action/masturbate_penis/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops jerking off.")) + +/decl/sex_action/masturbate_penis/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_penis_over.dm b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_penis_over.dm new file mode 100644 index 000000000000..ec2dc55efb1e --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_penis_over.dm @@ -0,0 +1,43 @@ +/decl/sex_action/masturbate_penis_over + name = "Jerk over them" + check_same_tile = FALSE + +/decl/sex_action/masturbate_penis_over/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_penis_over/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + if(!user.sexcon.can_use_penis()) + return + return TRUE + +/decl/sex_action/masturbate_penis_over/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts jerking over [target]...")) + +/decl/sex_action/masturbate_penis_over/on_perform(mob/living/human/user, mob/living/human/target) + var/chosen_verb = pick(list("jerks [user.p_their()] cock", "strokes [user.p_their()] cock", "masturbates", "jerks off")) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] [chosen_verb] over [target]")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 4, TRUE) + + if(user.sexcon.check_active_ejaculation()) + user.visible_message(span_love("[user] cums over [target]'s body!")) + user.sexcon.cum_onto() + +/decl/sex_action/masturbate_penis_over/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops jerking off.")) + +/decl/sex_action/masturbate_penis_over/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_vagina.dm b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_vagina.dm new file mode 100644 index 000000000000..04c7085dc189 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_vagina.dm @@ -0,0 +1,37 @@ +/decl/sex_action/masturbate_vagina + name = "Stroke clit" + +/decl/sex_action/masturbate_vagina/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user != target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_vagina/can_perform(mob/living/user, mob/living/target) + if(user != target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_vagina/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts stroking [user.p_their()] clit...")) + +/decl/sex_action/masturbate_vagina/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] strokes [user.p_their()] clit...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 4, TRUE) + + user.sexcon.handle_passive_ejaculation() + +/decl/sex_action/masturbate_vagina/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops stroking.")) + +/decl/sex_action/masturbate_vagina/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_vagina_finger.dm b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_vagina_finger.dm new file mode 100644 index 000000000000..d7d7ef74e134 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/masturbate/masturbate_vagina_finger.dm @@ -0,0 +1,37 @@ +/decl/sex_action/masturbate_vagina_finger + name = "Finger pussy" + +/decl/sex_action/masturbate_vagina_finger/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user != target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_vagina_finger/can_perform(mob/living/user, mob/living/target) + if(user != target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/masturbate_vagina_finger/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts fingering [user.p_their()] [pick("slit","cunt","pussy","snatch")]...")) + +/decl/sex_action/masturbate_vagina_finger/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] fingers [user.p_their()] [pick("slit","cunt","pussy","snatch")]...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 4, TRUE) + + user.sexcon.handle_passive_ejaculation() + +/decl/sex_action/masturbate_vagina_finger/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops fingering.")) + +/decl/sex_action/masturbate_vagina_finger/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/oral/blowjob.dm b/mods/content/sexcon/datum/sex_actions/oral/blowjob.dm new file mode 100644 index 000000000000..9bb75d104821 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/oral/blowjob.dm @@ -0,0 +1,44 @@ +/decl/sex_action/blowjob + name = "Suck them off" + uid = "sexcon_oral_blowjob" + check_same_tile = FALSE + +/decl/sex_action/blowjob/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/blowjob/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/blowjob/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts sucking [target]'s cock...")) + +/decl/sex_action/blowjob/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] sucks [target]'s cock...")) + user.make_sucking_noise() + + user.sexcon.perform_sex_action(target, 2, 0, TRUE) + if(!target.sexcon.considered_limp()) + user.sexcon.perform_deepthroat_oxyloss(user, 1.3) + if(target.sexcon.check_active_ejaculation()) + target.visible_message(span_love("[target] cums into [user]'s mouth!")) + target.sexcon.cum_into() + +/decl/sex_action/blowjob/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops sucking [target]'s cock ...")) + +/decl/sex_action/blowjob/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/oral/crotch_nuzzle.dm b/mods/content/sexcon/datum/sex_actions/oral/crotch_nuzzle.dm new file mode 100644 index 000000000000..5d1809e78c25 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/oral/crotch_nuzzle.dm @@ -0,0 +1,34 @@ +/decl/sex_action/crotch_nuzzle + name = "Nuzzle their crotch" + uid = "sexcon_oral_crotchnuzzle" + +/decl/sex_action/crotch_nuzzle/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/crotch_nuzzle/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + return TRUE + +/decl/sex_action/crotch_nuzzle/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] moves [user.p_their()] head against [target]'s crotch...")) + +/decl/sex_action/crotch_nuzzle/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] nuzzles [target]'s crotch...")) + + user.sexcon.perform_sex_action(target, 0.5, 0, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/crotch_nuzzle/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops nuzzling [target]'s crotch...")) + +/decl/sex_action/crotch_nuzzle/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/oral/cunnilingus.dm b/mods/content/sexcon/datum/sex_actions/oral/cunnilingus.dm new file mode 100644 index 000000000000..5c6d74e8d7e1 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/oral/cunnilingus.dm @@ -0,0 +1,41 @@ +/decl/sex_action/cunnilingus + name = "Suck their cunt off" + uid = "sexcon_oral_cunnilngus" + +/decl/sex_action/cunnilingus/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/cunnilingus/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + return TRUE + +/decl/sex_action/cunnilingus/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts sucking [target]'s clit...")) + +/decl/sex_action/cunnilingus/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] sucks [target]'s clit...")) + user.make_sucking_noise() + + user.sexcon.perform_sex_action(target, 2, 3, TRUE) + if(target.sexcon.check_active_ejaculation()) + target.visible_message(span_love("[target] ejaculates into [user]'s mouth!")) + target.sexcon.cum_into() + +/decl/sex_action/cunnilingus/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops sucking [target]'s clit ...")) + +/decl/sex_action/cunnilingus/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/oral/foot_lick.dm b/mods/content/sexcon/datum/sex_actions/oral/foot_lick.dm new file mode 100644 index 000000000000..4e23959760e1 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/oral/foot_lick.dm @@ -0,0 +1,30 @@ +/decl/sex_action/foot_lick + name = "Lick their feet" + check_same_tile = FALSE + uid = "sexcon_oral_footlick" + +/decl/sex_action/foot_lick/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/foot_lick/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_R_FOOT)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_L_FOOT)) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + return TRUE + +/decl/sex_action/foot_lick/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts licking [target]'s feet...")) + +/decl/sex_action/foot_lick/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] licks [target]'s feet...")) + user.make_sucking_noise() + +/decl/sex_action/foot_lick/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops licking [target]'s feet ...")) diff --git a/mods/content/sexcon/datum/sex_actions/oral/kissing.dm b/mods/content/sexcon/datum/sex_actions/oral/kissing.dm new file mode 100644 index 000000000000..6ab78a842517 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/oral/kissing.dm @@ -0,0 +1,36 @@ +/decl/sex_action/kissing + name = "Make out with them" + uid = "sexcon_oral_kissing" + check_same_tile = FALSE + +/decl/sex_action/kissing/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/kissing/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(target.check_mouth_coverage()) + return FALSE + if(user.check_mouth_coverage()) + return FALSE + return TRUE + +/decl/sex_action/kissing/on_start(mob/living/human/user, mob/living/human/target) + ..() + user.visible_message(SPAN_WARNING("[user] starts making out with [target]...")) + +/decl/sex_action/kissing/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] makes out with [target]...")) + user.make_sucking_noise() + + user.sexcon.perform_sex_action(user, 1, 2, TRUE) + user.sexcon.handle_passive_ejaculation() + + user.sexcon.perform_sex_action(target, 1, 2, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/kissing/on_finish(mob/living/human/user, mob/living/human/target) + ..() + user.visible_message(SPAN_WARNING("[user] stops making out with [target] ...")) diff --git a/mods/content/sexcon/datum/sex_actions/oral/nuzzle_armpit.dm b/mods/content/sexcon/datum/sex_actions/oral/nuzzle_armpit.dm new file mode 100644 index 000000000000..f3f33240a362 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/oral/nuzzle_armpit.dm @@ -0,0 +1,26 @@ +/decl/sex_action/armpit_nuzzle + name = "Nuzzle their armpit" + uid = "sexcon_oral_armpitnuzzle" + +/decl/sex_action/armpit_nuzzle/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/armpit_nuzzle/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_CHEST)) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + return TRUE + +/decl/sex_action/armpit_nuzzle/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] moves [user.p_their()] head against [target]'s armpit...")) + +/decl/sex_action/armpit_nuzzle/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] nuzzles [target]'s armpit...")) + +/decl/sex_action/armpit_nuzzle/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops nuzzling [target]'s armpit...")) diff --git a/mods/content/sexcon/datum/sex_actions/oral/rimming.dm b/mods/content/sexcon/datum/sex_actions/oral/rimming.dm new file mode 100644 index 000000000000..995a24844e00 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/oral/rimming.dm @@ -0,0 +1,35 @@ +/decl/sex_action/rimming + name = "Rim them" + uid = "sexcon_oral_rimming" + +/decl/sex_action/rimming/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + return TRUE + +/decl/sex_action/rimming/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + return TRUE + +/decl/sex_action/rimming/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts rimming [target]'s butt...")) + +/decl/sex_action/rimming/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] rims [target]'s butt...")) + user.make_sucking_noise() + + user.sexcon.perform_sex_action(target, 2, 0, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/rimming/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops rimming [target]'s butt ...")) + +/decl/sex_action/rimming/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/oral/suck_balls.dm b/mods/content/sexcon/datum/sex_actions/oral/suck_balls.dm new file mode 100644 index 000000000000..a86a988d3fe1 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/oral/suck_balls.dm @@ -0,0 +1,39 @@ +/decl/sex_action/suck_balls + name = "Suck their balls" + uid = "sexcon_oral_suckballs" + +/decl/sex_action/suck_balls/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_TESTICLES)) + return FALSE + return TRUE + +/decl/sex_action/suck_balls/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_TESTICLES)) + return FALSE + return TRUE + +/decl/sex_action/suck_balls/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts sucking [target]'s balls...")) + +/decl/sex_action/suck_balls/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] sucks [target]'s balls...")) + user.make_sucking_noise() + + user.sexcon.perform_sex_action(target, 1, 3, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/suck_balls/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops sucking [target]'s balls ...")) + +/decl/sex_action/suck_balls/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/oral/suck_nipples.dm b/mods/content/sexcon/datum/sex_actions/oral/suck_nipples.dm new file mode 100644 index 000000000000..232b98707fec --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/oral/suck_nipples.dm @@ -0,0 +1,48 @@ +/decl/sex_action/suck_nipples + name = "Suck their nipples" + uid = "sexcon_oral_sucknipples" + check_same_tile = FALSE + +/decl/sex_action/suck_nipples/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_BREASTS)) + return FALSE + return TRUE + +/decl/sex_action/suck_nipples/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_CHEST, TRUE)) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_BREASTS)) + return FALSE + return TRUE + +/decl/sex_action/suck_nipples/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] starts sucking [target]'s nipples...")) + +/decl/sex_action/suck_nipples/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] sucks [target]'s nipples...")) + user.make_sucking_noise() + + user.sexcon.perform_sex_action(target, 1, 3, TRUE) + target.sexcon.handle_passive_ejaculation() + + var/obj/item/organ/breasts/breasts = target.getorganslot(ORGAN_SLOT_BREASTS) + var/milk_to_add = min(max(breasts.breast_size, 1), breasts.milk_stored) + if(breasts.lactating && milk_to_add > 0 && prob(25)) + user.reagents.add_reagent(/datum/reagent/consumable/milk, milk_to_add) + breasts.milk_stored -= milk_to_add + to_chat(user, span_notice("I can taste milk.")) + to_chat(target, span_notice("I can feel milk leak from my buds.")) + +/decl/sex_action/suck_nipples/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] stops sucking [target]'s nipples ...")) + +/decl/sex_action/suck_nipples/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/sex/anal_ride_sex.dm b/mods/content/sexcon/datum/sex_actions/sex/anal_ride_sex.dm new file mode 100644 index 000000000000..e40da6702971 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/sex/anal_ride_sex.dm @@ -0,0 +1,51 @@ +/decl/sex_action/anal_ride_sex + name = "Ride them anally" + uid = "sexcon_sex_analride" + stamina_cost = 1.0 + aggro_grab_instead_same_tile = FALSE + +/decl/sex_action/anal_ride_sex/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/anal_ride_sex/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/anal_ride_sex/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] gets on top of [target] and begins riding [target.p_them()] with [user.p_their()] butt!")) + playsound(target, list('mods/content/sexcon/sounds/mat/insert (1).ogg','mods/content/sexcon/sounds/mat/insert (2).ogg'), 20, TRUE, ignore_walls = FALSE) + +/decl/sex_action/anal_ride_sex/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] rides [target].")) + playsound(target, 'mods/content/sexcon/sounds/mat/segso.ogg', 50, TRUE, -2, ignore_walls = FALSE) + + if(target.sexcon.considered_limp()) + user.sexcon.perform_sex_action(target, 1.2, 4, TRUE) + else + user.sexcon.perform_sex_action(target, 2.4, 9, TRUE) + user.sexcon.handle_passive_ejaculation() + + user.sexcon.perform_sex_action(target, 2, 4, FALSE) + if(target.sexcon.check_active_ejaculation()) + target.visible_message(span_love("[target] cums into [user]'s butt!")) + target.sexcon.cum_into() + target.virginity = FALSE + +/decl/sex_action/anal_ride_sex/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] gets off [target].")) + +/decl/sex_action/anal_ride_sex/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/sex/anal_sex.dm b/mods/content/sexcon/datum/sex_actions/sex/anal_sex.dm new file mode 100644 index 000000000000..476478224c65 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/sex/anal_sex.dm @@ -0,0 +1,52 @@ +/decl/sex_action/anal_sex + name = "Sodomize them" + uid = "sexcon_sex_anal" + stamina_cost = 1.0 + +/decl/sex_action/anal_sex/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/anal_sex/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + if(!user.sexcon.can_use_penis()) + return + return TRUE + +/decl/sex_action/anal_sex/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] slides [user.p_their()] cock into [target]'s butt!")) + playsound(target, list('mods/content/sexcon/sounds/mat/insert (1).ogg','mods/content/sexcon/sounds/mat/insert (2).ogg'), 20, TRUE, ignore_walls = FALSE) + +/decl/sex_action/anal_sex/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] fucks [target]'s ass.")) + playsound(target, 'mods/content/sexcon/sounds/mat/segso.ogg', 50, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 0, TRUE) + if(user.sexcon.check_active_ejaculation()) + user.visible_message(span_love("[user] cums into [target]'s butt!")) + user.sexcon.cum_into() + user.virginity = FALSE + + if(user.sexcon.considered_limp()) + user.sexcon.perform_sex_action(target, 1.2, 4, FALSE) + else + user.sexcon.perform_sex_action(target, 2.4, 9, FALSE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/anal_sex/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [user.p_their()] cock out of [target]'s butt.")) + +/decl/sex_action/anal_sex/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/sex/throat_sex.dm b/mods/content/sexcon/datum/sex_actions/sex/throat_sex.dm new file mode 100644 index 000000000000..2386f8aab06c --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/sex/throat_sex.dm @@ -0,0 +1,53 @@ +/decl/sex_action/throat_sex + name = "Fuck their throat" + uid = "sexcon_sex_throat" + stamina_cost = 1.0 + +/decl/sex_action/throat_sex/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/throat_sex/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + if(!user.sexcon.can_use_penis()) + return + return TRUE + +/decl/sex_action/throat_sex/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] slides [user.p_their()] cock into [target]'s throat!")) + playsound(target, list('mods/content/sexcon/sounds/mat/insert (1).ogg','mods/content/sexcon/sounds/mat/insert (2).ogg'), 20, TRUE, ignore_walls = FALSE) + +/decl/sex_action/throat_sex/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] fucks [target]'s throat.")) + playsound(target, 'mods/content/sexcon/sounds/mat/segso.ogg', 50, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 0, TRUE) + if(user.sexcon.check_active_ejaculation()) + user.visible_message(span_love("[user] cums into [target]'s throat!")) + user.sexcon.cum_into() + user.virginity = FALSE + + if(user.sexcon.considered_limp()) + user.sexcon.perform_sex_action(target, 0, 2, FALSE) + else + user.sexcon.perform_sex_action(target, 0, 7, FALSE) + user.sexcon.perform_deepthroat_oxyloss(target, 2.6) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/throat_sex/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] pulls [user.p_their()] cock out of [target]'s throat.")) + +/decl/sex_action/throat_sex/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/sex/vaginal_ride_sex.dm b/mods/content/sexcon/datum/sex_actions/sex/vaginal_ride_sex.dm new file mode 100644 index 000000000000..de7f2f611238 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/sex/vaginal_ride_sex.dm @@ -0,0 +1,57 @@ +/decl/sex_action/vaginal_ride_sex + name = "Ride them" + uid = "sexcon_sex_vaginalride" + stamina_cost = 1.0 + aggro_grab_instead_same_tile = FALSE + +/decl/sex_action/vaginal_ride_sex/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/vaginal_ride_sex/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/vaginal_ride_sex/on_start(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] gets on top of [target] and begins riding [target.p_them()] with [user.p_their()] cunt!")) + playsound(target, list('mods/content/sexcon/sounds/mat/insert (1).ogg','mods/content/sexcon/sounds/mat/insert (2).ogg'), 20, TRUE, ignore_walls = FALSE) + +/decl/sex_action/vaginal_ride_sex/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] rides [target].")) + playsound(target, 'mods/content/sexcon/sounds/mat/segso.ogg', 50, TRUE, -2, ignore_walls = FALSE) + + if(target.sexcon.considered_limp()) + user.sexcon.perform_sex_action(target, 1.2, 3, TRUE) + else + user.sexcon.perform_sex_action(target, 2.4, 7, TRUE) + user.sexcon.handle_passive_ejaculation() + + user.sexcon.perform_sex_action(target, 2, 4, FALSE) + if(target.sexcon.check_active_ejaculation()) + target.visible_message(span_love("[target] cums into [user]'s cunt!")) + target.sexcon.cum_into() + target.try_impregnate(user) + target.virginity = FALSE + user.virginity = FALSE + +/decl/sex_action/vaginal_ride_sex/on_finish(mob/living/human/user, mob/living/human/target) + user.visible_message(span_warning("[user] gets off [target].")) + +/decl/sex_action/vaginal_ride_sex/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/sex/vaginal_sex.dm b/mods/content/sexcon/datum/sex_actions/sex/vaginal_sex.dm new file mode 100644 index 000000000000..9dd25120ce39 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/sex/vaginal_sex.dm @@ -0,0 +1,64 @@ +/decl/sex_action/vaginal_sex + name = "Fuck their cunt" + uid = "sexcon_sex_vaginal" + stamina_cost = 1.0 + +/decl/sex_action/vaginal_sex/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + return TRUE + +/decl/sex_action/vaginal_sex/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_PENIS)) + return FALSE + if(!user.sexcon.can_use_penis()) + return + return TRUE + +/decl/sex_action/vaginal_sex/on_start(mob/living/human/user, mob/living/human/target) + . = ..() + var/decl/pronouns/pronouns = . + + user.visible_message(span_warning("[user] slides [pronouns.his] cock into [target]'s cunt!")) + playsound(target, list('mods/content/sexcon/sounds/mat/insert (1).ogg','mods/content/sexcon/sounds/mat/insert (2).ogg'), 20, TRUE, ignore_walls = FALSE) + +/decl/sex_action/vaginal_sex/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] fucks [target]'s cunt.")) + playsound(target, 'mods/content/sexcon/sounds/mat/segso.ogg', 50, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 0, TRUE) + if(user.sexcon.check_active_ejaculation()) + user.visible_message(span_love("[user] cums into [target]'s cunt!")) + user.sexcon.cum_into() + user.try_impregnate(target) + user.virginity = FALSE + target.virginity = FALSE + + if(user.sexcon.considered_limp()) + user.sexcon.perform_sex_action(target, 1.2, 3, FALSE) + else + user.sexcon.perform_sex_action(target, 2.4, 7, FALSE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/vaginal_sex/on_finish(mob/living/human/user, mob/living/human/target) + . = ..() + var/decl/pronouns/pronouns = . + + user.visible_message(span_warning("[user] pulls [pronouns.his] cock out of [target]'s cunt.")) + +/decl/sex_action/vaginal_sex/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/toy/toy_anal.dm b/mods/content/sexcon/datum/sex_actions/toy/toy_anal.dm new file mode 100644 index 000000000000..d75b535de664 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/toy/toy_anal.dm @@ -0,0 +1,39 @@ +/decl/sex_action/toy_anal + name = "Pleasure butt with toy" + +/decl/sex_action/toy_anal/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user != target) + return FALSE + if(!get_dildo_in_either_hand(user)) + return FALSE + return TRUE + +/decl/sex_action/toy_anal/can_perform(mob/living/human/user, mob/living/human/target) + if(user != target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_dildo_in_either_hand(user)) + return FALSE + return TRUE + +/decl/sex_action/toy_anal/on_start(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(span_warning("[user] starts shoves [dildo] in [user.p_their()] butt...")) + +/decl/sex_action/toy_anal/on_perform(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] pleasures [user.p_their()] butt with \the [dildo].")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 6, TRUE) + user.sexcon.handle_passive_ejaculation() + +/decl/sex_action/toy_anal/on_finish(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(span_warning("[user] pulls \the [dildo] from [user.p_their()] butt.")) + +/decl/sex_action/toy_anal/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/toy/toy_helpers.dm b/mods/content/sexcon/datum/sex_actions/toy/toy_helpers.dm new file mode 100644 index 000000000000..7235c9eeb5a9 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/toy/toy_helpers.dm @@ -0,0 +1,9 @@ +/proc/get_dildo_in_either_hand(mob/living/human/user) + for(var/obj/item/thing in user.held_items) + if(thing == null) + continue + if(!istype(thing, /obj/item/dildo)) + continue + return thing + return null + diff --git a/mods/content/sexcon/datum/sex_actions/toy/toy_oral.dm b/mods/content/sexcon/datum/sex_actions/toy/toy_oral.dm new file mode 100644 index 000000000000..2399144fe8df --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/toy/toy_oral.dm @@ -0,0 +1,36 @@ +/decl/sex_action/toy_oral + name = "Swallow toy" + +/decl/sex_action/toy_oral/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user != target) + return FALSE + if(!get_dildo_in_either_hand(user)) + return FALSE + return TRUE + +/decl/sex_action/toy_oral/can_perform(mob/living/human/user, mob/living/human/target) + if(user != target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + if(!get_dildo_in_either_hand(user)) + return FALSE + return TRUE + +/decl/sex_action/toy_oral/on_start(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(span_warning("[user] starts swallowing on \the [dildo]...")) + +/decl/sex_action/toy_oral/on_perform(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] blows \the [dildo]...")) + user.make_sucking_noise() + +/decl/sex_action/toy_oral/on_finish(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(span_warning("[user] stops blowing \the [dildo].")) + +/decl/sex_action/toy_oral/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/toy/toy_other_anal.dm b/mods/content/sexcon/datum/sex_actions/toy/toy_other_anal.dm new file mode 100644 index 000000000000..d57d1893477f --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/toy/toy_other_anal.dm @@ -0,0 +1,38 @@ +/decl/sex_action/toy_other_anal + name = "Use toy on their butt" + +/decl/sex_action/toy_other_anal/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_dildo_in_either_hand(user)) + return FALSE + return TRUE + +/decl/sex_action/toy_other_anal/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!get_dildo_in_either_hand(user)) + return FALSE + return TRUE + +/decl/sex_action/toy_other_anal/on_start(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(span_warning("[user] shoves \the [dildo] in [target]'s butt...")) + +/decl/sex_action/toy_other_anal/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] pleasures [target]'s butt...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(target, 2, 6, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/toy_other_anal/on_finish(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(span_warning("[user] pulls \the [dildo] from [target]'s butt.")) + +/decl/sex_action/toy_other_anal/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/toy/toy_other_oral.dm b/mods/content/sexcon/datum/sex_actions/toy/toy_other_oral.dm new file mode 100644 index 000000000000..9cc083b29e6f --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/toy/toy_other_oral.dm @@ -0,0 +1,35 @@ +/decl/sex_action/toy_other_oral + name = "Use toy on their mouth" + +/decl/sex_action/toy_other_oral/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_dildo_in_either_hand(user)) + return FALSE + return TRUE + +/decl/sex_action/toy_other_oral/can_perform(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_MOUTH)) + return FALSE + if(!get_dildo_in_either_hand(user)) + return FALSE + return TRUE + +/decl/sex_action/toy_other_oral/on_start(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(span_warning("[user] forces [target]'s to gobble on \the [dildo]...")) + +/decl/sex_action/toy_other_oral/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] forces [target] to suck on the dildo...")) + target.make_sucking_noise() + +/decl/sex_action/toy_other_oral/on_finish(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(span_warning("[user] pulls \the [dildo] from [target]'s mouth.")) + +/decl/sex_action/toy_other_oral/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/toy/toy_other_vaginal.dm b/mods/content/sexcon/datum/sex_actions/toy/toy_other_vaginal.dm new file mode 100644 index 000000000000..e4c6680854c1 --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/toy/toy_other_vaginal.dm @@ -0,0 +1,42 @@ +/decl/sex_action/toy_other_vagina + name = "Use toy on their cunt" + +/decl/sex_action/toy_other_vagina/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user == target) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + if(!get_dildo_in_either_hand(user)) + return FALSE + return TRUE + +/decl/sex_action/toy_other_vagina/can_perform(mob/living/user, mob/living/target) + if(user == target) + return FALSE + if(!get_location_accessible(target, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!target.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + if(!get_dildo_in_either_hand(user)) + return FALSE + return TRUE + +/decl/sex_action/toy_other_vagina/on_start(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(span_warning("[user] shoves \the [dildo] in [target]'s cunt...")) + +/decl/sex_action/toy_other_vagina/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] pleasures [target]'s cunt...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(target, 2, 4, TRUE) + target.sexcon.handle_passive_ejaculation() + +/decl/sex_action/toy_other_vagina/on_finish(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(span_warning("[user] pulls out \the [dildo] from [target]'s cunt.")) + +/decl/sex_action/toy_other_vagina/is_finished(mob/living/human/user, mob/living/human/target) + if(target.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sex_actions/toy/toy_vaginal.dm b/mods/content/sexcon/datum/sex_actions/toy/toy_vaginal.dm new file mode 100644 index 000000000000..c77a6686c4bc --- /dev/null +++ b/mods/content/sexcon/datum/sex_actions/toy/toy_vaginal.dm @@ -0,0 +1,43 @@ +/decl/sex_action/toy_vagina + name = "Pleasure cunt with toy" + +/decl/sex_action/toy_vagina/shows_on_menu(mob/living/human/user, mob/living/human/target) + if(user != target) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + if(!get_dildo_in_either_hand(user)) + return FALSE + return TRUE + +/decl/sex_action/toy_vagina/can_perform(mob/living/user, mob/living/target) + if(user != target) + return FALSE + if(!get_location_accessible(user, BODY_ZONE_PRECISE_GROIN, TRUE)) + return FALSE + if(!user.getorganslot(ORGAN_SLOT_VAGINA)) + return FALSE + if(!get_dildo_in_either_hand(user)) + return FALSE + return TRUE + +/decl/sex_action/toy_vagina/on_start(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(span_warning("[user] shoves \the [dildo] in [user.p_their()] cunt...")) + +/decl/sex_action/toy_vagina/on_perform(mob/living/human/user, mob/living/human/target) + user.visible_message(user.sexcon.spanify_force("[user] [user.sexcon.get_generic_force_adjective()] pleasures [user.p_their()] cunt...")) + playsound(user, 'mods/content/sexcon/sounds/mat/fingering.ogg', 30, TRUE, -2, ignore_walls = FALSE) + + user.sexcon.perform_sex_action(user, 2, 4, TRUE) + + user.sexcon.handle_passive_ejaculation() + +/decl/sex_action/toy_vagina/on_finish(mob/living/human/user, mob/living/human/target) + var/obj/item/dildo = get_dildo_in_either_hand(user) + user.visible_message(span_warning("[user] pulls out \the [dildo] from [user.p_their()] cunt.")) + +/decl/sex_action/toy_vagina/is_finished(mob/living/human/user, mob/living/human/target) + if(user.sexcon.finished_check()) + return TRUE + return FALSE diff --git a/mods/content/sexcon/datum/sexcon.dm b/mods/content/sexcon/datum/sexcon.dm new file mode 100644 index 000000000000..fb4e496b684c --- /dev/null +++ b/mods/content/sexcon/datum/sexcon.dm @@ -0,0 +1,603 @@ +/datum/sex_controller + /// The user and the owner of the controller + VAR_PRIVATE/mob/living/human/user + /// Target of our actions, can be ourself + VAR_PRIVATE/mob/living/human/target + /// Whether the user desires to stop his current action + var/desire_stop = FALSE + /// What is the current performed action + var/decl/sex_action/current_action + /// Enum of desired speed + var/speed = SEX_SPEED_MID + /// Enum of desired force + var/force = SEX_FORCE_MID + /// Our arousal + var/arousal = 0 + /// Our charge gauge + var/charge = SEX_MAX_CHARGE + /// Whether we want to screw until finished, or non stop + var/do_until_finished = TRUE + /// Arousal won't change if active. + var/arousal_frozen = FALSE + var/last_arousal_increase_time = 0 + var/last_ejaculation_time = 0 + var/last_moan = 0 + var/last_pain = 0 + +/datum/sex_controller/New(mob/living/human/owner) + user = owner + +/datum/sex_controller/Destroy() + user = null + target = null + . = ..() + +/datum/sex_controller/proc/set_target(mob/living/human/new_target) + target = new_target + +/datum/sex_controller/proc/is_spent() + if(charge < CHARGE_FOR_CLIMAX) + return TRUE + return FALSE + +/datum/sex_controller/proc/finished_check() + if(!do_until_finished) + return FALSE + if(!just_ejaculated()) + return FALSE + return TRUE + +/datum/sex_controller/proc/adjust_speed(amt) + speed = clamp(speed + amt, SEX_SPEED_MIN, SEX_SPEED_MAX) + +/datum/sex_controller/proc/adjust_force(amt) + force = clamp(force + amt, SEX_FORCE_MIN, SEX_FORCE_MAX) + +/datum/sex_controller/proc/update_pink_screen() + var/severity = 0 + switch(arousal) + if(1 to 10) + severity = 1 + if(10 to 20) + severity = 2 + if(20 to 30) + severity = 3 + if(30 to 40) + severity = 4 + if(40 to 50) + severity = 5 + if(50 to 60) + severity = 6 + if(60 to 70) + severity = 7 + if(70 to 80) + severity = 8 + if(80 to 90) + severity = 9 + if(90 to INFINITY) + severity = 10 + + if(severity > 0) + user.overlay_fullscreen("horny", /obj/screen/fullscreen/love, severity) + else + user.clear_fullscreen("horny") + +/datum/sex_controller/proc/start(mob/living/human/new_target) + if(!ishuman(new_target)) + return + set_target(new_target) + show_ui() + +/datum/sex_controller/proc/cum_onto() + log_attack("[key_name(user)] came onto [key_name(target)]") + playsound(target, 'mods/content/sexcon/sounds/mat/endout.ogg', 50, TRUE, ignore_walls = FALSE) + //add_cum_floor(get_turf(target)) + after_ejaculation() + +/datum/sex_controller/proc/cum_into(oral = FALSE) + log_attack("[key_name(user)] came inside [key_name(target)]") + if(oral) + playsound(target, pick(list('mods/content/sexcon/sounds/mat/mouthend (1).ogg','mods/content/sexcon/sounds/mat/mouthend (2).ogg')), 100, FALSE, ignore_walls = FALSE) + else + playsound(target, 'mods/content/sexcon/sounds/mat/endin.ogg', 50, TRUE, ignore_walls = FALSE) + after_ejaculation() + +/datum/sex_controller/proc/ejaculate() + log_attack("[key_name(user)] ejaculated") + //user.visible_message(span_love("[user] makes a mess!")) + playsound(user, 'mods/content/sexcon/sounds/mat/endout.ogg', 50, TRUE, ignore_walls = FALSE) + //add_cum_floor(get_turf(user)) + after_ejaculation() + +/datum/sex_controller/proc/after_ejaculation() + set_arousal(40) + adjust_charge(-CHARGE_FOR_CLIMAX) + //if(user.has_flaw(/datum/charflaw/addiction/lovefiend)) + // user.sate_addiction() + /// TODO: add this stressor + //user.add_stressor(/datum/stressor/cumok, 10 MINUTES) + playsound(user, pick(global.sex_moan_heavy), 100, TRUE) + user.playsound_local(user, 'mods/content/sexcon/sounds/mat/end.ogg', 100) + last_ejaculation_time = world.time + +/datum/sex_controller/proc/just_ejaculated() + return (last_ejaculation_time + 2 SECONDS >= world.time) + +/datum/sex_controller/proc/set_charge(amount) + var/empty = (charge < CHARGE_FOR_CLIMAX) + charge = clamp(amount, 0, SEX_MAX_CHARGE) + var/after_empty = (charge < CHARGE_FOR_CLIMAX) + if(empty && !after_empty) + to_chat(user, SPAN_NOTICE("I feel like I'm not so spent anymore")) + if(!empty && after_empty) + to_chat(user, SPAN_NOTICE("I'm spent!")) + +/datum/sex_controller/proc/adjust_charge(amount) + set_charge(charge + amount) + +/datum/sex_controller/proc/handle_charge(dt) + //if(user.has_flaw(/datum/charflaw/addiction/lovefiend)) + // dt *= 2 + adjust_charge(dt * CHARGE_RECHARGE_RATE) + if(is_spent()) + if(arousal > 60) + to_chat(user, SPAN_WARNING("I'm too spent!")) + adjust_arousal(-20) + adjust_arousal(-dt * SPENT_AROUSAL_RATE) + +/datum/sex_controller/proc/set_arousal(amount) + if(amount > arousal) + last_arousal_increase_time = world.time + arousal = clamp(amount, 0, MAX_AROUSAL) + update_pink_screen() + update_blueballs() + //update_erect_state() + +/* +/datum/sex_controller/proc/update_erect_state() + var/obj/item/organ/penis/penis = user.getorganslot(ORGAN_SLOT_PENIS) + if(penis) + penis.update_erect_state() +*/ + +/datum/sex_controller/proc/adjust_arousal(amount) + set_arousal(arousal + amount) + +/datum/sex_controller/proc/perform_deepthroat_oxyloss(mob/living/human/action_target, oxyloss_amt) + var/oxyloss_multiplier = 0 + switch(force) + if(SEX_FORCE_LOW) + oxyloss_multiplier = 0 + if(SEX_FORCE_MID) + oxyloss_multiplier = 0 + if(SEX_FORCE_HIGH) + oxyloss_multiplier = 1.0 + if(SEX_FORCE_EXTREME) + oxyloss_multiplier = 2.0 + + oxyloss_amt *= oxyloss_multiplier + if(oxyloss_amt <= 0) + return + + action_target.take_damage(oxyloss_amt, OXY) + +/datum/sex_controller/proc/perform_sex_action(mob/living/human/action_target, arousal_amt, pain_amt, giving) + action_target.sexcon.receive_sex_action(arousal_amt, pain_amt, giving, force, speed) + +/datum/sex_controller/proc/receive_sex_action(arousal_amt, pain_amt, giving, applied_force, applied_speed) + arousal_amt *= get_force_pleasure_multiplier(applied_force, giving) + pain_amt *= get_force_pain_multiplier(applied_force) + pain_amt *= get_speed_pain_multiplier(applied_speed) + + if(user.stat == DEAD) + arousal_amt = 0 + pain_amt = 0 + + if(!arousal_frozen) + adjust_arousal(arousal_amt) + + damage_from_pain(pain_amt) + try_do_moan(arousal_amt, pain_amt, applied_force, giving) + try_do_pain_effect(pain_amt, giving) + +/datum/sex_controller/proc/damage_from_pain(pain_amt) + if(pain_amt < PAIN_MINIMUM_FOR_DAMAGE) + return + var/damage = (pain_amt / PAIN_DAMAGE_DIVISOR) + user.apply_damage(damage, PAIN, BP_CHEST) + +/datum/sex_controller/proc/try_do_moan(arousal_amt, pain_amt, applied_force, giving) + if(arousal_amt < 1.5) + return + if(user.stat != CONSCIOUS) + return + if(last_moan + MOAN_COOLDOWN >= world.time) + return + if(prob(50)) + return + var/chosen_emote + switch(arousal_amt) + if(0 to 5) + chosen_emote = pick(global.sex_moan_light) + if(5 to INFINITY) + chosen_emote = pick(global.sex_moan_heavy) + /* + if(pain_amt >= PAIN_MILD_EFFECT) + if(giving) + if(prob(30)) + chosen_emote = "groan" + else + if(prob(40)) + chosen_emote = "painmoan" + if(pain_amt >= PAIN_MED_EFFECT) + if(giving) + if(prob(50)) + chosen_emote = "groan" + else + if(prob(60)) + chosen_emote = "painmoan" + */ + last_moan = world.time + playsound(user, chosen_emote, 100, TRUE) + +/datum/sex_controller/proc/try_do_pain_effect(pain_amt, giving) + if(pain_amt < PAIN_MILD_EFFECT) + return + if(last_pain + PAIN_COOLDOWN >= world.time) + return + if(prob(50)) + return + last_pain = world.time + if(pain_amt >= PAIN_HIGH_EFFECT) + var/pain_msg = pick(list("IT HURTS!!!", "IT NEEDS TO STOP!!!", "I CAN'T TAKE IT ANYMORE!!!")) + user.custom_pain(pain_msg, pain_amt, TRUE, nohalloss = TRUE) + if(prob(70) && user.stat == CONSCIOUS) + user.visible_message(SPAN_WARNING("[user] shudders in pain!")) + else if(pain_amt >= PAIN_MED_EFFECT) + var/pain_msg = pick(list("It hurts!", "It pains me!")) + user.custom_pain(pain_msg, pain_amt, TRUE, nohalloss = TRUE) + if(prob(40) && user.stat == CONSCIOUS) + user.visible_message(SPAN_WARNING("[user] shudders in pain!")) + else + var/pain_msg = pick(list("It hurts a little...", "It stings...", "I'm aching...")) + user.custom_pain(pain_msg, pain_amt, TRUE, nohalloss = TRUE) + +/datum/sex_controller/proc/update_blueballs() + if(arousal >= BLUEBALLS_GAIN_THRESHOLD) + return + //user.add_stressor(/datum/stressor/blueb, 5 MINUTES) + else if (arousal <= BLUEBALLS_LOOSE_THRESHOLD) + return + //user.remove_stressor(/datum/stressor/blueb) + +/datum/sex_controller/proc/check_active_ejaculation() + if(arousal < ACTIVE_EJAC_THRESHOLD) + return FALSE + if(is_spent()) + return FALSE + if(!can_ejaculate()) + return FALSE + return TRUE + +/datum/sex_controller/proc/can_ejaculate() + //if(!user.getorganslot(ORGAN_SLOT_TESTICLES) && !user.getorganslot(ORGAN_SLOT_VAGINA)) + // return FALSE + //if(HAS_TRAIT(user, TRAIT_LIMPDICK)) + // return FALSE + return TRUE + +/datum/sex_controller/proc/handle_passive_ejaculation() + if(arousal < PASSIVE_EJAC_THRESHOLD) + return + if(is_spent()) + return + if(!can_ejaculate()) + return FALSE + ejaculate() + +/datum/sex_controller/proc/can_use_penis() + //if(HAS_TRAIT(user, TRAIT_LIMPDICK)) + // return FALSE + //var/obj/item/organ/penis/penor = user.getorganslot(ORGAN_SLOT_PENIS) + //if(!penor) + // return FALSE + //if(!penor.functional) + // return FALSE + return TRUE + +/datum/sex_controller/proc/considered_limp() + if(arousal >= AROUSAL_HARD_ON_THRESHOLD) + return FALSE + return TRUE + +/datum/sex_controller/proc/process_sexcon(dt) + handle_arousal_unhorny(dt) + handle_charge(dt) + handle_passive_ejaculation() + +/datum/sex_controller/proc/handle_arousal_unhorny(dt) + if(arousal_frozen) + return + if(!can_ejaculate()) + adjust_arousal(-dt * IMPOTENT_AROUSAL_LOSS_RATE) + if(last_arousal_increase_time + AROUSAL_TIME_TO_UNHORNY >= world.time) + return + var/rate + switch(arousal) + if(-INFINITY to 25) + rate = AROUSAL_LOW_UNHORNY_RATE + if(25 to 40) + rate = AROUSAL_MID_UNHORNY_RATE + if(40 to INFINITY) + rate = AROUSAL_HIGH_UNHORNY_RATE + adjust_arousal(-dt * rate) + +/datum/sex_controller/proc/show_ui() + var/list/dat = list() + var/force_name = get_force_string() + var/speed_name = get_speed_string() + dat += "
| "
+ var/link = ""
+ if(!can_perform_action(saction))
+ link = "linkOff"
+ if(current_action == saction)
+ link = "linkOn"
+ dat += " | "
+ i++
+ if(i >= 2)
+ i = 0
+ dat += "
w{1)>o8g!!ujhK!^hI%LkX3bDo#s;5
zW%cAuj##^=T_ }nYD)5R6~Q@)Uv9jZz1|S=cJ$qbKTb@r-+w6Q^kB01zz82cVKFV
zF8?w`YagkhG~=8;
u0AI<
zMPAGw?Yy>@mJ-KHuITohjhOM5FRiU?Z2Icqh3&b~i|7~M>67|>-xxEZzI17NdLhSl
zp=b>m;*xBMY)sE&!v-&chJ(!m5I;;d5g*CO
z!nD#t
1eNI%q0&N`eMpF4eCTEyPTdaGs0mb&y}P-~SQL5U
z2J@7NkDVzN^OPTaRW|kYL1p(3%fsIF=N5OzAL?KJ;dh10#MSyi(@Or8(X~}Jh%Dxr
zpH}&-s~SIS1A7gx6bf7N_!q@q-_l$u9DW{<#Kb;xec5!2*V;bZzE}Re-l8*9oiyL@
zC0`D
q=UAoDk@=TV-K9;3WcE%lZ1T2UD{G3HDV0&n&(R(0$s=d7
z5;J~#aD6oVusv*z9ZW`ldC!jwN%Q+^@-QLJ%PhpKLmgxrf_9f|dVyN++MQ)}*f0fu
zGY-iiY#4wV8E0z8SE|a-xbXoE3Td+Z%B(I(q)elpTUy(7^Ru5spM9oEZ^~J_iqh1UxXOZ6-6q)mQucCx1@b}VUY+T&V#Zs`!Y#zV;&Pttx8L?UzU
znu%$vF=92>>>S+Ed;w~!nr0@mInfJ^56d-K6YUI*