diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm
index 2d989821cf4..718ebccdb77 100644
--- a/code/__DEFINES/inventory.dm
+++ b/code/__DEFINES/inventory.dm
@@ -61,22 +61,8 @@
/// Legcuff slot (bolas, beartraps)
#define ITEM_SLOT_LEGCUFFED (1<<19)
-//SKYRAT EDIT ADDITION BEGIN - ERP UPDATE
-/// Penis slot
-#define ITEM_SLOT_PENIS (1<<20)
-/// Vagina slot
-#define ITEM_SLOT_VAGINA (1<<21)
-/// Anus slot
-#define ITEM_SLOT_ANUS (1<<22)
-/// Nipples slot
-#define ITEM_SLOT_NIPPLES (1<<23)
-//SKYRATE EDIT ADDITION END
-
/// Total amount of slots
-//SKYRAT EDIT CHANGE BEGIN
-// #define SLOTS_AMT 20 // Keep this up to date! - SKYRAT EDIT - ORIGINAL
-#define SLOTS_AMT 24 // Keep this up to date!
-//SKYRAT EDIT CHANGE END
+#define SLOTS_AMT 20 // Keep this up to date!
//SLOT GROUP HELPERS
#define ITEM_SLOT_POCKETS (ITEM_SLOT_LPOCKET|ITEM_SLOT_RPOCKET)
diff --git a/code/__DEFINES/~skyrat_defines/_organ_defines.dm b/code/__DEFINES/~skyrat_defines/_organ_defines.dm
index 1002254fa48..c944243da57 100644
--- a/code/__DEFINES/~skyrat_defines/_organ_defines.dm
+++ b/code/__DEFINES/~skyrat_defines/_organ_defines.dm
@@ -6,5 +6,6 @@
#define ORGAN_SLOT_TESTICLES "testicles"
#define ORGAN_SLOT_BREASTS "breasts"
#define ORGAN_SLOT_ANUS "anus"
+#define ORGAN_SLOT_NIPPLES "nipples"
#define ORGAN_SLOT_WINGS "wings"
diff --git a/code/__DEFINES/~skyrat_defines/inventory.dm b/code/__DEFINES/~skyrat_defines/inventory.dm
index 9ae86967643..318c11e19fe 100644
--- a/code/__DEFINES/~skyrat_defines/inventory.dm
+++ b/code/__DEFINES/~skyrat_defines/inventory.dm
@@ -4,3 +4,9 @@
#define STYLE_TAUR_HOOF (1<<4)
#define STYLE_TAUR_ALL (STYLE_TAUR_SNAKE|STYLE_TAUR_PAW|STYLE_TAUR_HOOF)
#define STYLE_TESHARI (1<<6) //teshari clothes and shit
+
+// We don't use the HUD for these, they have their own menu.
+#define LEWD_SLOT_VAGINA (1<<0)
+#define LEWD_SLOT_PENIS (1<<1)
+#define LEWD_SLOT_ANUS (1<<2)
+#define LEWD_SLOT_NIPPLES (1<<3)
diff --git a/code/__DEFINES/~skyrat_defines/lewd_defines.dm b/code/__DEFINES/~skyrat_defines/lewd_defines.dm
new file mode 100644
index 00000000000..a1edae8aa01
--- /dev/null
+++ b/code/__DEFINES/~skyrat_defines/lewd_defines.dm
@@ -0,0 +1,49 @@
+/// To prevent an issue with stupidly low negative values.
+#define AROUSAL_MINIMUM 0
+#define AROUSAL_MINIMUM_DETECTABLE 10
+#define AROUSAL_LOW 30
+#define AROUSAL_MEDIUM 70
+#define AROUSAL_HIGH 85
+#define AROUSAL_AUTO_CLIMAX_THRESHOLD 90
+#define AROUSAL_LIMIT 100
+
+#define REQUIRE_GENITAL_EXPOSED 1
+#define REQUIRE_GENITAL_UNEXPOSED 2
+#define REQUIRE_GENITAL_ANY 3
+
+#define TRAIT_MASOCHISM "masochism"
+#define TRAIT_SADISM "sadism"
+#define TRAIT_NEVERBONER "neverboner"
+#define TRAIT_BIMBO "bimbo"
+#define TRAIT_RIGGER "rigger"
+#define TRAIT_ROPEBUNNY "rope bunny"
+///traits gained by brain traumas, can be removed if the brain trauma is gone
+#define APHRO_TRAIT "aphro"
+///traits gained by quirks, cannot be removed unless the quirk itself is gone
+#define LEWDQUIRK_TRAIT "lewdquirks"
+///traits gained by chemicals, you get the idea
+#define LEWDCHEM_TRAIT "lewdchem"
+
+#define STRAPON_TRAIT "strapon"
+
+#define CONDOM_BROKEN "broken"
+
+#define BREAST_SIZE_FLATCHESTED "Flatchested"
+#define BREAST_SIZE_A "A"
+#define BREAST_SIZE_B "B"
+#define BREAST_SIZE_C "C"
+#define BREAST_SIZE_D "D"
+// Ouch, my back.
+#define BREAST_SIZE_E "E"
+#define BREAST_SIZE_F "F"
+#define BREAST_SIZE_G "G"
+#define BREAST_SIZE_H "H"
+#define BREAST_SIZE_I "I"
+#define BREAST_SIZE_J "J"
+#define BREAST_SIZE_K "K"
+#define BREAST_SIZE_L "L"
+#define BREAST_SIZE_M "M"
+#define BREAST_SIZE_N "N"
+#define BREAST_SIZE_O "O"
+#define BREAST_SIZE_P "P"
+#define BREAST_SIZE_BEYOND_MEASUREMENT "beyond measurement"
diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm
index 900a635bdc4..e9df60b699e 100644
--- a/code/__HELPERS/type2type.dm
+++ b/code/__HELPERS/type2type.dm
@@ -244,14 +244,6 @@ GLOBAL_LIST_INIT(modulo_angle_to_dir, list(NORTH,NORTHEAST,EAST,SOUTHEAST,SOUTH,
if(ITEM_SLOT_LEGCUFFED)
return pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- if(ITEM_SLOT_PENIS, ITEM_SLOT_VAGINA, ITEM_SLOT_ANUS)
- return BODY_ZONE_PRECISE_GROIN
-
- if(ITEM_SLOT_NIPPLES)
- return BODY_ZONE_CHEST
- //SKYRAT EDIT ADDITION END
-
//adapted from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
/proc/heat2colour(temp)
return rgb(heat2colour_r(temp), heat2colour_g(temp), heat2colour_b(temp))
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index b93c50fc143..d615d17abde 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -223,9 +223,6 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
pull_icon = null
QDEL_LIST(toggleable_inventory)
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- QDEL_LIST(erp_toggleable_inventory) // Destroy ERP stuff
- //SKYRAT EDIT ADDITION END
QDEL_LIST(hotkeybuttons)
throw_icon = null
QDEL_LIST(infodisplay)
@@ -321,10 +318,6 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
screenmob.client.screen += static_inventory
if(toggleable_inventory.len && screenmob.hud_used && screenmob.hud_used.inventory_shown)
screenmob.client.screen += toggleable_inventory
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- if(erp_toggleable_inventory.len && screenmob.hud_used && screenmob.hud_used.ERP_inventory_shown && screenmob.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- screenmob.client.screen += erp_toggleable_inventory
- //SKYRAT EDIT ADDITION END
if(hotkeybuttons.len && !hotkey_ui_hidden)
screenmob.client.screen += hotkeybuttons
if(infodisplay.len)
@@ -341,10 +334,6 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
screenmob.client.screen -= static_inventory
if(toggleable_inventory.len)
screenmob.client.screen -= toggleable_inventory
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- if(erp_toggleable_inventory.len && screenmob.hud_used && screenmob.hud_used.ERP_inventory_shown && screenmob.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- screenmob.client.screen -= erp_toggleable_inventory
- //SKYRAT EDIT ADDITION END
if(hotkeybuttons.len)
screenmob.client.screen -= hotkeybuttons
if(infodisplay.len)
@@ -365,10 +354,6 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
screenmob.client.screen -= static_inventory
if(toggleable_inventory.len)
screenmob.client.screen -= toggleable_inventory
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- if(toggleable_inventory.len && screenmob.hud_used && screenmob.hud_used.ERP_inventory_shown && screenmob.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- screenmob.client.screen -= erp_toggleable_inventory
- //SKYRAT EDIT ADDITION END
if(hotkeybuttons.len)
screenmob.client.screen -= hotkeybuttons
if(infodisplay.len)
@@ -430,21 +415,6 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
ui_style = new_ui_style
build_hand_slots()
-//SKYRAT EDIT - ADDITION - ERP ICONS FIX
-
-/datum/hud/proc/update_erp_ui_style(new_erp_ui_style)
- // do nothing if overridden by a subtype or already on that style
- if (initial(erp_ui_style) || erp_ui_style == new_erp_ui_style)
- return
-
- for(var/atom/item in erp_toggleable_inventory)
- if (item.icon == erp_ui_style)
- item.icon = new_erp_ui_style
-
- erp_ui_style = new_erp_ui_style
-
-//SKYRAT EDIT - ADDITION - ERP ICONS FIX - END
-
//Triggered when F12 is pressed (Unless someone changed something in the DMF)
/mob/verb/button_pressed_F12()
set name = "F12"
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 9ba867ad381..a7bc7370e72 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -21,37 +21,6 @@
usr.hud_used.inventory_shown = TRUE
usr.client.screen += targetmob.hud_used.toggleable_inventory
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- if(usr.hud_used.inventory_shown && targetmob.hud_used)
- for (var/atom/movable/screen/human/using in targetmob.hud_used.static_inventory)
- if(using.screen_loc == ui_erp_inventory)
- using.screen_loc = ui_erp_inventory_up // Move up ERP inventory button
- for (var/atom/movable/screen/inventory/inv in targetmob.hud_used.erp_toggleable_inventory)
- // Move up ERP hud slots
- if(inv.screen_loc == ui_vagina_down)
- inv.screen_loc = ui_vagina
- if(inv.screen_loc == ui_anus_down)
- inv.screen_loc = ui_anus
- if(inv.screen_loc == ui_nipples_down)
- inv.screen_loc = ui_nipples
- if(inv.screen_loc == ui_penis_down)
- inv.screen_loc = ui_penis
- else
- for (var/atom/movable/screen/human/using in targetmob.hud_used.static_inventory)
- if(using.screen_loc == ui_erp_inventory_up)
- using.screen_loc = ui_erp_inventory // Move down ERP inventory button
- for (var/atom/movable/screen/inventory/inv in targetmob.hud_used.erp_toggleable_inventory)
- // Move up ERP hud slots
- if(inv.screen_loc == ui_vagina)
- inv.screen_loc = ui_vagina_down
- if(inv.screen_loc == ui_anus)
- inv.screen_loc = ui_anus_down
- if(inv.screen_loc == ui_nipples)
- inv.screen_loc = ui_nipples_down
- if(inv.screen_loc == ui_penis)
- inv.screen_loc = ui_penis_down
- //SKYRAT EDIT ADDITION END
-
targetmob.hud_used.hidden_inventory_update(usr)
/atom/movable/screen/human/equip
@@ -233,17 +202,6 @@
using.hud = src
static_inventory += using
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- using = new /atom/movable/screen/human/erp_toggle()
- using.icon = ui_style
- using.screen_loc = ui_erp_inventory
- using.hud = src
- // When creating a character, we will check if the ERP is enabled on the client, if not, then the ERP button is immediately invisible
- if(!owner.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- using.invisibility = 100
- static_inventory += using
- //SKYRAT EDIT ADDITION END
-
using = new /atom/movable/screen/human/equip()
using.icon = ui_style
using.screen_loc = ui_equip_position(mymob)
@@ -305,44 +263,6 @@
inv_box.hud = src
static_inventory += inv_box
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- inv_box = new /atom/movable/screen/inventory()
- inv_box.name = "vagina"
- inv_box.icon = erp_ui_style
- inv_box.icon_state = "vagina"
- inv_box.screen_loc = ui_vagina_down
- inv_box.slot_id = ITEM_SLOT_VAGINA
- inv_box.hud = src
- erp_toggleable_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory()
- inv_box.name = "anus"
- inv_box.icon = erp_ui_style
- inv_box.icon_state = "anus"
- inv_box.screen_loc = ui_anus_down
- inv_box.slot_id = ITEM_SLOT_ANUS
- inv_box.hud = src
- erp_toggleable_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory()
- inv_box.name = "nipples"
- inv_box.icon = erp_ui_style
- inv_box.icon_state = "nipples"
- inv_box.screen_loc = ui_nipples_down
- inv_box.slot_id = ITEM_SLOT_NIPPLES
- inv_box.hud = src
- erp_toggleable_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory()
- inv_box.name = "penis"
- inv_box.icon = erp_ui_style
- inv_box.icon_state = "penis"
- inv_box.screen_loc = ui_penis_down
- inv_box.slot_id = ITEM_SLOT_PENIS
- inv_box.hud = src
- erp_toggleable_inventory += inv_box
- //SKYRAT EDIT ADDITION END
-
throw_icon = new /atom/movable/screen/throw_catch()
throw_icon.icon = ui_style
throw_icon.screen_loc = ui_drop_throw
@@ -461,42 +381,6 @@
if(H.wear_neck) screenmob.client.screen -= H.wear_neck
if(H.head) screenmob.client.screen -= H.head
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- if(screenmob.hud_used.ERP_inventory_shown && screenmob.hud_used.hud_shown && H.client.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- if(H.vagina)
- // This shity code need for hanlde an moving UI stuff when default inventory expand/collapse
- if(screenmob.hud_used.inventory_shown && screenmob.hud_used)
- H.vagina.screen_loc = ui_vagina
- else
- H.vagina.screen_loc = ui_vagina_down
- screenmob.client.screen += H.vagina
- if(H.anus)
- if(screenmob.hud_used.inventory_shown && screenmob.hud_used)
- H.anus.screen_loc = ui_anus
- else
- H.anus.screen_loc = ui_anus_down
- screenmob.client.screen += H.anus
- if(H.nipples)
- if(screenmob.hud_used.inventory_shown && screenmob.hud_used)
- H.nipples.screen_loc = ui_nipples
- else
- H.nipples.screen_loc = ui_nipples_down
- screenmob.client.screen += H.nipples
- if(H.penis)
- if(screenmob.hud_used.inventory_shown && screenmob.hud_used)
- H.penis.screen_loc = ui_penis
- else
- H.penis.screen_loc = ui_penis_down
- screenmob.client.screen += H.penis
- else
- if(H.vagina) screenmob.client.screen -= H.vagina
- if(H.anus) screenmob.client.screen -= H.anus
- if(H.nipples) screenmob.client.screen -= H.nipples
- if(H.penis) screenmob.client.screen -= H.penis
- //SKYRAT EDIT ADDITION END
-
-
-
/datum/hud/human/persistent_inventory_update(mob/viewer)
if(!mymob)
return
@@ -526,22 +410,6 @@
H.r_store.screen_loc = ui_storage2
screenmob.client.screen += H.r_store
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- if(H.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- if(H.vagina)
- H.vagina.screen_loc = ui_vagina
- screenmob.client.screen += H.vagina
- if(H.anus)
- H.anus.screen_loc = ui_anus
- screenmob.client.screen += H.anus
- if(H.nipples)
- H.nipples.screen_loc = ui_nipples
- screenmob.client.screen += H.nipples
- if(H.penis)
- H.penis.screen_loc = ui_penis
- screenmob.client.screen += H.penis
- //SKYRAT EDIT ADDITION END
-
else
if(H.s_store)
screenmob.client.screen -= H.s_store
@@ -556,17 +424,6 @@
if(H.r_store)
screenmob.client.screen -= H.r_store
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- if(H.vagina)
- screenmob.client.screen -= H.vagina
- if(H.anus)
- screenmob.client.screen -= H.anus
- if(H.nipples)
- screenmob.client.screen -= H.nipples
- if(H.penis)
- screenmob.client.screen -= H.penis
- //SKYRAT EDIT ADDITION END
-
if(hud_version != HUD_STYLE_NOHUD)
for(var/obj/item/I in H.held_items)
I.screen_loc = ui_hand_position(H.get_held_index_of_item(I))
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 9590a662f37..8d20a87e474 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -876,17 +876,6 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
if(flags & ITEM_SLOT_NECK)
owner.update_worn_neck()
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- if(flags & ITEM_SLOT_VAGINA)
- owner.update_inv_vagina()
- if(flags & ITEM_SLOT_ANUS)
- owner.update_inv_anus()
- if(flags & ITEM_SLOT_NIPPLES)
- owner.update_inv_nipples()
- if(flags & ITEM_SLOT_PENIS)
- owner.update_inv_penis()
- //SKYRAT EDIT ADDITION END
-
///Returns the temperature of src. If you want to know if an item is hot use this proc.
/obj/item/proc/get_temperature()
return heat
diff --git a/code/modules/client/preferences/ui_style.dm b/code/modules/client/preferences/ui_style.dm
index d7e9907df59..08f1af6c7dd 100644
--- a/code/modules/client/preferences/ui_style.dm
+++ b/code/modules/client/preferences/ui_style.dm
@@ -24,4 +24,3 @@
/datum/preference/choiced/ui_style/apply_to_client(client/client, value)
client.mob?.hud_used?.update_ui_style(ui_style2icon(value))
- client.mob?.hud_used?.update_erp_ui_style(erp_ui_style2icon(value)) //SKYRAT EDIT - ADDITION - ERP ICONS FIX
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 1800fd258ad..b2be241a78d 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -473,7 +473,7 @@
//SKYRAT EDIT ADDITION END
//SKYRAT EDIT ADDITION BEGIN - CUSTOMIZATION
- for(var/genital in list("penis", "testicles", "vagina", "breasts", "anus"))
+ for(var/genital in possible_genitals)
if(dna.species.mutant_bodyparts[genital])
var/datum/sprite_accessory/genital/G = GLOB.sprite_accessories[genital][dna.species.mutant_bodyparts[genital][MUTANT_INDEX_NAME]]
if(G)
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 9cdd58eb869..703ec886d5c 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -25,16 +25,6 @@
return r_store
if(ITEM_SLOT_SUITSTORE)
return s_store
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- if(ITEM_SLOT_VAGINA)
- return vagina
- if(ITEM_SLOT_ANUS)
- return anus
- if(ITEM_SLOT_NIPPLES)
- return nipples
- if(ITEM_SLOT_PENIS)
- return penis
- //SKYRAT EDIT ADDITION END
return ..()
/mob/living/carbon/human/get_slot_by_item(obj/item/looking_for)
@@ -175,13 +165,6 @@
if(I.flags_inv & HIDEJUMPSUIT)
update_worn_undersuit()
- //SKYRAT EDIT ADDITION - ERP UPDATE
- if(I.flags_inv & HIDESEXTOY)
- update_inv_anus()
- update_inv_vagina()
- update_inv_penis()
- update_inv_nipples()
- //SKYRAT EDIT ADDITION END
if(wear_suit.breakouttime) //when equipping a straightjacket
ADD_TRAIT(src, TRAIT_RESTRAINED, SUIT_TRAIT)
stop_pulling() //can't pull if restrained
@@ -205,44 +188,6 @@
s_store = I
update_suit_storage()
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- if(ITEM_SLOT_PENIS)
- if(src.is_bottomless())
- if(penis)
- return
- penis = I
- update_inv_penis()
- else
- to_chat(usr, "[src] is not bottomless, you cannot access [usr == src ? "your" : src.p_their()] penis!")
-
- if(ITEM_SLOT_VAGINA)
- if(src.is_bottomless())
- if(vagina)
- return
- vagina = I
- update_inv_vagina()
- else
- to_chat(usr, "[src] is not bottomless, you cannot access [usr == src ? "your" : src.p_their()] vagina!")
-
- if(ITEM_SLOT_ANUS)
- if(src.is_bottomless())
- if(anus)
- return
- anus = I
- update_inv_anus()
- else
- to_chat(usr, "[src] is not bottomless, you cannot access [usr == src ? "your" : src.p_their()] anus!")
-
- if(ITEM_SLOT_NIPPLES)
- if(src.is_topless())
- if(nipples)
- return
- nipples = I
- update_inv_nipples()
- else
- to_chat(usr, "[src] is not topless, you cannot access [usr == src ? "your" : src.p_their()] nipples!")
- //SKYRAT EDIT ADDITION END
-
else
to_chat(src, span_danger("You are trying to equip this item to an unsupported inventory slot. Report this to a coder!"))
@@ -281,12 +226,9 @@
if(I.flags_inv & HIDEJUMPSUIT)
update_worn_undersuit()
- // SKYRAT EDIT ADDITION START
+ // SKYRAT EDIT ADDITION START - ERP Overlays
if(I.flags_inv & HIDESEXTOY)
- update_inv_anus()
- update_inv_vagina()
- update_inv_penis()
- update_inv_nipples()
+ update_inv_lewd()
// SKYRAT EDIT ADDITION END
update_worn_oversuit()
@@ -358,25 +300,6 @@
if(!QDELETED(src))
update_suit_storage()
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- else if(I == vagina)
- vagina = null
- if(!QDELETED(src))
- update_inv_vagina()
- else if(I == anus)
- anus = null
- if(!QDELETED(src))
- update_inv_anus()
- else if(I == nipples)
- nipples = null
- if(!QDELETED(src))
- update_inv_nipples()
- else if(I == penis)
- penis = null
- if(!QDELETED(src))
- update_inv_penis()
- //SKYRAT EDIT ADDITION END
-
update_equipment_speed_mods()
// Send a signal for when we unequip an item that used to cover our feet/shoes. Used for bloody feet
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 9b30ba3f092..fa8e79b3a27 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -987,30 +987,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
return TRUE
return FALSE
- //SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
- if(ITEM_SLOT_VAGINA)
- if(H.is_bottomless())
- if(H.getorganslot(ORGAN_SLOT_VAGINA))
- return equip_delay_self_check(I, H, bypass_equip_delay_self)
- return FALSE
- return FALSE
- if(ITEM_SLOT_ANUS)
- if(H.is_bottomless())
- if(H.getorganslot(ORGAN_SLOT_ANUS))
- return equip_delay_self_check(I, H, bypass_equip_delay_self)
- return FALSE
- if(ITEM_SLOT_NIPPLES)
- if(H.is_topless())
- return equip_delay_self_check(I, H, bypass_equip_delay_self)
- return FALSE
- if(ITEM_SLOT_PENIS)
- if(H.is_bottomless())
- if(H.getorganslot(ORGAN_SLOT_PENIS))
- return equip_delay_self_check(I, H, bypass_equip_delay_self)
- return FALSE
- return FALSE
- //SKYRAT EDIT ADDITION END
-
return FALSE //Unsupported slot
/datum/species/proc/equip_delay_self_check(obj/item/I, mob/living/carbon/human/H, bypass_equip_delay_self)
@@ -1402,6 +1378,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
H.update_damage_overlays()
else//no bodypart, we deal damage with a more general method.
H.adjustBruteLoss(damage_amount)
+ INVOKE_ASYNC(H, /mob/living/carbon/human/.proc/adjust_pain, damage_amount) // SKYRAT EDIT ADDITION - ERP Pain
if(BURN)
H.damageoverlaytemp = 20
var/damage_amount = forced ? damage : damage * hit_percent * burnmod * H.physiology.burn_mod
@@ -1410,6 +1387,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
H.update_damage_overlays()
else
H.adjustFireLoss(damage_amount)
+ INVOKE_ASYNC(H, /mob/living/carbon/human/.proc/adjust_pain, damage_amount) // SKYRAT EDIT ADDITION - ERP Pain
if(TOX)
var/damage_amount = forced ? damage : damage * hit_percent * H.physiology.tox_mod
H.adjustToxLoss(damage_amount)
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 9cf0de05daa..1053b6672dd 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -42,7 +42,6 @@
if(hud_used)
hud_used.show_hud(hud_used.hud_version)
hud_used.update_ui_style(ui_style2icon(client.prefs?.read_preference(/datum/preference/choiced/ui_style)))
- hud_used.update_erp_ui_style(erp_ui_style2icon(client.prefs?.read_preference(/datum/preference/choiced/ui_style))) //SKYRAT EDIT - ADDITION - ERP ICONS FIX
next_move = 1
diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm
index 5a1ce5826cc..7d2b4e72d51 100644
--- a/code/modules/reagents/chemistry/reagents.dm
+++ b/code/modules/reagents/chemistry/reagents.dm
@@ -212,14 +212,6 @@ Primarily used in reagents/reaction_agents
/// Called when an overdose starts
/datum/reagent/proc/overdose_start(mob/living/M)
- ///SKYRAT EDIT ADDITION: Because these chemicals shouldn't bear the same weight as normal / debatably more harmful chemicals.
- if(name == "dopamine")///This one also shouldn't have any negative mood effect.
- return
- if(name == "succubus milk" || name == "incubus draft" || name == "Camphor" || name == "Pentacamphor")
- to_chat(M, span_userdanger("You feel like you took too much [name]!"))
- M.add_mood_event("[type]_overdose", /datum/mood_event/minor_overdose, name)
- return
- ///SKYRAT EDIT END
to_chat(M, span_userdanger("You feel like you took too much of [name]!"))
M.add_mood_event("[type]_overdose", /datum/mood_event/overdose, name)
return
diff --git a/modular_skyrat/master_files/code/modules/client/preferences/body_size.dm b/modular_skyrat/master_files/code/modules/client/preferences/body_size.dm
new file mode 100644
index 00000000000..afb8364f20c
--- /dev/null
+++ b/modular_skyrat/master_files/code/modules/client/preferences/body_size.dm
@@ -0,0 +1,17 @@
+/datum/preference/numeric/body_size
+ category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
+ savefile_identifier = PREFERENCE_CHARACTER
+ savefile_key = "body_size"
+ minimum = BODY_SIZE_MIN
+ maximum = BODY_SIZE_MAX
+ step = 0.01
+
+/datum/preference/numeric/body_size/is_accessible(datum/preferences/preferences)
+ var/passed_initial_check = ..(preferences)
+ return passed_initial_check
+
+/datum/preference/numeric/body_size/apply_to_human(mob/living/carbon/human/target, value)
+ target.dna.features["body_size"] = value
+
+/datum/preference/numeric/body_size/create_default_value()
+ return BODY_SIZE_NORMAL
diff --git a/modular_skyrat/master_files/code/modules/client/preferences/erp_preferences.dm b/modular_skyrat/master_files/code/modules/client/preferences/erp_preferences.dm
index b658c510ef3..767af505b0e 100644
--- a/modular_skyrat/master_files/code/modules/client/preferences/erp_preferences.dm
+++ b/modular_skyrat/master_files/code/modules/client/preferences/erp_preferences.dm
@@ -53,9 +53,35 @@
return FALSE
. = ..()
+/datum/preference/toggle/erp/apply_to_client_updated(client/client, value)
+ . = ..()
+ var/mob/living/carbon/human/target = client?.mob
+ if(!value && istype(target))
+ target.arousal = 0
+ target.pain = 0
+ target.pleasure = 0
+
/datum/preference/toggle/erp/sex_toy
savefile_key = "sextoy_pref"
+/datum/preference/toggle/erp/sex_toy/apply_to_client_updated(client/client, value)
+ apply_to_client(client, value)
+ if(!value)
+ if(ishuman(client.mob))
+ var/mob/living/carbon/human/target = client.mob
+ if(target.vagina != null)
+ target.dropItemToGround(target.vagina, TRUE, target.loc, TRUE, FALSE, TRUE)
+ if(target.anus != null)
+ target.dropItemToGround(target.anus, TRUE, target.loc, TRUE, FALSE, TRUE)
+ if(target.nipples != null)
+ target.dropItemToGround(target.nipples, TRUE, target.loc, TRUE, FALSE, TRUE)
+ if(target.penis != null)
+ target.dropItemToGround(target.penis, TRUE, target.loc, TRUE, FALSE, TRUE)
+
+
+ client.mob.hud_used.hidden_inventory_update(client.mob)
+ client.mob.hud_used.persistent_inventory_update(client.mob)
+
/datum/preference/toggle/erp/bimbofication
savefile_key = "bimbofication_pref"
@@ -77,6 +103,9 @@
/datum/preference/toggle/erp/autoemote
savefile_key = "autoemote_pref"
+/datum/preference/toggle/erp/new_genitalia_growth
+ savefile_key = "new_genitalia_growth_pref"
+
/datum/preference/choiced/erp_status
category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
savefile_identifier = PREFERENCE_CHARACTER
diff --git a/modular_skyrat/master_files/code/modules/client/preferences/cursed_shit.dm b/modular_skyrat/master_files/code/modules/client/preferences/genitals.dm
similarity index 89%
rename from modular_skyrat/master_files/code/modules/client/preferences/cursed_shit.dm
rename to modular_skyrat/master_files/code/modules/client/preferences/genitals.dm
index 4a6c6c65ddc..8a3a7e1e43f 100644
--- a/modular_skyrat/master_files/code/modules/client/preferences/cursed_shit.dm
+++ b/modular_skyrat/master_files/code/modules/client/preferences/genitals.dm
@@ -95,7 +95,7 @@
/datum/preference/choiced/genital/penis
savefile_key = "feature_penis"
- relevant_mutant_bodypart = "penis"
+ relevant_mutant_bodypart = ORGAN_SLOT_PENIS
/datum/preference/choiced/genital/penis/create_default_value()
var/datum/sprite_accessory/genital/penis/none/default = /datum/sprite_accessory/genital/penis/none
@@ -103,7 +103,7 @@
/datum/preference/toggle/genital_skin_tone/penis
savefile_key = "penis_skin_tone"
- relevant_mutant_bodypart = "penis"
+ relevant_mutant_bodypart = ORGAN_SLOT_PENIS
genital_pref_type = /datum/preference/choiced/genital/penis
/datum/preference/toggle/genital_skin_tone/penis/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
@@ -111,7 +111,7 @@
/datum/preference/toggle/genital_skin_color/penis
savefile_key = "penis_skin_color"
- relevant_mutant_bodypart = "penis"
+ relevant_mutant_bodypart = ORGAN_SLOT_PENIS
genital_pref_type = /datum/preference/choiced/genital/penis
/datum/preference/toggle/genital_skin_color/penis/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
@@ -121,7 +121,7 @@
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "penis_length"
- relevant_mutant_bodypart = "penis"
+ relevant_mutant_bodypart = ORGAN_SLOT_PENIS
minimum = PENIS_MIN_LENGTH
maximum = PENIS_MAX_LENGTH
@@ -141,7 +141,7 @@
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "penis_girth"
- relevant_mutant_bodypart = "penis"
+ relevant_mutant_bodypart = ORGAN_SLOT_PENIS
minimum = PENIS_MIN_LENGTH
maximum = PENIS_MAX_GIRTH
@@ -159,13 +159,13 @@
/datum/preference/tri_color/genital/penis
savefile_key = "penis_color"
- relevant_mutant_bodypart = "penis"
+ relevant_mutant_bodypart = ORGAN_SLOT_PENIS
type_to_check = /datum/preference/choiced/genital/penis
skin_color_type = /datum/preference/toggle/genital_skin_color/penis
/datum/preference/tri_bool/genital/penis
savefile_key = "penis_emissive"
- relevant_mutant_bodypart = "penis"
+ relevant_mutant_bodypart = ORGAN_SLOT_PENIS
type_to_check = /datum/preference/choiced/genital/penis
skin_color_type = /datum/preference/toggle/genital_skin_color/penis
@@ -174,7 +174,7 @@
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "penis_taur_mode_toggle"
default_value = FALSE
- relevant_mutant_bodypart = "penis"
+ relevant_mutant_bodypart = ORGAN_SLOT_PENIS
/datum/preference/toggle/penis_taur_mode/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
target.dna.features["penis_taur"] = value
@@ -191,7 +191,7 @@
savefile_key = "penis_sheath"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
- relevant_mutant_bodypart = "penis"
+ relevant_mutant_bodypart = ORGAN_SLOT_PENIS
/datum/preference/choiced/penis_sheath/is_accessible(datum/preferences/preferences)
var/passed_initial_check = ..(preferences)
@@ -212,7 +212,7 @@
/datum/preference/choiced/genital/testicles
savefile_key = "feature_testicles"
- relevant_mutant_bodypart = "testicles"
+ relevant_mutant_bodypart = ORGAN_SLOT_TESTICLES
/datum/preference/choiced/genital/testicles/create_default_value()
var/datum/sprite_accessory/genital/testicles/none/default = /datum/sprite_accessory/genital/testicles/none
@@ -220,7 +220,7 @@
/datum/preference/toggle/genital_skin_tone/testicles
savefile_key = "testicles_skin_tone"
- relevant_mutant_bodypart = "testicles"
+ relevant_mutant_bodypart = ORGAN_SLOT_TESTICLES
genital_pref_type = /datum/preference/choiced/genital/testicles
/datum/preference/toggle/genital_skin_tone/testicles/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
@@ -228,7 +228,7 @@
/datum/preference/toggle/genital_skin_color/testicles
savefile_key = "testicles_skin_color"
- relevant_mutant_bodypart = "testicles"
+ relevant_mutant_bodypart = ORGAN_SLOT_TESTICLES
genital_pref_type = /datum/preference/choiced/genital/testicles
/datum/preference/toggle/genital_skin_color/testicles/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
@@ -236,13 +236,13 @@
/datum/preference/tri_color/genital/testicles
savefile_key = "testicles_color"
- relevant_mutant_bodypart = "testicles"
+ relevant_mutant_bodypart = ORGAN_SLOT_TESTICLES
type_to_check = /datum/preference/choiced/genital/testicles
skin_color_type = /datum/preference/toggle/genital_skin_color/testicles
/datum/preference/tri_bool/genital/testicles
savefile_key = "testicles_emissive"
- relevant_mutant_bodypart = "testicles"
+ relevant_mutant_bodypart = ORGAN_SLOT_TESTICLES
type_to_check = /datum/preference/choiced/genital/testicles
skin_color_type = /datum/preference/toggle/genital_skin_color/testicles
@@ -250,7 +250,7 @@
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "balls_size"
- relevant_mutant_bodypart = "testicles"
+ relevant_mutant_bodypart = ORGAN_SLOT_TESTICLES
minimum = 0
maximum = 3
@@ -270,11 +270,11 @@
/datum/preference/choiced/genital/vagina
savefile_key = "feature_vagina"
- relevant_mutant_bodypart = "vagina"
+ relevant_mutant_bodypart = ORGAN_SLOT_VAGINA
/datum/preference/toggle/genital_skin_tone/vagina
savefile_key = "vagina_skin_tone"
- relevant_mutant_bodypart = "vagina"
+ relevant_mutant_bodypart = ORGAN_SLOT_VAGINA
genital_pref_type = /datum/preference/choiced/genital/vagina
/datum/preference/toggle/genital_skin_tone/vagina/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
@@ -282,7 +282,7 @@
/datum/preference/toggle/genital_skin_color/vagina
savefile_key = "vagina_skin_color"
- relevant_mutant_bodypart = "vagina"
+ relevant_mutant_bodypart = ORGAN_SLOT_VAGINA
genital_pref_type = /datum/preference/choiced/genital/vagina
/datum/preference/toggle/genital_skin_color/vagina/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
@@ -294,13 +294,13 @@
/datum/preference/tri_color/genital/vagina
savefile_key = "vagina_color"
- relevant_mutant_bodypart = "vagina"
+ relevant_mutant_bodypart = ORGAN_SLOT_VAGINA
type_to_check = /datum/preference/choiced/genital/vagina
skin_color_type = /datum/preference/toggle/genital_skin_color/vagina
/datum/preference/tri_bool/genital/vagina
savefile_key = "vagina_emissive"
- relevant_mutant_bodypart = "vagina"
+ relevant_mutant_bodypart = ORGAN_SLOT_VAGINA
type_to_check = /datum/preference/choiced/genital/vagina
skin_color_type = /datum/preference/toggle/genital_skin_color/vagina
@@ -308,7 +308,7 @@
/datum/preference/choiced/genital/womb
savefile_key = "feature_womb"
- relevant_mutant_bodypart = "womb"
+ relevant_mutant_bodypart = ORGAN_SLOT_WOMB
/datum/preference/choiced/genital/womb/create_default_value()
var/datum/sprite_accessory/genital/womb/none/default = /datum/sprite_accessory/genital/womb/none
@@ -318,7 +318,7 @@
/datum/preference/choiced/genital/breasts
savefile_key = "feature_breasts"
- relevant_mutant_bodypart = "breasts"
+ relevant_mutant_bodypart = ORGAN_SLOT_BREASTS
/datum/preference/choiced/genital/breasts/create_default_value()
var/datum/sprite_accessory/genital/breasts/none/default = /datum/sprite_accessory/genital/breasts/none
@@ -326,7 +326,7 @@
/datum/preference/toggle/genital_skin_tone/breasts
savefile_key = "breasts_skin_tone"
- relevant_mutant_bodypart = "breasts"
+ relevant_mutant_bodypart = ORGAN_SLOT_BREASTS
genital_pref_type = /datum/preference/choiced/genital/breasts
/datum/preference/toggle/genital_skin_tone/breasts/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
@@ -334,7 +334,7 @@
/datum/preference/toggle/genital_skin_color/breasts
savefile_key = "breasts_skin_color"
- relevant_mutant_bodypart = "breasts"
+ relevant_mutant_bodypart = ORGAN_SLOT_BREASTS
genital_pref_type = /datum/preference/choiced/genital/breasts
/datum/preference/toggle/genital_skin_color/breasts/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
@@ -342,13 +342,13 @@
/datum/preference/tri_color/genital/breasts
savefile_key = "breasts_color"
- relevant_mutant_bodypart = "breasts"
+ relevant_mutant_bodypart = ORGAN_SLOT_BREASTS
type_to_check = /datum/preference/choiced/genital/breasts
skin_color_type = /datum/preference/toggle/genital_skin_color/breasts
/datum/preference/tri_bool/genital/breasts
savefile_key = "breasts_emissive"
- relevant_mutant_bodypart = "breasts"
+ relevant_mutant_bodypart = ORGAN_SLOT_BREASTS
type_to_check = /datum/preference/choiced/genital/breasts
skin_color_type = /datum/preference/toggle/genital_skin_color/breasts
@@ -357,7 +357,7 @@
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "breasts_lactation_toggle"
default_value = FALSE
- relevant_mutant_bodypart = "breasts"
+ relevant_mutant_bodypart = ORGAN_SLOT_BREASTS
/datum/preference/toggle/breasts_lactation/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
target.dna.features["breasts_lactation"] = value
@@ -368,52 +368,33 @@
var/part_enabled = is_factual_sprite_accessory(relevant_mutant_bodypart, preferences.read_preference(/datum/preference/choiced/genital/breasts))
return part_enabled && (passed_initial_check || allowed)
-/datum/preference/numeric/breasts_size
+/datum/preference/choiced/breasts_size
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "breasts_size"
- relevant_mutant_bodypart = "breasts"
- minimum = 0
- maximum = 16
+ relevant_mutant_bodypart = ORGAN_SLOT_BREASTS
-/datum/preference/numeric/breasts_size/is_accessible(datum/preferences/preferences)
+/datum/preference/choiced/breasts_size/init_possible_values()
+ return GLOB.preference_breast_sizes
+
+/datum/preference/choiced/breasts_size/is_accessible(datum/preferences/preferences)
var/passed_initial_check = ..(preferences)
var/allowed = preferences.read_preference(/datum/preference/toggle/allow_mismatched_parts)
var/part_enabled = is_factual_sprite_accessory(relevant_mutant_bodypart, preferences.read_preference(/datum/preference/choiced/genital/breasts))
return ((passed_initial_check || allowed) && part_enabled)
-/datum/preference/numeric/breasts_size/apply_to_human(mob/living/carbon/human/target, value)
- target.dna.features["breasts_size"] = value
+/datum/preference/choiced/breasts_size/apply_to_human(mob/living/carbon/human/target, value)
+ target.dna.features["breasts_size"] = GLOB.breast_size_to_number[value]
-/datum/preference/numeric/breasts_size/create_default_value()
- return 4
+/datum/preference/choiced/breasts_size/create_default_value()
+ return BREAST_SIZE_C
// ANUS
/datum/preference/choiced/genital/anus
savefile_key = "feature_anus"
- relevant_mutant_bodypart = "anus"
+ relevant_mutant_bodypart = ORGAN_SLOT_ANUS
/datum/preference/choiced/genital/anus/create_default_value()
var/datum/sprite_accessory/genital/anus/none/default = /datum/sprite_accessory/genital/anus/none
return initial(default.name)
-
-// OTHER
-
-/datum/preference/numeric/body_size
- category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
- savefile_identifier = PREFERENCE_CHARACTER
- savefile_key = "body_size"
- minimum = BODY_SIZE_MIN
- maximum = BODY_SIZE_MAX
- step = 0.01
-
-/datum/preference/numeric/body_size/is_accessible(datum/preferences/preferences)
- var/passed_initial_check = ..(preferences)
- return passed_initial_check
-
-/datum/preference/numeric/body_size/apply_to_human(mob/living/carbon/human/target, value)
- target.dna.features["body_size"] = value
-
-/datum/preference/numeric/body_size/create_default_value()
- return BODY_SIZE_NORMAL
diff --git a/modular_skyrat/master_files/code/modules/client/preferences_savefile.dm b/modular_skyrat/master_files/code/modules/client/preferences_savefile.dm
index ba6b51ad1b1..9a4eeff33c1 100644
--- a/modular_skyrat/master_files/code/modules/client/preferences_savefile.dm
+++ b/modular_skyrat/master_files/code/modules/client/preferences_savefile.dm
@@ -3,7 +3,7 @@
* You can't really use the non-modular version, least you eventually want asinine merge
* conflicts and/or potentially disastrous issues to arise, so here's your own.
*/
-#define MODULAR_SAVEFILE_VERSION_MAX 1
+#define MODULAR_SAVEFILE_VERSION_MAX 2
#define MODULAR_SAVEFILE_UP_TO_DATE -1
@@ -110,6 +110,13 @@
for(var/pref_type in subtypesof(/datum/preference/toggle/genital_skin_tone))
write_preference(GLOB.preference_entries[pref_type], TRUE)
+ if(current_version < 2)
+ var/list/old_breast_prefs
+ READ_FILE(save["breasts_size"], old_breast_prefs)
+ if(old_breast_prefs) // Can't be too careful
+ // You weren't meant to be able to pick sizes over this anyways.
+ write_preference(GLOB.preference_entries[/datum/preference/choiced/breasts_size], min(GLOB.breast_size_translation["[old_breast_prefs]"], 10))
+
/datum/preferences/proc/check_migration()
if(!tgui_prefs_migration)
to_chat(parent, examine_block(span_redtext("CRITICAL FAILURE IN PREFERENCE MIGRATION, REPORT THIS IMMEDIATELY.")))
diff --git a/modular_skyrat/modules/customization/_globalvars/lists.dm b/modular_skyrat/modules/customization/_globalvars/lists.dm
index a7bec54f41d..03a9d43be53 100644
--- a/modular_skyrat/modules/customization/_globalvars/lists.dm
+++ b/modular_skyrat/modules/customization/_globalvars/lists.dm
@@ -1,67 +1,115 @@
//DO NOT CHANGE THOSE LISTS UNLESS YOU KNOW WHAT YOURE DOING (will mess savefiles)
-GLOBAL_LIST_INIT(breasts_size_translation, list("0" = "Flatchested",
- "1" = "A",
- "2" = "B",
- "3" = "C",
- "4" = "D",
- "5" = "E",
- "6" = "F",
- "7" = "G",
- "8" = "H",
- "9" = "I",
- "10" = "J",
- "11" = "K",
- "12" = "L",
- "13" = "M",
- "14" = "N",
- "15" = "O",
- "16" = "P",
- ))
+GLOBAL_LIST_INIT(breast_size_translation, list(
+ "0" = BREAST_SIZE_FLATCHESTED,
+ "1" = BREAST_SIZE_A,
+ "2" = BREAST_SIZE_B,
+ "3" = BREAST_SIZE_C,
+ "4" = BREAST_SIZE_D,
+ "5" = BREAST_SIZE_E,
+ "6" = BREAST_SIZE_F,
+ "7" = BREAST_SIZE_G,
+ "8" = BREAST_SIZE_H,
+ "9" = BREAST_SIZE_I,
+ "10" = BREAST_SIZE_J,
+ "11" = BREAST_SIZE_K, // Succubus milk moment.
+ "12" = BREAST_SIZE_L,
+ "13" = BREAST_SIZE_M,
+ "14" = BREAST_SIZE_N,
+ "15" = BREAST_SIZE_O,
+ "16" = BREAST_SIZE_P,
+ ))
-GLOBAL_LIST_INIT(balls_size_translation, list("0" = "Small",
- "1" = "Average",
- "2" = "Big",
- "3" = "Enormous"
- ))
+GLOBAL_LIST_INIT(breast_size_to_number, list(
+ BREAST_SIZE_FLATCHESTED = 0,
+ BREAST_SIZE_A = 1,
+ BREAST_SIZE_B = 2,
+ BREAST_SIZE_C = 3,
+ BREAST_SIZE_D = 4,
+ BREAST_SIZE_E = 5,
+ BREAST_SIZE_F = 6,
+ BREAST_SIZE_G = 7,
+ BREAST_SIZE_H = 8,
+ BREAST_SIZE_I = 9,
+ BREAST_SIZE_J = 10,
+ BREAST_SIZE_K = 11, // Succubus milk moment.
+ BREAST_SIZE_L = 12,
+ BREAST_SIZE_M = 13,
+ BREAST_SIZE_N = 14,
+ BREAST_SIZE_O = 15,
+ BREAST_SIZE_P = 16,
+ ))
-GLOBAL_LIST_INIT(marking_zone_to_bitflag, list(BODY_ZONE_HEAD = HEAD,
- BODY_ZONE_CHEST = CHEST,
- BODY_ZONE_L_LEG = LEG_LEFT,
- BODY_ZONE_R_LEG = LEG_RIGHT,
- BODY_ZONE_L_ARM = ARM_LEFT,
- BODY_ZONE_R_ARM = ARM_RIGHT,
- BODY_ZONE_PRECISE_L_HAND = HAND_LEFT,
- BODY_ZONE_PRECISE_R_HAND = HAND_RIGHT
- ))
+GLOBAL_LIST_INIT(balls_size_translation, list(
+ "0" = "Small",
+ "1" = "Average",
+ "2" = "Big",
+ "3" = "Enormous"
+ ))
-//GLOBAL_LIST_INIT(marking_zones, list("Head","Chest","Left Leg","Right Leg","Left Arm","Right Arm","Left Hand","Right Hand"))
+GLOBAL_LIST_INIT(marking_zone_to_bitflag, list(
+ BODY_ZONE_HEAD = HEAD,
+ BODY_ZONE_CHEST = CHEST,
+ BODY_ZONE_L_LEG = LEG_LEFT,
+ BODY_ZONE_R_LEG = LEG_RIGHT,
+ BODY_ZONE_L_ARM = ARM_LEFT,
+ BODY_ZONE_R_ARM = ARM_RIGHT,
+ BODY_ZONE_PRECISE_L_HAND = HAND_LEFT,
+ BODY_ZONE_PRECISE_R_HAND = HAND_RIGHT
+ ))
-GLOBAL_LIST_INIT(marking_zones, list(BODY_ZONE_HEAD,BODY_ZONE_CHEST,BODY_ZONE_L_LEG,BODY_ZONE_R_LEG,BODY_ZONE_L_ARM,BODY_ZONE_R_ARM,BODY_ZONE_PRECISE_L_HAND,BODY_ZONE_PRECISE_R_HAND))
+GLOBAL_LIST_INIT(marking_zones, list(
+ BODY_ZONE_HEAD,
+ BODY_ZONE_CHEST,
+ BODY_ZONE_L_LEG,
+ BODY_ZONE_R_LEG,
+ BODY_ZONE_L_ARM,
+ BODY_ZONE_R_ARM,
+ BODY_ZONE_PRECISE_L_HAND,
+ BODY_ZONE_PRECISE_R_HAND,
+ ))
///Those are the values available from prefs
-GLOBAL_LIST_INIT(preference_breast_sizes, list("Flatchested","A","B","C","D","E","F","G","H","I","J"))
+GLOBAL_LIST_INIT(preference_breast_sizes, list(
+ BREAST_SIZE_FLATCHESTED,
+ BREAST_SIZE_A,
+ BREAST_SIZE_B,
+ BREAST_SIZE_C,
+ BREAST_SIZE_D,
+ BREAST_SIZE_E,
+ BREAST_SIZE_F,
+ BREAST_SIZE_G,
+ BREAST_SIZE_H,
+ BREAST_SIZE_I,
+ BREAST_SIZE_J,
+ ))
-GLOBAL_LIST_INIT(preference_balls_sizes, list("Small","Average","Big","Enormous"))
+GLOBAL_LIST_INIT(preference_balls_sizes, list(
+ "Small",
+ "Average",
+ "Big",
+ "Enormous",
+ ))
-GLOBAL_LIST_INIT(robotic_styles_list, list("None" = "None",
- "Surplus" = 'icons/mob/augmentation/surplus_augments.dmi',
- "Cyborg" = 'icons/mob/augmentation/augments.dmi',
- "Engineering" = 'icons/mob/augmentation/augments_engineer.dmi',
- "Mining" = 'icons/mob/augmentation/augments_mining.dmi',
- "Security" = 'icons/mob/augmentation/augments_security.dmi',
- "Morpheus Cyberkinetics" = 'modular_skyrat/master_files/icons/mob/augmentation/mcgipc.dmi',
- "Bishop Cyberkinetics" = 'modular_skyrat/master_files/icons/mob/augmentation/bshipc.dmi',
- "Bishop Cyberkinetics 2.0" = 'modular_skyrat/master_files/icons/mob/augmentation/bs2ipc.dmi',
- "Hephaestus Industries" = 'modular_skyrat/master_files/icons/mob/augmentation/hsiipc.dmi',
- "Hephaestus Industries 2.0" = 'modular_skyrat/master_files/icons/mob/augmentation/hi2ipc.dmi',
- "Shellguard Munitions Standard Series" = 'modular_skyrat/master_files/icons/mob/augmentation/sgmipc.dmi',
- "Ward-Takahashi Manufacturing" = 'modular_skyrat/master_files/icons/mob/augmentation/wtmipc.dmi',
- "Xion Manufacturing Group" = 'modular_skyrat/master_files/icons/mob/augmentation/xmgipc.dmi',
- "Xion Manufacturing Group 2.0" = 'modular_skyrat/master_files/icons/mob/augmentation/xm2ipc.dmi',
- "Zeng-Hu Pharmaceuticals" = 'modular_skyrat/master_files/icons/mob/augmentation/zhpipc.dmi'
- ))
+GLOBAL_LIST_INIT(robotic_styles_list, list(
+ "None" = "None",
+ "Surplus" = 'icons/mob/augmentation/surplus_augments.dmi',
+ "Cyborg" = 'icons/mob/augmentation/augments.dmi',
+ "Engineering" = 'icons/mob/augmentation/augments_engineer.dmi',
+ "Mining" = 'icons/mob/augmentation/augments_mining.dmi',
+ "Security" = 'icons/mob/augmentation/augments_security.dmi',
+ "Morpheus Cyberkinetics" = 'modular_skyrat/master_files/icons/mob/augmentation/mcgipc.dmi',
+ "Bishop Cyberkinetics" = 'modular_skyrat/master_files/icons/mob/augmentation/bshipc.dmi',
+ "Bishop Cyberkinetics 2.0" = 'modular_skyrat/master_files/icons/mob/augmentation/bs2ipc.dmi',
+ "Hephaestus Industries" = 'modular_skyrat/master_files/icons/mob/augmentation/hsiipc.dmi',
+ "Hephaestus Industries 2.0" = 'modular_skyrat/master_files/icons/mob/augmentation/hi2ipc.dmi',
+ "Shellguard Munitions Standard Series" = 'modular_skyrat/master_files/icons/mob/augmentation/sgmipc.dmi',
+ "Ward-Takahashi Manufacturing" = 'modular_skyrat/master_files/icons/mob/augmentation/wtmipc.dmi',
+ "Xion Manufacturing Group" = 'modular_skyrat/master_files/icons/mob/augmentation/xmgipc.dmi',
+ "Xion Manufacturing Group 2.0" = 'modular_skyrat/master_files/icons/mob/augmentation/xm2ipc.dmi',
+ "Zeng-Hu Pharmaceuticals" = 'modular_skyrat/master_files/icons/mob/augmentation/zhpipc.dmi'
+ ))
//ghoul colors
GLOBAL_LIST_INIT(color_list_ghoul, list( \
diff --git a/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/genitals.dm b/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/genitals.dm
index 6562d4b5fd5..b4877bd423d 100644
--- a/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/genitals.dm
+++ b/modular_skyrat/modules/customization/modules/mob/dead/new_player/sprite_accessories/genitals.dm
@@ -35,7 +35,7 @@
icon = 'modular_skyrat/master_files/icons/mob/sprite_accessory/genitals/penis_onmob.dmi'
organ_type = /obj/item/organ/external/genital/penis
associated_organ_slot = ORGAN_SLOT_PENIS
- key = "penis"
+ key = ORGAN_SLOT_PENIS
color_src = USE_MATRIXED_COLORS
always_color_customizable = TRUE
center = TRUE
@@ -117,7 +117,7 @@
icon = 'modular_skyrat/master_files/icons/mob/sprite_accessory/genitals/testicles_onmob.dmi'
organ_type = /obj/item/organ/external/genital/testicles
associated_organ_slot = ORGAN_SLOT_TESTICLES
- key = "testicles"
+ key = ORGAN_SLOT_TESTICLES
always_color_customizable = TRUE
special_icon_case = TRUE
special_x_dimension = TRUE
@@ -168,7 +168,7 @@
icon = 'modular_skyrat/master_files/icons/mob/sprite_accessory/genitals/vagina_onmob.dmi'
organ_type = /obj/item/organ/external/genital/vagina
associated_organ_slot = ORGAN_SLOT_VAGINA
- key = "vagina"
+ key = ORGAN_SLOT_VAGINA
always_color_customizable = TRUE
default_color = "#FFCCCC"
relevent_layers = list(BODY_FRONT_LAYER)
@@ -224,7 +224,7 @@
/datum/sprite_accessory/genital/womb
organ_type = /obj/item/organ/external/genital/womb
associated_organ_slot = ORGAN_SLOT_WOMB
- key = "womb"
+ key = ORGAN_SLOT_WOMB
genetic = TRUE
/datum/sprite_accessory/genital/womb/none
@@ -241,7 +241,7 @@
/datum/sprite_accessory/genital/anus
organ_type = /obj/item/organ/external/genital/anus
associated_organ_slot = ORGAN_SLOT_ANUS
- key = "anus"
+ key = ORGAN_SLOT_ANUS
genetic = TRUE
/datum/sprite_accessory/genital/anus/is_hidden(mob/living/carbon/human/owner, obj/item/bodypart/bodypart)
@@ -264,7 +264,7 @@
icon = 'modular_skyrat/master_files/icons/mob/sprite_accessory/genitals/breasts_onmob.dmi'
organ_type = /obj/item/organ/external/genital/breasts
associated_organ_slot = ORGAN_SLOT_BREASTS
- key = "breasts"
+ key = ORGAN_SLOT_BREASTS
always_color_customizable = TRUE
default_color = DEFAULT_SKIN_OR_PRIMARY
relevent_layers = list(BODY_BEHIND_LAYER, BODY_FRONT_LAYER)
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/human.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/human.dm
index 8b6b08d6a18..edfa5bbbabe 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/human.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/human.dm
@@ -1,3 +1,6 @@
+/mob/living/carbon/human
+ var/static/list/possible_genitals = list(ORGAN_SLOT_PENIS, ORGAN_SLOT_TESTICLES, ORGAN_SLOT_VAGINA, ORGAN_SLOT_BREASTS, ORGAN_SLOT_ANUS)
+
/mob/living/carbon/human/Topic(href, href_list)
. = ..()
@@ -5,7 +8,7 @@
switch(href_list["lookup_info"])
if("genitals")
var/list/line = list()
- for(var/genital in list("penis", "testicles", "vagina", "breasts", "anus"))
+ for(var/genital in possible_genitals)
if(!dna.species.mutant_bodyparts[genital])
continue
var/datum/sprite_accessory/genital/G = GLOB.sprite_accessories[genital][dna.species.mutant_bodyparts[genital][MUTANT_INDEX_NAME]]
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm
index 5de48571504..b1b708e16bd 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm
@@ -262,12 +262,12 @@ GLOBAL_LIST_EMPTY(customizable_races)
/datum/species/New()
. = ..()
if(can_have_genitals)
- default_mutant_bodyparts["vagina"] = "None"
- default_mutant_bodyparts["womb"] = "None"
- default_mutant_bodyparts["testicles"] = "None"
- default_mutant_bodyparts["breasts"] = "None"
- default_mutant_bodyparts["anus"] = "None"
- default_mutant_bodyparts["penis"] = "None"
+ default_mutant_bodyparts[ORGAN_SLOT_VAGINA] = "None"
+ default_mutant_bodyparts[ORGAN_SLOT_WOMB] = "None"
+ default_mutant_bodyparts[ORGAN_SLOT_TESTICLES] = "None"
+ default_mutant_bodyparts[ORGAN_SLOT_BREASTS] = "None"
+ default_mutant_bodyparts[ORGAN_SLOT_ANUS] = "None"
+ default_mutant_bodyparts[ORGAN_SLOT_PENIS] = "None"
/datum/species/dullahan
mutant_bodyparts = list()
diff --git a/modular_skyrat/modules/customization/modules/surgery/organs/genitals.dm b/modular_skyrat/modules/customization/modules/surgery/organs/genitals.dm
index b80e7eba944..2f889b56098 100644
--- a/modular_skyrat/modules/customization/modules/surgery/organs/genitals.dm
+++ b/modular_skyrat/modules/customization/modules/surgery/organs/genitals.dm
@@ -92,7 +92,7 @@
icon = 'modular_skyrat/master_files/icons/obj/genitals/penis.dmi'
zone = BODY_ZONE_PRECISE_GROIN
slot = ORGAN_SLOT_PENIS
- mutantpart_key = "penis"
+ mutantpart_key = ORGAN_SLOT_PENIS
mutantpart_info = list(MUTANT_INDEX_NAME = "Human", MUTANT_INDEX_COLOR_LIST = list("#FFEEBB"))
drop_when_organ_spilling = FALSE
var/girth = 9
@@ -181,7 +181,7 @@
uses_skintones = accessory.has_skintone_shading
/obj/item/organ/external/genital/penis/get_global_feature_list()
- return GLOB.sprite_accessories["penis"]
+ return GLOB.sprite_accessories[ORGAN_SLOT_PENIS]
/obj/item/organ/external/genital/testicles
@@ -189,7 +189,7 @@
desc = "A male reproductive organ."
icon_state = "testicles"
icon = 'modular_skyrat/master_files/icons/obj/genitals/testicles.dmi'
- mutantpart_key = "testicles"
+ mutantpart_key = ORGAN_SLOT_TESTICLES
mutantpart_info = list(MUTANT_INDEX_NAME = "Pair", MUTANT_INDEX_COLOR_LIST = list("#FFEEBB"))
zone = BODY_ZONE_PRECISE_GROIN
slot = ORGAN_SLOT_TESTICLES
@@ -228,7 +228,7 @@
return passed_string
/obj/item/organ/external/genital/testicles/get_global_feature_list()
- return GLOB.sprite_accessories["testicles"]
+ return GLOB.sprite_accessories[ORGAN_SLOT_TESTICLES]
/obj/item/organ/external/genital/testicles/proc/balls_size_to_description(number)
@@ -236,7 +236,7 @@
number = 0
var/returned = GLOB.balls_size_translation["[number]"]
if(!returned)
- returned = "beyond measurement"
+ returned = BREAST_SIZE_BEYOND_MEASUREMENT
return returned
/obj/item/organ/external/genital/testicles/proc/balls_description_to_size(cup)
@@ -250,7 +250,7 @@
name = "vagina"
icon = 'modular_skyrat/master_files/icons/obj/genitals/vagina.dmi'
icon_state = "vagina"
- mutantpart_key = "vagina"
+ mutantpart_key = ORGAN_SLOT_VAGINA
mutantpart_info = list(MUTANT_INDEX_NAME = "Human", MUTANT_INDEX_COLOR_LIST = list("#FFEEBB"))
zone = BODY_ZONE_PRECISE_GROIN
slot = ORGAN_SLOT_VAGINA
@@ -285,7 +285,7 @@
uses_skintones = accessory.has_skintone_shading
/obj/item/organ/external/genital/vagina/get_global_feature_list()
- return GLOB.sprite_accessories["vagina"]
+ return GLOB.sprite_accessories[ORGAN_SLOT_VAGINA]
/obj/item/organ/external/genital/womb
@@ -293,7 +293,7 @@
desc = "A female reproductive organ."
icon = 'modular_skyrat/master_files/icons/obj/genitals/vagina.dmi'
icon_state = "womb"
- mutantpart_key = "womb"
+ mutantpart_key = ORGAN_SLOT_WOMB
mutantpart_info = list(MUTANT_INDEX_NAME = "Normal", MUTANT_INDEX_COLOR_LIST = list("FFEEBB"))
zone = BODY_ZONE_PRECISE_GROIN
slot = ORGAN_SLOT_WOMB
@@ -303,7 +303,7 @@
drop_when_organ_spilling = FALSE
/obj/item/organ/external/genital/womb/get_global_feature_list()
- return GLOB.sprite_accessories["womb"]
+ return GLOB.sprite_accessories[ORGAN_SLOT_WOMB]
/obj/item/organ/external/genital/anus
@@ -311,7 +311,7 @@
desc = "What do you want me to tell you?"
icon = 'modular_skyrat/master_files/icons/obj/genitals/anus.dmi'
icon_state = "anus"
- mutantpart_key = "anus"
+ mutantpart_key = ORGAN_SLOT_ANUS
mutantpart_info = list(MUTANT_INDEX_NAME = "Normal", MUTANT_INDEX_COLOR_LIST = list("FEB"))
zone = BODY_ZONE_PRECISE_GROIN
slot = ORGAN_SLOT_ANUS
@@ -327,7 +327,7 @@
return returned_string
/obj/item/organ/external/genital/anus/get_global_feature_list()
- return GLOB.sprite_accessories["anus"]
+ return GLOB.sprite_accessories[ORGAN_SLOT_ANUS]
/obj/item/organ/external/genital/breasts
@@ -336,7 +336,7 @@
icon_state = "breasts"
icon = 'modular_skyrat/master_files/icons/obj/genitals/breasts.dmi'
genital_type = "pair"
- mutantpart_key = "breasts"
+ mutantpart_key = ORGAN_SLOT_BREASTS
mutantpart_info = list(MUTANT_INDEX_NAME = "Pair", MUTANT_INDEX_COLOR_LIST = list("#FFEEBB"))
zone = BODY_ZONE_CHEST
slot = ORGAN_SLOT_BREASTS
@@ -349,9 +349,9 @@
var/size_description
var/translation = breasts_size_to_cup(genital_size)
switch(translation)
- if("Flatchested")
+ if(BREAST_SIZE_FLATCHESTED)
size_description = " They are small and flat, however."
- if("beyond measurement")
+ if(BREAST_SIZE_BEYOND_MEASUREMENT)
size_description = " Their size is enormous, you estimate they're around [genital_size] inches in diameter."
else
size_description = " You estimate they are [translation]-cups."
@@ -400,19 +400,19 @@
uses_skintones = accessory.has_skintone_shading
/obj/item/organ/external/genital/breasts/get_global_feature_list()
- return GLOB.sprite_accessories["breasts"]
+ return GLOB.sprite_accessories[ORGAN_SLOT_BREASTS]
/obj/item/organ/external/genital/breasts/proc/breasts_size_to_cup(number)
if(number < 0)
number = 0
- var/returned = GLOB.breasts_size_translation["[number]"]
+ var/returned = GLOB.breast_size_translation["[number]"]
if(!returned)
- returned = "beyond measurement"
+ returned = BREAST_SIZE_BEYOND_MEASUREMENT
return returned
/obj/item/organ/external/genital/breasts/proc/breasts_cup_to_size(cup)
- for(var/key in GLOB.breasts_size_translation)
- if(GLOB.breasts_size_translation[key] == cup)
+ for(var/key in GLOB.breast_size_translation)
+ if(GLOB.breast_size_translation[key] == cup)
return text2num(key)
return 0
diff --git a/modular_skyrat/modules/event_vote/code/event_vote.dm b/modular_skyrat/modules/event_vote/code/event_vote.dm
index a97301c36de..5632b6de8b8 100644
--- a/modular_skyrat/modules/event_vote/code/event_vote.dm
+++ b/modular_skyrat/modules/event_vote/code/event_vote.dm
@@ -505,9 +505,9 @@
log_data += "Sex: [iterating_human.gender]"
log_data += "Age: [iterating_human.age]"
log_data += "Body Type: [iterating_human.physique]"
- log_data += "Penis: [iterating_human.dna.mutant_bodyparts["penis"]["name"]]"
- log_data += "Vagina: [iterating_human.dna.mutant_bodyparts["vagina"]["name"]]"
- log_data += "Breasts: [iterating_human.dna.mutant_bodyparts["breasts"]["name"]]"
+ log_data += "Penis: [iterating_human.dna.mutant_bodyparts[ORGAN_SLOT_PENIS]["name"]]"
+ log_data += "Vagina: [iterating_human.dna.mutant_bodyparts[ORGAN_SLOT_VAGINA]["name"]]"
+ log_data += "Breasts: [iterating_human.dna.mutant_bodyparts[ORGAN_SLOT_BREASTS]["name"]]"
log_data += "Body size: [iterating_human.dna.features["body_size"]]"
log_data += "END ENTRY"
diff --git a/modular_skyrat/modules/interaction_menu/code/interaction_component.dm b/modular_skyrat/modules/interaction_menu/code/interaction_component.dm
index ac48006f265..b39449501b8 100644
--- a/modular_skyrat/modules/interaction_menu/code/interaction_component.dm
+++ b/modular_skyrat/modules/interaction_menu/code/interaction_component.dm
@@ -61,7 +61,6 @@
return TRUE
/// UI Control
-
/datum/component/interactable/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
@@ -95,8 +94,39 @@
data["self"] = self.name
data["block_interact"] = interact_next >= world.time
data["interactions"] = categories
+
+ var/list/parts = list()
+
+ if(ishuman(user) && can_lewd_strip(user, self))
+ if(self.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
+ if(self.has_vagina())
+ parts += list(generate_strip_entry(ORGAN_SLOT_VAGINA, self, user, self.vagina))
+ if(self.has_penis())
+ parts += list(generate_strip_entry(ORGAN_SLOT_PENIS, self, user, self.penis))
+ if(self.has_anus())
+ parts += list(generate_strip_entry(ORGAN_SLOT_ANUS, self, user, self.anus))
+ parts += list(generate_strip_entry(ORGAN_SLOT_NIPPLES, self, user, self.nipples))
+
+ data["lewd_slots"] = parts
+
return data
+/**
+ * Takes the organ slot name, along with a target and source, along with the item on the target that the source can potentially interact with.
+ * If the source can't interact with said slot, or there is no item in the first place, it'll set the icon to null to indicate that TGUI should put a placeholder sprite.
+ *
+ * Arguments:
+ * * name - The name of slot to check and return inside the generated list.
+ * * target - The mob that's being interacted with.
+ * * source - The mob that's interacting.
+ * * item - The item that's currently inside said slot. Can be null.
+ */
+/datum/component/interactable/proc/generate_strip_entry(name, mob/living/carbon/human/target, mob/living/carbon/human/source, obj/item/clothing/sextoy/item)
+ return list(
+ "name" = name,
+ "img" = (item && can_lewd_strip(source, target, name)) ? icon2base64(icon(item.icon, item.icon_state, SOUTH, 1)) : null
+ )
+
/datum/component/interactable/ui_act(action, list/params)
. = ..()
if(.)
@@ -113,4 +143,85 @@
interact_next = interaction_component.interact_last + INTERACTION_COOLDOWN
interaction_component.interact_next = interact_next
return TRUE
+
+ if(params["item_slot"])
+ // This code should be easy enough to follow... I hope.
+ var/item_index = params["item_slot"]
+ var/mob/living/carbon/human/source = locate(params["userref"])
+ var/mob/living/carbon/human/target = locate(params["selfref"])
+ var/obj/item/new_item = source.get_active_held_item()
+ var/obj/item/existing_item = target.vars[item_index]
+
+ if(!existing_item && !new_item)
+ source.show_message(span_warning("No item to insert or remove!"))
+ return
+
+ if(!existing_item && !istype(new_item, /obj/item/clothing/sextoy))
+ source.show_message(span_warning("The item you're holding is not a toy!"))
+ return
+
+ if(can_lewd_strip(source, target, item_index) && is_toy_compatible(new_item, item_index))
+ var/internal = (item_index in list(ORGAN_SLOT_VAGINA, ORGAN_SLOT_ANUS))
+ var/insert_or_attach = internal ? "insert" : "attach"
+ var/into_or_onto = internal ? "into" : "onto"
+
+ if(existing_item)
+ source.visible_message(span_purple("[source.name] starts trying to remove something from [target.name]'s [item_index]."), span_purple("You start to remove [existing_item.name] from [target.name]'s [item_index]."), span_purple("You hear someone trying to remove something from someone nearby."), vision_distance = 1, ignored_mobs = list(target))
+ else if (new_item)
+ source.visible_message(span_purple("[source.name] starts trying to [insert_or_attach] the [new_item.name] [into_or_onto] [target.name]'s [item_index]."), span_purple("You start to [insert_or_attach] the [new_item.name] [into_or_onto] [target.name]'s [item_index]."), span_purple("You hear someone trying to [insert_or_attach] something [into_or_onto] someone nearby."), vision_distance = 1, ignored_mobs = list(target))
+ if (source != target)
+ target.show_message(span_warning("[source.name] is trying to [existing_item ? "remove the [existing_item.name] [internal ? "in" : "on"]" : new_item ? "is trying to [insert_or_attach] the [new_item.name] [into_or_onto]" : span_alert("What the fuck, impossible condition? interaction_component.dm!")] your [item_index]!"))
+ if(do_after(
+ source,
+ 5 SECONDS,
+ target,
+ interaction_key = "interation_[item_index]"
+ ) && can_lewd_strip(source, target, item_index))
+
+ if(existing_item)
+ source.visible_message(span_purple("[source.name] removes [existing_item.name] from [target.name]'s [item_index]."), span_purple("You remove [existing_item.name] from [target.name]'s [item_index]."), span_purple("You hear someone remove something from someone nearby."), vision_distance = 1)
+ target.dropItemToGround(existing_item, force = TRUE) // Force is true, cause nodrop shouldn't affect lewd items.
+ target.vars[item_index] = null
+ else if (new_item)
+ source.visible_message(span_purple("[source.name] [internal ? "inserts" : "attaches"] the [new_item.name] [into_or_onto] [target.name]'s [item_index]."), span_purple("You [insert_or_attach] the [new_item.name] [into_or_onto] [target.name]'s [item_index]."), span_purple("You hear someone [insert_or_attach] something [into_or_onto] someone nearby."), vision_distance = 1)
+ target.vars[item_index] = new_item
+ new_item.forceMove(target)
+ target.update_inv_lewd()
+
+ else
+ source.show_message(span_warning("Failed to adjust [target.name]'s toys!"))
+
+ return TRUE
+
message_admins("Unhandled interaction '[params["interaction"]]'. Inform coders.")
+
+/// Checks if the target has ERP toys enabled, and can be logially reached by the user.
+/datum/component/interactable/proc/can_lewd_strip(mob/living/carbon/human/source, mob/living/carbon/human/target, slot_index)
+ if(!target.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
+ return FALSE
+ if(!(source.loc == target.loc || source.Adjacent(target)))
+ return FALSE
+ if(!source.has_arms())
+ return FALSE
+ if(!slot_index) // This condition is for the UI to decide if the button is shown at all. Slot index should never be null otherwise.
+ return TRUE
+ if(slot_index == ORGAN_SLOT_NIPPLES && !target.is_topless())
+ return FALSE
+ return target.is_bottomless()
+
+/// Decides if a player should be able to insert or remove an item from a provided lewd slot_index.
+/datum/component/interactable/proc/is_toy_compatible(obj/item/clothing/sextoy/item, slot_index)
+ if(!item) // Used for UI code, should never be actually null during actual logic code.
+ return TRUE
+
+ switch(slot_index)
+ if(ORGAN_SLOT_VAGINA)
+ return item.lewd_slot_flags & LEWD_SLOT_VAGINA
+ if(ORGAN_SLOT_PENIS)
+ return item.lewd_slot_flags & LEWD_SLOT_PENIS
+ if(ORGAN_SLOT_ANUS)
+ return item.lewd_slot_flags & LEWD_SLOT_ANUS
+ if(ORGAN_SLOT_NIPPLES)
+ return item.lewd_slot_flags & LEWD_SLOT_NIPPLES
+ else
+ return FALSE
diff --git a/modular_skyrat/modules/interaction_menu/code/interaction_datum.dm b/modular_skyrat/modules/interaction_menu/code/interaction_datum.dm
index 0ea0df638a2..0991f09b9db 100644
--- a/modular_skyrat/modules/interaction_menu/code/interaction_datum.dm
+++ b/modular_skyrat/modules/interaction_menu/code/interaction_datum.dm
@@ -119,12 +119,12 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction)
SEND_SOUND(sound_cache, mob)
if(lewd)
- user.adjustPleasure(user_pleasure)
- user.adjustArousal(user_arousal)
- user.adjustPain(user_pain)
- target.adjustPleasure(target_pleasure)
- target.adjustArousal(target_arousal)
- target.adjustPain(target_pain)
+ user.adjust_pleasure(user_pleasure)
+ user.adjust_arousal(user_arousal)
+ user.adjust_pain(user_pain)
+ target.adjust_pleasure(target_pleasure)
+ target.adjust_arousal(target_arousal)
+ target.adjust_pain(target_pain)
/datum/interaction/proc/load_from_json(path)
var/fpath = path
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/arousal_system.dm b/modular_skyrat/modules/modular_items/lewd_items/code/arousal_system.dm
deleted file mode 100644
index cdc0d4c305b..00000000000
--- a/modular_skyrat/modules/modular_items/lewd_items/code/arousal_system.dm
+++ /dev/null
@@ -1,880 +0,0 @@
-#define AROUS_SYS_LITTLE 30
-#define AROUS_SYS_STRONG 70
-#define AROUS_SYS_READYTOCUM 90
-#define PAIN_SYS_LIMIT 50
-#define PLEAS_SYS_EDGE 85
-
-#define CUM_MALE 1
-#define CUM_FEMALE 2
-// #define ITEM_SLOT_PENIS (1<<20)
-
-#define TRAIT_MASOCHISM "masochism"
-#define TRAIT_SADISM "sadism"
-#define TRAIT_NEVERBONER "neverboner"
-#define TRAIT_BIMBO "bimbo"
-#define TRAIT_RIGGER "rigger"
-#define TRAIT_ROPEBUNNY "rope bunny"
-#define APHRO_TRAIT "aphro" ///traits gained by brain traumas, can be removed if the brain trauma is gone
-#define LEWDQUIRK_TRAIT "lewdquirks" ///traits gained by quirks, cannot be removed unless the quirk itself is gone
-#define LEWDCHEM_TRAIT "lewdchem" ///traits gained by chemicals, you get the idea
-
-#define STRAPON_TRAIT "strapon"
-
-/*
-* DECALS
-*/
-
-/obj/effect/decal/cleanable/cum
- name = "cum"
- desc = "Ew... Gross."
- icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_decals/lewd_decals.dmi'
- icon_state = "cum_1"
- random_icon_states = list("cum_1", "cum_2", "cum_3", "cum_4")
- beauty = -50
-
-/obj/effect/decal/cleanable/cum/femcum
- name = "female cum"
- desc = "Uhh... Someone had fun..."
- icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_decals/lewd_decals.dmi'
- icon_state = "femcum_1"
- random_icon_states = list("femcum_1", "femcum_2", "femcum_3", "femcum_4")
- beauty = -50
-
-/*
-* REAGENTS
-*/
-
-/datum/reagent/consumable/girlcum
- name = "girlcum"
- description = "Uhh... Someone had fun."
- taste_description = "astringent and sweetish"
- color = "#ffffffb0"
- glass_name = "glass of girlcum"
- glass_desc = "A strange white liquid... Ew!"
- reagent_state = LIQUID
- shot_glass_icon_state = "shotglasswhite"
-
-/datum/reagent/consumable/cum
- name = "cum"
- description = "A fluid secreted by the sexual organs of many species."
- taste_description = "musky and salty"
- color = "#ffffffff"
- glass_name = "glass of cum"
- glass_desc = "O-oh, my...~"
- reagent_state = LIQUID
- shot_glass_icon_state = "shotglasswhite"
-
-/datum/reagent/consumable/breast_milk
- name = "breast milk"
- description = "This looks familiar... Wait, it's milk!"
- taste_description = "warm and creamy"
- color = "#ffffffff"
- glass_icon_state = "glass_white"
- glass_name = "glass of breast milk"
- glass_desc = "almost like normal milk."
- reagent_state = LIQUID
-
-/datum/reagent/drug/dopamine
- name = "dopamine"
- description = "Pure happiness"
- taste_description = "passion fruit, banana and hint of apple"
- color = "#97ffee"
- glass_name = "dopamine"
- glass_desc = "A deliciously flavored reagent. You feel happy even looking at it."
- reagent_state = LIQUID
- overdose_threshold = 10
-
-/datum/reagent/drug/dopamine/on_mob_add(mob/living/carbon/human/affected_mob)
- affected_mob.add_mood_event("[type]_start", /datum/mood_event/orgasm, name)
- ..()
-
-/datum/reagent/drug/dopamine/on_mob_life(mob/living/carbon/affected_mob, delta_time, times_fired)
- affected_mob.set_drugginess(2 SECONDS * REM * delta_time)
- if(prob(7))
- affected_mob.try_lewd_autoemote(pick("shaking", "moan"))
- ..()
-
-/datum/reagent/drug/dopamine/overdose_start(mob/living/carbon/human/human_mob)
- if(HAS_TRAIT(human_mob, TRAIT_BIMBO))
- return
- to_chat(human_mob, span_userdanger("You don't want to cum anymore!"))
- human_mob.add_mood_event("[type]_overdose", /datum/mood_event/overgasm, name)
-
-/datum/reagent/drug/dopamine/overdose_process(mob/living/carbon/human/affected_mob)
- affected_mob.adjustArousal(0.5)
- affected_mob.adjustPleasure(0.3)
- affected_mob.adjustPain(-0.5)
- if(prob(2))
- affected_mob.try_lewd_autoemote(pick("moan", "twitch_s"))
- return
-
-/*
-* INITIALISE
-*/
-
-/obj/item/organ/external/genital
- var/datum/reagents/internal_fluids
- var/list/contained_item
- var/obj/item/inserted_item //Used for toys
-
-/obj/item/organ/external/genital/breasts/build_from_dna(datum/dna/DNA, associated_key)
- . = ..()
- var/breasts_count = 0
- var/size = 0.5
- if(DNA.features["breasts_size"] > 0)
- size = DNA.features["breasts_size"]
-
- switch(genital_type)
- if("pair")
- breasts_count = 2
- if("quad")
- breasts_count = 2.5
- if("sextuple")
- breasts_count = 3
- internal_fluids = new /datum/reagents(size * breasts_count * 60)
-
-/obj/item/organ/external/genital/testicles/build_from_dna(datum/dna/DNA, associated_key)
- . = ..()
- var/size = 0.5
- if(DNA.features["balls_size"] > 0)
- size = DNA.features["balls_size"]
-
- internal_fluids = new /datum/reagents(size * 20)
-
-/obj/item/organ/external/genital/vagina/build_from_dna(datum/dna/DNA, associated_key)
- . = ..()
- internal_fluids = new /datum/reagents(10)
-
-/mob/living/carbon/human
- var/arousal = 0
- var/pleasure = 0
- var/pain = 0
-
- var/pain_limit = 0
- var/arousal_status = AROUSAL_NONE
-
-/mob/living/carbon/human/Initialize(mapload)
- . = ..()
- if(!istype(src, /mob/living/carbon/human/species/monkey))
- apply_status_effect(/datum/status_effect/aroused)
- apply_status_effect(/datum/status_effect/body_fluid_regen)
-
-/*
-* VERBS
-*/
-
-/mob/living/carbon/human/verb/arousal_panel()
- set name = "Climax"
- set category = "IC"
-
- if(!has_status_effect(/datum/status_effect/climax_cooldown))
- if(tgui_alert(usr, "Are you sure you want to cum?", "Climax", list("Yes", "No")) == "Yes")
- if(stat != CONSCIOUS)
- to_chat(usr, span_warning("You can't climax right now..."))
- return
- else
- climax(TRUE)
- else
- to_chat(src, span_warning("You can't cum right now!"))
-
-//Removing ERP IC verb depending on config
-/mob/living/carbon/human/Initialize(mapload)
- . = ..()
- if(CONFIG_GET(flag/disable_erp_preferences))
- verbs -= /mob/living/carbon/human/verb/arousal_panel
-
-/*
-* FLUIDS
-*/
-
-/datum/status_effect/body_fluid_regen
- id = "body fluid regen"
- tick_interval = 50
- duration = -1
- alert_type = null
-
-/datum/status_effect/body_fluid_regen/tick()
- var/mob/living/carbon/human/affected_mob = owner
- if(owner.stat != DEAD && affected_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- var/obj/item/organ/external/genital/testicles/balls = owner.getorganslot(ORGAN_SLOT_TESTICLES)
- var/obj/item/organ/external/genital/breasts/breasts = owner.getorganslot(ORGAN_SLOT_BREASTS)
- var/obj/item/organ/external/genital/vagina/vagina = owner.getorganslot(ORGAN_SLOT_VAGINA)
-
- var/interval = 5
- if(balls)
- if(affected_mob.arousal >= AROUS_SYS_LITTLE)
- var/regen = (affected_mob.arousal / 25) * (balls.internal_fluids.maximum_volume / 235) * interval
- balls.internal_fluids.add_reagent(/datum/reagent/consumable/cum, regen)
-
- if(breasts)
- if(breasts.lactates == TRUE)
- var/regen = ((owner.nutrition / (NUTRITION_LEVEL_WELL_FED / 100)) / 100) * (breasts.internal_fluids.maximum_volume / 11000) * interval
- if(!breasts.internal_fluids.holder_full())
- owner.adjust_nutrition(-regen / 2)
- breasts.internal_fluids.add_reagent(/datum/reagent/consumable/breast_milk, regen)
-
- if(vagina)
- if(affected_mob.arousal >= AROUS_SYS_LITTLE)
- var/regen = (affected_mob.arousal / 25) * (vagina.internal_fluids.maximum_volume / 250) * interval
- vagina.internal_fluids.add_reagent(/datum/reagent/consumable/girlcum, regen)
- if(vagina.internal_fluids.holder_full() && regen >= 0.15)
- regen = regen
- else
- vagina.internal_fluids.remove_any(0.05)
-
-/*
-* AROUSAL
-*/
-
-/mob/living/carbon/human/proc/get_arousal()
- return arousal
-
-/mob/living/carbon/human/proc/adjustArousal(arous = 0)
- if(stat != DEAD && client?.prefs?.read_preference(/datum/preference/toggle/erp))
- arousal += arous
-
- var/arousal_flag = AROUSAL_NONE
- if(arousal >= AROUS_SYS_STRONG)
- arousal_flag = AROUSAL_FULL
- else if(arousal >= AROUS_SYS_LITTLE)
- arousal_flag = AROUSAL_PARTIAL
-
- if(arousal_status != arousal_flag) // Set organ arousal status
- arousal_status = arousal_flag
- if(istype(src, /mob/living/carbon/human))
- var/mob/living/carbon/human/target = src
- for(var/i = 1, i <= target.external_organs.len, i++)
- if(istype(target.external_organs[i], /obj/item/organ/external/genital))
- var/obj/item/organ/external/genital/target_genital = target.external_organs[i]
- if(!target_genital.aroused == AROUSAL_CANT)
- target_genital.aroused = arousal_status
- target_genital.update_sprite_suffix()
- target.update_body()
- else
- arousal -= abs(arous)
-
- arousal = min(max(arousal, 0), 100)
-
-/datum/status_effect/aroused
- id = "aroused"
- tick_interval = 10
- duration = -1
- alert_type = null
-
-/datum/status_effect/aroused/tick()
- var/mob/living/carbon/human/affected_mob = owner
- var/temp_arousal = -0.1
- var/temp_pleasure = -0.5
- var/temp_pain = -0.5
- if(affected_mob.stat != DEAD)
-
- var/obj/item/organ/external/genital/testicles/balls = affected_mob.getorganslot(ORGAN_SLOT_TESTICLES)
- if(balls)
- if(balls.internal_fluids.holder_full())
- temp_arousal += 0.08
-
- if(HAS_TRAIT(affected_mob, TRAIT_MASOCHISM))
- temp_pain -= 0.5
- if(HAS_TRAIT(affected_mob, TRAIT_NEVERBONER))
- temp_pleasure -= 50
- temp_arousal -= 50
-
- if(affected_mob.pain > affected_mob.pain_limit)
- temp_arousal -= 0.1
- if(affected_mob.arousal >= AROUS_SYS_STRONG && affected_mob.stat != DEAD)
- if(prob(3))
- affected_mob.try_lewd_autoemote(pick("moan", "blush"))
- temp_pleasure += 0.1
- //moan
- if(affected_mob.pleasure >= PLEAS_SYS_EDGE && affected_mob.stat != DEAD)
- if(prob(3))
- affected_mob.try_lewd_autoemote(pick("moan", "twitch_s"))
- //moan x2
-
- affected_mob.adjustArousal(temp_arousal)
- affected_mob.adjustPleasure(temp_pleasure)
- affected_mob.adjustPain(temp_pain)
-
-/*
-* PAIN
-*/
-
-/mob/living/carbon/human/proc/get_pain()
- return pain
-
-/mob/living/carbon/human/proc/adjustPain(change_amount = 0)
- if(stat != DEAD && client?.prefs?.read_preference(/datum/preference/toggle/erp))
- if(pain > pain_limit || change_amount > pain_limit / 10) // pain system // YOUR SYSTEM IS PAIN, WHY WE'RE GETTING AROUSED BY STEPPING ON ANTS?!
- if(HAS_TRAIT(src, TRAIT_MASOCHISM))
- var/arousal_adjustment = change_amount - (pain_limit / 10)
- if(arousal_adjustment > 0)
- adjustArousal(-arousal_adjustment)
- else
- if(change_amount > 0)
- adjustArousal(-change_amount)
- if(prob(2) && pain > pain_limit && change_amount > pain_limit / 10)
- try_lewd_autoemote(pick("scream", "shiver")) //SCREAM!!!
- else
- if(change_amount > 0)
- adjustArousal(change_amount)
- if(HAS_TRAIT(src, TRAIT_MASOCHISM))
- var/pleasure_adjustment = change_amount / 2
- adjustPleasure(pleasure_adjustment)
- pain += change_amount
- else
- pain -= abs(change_amount)
- pain = min(max(pain, 0), 100)
-
-/*
-* PLEASURE
-*/
-
-/mob/living/carbon/human/proc/get_pleasure()
- return pleasure
-
-/mob/living/carbon/human/proc/adjustPleasure(pleas = 0)
- if(stat != DEAD && client?.prefs?.read_preference(/datum/preference/toggle/erp))
- pleasure += pleas
- if(pleasure >= 100) // lets cum
- climax(FALSE)
- else
- pleasure -= abs(pleas)
- pleasure = min(max(pleasure, 0), 100)
-
-// Get damage for pain system
-/datum/species/apply_damage(damage, damagetype, def_zone, blocked, mob/living/carbon/human/affected_mob, forced, spread_damage, wound_bonus, bare_wound_bonus, sharpness, attack_direction)
- . = ..()
- if(!.)
- return
- if(affected_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- return
- var/hit_percent = (100 - (blocked + armor)) / 100
- hit_percent = (hit_percent * (100 - affected_mob.physiology.damage_resistance)) / 100
- switch(damagetype)
- if(BRUTE)
- var/amount = forced ? damage : damage * hit_percent * brutemod * affected_mob.physiology.brute_mod
- INVOKE_ASYNC(affected_mob, /mob/living/carbon/human/.proc/adjustPain, amount)
- if(BURN)
- var/amount = forced ? damage : damage * hit_percent * burnmod * affected_mob.physiology.burn_mod
- INVOKE_ASYNC(affected_mob, /mob/living/carbon/human/.proc/adjustPain, amount)
-
-/*
-* CLIMAX
-*/
-
-/datum/mood_event/orgasm
- description = span_purple("Woah... This pleasant tiredness... I love it.\n")
- timeout = 5 MINUTES
-
-/datum/mood_event/climaxself
- description = span_purple("I just came in my own underwear. Messy.\n")
- timeout = 4 MINUTES
-
-/datum/mood_event/overgasm
- description = span_warning("Uhh... I don't want to be horny anymore.\n") //Me too, buddy. Me too.
- timeout = 10 MINUTES
-
-/mob/living/carbon/human/proc/climax(manual = TRUE)
- if (CONFIG_GET(flag/disable_erp_preferences))
- return
-
- if(!client?.prefs?.read_preference(/datum/preference/toggle/erp/autocum) && manual != TRUE)
- return
-
- var/obj/item/organ/external/genital/penis/penis = getorganslot(ORGAN_SLOT_PENIS)
- var/obj/item/organ/external/genital/testicles/testicles = getorganslot(ORGAN_SLOT_TESTICLES)
- var/obj/item/organ/external/genital/vagina/vagina = getorganslot(ORGAN_SLOT_VAGINA)
-
- if(!has_status_effect(/datum/status_effect/climax_cooldown) && client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- if(!HAS_TRAIT(src, TRAIT_NEVERBONER) && !has_status_effect(/datum/status_effect/climax_cooldown))
- switch(gender)
- if(MALE)
- playsound(get_turf(src), pick('modular_skyrat/modules/modular_items/lewd_items/sounds/final_m1.ogg',
- 'modular_skyrat/modules/modular_items/lewd_items/sounds/final_m2.ogg',
- 'modular_skyrat/modules/modular_items/lewd_items/sounds/final_m3.ogg'), 50, TRUE, ignore_walls = FALSE)
- if(FEMALE)
- playsound(get_turf(src), pick('modular_skyrat/modules/modular_items/lewd_items/sounds/final_f1.ogg',
- 'modular_skyrat/modules/modular_items/lewd_items/sounds/final_f2.ogg',
- 'modular_skyrat/modules/modular_items/lewd_items/sounds/final_f3.ogg'), 50, TRUE, ignore_walls = FALSE)
- if(penis)
- if(!testicles) //If we have no god damn balls, we can't cum anywhere... GET BALLS!
- visible_message(span_userlove("[src] orgasms, but nothing comes out of [p_their()] dick!"), \
- span_userlove("You orgasm, it feels great, but nothing comes out of your dick!"))
- apply_status_effect(/datum/status_effect/climax)
- apply_status_effect(/datum/status_effect/climax_cooldown)
- add_mood_event("orgasm", /datum/mood_event/climaxself)
- return TRUE
-
- if(!is_bottomless() && penis.visibility_preference != GENITAL_ALWAYS_SHOW)
- visible_message(span_userlove("[src] cums into their clothes!"), \
- span_userlove("You shoot your load, but you weren't naked, so you mess up your clothes!"))
- apply_status_effect(/datum/status_effect/climax)
- apply_status_effect(/datum/status_effect/climax_cooldown)
- add_mood_event("orgasm", /datum/mood_event/climaxself)
- return TRUE
-
- var/list/interactable_inrange_humans = list()
-
- for(var/mob/living/carbon/human/iterating_human in view(1, src))
- if(iterating_human == src)
- continue
- //if(iterating_human.client?.prefs?.read_preference(/datum/preference/toggle/erp))
- interactable_inrange_humans[iterating_human.name] = iterating_human
-
- var/list/buttons = list("On the floor")
-
- if(interactable_inrange_humans.len)
- buttons += "Inside/on someone"
-
- var/climax_choice = tgui_alert(src, "You are cumming, choose where to shoot your load.", "Load preference!", buttons)
-
- visible_message(span_purple("[src] is cumming!"), span_purple("You are cumming!"))
-
- var/create_cum_decal = FALSE
-
- if(!climax_choice || climax_choice == "On the floor")
- if(wear_condom())
- var/obj/item/clothing/sextoy/condom/condom = get_item_by_slot(ITEM_SLOT_PENIS)
- if(condom.condom_state == "broken")
- create_cum_decal = TRUE
- visible_message(span_userlove("[src] shoots [p_their()] load into [condom], sending cum onto the floor!"), \
- span_userlove("You shoot string after string of cum, but it sprays out of [condom], hitting the floor!"))
- else
- condom.condom_use()
- visible_message(span_userlove("[src] shoots [p_their()] load into [condom], filling it up!"), \
- span_userlove("You shoot your thick load into [condom] and it catches it all!"))
- else
- create_cum_decal = TRUE
- visible_message(span_userlove("[src] shoots their sticky load onto the floor!"), \
- span_userlove("You shoot string after string of hot cum, hitting the floor!"))
- else
- var/target_choice = tgui_input_list(src, "Choose a person to cum in or on~", "Choose target!", interactable_inrange_humans)
- if(!target_choice)
- create_cum_decal = TRUE
- visible_message(span_userlove("[src] shoots their sticky load onto the floor!"), \
- span_userlove("You shoot string after string of hot cum, hitting the floor!"))
- else
- var/mob/living/carbon/human/target_human = interactable_inrange_humans[target_choice]
- var/obj/item/organ/external/genital/vagina/target_vagina = target_human.getorganslot(ORGAN_SLOT_VAGINA)
- var/obj/item/organ/external/genital/anus/target_anus = target_human.getorganslot(ORGAN_SLOT_ANUS)
- var/obj/item/organ/external/genital/penis/target_penis = target_human.getorganslot(ORGAN_SLOT_PENIS)
-
- var/list/target_buttons = list()
-
- if(!target_human.wear_mask)
- target_buttons += "mouth"
-
- if(target_vagina && target_vagina?.is_exposed())
- target_buttons += "vagina"
-
- if(target_anus && target_anus?.is_exposed())
- target_buttons += "asshole"
-
- if(target_penis && target_penis.is_exposed() && target_penis.sheath != "None")
- target_buttons += "sheath"
-
- target_buttons += "On them"
-
- var/climax_into_choice = tgui_input_list(src, "Where on or in [target_human] do you wish to cum?", "Final frontier!", target_buttons)
-
- if(!climax_into_choice)
- create_cum_decal = TRUE
- visible_message(span_userlove("[src] shoots their sticky load onto the floor!"), \
- span_userlove("You shoot string after string of hot cum, hitting the floor!"))
- else if(climax_into_choice == "On them")
- create_cum_decal = TRUE
- visible_message(span_userlove("[src] shoots their sticky load onto the [target_human]!"), \
- span_userlove("You shoot string after string of hot cum onto [target_human]!"))
- else
- visible_message(span_userlove("[src] hilts [p_their()] cock into [target_human]'s [climax_into_choice], shooting cum into it!"), \
- span_userlove("You hilt your cock into [target_human]'s [climax_into_choice], shooting cum into it!"))
- to_chat(target_human, span_userlove("Your [climax_into_choice] fills with warm cum as [src] shoots [p_their()] load into it."))
- try_lewd_autoemote("moan")
- testicles.reagents.remove_all(testicles.reagents.total_volume * 0.6)
- apply_status_effect(/datum/status_effect/climax)
- apply_status_effect(/datum/status_effect/climax_cooldown)
- if(create_cum_decal)
- add_cum_splatter_floor(get_turf(src))
- return TRUE
- if(vagina)
- if(is_bottomless() || vagina.visibility_preference == GENITAL_ALWAYS_SHOW)
- apply_status_effect(/datum/status_effect/climax)
- apply_status_effect(/datum/status_effect/climax_cooldown)
- visible_message(span_purple("[src] is cumming!"), span_purple("You are cumming!"))
- var/turf/our_turf = get_turf(src)
- add_cum_splatter_floor(our_turf, female = TRUE)
- else
- apply_status_effect(/datum/status_effect/climax)
- apply_status_effect(/datum/status_effect/climax_cooldown)
- visible_message(span_purple("[src] cums in their underwear!"), \
- span_purple("You cum in your underwear! Eww."))
- add_mood_event("orgasm", /datum/mood_event/climaxself)
- return TRUE
- else
- visible_message(span_purple("[src] twitches, trying to cum, but with no result."), \
- span_purple("You can't have an orgasm!"))
- return TRUE
-
-// Let's not force lewd emotes from folk who don't want them, mmm~?
-/mob/living/carbon/proc/try_lewd_autoemote(emote)
- if(!client?.prefs?.read_preference(/datum/preference/toggle/erp/autoemote))
- return
- emote(emote)
-
-/datum/status_effect/climax_cooldown
- id = "climax_cooldown"
- tick_interval = 10
- duration = 30 SECONDS
- alert_type = null
-
-/datum/status_effect/climax_cooldown/tick()
- var/obj/item/organ/external/genital/vagina/vagina = owner.getorganslot(ORGAN_SLOT_VAGINA)
- var/obj/item/organ/external/genital/testicles/balls = owner.getorganslot(ORGAN_SLOT_TESTICLES)
- var/obj/item/organ/external/genital/testicles/penis = owner.getorganslot(ORGAN_SLOT_PENIS)
- var/obj/item/organ/external/genital/testicles/anus = owner.getorganslot(ORGAN_SLOT_ANUS)
-
- if(penis)
- penis.aroused = AROUSAL_NONE
- if(vagina)
- vagina.aroused = AROUSAL_NONE
- if(balls)
- balls.aroused = AROUSAL_NONE
- if(anus)
- anus.aroused = AROUSAL_NONE
-
-/datum/status_effect/masturbation_climax
- id = "climax"
- tick_interval = 10
- duration = 50 //Multiplayer better than singleplayer mode.
- alert_type = null
-
-/datum/status_effect/masturbation_climax/tick() //this one should not leave decals on the floor. Used in case if character cumming in beaker.
- var/mob/living/carbon/human/affected_mob = owner
- if(affected_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- var/temp_arousal = -12
- var/temp_pleasure = -12
- var/temp_stamina = 8
-
- owner.reagents.add_reagent(/datum/reagent/drug/dopamine, 0.3)
- owner.adjustStaminaLoss(temp_stamina)
- affected_mob.adjustArousal(temp_arousal)
- affected_mob.adjustPleasure(temp_pleasure)
-
-/datum/status_effect/climax
- id = "climax"
- tick_interval = 10
- duration = 100
- alert_type = null
-
-/datum/status_effect/climax/tick()
- var/mob/living/carbon/human/affected_mob = owner
- if(affected_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- var/temp_arousal = -12
- var/temp_pleasure = -12
- var/temp_stamina = 15
-
- owner.reagents.add_reagent(/datum/reagent/drug/dopamine, 0.5)
- owner.adjustStaminaLoss(temp_stamina)
- affected_mob.adjustArousal(temp_arousal)
- affected_mob.adjustPleasure(temp_pleasure)
-
-/*
-* SPANKING
-*/
-
-//Hips are red after spanking
-/datum/status_effect/spanked
- id = "spanked"
- duration = 300 SECONDS
- alert_type = null
-
-/mob/living/carbon/human/examine(mob/user)
- . = ..()
- var/mob/living/affected_mob = user
- if(stat != DEAD && !HAS_TRAIT(src, TRAIT_FAKEDEATH) && src != affected_mob)
- if(src != user)
- if(has_status_effect(/datum/status_effect/spanked) && is_bottomless())
- . += span_purple("[user.p_their(TRUE)] butt has a red tint to it.") + "\n"
-
-//Mood boost for masochist
-/datum/mood_event/perv_spanked
- description = span_purple("Ah, yes! More! Punish me!\n")
- timeout = 5 MINUTES
-
-/*
-* SUBSPACE EFFECT
-*/
-
-/datum/status_effect/subspace
- id = "subspace"
- tick_interval = 10
- duration = 5 MINUTES
- alert_type = null
-
-/datum/status_effect/subspace/on_apply()
- . = ..()
- var/mob/living/carbon/human/target = owner
- target.add_mood_event("subspace", /datum/mood_event/subspace)
-
-/datum/status_effect/subspace/on_remove()
- . = ..()
- var/mob/living/carbon/human/target = owner
- target.clear_mood_event("subspace")
-
-/datum/mood_event/subspace
- description = span_purple("Everything is so woozy... Pain feels so... Awesome.\n")
-
-/datum/status_effect/ropebunny
- id = "ropebunny"
- tick_interval = 10
- duration = INFINITE
- alert_type = null
-
-/datum/status_effect/ropebunny/on_apply()
- . = ..()
- var/mob/living/carbon/human/target = owner
- target.add_mood_event("ropebunny", /datum/mood_event/ropebunny)
-
-/datum/status_effect/ropebunny/on_remove()
- . = ..()
- var/mob/living/carbon/human/target = owner
- target.clear_mood_event("ropebunny")
-
-/datum/mood_event/ropebunny
- description = span_purple("I'm tied! Cannot move! These ropes... Ah!~")
- mood_change = 0 // I don't want to doom the station to sonic-speed perverts, but still want to keep this as mood modifier.
-
-/*
-* AROUSAL INDICATOR
-*/
-
-/obj/item/organ/internal/brain/on_life(delta_time, times_fired) //All your horny is here *points to the head*
- . = ..()
- var/mob/living/carbon/human/brain_owner = owner
- if(istype(brain_owner, /mob/living/carbon/human) && brain_owner.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- if(!(organ_flags & ORGAN_FAILING))
- brain_owner.dna.species.handle_arousal(brain_owner, delta_time, times_fired)
-
-//screen alert
-
-/atom/movable/screen/alert/aroused_x
- name = "Aroused"
- desc = "It's a little hot in here"
- icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_icons.dmi'
- icon_state = "arousal_small"
- var/mutable_appearance/pain_overlay
- var/mutable_appearance/pleasure_overlay
- var/pain_level = "small"
- var/pleasure_level = "small"
-
-/atom/movable/screen/alert/aroused_x/Initialize(mapload)
- .=..()
- pain_overlay = update_pain()
- pleasure_overlay = update_pleasure()
-
-/atom/movable/screen/alert/aroused_x/proc/update_pain()
- if(pain_level)
- return mutable_appearance('modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_icons.dmi', "pain_[pain_level]")
-
-/atom/movable/screen/alert/aroused_x/proc/update_pleasure()
- if(pleasure_level)
- return mutable_appearance('modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_icons.dmi', "pleasure_[pleasure_level]")
-
-/datum/species/proc/throw_arousalalert(level, atom/movable/screen/alert/aroused_x/arousal_alert, mob/living/carbon/human/targeted_human)
- targeted_human.throw_alert("aroused", /atom/movable/screen/alert/aroused_x)
- arousal_alert?.icon_state = level
- arousal_alert?.update_icon()
-
-/datum/species/proc/overlay_pain(level, atom/movable/screen/alert/aroused_x/arousal_alert)
- arousal_alert?.cut_overlay(arousal_alert.pain_overlay)
- arousal_alert?.pain_level = level
- arousal_alert?.pain_overlay = arousal_alert.update_pain()
- arousal_alert?.add_overlay(arousal_alert.pain_overlay)
- arousal_alert?.update_overlays()
-
-/datum/species/proc/overlay_pleasure(level, atom/movable/screen/alert/aroused_x/arousal_alert)
- arousal_alert?.cut_overlay(arousal_alert.pleasure_overlay)
- arousal_alert?.pleasure_level = level
- arousal_alert?.pleasure_overlay = arousal_alert.update_pleasure()
- arousal_alert?.add_overlay(arousal_alert.pleasure_overlay)
- arousal_alert?.update_overlays()
-
-/datum/species/proc/handle_arousal(mob/living/carbon/human/target_human, atom/movable/screen/alert/aroused_x)
- var/atom/movable/screen/alert/aroused_x/arousal_alert = target_human.alerts["aroused"]
- if(target_human.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- switch(target_human.arousal)
- if(-100 to 1)
- target_human.clear_alert("aroused", /atom/movable/screen/alert/aroused_x)
- if(1 to 25)
- throw_arousalalert("arousal_small", arousal_alert, target_human)
- if(25 to 50)
- throw_arousalalert("arousal_medium", arousal_alert, target_human)
- if(50 to 75)
- throw_arousalalert("arousal_high", arousal_alert, target_human)
- if(75 to INFINITY) //to prevent that 101 arousal that can make icon disappear or something.
- throw_arousalalert("arousal_max", arousal_alert, target_human)
-
- if(target_human.arousal > 1)
- switch(target_human.pain)
- if(-100 to 1) //to prevent same thing with pain
- arousal_alert?.cut_overlay(arousal_alert.pain_overlay)
- if(1 to 25)
- overlay_pain("small", arousal_alert)
- if(25 to 50)
- overlay_pain("medium", arousal_alert)
- if(50 to 75)
- overlay_pain("high", arousal_alert)
- if(75 to INFINITY)
- overlay_pain("max", arousal_alert)
-
- if(target_human.arousal > 1)
- switch(target_human.pleasure)
- if(-100 to 1) //to prevent same thing with pleasure
- arousal_alert?.cut_overlay(arousal_alert.pleasure_overlay)
- if(1 to 25)
- overlay_pleasure("small", arousal_alert)
- if(25 to 60)
- overlay_pleasure("medium", arousal_alert)
- if(60 to 85)
- overlay_pleasure("high", arousal_alert)
- if(85 to INFINITY)
- overlay_pleasure("max", arousal_alert)
- else
- if(arousal_alert?.pleasure_level in list("small", "medium", "high", "max"))
- arousal_alert.cut_overlay(arousal_alert.pleasure_overlay)
- if(arousal_alert?.pain_level in list("small", "medium", "high", "max"))
- arousal_alert.cut_overlay(arousal_alert.pain_overlay)
-
-/datum/emote/living/cum
- key = "cum"
- key_third_person = "cums"
- cooldown = 30 SECONDS
-
-/datum/emote/living/cum/check_config()
- return !CONFIG_GET(flag/disable_erp_preferences)
-
-/datum/emote/living/cum/can_run_emote(mob/user, status_check = TRUE, intentional = FALSE)
- if (!check_config())
- return FALSE
- . = ..()
-
-/datum/emote/living/cum/run_emote(mob/living/user, params, type_override, intentional)
- if (!check_config())
- return
- . = ..()
- if(!.)
- return
-
-
- var/obj/item/coomer = new /obj/item/hand_item/coom(user)
- var/mob/living/carbon/human/target = user
- var/obj/item/held = user.get_active_held_item()
- var/obj/item/unheld = user.get_inactive_held_item()
- if(user.put_in_hands(coomer) && target.dna.species.mutant_bodyparts["testicles"] && target.dna.species.mutant_bodyparts["penis"])
- if(held || unheld)
- if(!((held.name == "cum" && held.item_flags == DROPDEL | ABSTRACT | HAND_ITEM) || (unheld.name == "cum" && unheld.item_flags == DROPDEL | ABSTRACT | HAND_ITEM)))
- to_chat(user, span_notice("You mentally prepare yourself to masturbate."))
- else
- qdel(coomer)
- else
- to_chat(user, span_notice("You mentally prepare yourself to masturbate."))
- else
- qdel(coomer)
- to_chat(user, span_warning("You're incapable of masturbating."))
-
-/obj/item/hand_item/coom
- name = "cum"
- desc = "C-can I watch...?"
- icon = 'icons/obj/hydroponics/harvest.dmi'
- icon_state = "eggplant"
- inhand_icon_state = "nothing"
-
-/obj/item/hand_item/coom/attack(mob/living/target, mob/user, proximity)
- if (CONFIG_GET(flag/disable_erp_preferences))
- return
- if(!proximity)
- return
- if(!ishuman(target))
- return
- if(user.stat == DEAD)
- return
- var/mob/living/carbon/human/human_cumvictim = target
- if(!human_cumvictim.client)
- to_chat(user, span_warning("You can't cum onto [target]."))
- return
- var/mob/living/carbon/human/affected_human = user
- var/obj/item/organ/external/genital/testicles/testicles = affected_human.getorganslot(ORGAN_SLOT_TESTICLES)
- var/obj/item/organ/external/genital/penis/penis = affected_human.getorganslot(ORGAN_SLOT_PENIS)
- var/datum/sprite_accessory/genital/penis_accessory = GLOB.sprite_accessories["penis"][affected_human.dna.species.mutant_bodyparts["penis"][MUTANT_INDEX_NAME]]
- if(penis_accessory.is_hidden(affected_human))
- to_chat(user, span_notice("You need to expose yourself in order to masturbate."))
- return
- else if(penis.aroused != AROUSAL_FULL)
- to_chat(user, span_notice("You need to be aroused in order to masturbate."))
- return
- var/cum_volume = testicles.genital_size * 5 + 5
- var/datum/reagents/applied_reagents = new/datum/reagents(50)
- applied_reagents.add_reagent(/datum/reagent/consumable/cum, cum_volume)
- if(target == user)
- user.visible_message(span_warning("[user] starts masturbating onto [target.p_them()]self!"), span_danger("You start masturbating onto yourself!"))
- else
- user.visible_message(span_warning("[user] starts masturbating onto [target]!"), span_danger("You start masturbating onto [target]!"))
- if(do_after(user, 60, target))
- if(target == user)
- user.visible_message(span_warning("[user] cums on [target.p_them()]self!"), span_danger("You cum on yourself!"))
- else
- user.visible_message(span_warning("[user] cums on [target]!"), span_danger("You cum on [target]!"))
- applied_reagents.expose(target, TOUCH)
- log_combat(user, target, "came on")
- if(prob(40))
- affected_human.try_lewd_autoemote("moan")
- qdel(src)
-
-// Jerk off into bottles
-/obj/item/hand_item/coom/afterattack(obj/target, mob/user, proximity)
- . = ..()
- if (CONFIG_GET(flag/disable_erp_preferences))
- return
- if(!proximity)
- return
- if(ishuman(target))
- return
- if(user.stat == DEAD)
- return
- var/mob/living/carbon/human/affected_human = user
- var/obj/item/organ/external/genital/testicles/testicles = affected_human.getorganslot(ORGAN_SLOT_TESTICLES)
- var/obj/item/organ/external/genital/penis/penis = affected_human.getorganslot(ORGAN_SLOT_PENIS)
- var/datum/sprite_accessory/genital/spriteP = GLOB.sprite_accessories["penis"][affected_human.dna.species.mutant_bodyparts["penis"][MUTANT_INDEX_NAME]]
- if(spriteP.is_hidden(affected_human))
- to_chat(user, span_notice("You need to expose yourself in order to masturbate."))
- return
- else if(penis.aroused != AROUSAL_FULL)
- to_chat(user, span_notice("You need to be aroused in order to masturbate."))
- return
- if(target.is_refillable() && target.is_drainable())
- var/cum_volume = testicles.genital_size*5+5
- if(target.reagents.holder_full())
- to_chat(user, span_warning("[target] is full."))
- return
- var/datum/reagents/applied_reagents = new/datum/reagents(50)
- applied_reagents.add_reagent(/datum/reagent/consumable/cum, cum_volume)
- user.visible_message(span_warning("[user] starts masturbating into [target]!"), span_danger("You start masturbating into [target]!"))
- if(do_after(user, 60))
- user.visible_message(span_warning("[user] cums into [target]!"), span_danger("You cum into [target]!"))
- playsound(target, SFX_DESECRATION, 50, TRUE, ignore_walls = FALSE)
- applied_reagents.trans_to(target, cum_volume)
- if(prob(40))
- affected_human.try_lewd_autoemote("moan")
- qdel(src)
- else
- user.visible_message(span_warning("[user] starts masturbating onto [target]!"), span_danger("You start masturbating onto [target]!"))
- if(do_after(user, 60))
- user.visible_message(span_warning("[user] cums on [target]!"), span_danger("You cum on [target]!"))
- playsound(target, SFX_DESECRATION, 50, TRUE, ignore_walls = FALSE)
- affected_human.add_cum_splatter_floor(get_turf(target))
- if(prob(40))
- affected_human.try_lewd_autoemote("moan")
-
- if(target.icon_state=="stickyweb1"|target.icon_state=="stickyweb2")
- target.icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_decals/lewd_decals.dmi'
- qdel(src)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/decals.dm b/modular_skyrat/modules/modular_items/lewd_items/code/decals.dm
new file mode 100644
index 00000000000..9b79648728b
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/decals.dm
@@ -0,0 +1,15 @@
+/obj/effect/decal/cleanable/cum
+ name = "cum"
+ desc = "Ew... Gross."
+ icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_decals/lewd_decals.dmi'
+ icon_state = "cum_1"
+ random_icon_states = list("cum_1", "cum_2", "cum_3", "cum_4")
+ beauty = -50
+
+/obj/effect/decal/cleanable/cum/femcum
+ name = "female cum"
+ desc = "Uhh... Someone had fun..."
+ icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_decals/lewd_decals.dmi'
+ icon_state = "femcum_1"
+ random_icon_states = list("femcum_1", "femcum_2", "femcum_3", "femcum_4")
+ beauty = -50
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/arousal.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/arousal.dm
new file mode 100644
index 00000000000..3f5d02cc53a
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/arousal.dm
@@ -0,0 +1,22 @@
+/mob/living/carbon/human/proc/adjust_arousal(arous = 0)
+ if(stat >= DEAD || !client?.prefs?.read_preference(/datum/preference/toggle/erp))
+ return
+
+ var/arousal_flag = AROUSAL_NONE
+ if(arousal >= AROUSAL_MEDIUM)
+ arousal_flag = AROUSAL_FULL
+ else if(arousal >= AROUSAL_LOW)
+ arousal_flag = AROUSAL_PARTIAL
+
+ if(arousal_status != arousal_flag) // Set organ arousal status
+ arousal_status = arousal_flag
+ if(istype(src, /mob/living/carbon/human))
+ var/mob/living/carbon/human/target = src
+ for(var/obj/item/organ/external/genital/target_genital in target.external_organs)
+ if(!target_genital.aroused == AROUSAL_CANT)
+ target_genital.aroused = arousal_status
+ target_genital.update_sprite_suffix()
+ target.update_body()
+
+ arousal = clamp(arousal + arous, AROUSAL_MINIMUM, AROUSAL_LIMIT)
+
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/climax.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/climax.dm
new file mode 100644
index 00000000000..0d6bdcc4c0f
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/climax.dm
@@ -0,0 +1,159 @@
+#define CLIMAX_VAGINA "Vagina"
+#define CLIMAX_PENIS "Penis"
+#define CLIMAX_BOTH "Both"
+
+#define CLIMAX_ON_FLOOR "On the floor"
+#define CLIMAX_IN_OR_ON "Climax in or on someone"
+
+/mob/living/carbon/human/proc/climax(manual = TRUE)
+ if (CONFIG_GET(flag/disable_erp_preferences))
+ return
+
+ if(!client?.prefs?.read_preference(/datum/preference/toggle/erp/autocum) && !manual)
+ return
+
+ if(has_status_effect(/datum/status_effect/climax_cooldown) || !client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
+ return
+
+ if(HAS_TRAIT(src, TRAIT_NEVERBONER) || has_status_effect(/datum/status_effect/climax_cooldown) || (!has_vagina() && !has_penis()))
+ visible_message(span_purple("[src] twitches, trying to cum, but with no result."), \
+ span_purple("You can't have an orgasm!"))
+ return TRUE
+
+ // Reduce pop-ups and make it slightly more frictionless (lewd).
+ var/climax_choice = has_penis() ? CLIMAX_PENIS : CLIMAX_VAGINA
+
+ if(manual)
+ var/list/genitals = list()
+ if(has_vagina())
+ genitals.Add(CLIMAX_VAGINA)
+ if(has_penis())
+ genitals.Add(CLIMAX_PENIS)
+ genitals.Add(CLIMAX_BOTH)
+ else if(has_penis())
+ genitals.Add(CLIMAX_PENIS)
+ climax_choice = tgui_alert(src, "You are climaxing, choose which genitalia to climax with.", "Genitalia Preference!", genitals)
+
+ switch(gender)
+ if(MALE)
+ playsound(get_turf(src), pick('modular_skyrat/modules/modular_items/lewd_items/sounds/final_m1.ogg',
+ 'modular_skyrat/modules/modular_items/lewd_items/sounds/final_m2.ogg',
+ 'modular_skyrat/modules/modular_items/lewd_items/sounds/final_m3.ogg'), 50, TRUE, ignore_walls = FALSE)
+ if(FEMALE)
+ playsound(get_turf(src), pick('modular_skyrat/modules/modular_items/lewd_items/sounds/final_f1.ogg',
+ 'modular_skyrat/modules/modular_items/lewd_items/sounds/final_f2.ogg',
+ 'modular_skyrat/modules/modular_items/lewd_items/sounds/final_f3.ogg'), 50, TRUE, ignore_walls = FALSE)
+
+ var/self_orgasm = FALSE
+ var/self_their = p_their()
+
+ if(climax_choice == CLIMAX_PENIS || climax_choice == CLIMAX_BOTH)
+ var/obj/item/organ/external/genital/penis/penis = getorganslot(ORGAN_SLOT_PENIS)
+ if(!getorganslot(ORGAN_SLOT_TESTICLES)) //If we have no god damn balls, we can't cum anywhere... GET BALLS!
+ visible_message(span_userlove("[src] orgasms, but nothing comes out of [self_their] penis!"), \
+ span_userlove("You orgasm, it feels great, but nothing comes out of your penis!"))
+
+ else if(is_wearing_condom())
+ var/obj/item/clothing/sextoy/condom/condom = get_item_by_slot(LEWD_SLOT_PENIS)
+ condom.condom_use()
+ visible_message(span_userlove("[src] shoots [self_their] load into the [condom], filling it up!"), \
+ span_userlove("You shoot your thick load into the [condom] and it catches it all!"))
+
+ else if(!is_bottomless() && penis.visibility_preference != GENITAL_ALWAYS_SHOW)
+ visible_message(span_userlove("[src] cums inside [self_their] clothes!"), \
+ span_userlove("You shoot your load, but you weren't naked, so you mess up your clothes!"))
+ self_orgasm = TRUE
+
+ else
+ var/list/interactable_inrange_humans = list()
+
+ // Unfortunately prefs can't be checked here, because byond/tgstation moment.
+ for(var/mob/living/carbon/human/iterating_human in (view(1, src) - src))
+ interactable_inrange_humans[iterating_human.name] = iterating_human
+
+ var/list/buttons = list(CLIMAX_ON_FLOOR)
+ if(interactable_inrange_humans.len)
+ buttons += CLIMAX_IN_OR_ON
+
+ var/penis_climax_choice = tgui_alert(src, "Choose where to shoot your load.", "Load preference!", buttons)
+
+ var/create_cum_decal = FALSE
+
+ if(!penis_climax_choice || penis_climax_choice == CLIMAX_ON_FLOOR)
+ create_cum_decal = TRUE
+ visible_message(span_userlove("[src] shoots [self_their] sticky load onto the floor!"), \
+ span_userlove("You shoot string after string of hot cum, hitting the floor!"))
+
+ else
+ var/target_choice = tgui_input_list(src, "Choose a person to cum in or on.", "Choose target!", interactable_inrange_humans)
+ if(!target_choice)
+ create_cum_decal = TRUE
+ visible_message(span_userlove("[src] shoots [self_their] sticky load onto the floor!"), \
+ span_userlove("You shoot string after string of hot cum, hitting the floor!"))
+ else
+ var/mob/living/carbon/human/target_human = interactable_inrange_humans[target_choice]
+ var/target_human_them = target_human.p_them()
+
+ var/list/target_buttons = list()
+
+ if(!target_human.wear_mask)
+ target_buttons += "mouth"
+ if(target_human.has_vagina(REQUIRE_GENITAL_EXPOSED))
+ target_buttons += ORGAN_SLOT_VAGINA
+ if(target_human.has_anus(REQUIRE_GENITAL_EXPOSED))
+ target_buttons += "asshole"
+ if(target_human.has_penis(REQUIRE_GENITAL_EXPOSED))
+ var/obj/item/organ/external/genital/penis/other_penis = target_human.getorganslot(ORGAN_SLOT_PENIS)
+ if(other_penis.sheath != "None")
+ target_buttons += "sheath"
+ target_buttons += "On [target_human_them]"
+
+ var/climax_into_choice = tgui_input_list(src, "Where on or in [target_human] do you wish to cum?", "Final frontier!", target_buttons)
+
+ if(!climax_into_choice)
+ create_cum_decal = TRUE
+ visible_message(span_userlove("[src] shoots their sticky load onto the floor!"), \
+ span_userlove("You shoot string after string of hot cum, hitting the floor!"))
+ else if(climax_into_choice == "On [target_human_them]")
+ create_cum_decal = TRUE
+ visible_message(span_userlove("[src] shoots their sticky load onto [target_human]!"), \
+ span_userlove("You shoot string after string of hot cum onto [target_human]!"))
+ else
+ visible_message(span_userlove("[src] hilts [self_their] cock into [target_human]'s [climax_into_choice], shooting cum into [target_human_them]!"), \
+ span_userlove("You hilt your cock into [target_human]'s [climax_into_choice], shooting cum into [target_human_them]!"))
+ to_chat(target_human, span_userlove("Your [climax_into_choice] fills with warm cum as [src] shoots [self_their] load into it."))
+
+ var/obj/item/organ/external/genital/testicles/testicles = getorganslot(ORGAN_SLOT_TESTICLES)
+ testicles.reagents.remove_all(testicles.reagents.total_volume * 0.6)
+ if(create_cum_decal)
+ add_cum_splatter_floor(get_turf(src))
+
+ try_lewd_autoemote("moan")
+ if(climax_choice == CLIMAX_PENIS)
+ apply_status_effect(/datum/status_effect/climax)
+ apply_status_effect(/datum/status_effect/climax_cooldown)
+ if(self_orgasm)
+ add_mood_event("orgasm", /datum/mood_event/climaxself)
+ return TRUE
+
+ if(climax_choice == CLIMAX_VAGINA || climax_choice == CLIMAX_BOTH)
+ var/obj/item/organ/external/genital/vagina/vagina = getorganslot(ORGAN_SLOT_VAGINA)
+ if(is_bottomless() || vagina.visibility_preference == GENITAL_ALWAYS_SHOW)
+ visible_message(span_userlove("[src] twitches and moans as [p_they()] climax from their vagina!"), span_userlove("You twitch and moan as you climax from your vagina!"))
+ add_cum_splatter_floor(get_turf(src), female = TRUE)
+ else
+ visible_message(span_userlove("[src] cums in [self_their] underwear from [self_their] vagina!"), \
+ span_userlove("You cum in your underwear from your vagina! Eww."))
+ self_orgasm = TRUE
+
+ apply_status_effect(/datum/status_effect/climax)
+ apply_status_effect(/datum/status_effect/climax_cooldown)
+ if(self_orgasm)
+ add_mood_event("orgasm", /datum/mood_event/climaxself)
+ return TRUE
+
+#undef CLIMAX_VAGINA
+#undef CLIMAX_PENIS
+#undef CLIMAX_BOTH
+#undef CLIMAX_ON_FLOOR
+#undef CLIMAX_IN_OR_ON
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/mood_events.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/mood_events.dm
new file mode 100644
index 00000000000..42771f906e8
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/mood_events.dm
@@ -0,0 +1,11 @@
+/datum/mood_event/orgasm
+ description = span_purple("Woah... This pleasant tiredness... I love it.\n")
+ timeout = 5 MINUTES
+
+/datum/mood_event/climaxself
+ description = span_purple("I just came in my own underwear. Messy.\n")
+ timeout = 4 MINUTES
+
+/datum/mood_event/overgasm
+ description = span_warning("Uhh... I don't want to be horny anymore.\n") //Me too, buddy. Me too.
+ timeout = 10 MINUTES
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pain.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pain.dm
new file mode 100644
index 00000000000..072de283139
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pain.dm
@@ -0,0 +1,25 @@
+// I guess some people enjoy it.
+
+/// Adds or removes pain, this should be used instead of the modifying the var, due to quirk logic.
+/// Makes the human scream and shiver when pain hits the soft limit, provided autoemote is enabled.
+/mob/living/carbon/human/proc/adjust_pain(change_amount = 0)
+ if(stat >= DEAD || !client?.prefs?.read_preference(/datum/preference/toggle/erp))
+ return
+
+ if(pain > pain_limit || change_amount > pain_limit / 10) // pain system // YOUR SYSTEM IS PAIN, WHY WE'RE GETTING AROUSED BY STEPPING ON ANTS?!
+ if(HAS_TRAIT(src, TRAIT_MASOCHISM))
+ var/arousal_adjustment = change_amount - (pain_limit / 10)
+ if(arousal_adjustment > 0)
+ adjust_arousal(-arousal_adjustment)
+ else
+ if(change_amount > 0)
+ adjust_arousal(-change_amount)
+ if(prob(2) && pain > pain_limit && change_amount > pain_limit / 10)
+ try_lewd_autoemote(pick("scream", "shiver")) //SCREAM!!!
+
+ else if(HAS_TRAIT(src, TRAIT_MASOCHISM))
+ if(change_amount > 0)
+ adjust_arousal(change_amount)
+ adjust_pleasure(change_amount / 2)
+
+ pain = clamp(pain + change_amount, AROUSAL_MINIMUM, AROUSAL_LIMIT)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pleasure.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pleasure.dm
new file mode 100644
index 00000000000..bcda8503d21
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/pleasure.dm
@@ -0,0 +1,8 @@
+/mob/living/carbon/human/proc/adjust_pleasure(pleas = 0)
+ if(stat >= DEAD || !client?.prefs?.read_preference(/datum/preference/toggle/erp))
+ return
+
+ pleasure = clamp(pleasure + pleas, AROUSAL_MINIMUM, AROUSAL_LIMIT)
+
+ if(pleasure >= AROUSAL_AUTO_CLIMAX_THRESHOLD) // lets cum
+ climax(manual = FALSE)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/aroused.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/aroused.dm
new file mode 100644
index 00000000000..f500f87857a
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/aroused.dm
@@ -0,0 +1,63 @@
+#define AUTO_EMOTE_CHANCE 3
+
+#define MASOCHISM_PAIN_OFFSET 0.5
+#define NEVERBONER_PLEASURE_OFFSET 50
+
+#define BLUEBALL_AROUSAL_MODIFIER 0.08
+#define TOO_MUCH_PAIN_MODIFIER 0.1
+#define ITS_PRETTY_HOT_IN_HERE_MODIFIER 0.1
+
+#define BASE_AROUSAL_ADJUSTMENT -0.1
+#define BASE_PAIN_AND_PLEASURE_ADJUSTMENT -0.5
+
+/datum/status_effect/aroused
+ id = "aroused"
+ tick_interval = 10
+ duration = -1
+ alert_type = null
+
+/datum/status_effect/aroused/tick()
+ if(owner.stat >= DEAD || !owner.client?.prefs?.read_preference(/datum/preference/toggle/erp))
+ return
+
+ var/mob/living/carbon/human/affected_mob = owner
+ var/temp_arousal = BASE_AROUSAL_ADJUSTMENT
+ var/temp_pleasure = BASE_PAIN_AND_PLEASURE_ADJUSTMENT
+ var/temp_pain = BASE_PAIN_AND_PLEASURE_ADJUSTMENT
+
+ var/obj/item/organ/external/genital/testicles/balls = affected_mob.getorganslot(ORGAN_SLOT_TESTICLES)
+ if(balls && balls.internal_fluid_full())
+ temp_arousal += BLUEBALL_AROUSAL_MODIFIER
+
+ if(HAS_TRAIT(affected_mob, TRAIT_MASOCHISM))
+ temp_pain -= MASOCHISM_PAIN_OFFSET
+ if(HAS_TRAIT(affected_mob, TRAIT_NEVERBONER))
+ temp_pleasure -= NEVERBONER_PLEASURE_OFFSET
+ temp_arousal -= NEVERBONER_PLEASURE_OFFSET
+
+ if(affected_mob.pain > affected_mob.pain_limit)
+ temp_arousal -= TOO_MUCH_PAIN_MODIFIER
+ if(affected_mob.arousal >= AROUSAL_MEDIUM)
+ if(prob(AUTO_EMOTE_CHANCE))
+ affected_mob.try_lewd_autoemote(pick("moan", "blush"))
+ temp_pleasure += ITS_PRETTY_HOT_IN_HERE_MODIFIER
+ //moan
+ if(affected_mob.pleasure >= AROUSAL_HIGH && prob(AUTO_EMOTE_CHANCE))
+ affected_mob.try_lewd_autoemote(pick("moan", "twitch_s"))
+ //moan x2
+
+ affected_mob.adjust_arousal(temp_arousal)
+ affected_mob.adjust_pleasure(temp_pleasure)
+ affected_mob.adjust_pain(temp_pain)
+
+#undef AUTO_EMOTE_CHANCE
+
+#undef MASOCHISM_PAIN_OFFSET
+#undef NEVERBONER_PLEASURE_OFFSET
+
+#undef BLUEBALL_AROUSAL_MODIFIER
+#undef TOO_MUCH_PAIN_MODIFIER
+#undef ITS_PRETTY_HOT_IN_HERE_MODIFIER
+
+#undef BASE_AROUSAL_ADJUSTMENT
+#undef BASE_PAIN_AND_PLEASURE_ADJUSTMENT
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/climax.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/climax.dm
new file mode 100644
index 00000000000..6dcf4e37e14
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/climax.dm
@@ -0,0 +1,63 @@
+#define AROUSAL_REMOVAL_AMOUNT -12
+#define STAMINA_REMOVAL_AMOUNT_EXTERNAL 15
+#define STAMINA_REMOVAL_AMOUNT_SELF 8
+
+// Lowers arousal and pleasure by a bunch to not chain climax.
+
+/datum/status_effect/climax
+ id = "climax"
+ tick_interval = 1 SECONDS
+ duration = 10 SECONDS
+ alert_type = null
+
+/datum/status_effect/climax/tick()
+ if(!owner.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
+ return
+
+ var/mob/living/carbon/human/affected_mob = owner
+
+ owner.reagents.add_reagent(/datum/reagent/drug/aphrodisiac/dopamine, 0.5)
+ owner.adjustStaminaLoss(STAMINA_REMOVAL_AMOUNT_EXTERNAL)
+ affected_mob.adjust_arousal(AROUSAL_REMOVAL_AMOUNT)
+ affected_mob.adjust_pleasure(AROUSAL_REMOVAL_AMOUNT)
+
+// Likely ready to be deprecated code that could be removed, due to nymphomaniac not existing anymore.
+/datum/status_effect/masturbation_climax
+ id = "climax"
+ tick_interval = 1 SECONDS
+ duration = 5 SECONDS // Multiplayer better than singleplayer mode.
+ alert_type = null
+
+// This one should not leave decals on the floor. Used in case if character cumming in beaker.
+/datum/status_effect/masturbation_climax/tick()
+ if(!owner.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
+ return
+
+ var/mob/living/carbon/human/affected_mob = owner
+
+ owner.reagents.add_reagent(/datum/reagent/drug/aphrodisiac/dopamine, 0.3)
+ owner.adjustStaminaLoss(STAMINA_REMOVAL_AMOUNT_SELF)
+ affected_mob.adjust_arousal(AROUSAL_REMOVAL_AMOUNT)
+ affected_mob.adjust_pleasure(AROUSAL_REMOVAL_AMOUNT)
+
+// A second step in preventing chain climax, and also prevents spam.
+/datum/status_effect/climax_cooldown
+ id = "climax_cooldown"
+ tick_interval = 1 SECONDS
+ duration = 30 SECONDS
+ alert_type = null
+
+/datum/status_effect/climax_cooldown/tick()
+ var/obj/item/organ/external/genital/vagina/vagina = owner.getorganslot(ORGAN_SLOT_VAGINA)
+ var/obj/item/organ/external/genital/testicles/balls = owner.getorganslot(ORGAN_SLOT_TESTICLES)
+ var/obj/item/organ/external/genital/testicles/penis = owner.getorganslot(ORGAN_SLOT_PENIS)
+ var/obj/item/organ/external/genital/testicles/anus = owner.getorganslot(ORGAN_SLOT_ANUS)
+
+ if(penis)
+ penis.aroused = AROUSAL_NONE
+ if(vagina)
+ vagina.aroused = AROUSAL_NONE
+ if(balls)
+ balls.aroused = AROUSAL_NONE
+ if(anus)
+ anus.aroused = AROUSAL_NONE
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/fluid_generation.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/fluid_generation.dm
new file mode 100644
index 00000000000..f45fe00a6d8
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/fluid_generation.dm
@@ -0,0 +1,53 @@
+// These are effectively magic numbers.
+#define AROUSAL_MULTIPLIER 25
+#define BALLS_MULTIPLIER 235
+#define NUTRITION_MULTIPLIER 100
+#define NUTRITION_COST_MULTIPLIER 2
+// Breasts have ungodly scaling at larger sizes, so the massive multiplier to ensure there's no runaway production makes sense here.
+#define BREASTS_MULTIPLIER 11000
+#define VAGINA_MULTIPLIER 250
+#define VAGINA_FLUID_REMOVAL_AMOUNT -0.05
+#define BASE_MULTIPLIER 5
+
+/datum/status_effect/body_fluid_regen
+ id = "body fluid regen"
+ tick_interval = 5 SECONDS
+ duration = -1
+ alert_type = null
+
+/datum/status_effect/body_fluid_regen/tick()
+ if(owner.stat >= DEAD || !owner.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
+ return
+
+ var/mob/living/carbon/human/affected_mob = owner
+ var/obj/item/organ/external/genital/testicles/balls = owner.getorganslot(ORGAN_SLOT_TESTICLES)
+ var/obj/item/organ/external/genital/breasts/breasts = owner.getorganslot(ORGAN_SLOT_BREASTS)
+ var/obj/item/organ/external/genital/vagina/vagina = owner.getorganslot(ORGAN_SLOT_VAGINA)
+
+ if(balls)
+ if(affected_mob.arousal >= AROUSAL_LOW)
+ var/regen = (affected_mob.arousal / AROUSAL_MULTIPLIER) * (balls.internal_fluid_maximum / BALLS_MULTIPLIER) * BASE_MULTIPLIER
+ balls.internal_fluid_count += regen
+
+ if(breasts)
+ if(breasts.lactates == TRUE)
+ var/regen = ((owner.nutrition / (NUTRITION_LEVEL_WELL_FED / NUTRITION_MULTIPLIER)) / NUTRITION_MULTIPLIER) * (breasts.internal_fluid_maximum / BREASTS_MULTIPLIER) * BASE_MULTIPLIER
+ if(!breasts.internal_fluid_full())
+ owner.adjust_nutrition(-regen / NUTRITION_COST_MULTIPLIER)
+ breasts.adjust_internal_fluid(regen)
+
+ if(vagina)
+ if(affected_mob.arousal >= AROUSAL_LOW)
+ var/regen = (affected_mob.arousal / AROUSAL_MULTIPLIER) * (vagina.internal_fluid_maximum / VAGINA_MULTIPLIER) * BASE_MULTIPLIER
+ vagina.adjust_internal_fluid(regen)
+ else
+ vagina.adjust_internal_fluid(VAGINA_FLUID_REMOVAL_AMOUNT)
+
+#undef AROUSAL_MULTIPLIER
+#undef BALLS_MULTIPLIER
+#undef NUTRITION_MULTIPLIER
+#undef NUTRITION_COST_MULTIPLIER
+#undef BREASTS_MULTIPLIER
+#undef VAGINA_MULTIPLIER
+#undef VAGINA_FLUID_REMOVAL_AMOUNT
+#undef BASE_MULTIPLIER
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/ropebunny.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/ropebunny.dm
new file mode 100644
index 00000000000..6203b73c9c4
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/ropebunny.dm
@@ -0,0 +1,19 @@
+/datum/status_effect/ropebunny
+ id = "ropebunny"
+ tick_interval = 10
+ duration = INFINITE
+ alert_type = null
+
+/datum/status_effect/ropebunny/on_apply()
+ . = ..()
+ var/mob/living/carbon/human/target = owner
+ target.add_mood_event("ropebunny", /datum/mood_event/ropebunny)
+
+/datum/status_effect/ropebunny/on_remove()
+ . = ..()
+ var/mob/living/carbon/human/target = owner
+ target.clear_mood_event("ropebunny")
+
+/datum/mood_event/ropebunny
+ description = span_purple("I'm tied! Cannot move! These ropes... Ah!~")
+ mood_change = 0 // I don't want to doom the station to sonic-speed perverts, but still want to keep this as mood modifier.
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/spank_related.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/spank_related.dm
new file mode 100644
index 00000000000..1a6420e7b49
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_arousal/status_effects/spank_related.dm
@@ -0,0 +1,39 @@
+// This name means nothing. What does this even do?
+
+/datum/status_effect/subspace
+ id = "subspace"
+ tick_interval = 10
+ duration = 5 MINUTES
+ alert_type = null
+
+/datum/status_effect/subspace/on_apply()
+ . = ..()
+ var/mob/living/carbon/human/target = owner
+ target.add_mood_event("subspace", /datum/mood_event/subspace)
+
+/datum/status_effect/subspace/on_remove()
+ . = ..()
+ var/mob/living/carbon/human/target = owner
+ target.clear_mood_event("subspace")
+
+/datum/mood_event/subspace
+ description = span_purple("Everything is so woozy... Pain feels so... Awesome.\n")
+
+
+//Hips are red after spanking
+/datum/status_effect/spanked
+ id = "spanked"
+ duration = 300 SECONDS
+ alert_type = null
+
+/mob/living/carbon/human/examine(mob/user)
+ . = ..()
+ if(stat >= DEAD || HAS_TRAIT(src, TRAIT_FAKEDEATH) || src == user || !has_status_effect(/datum/status_effect/spanked) || !is_bottomless())
+ return
+
+ . += span_purple("[user.p_their(TRUE)] butt has a red tint to it.") + "\n"
+
+//Mood boost for masochist
+/datum/mood_event/perv_spanked
+ description = span_purple("Ah, yes! More! Punish me!\n")
+ timeout = 5 MINUTES
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/chemistry_for_ERP.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/chemistry_for_ERP.dm
deleted file mode 100644
index 56fd662e034..00000000000
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/chemistry_for_ERP.dm
+++ /dev/null
@@ -1,742 +0,0 @@
-// The base ERP chem. It handles pref and human type checks for you, so ALL chems relating to ERP should be subtypes of this.
-/datum/reagent/drug/aphrodisiac
- name = "liquid ERP"
- description = "ERP in its liquified form. Complain to a coder."
-
- /// What preference you need enabled for effects on life
- var/life_pref_datum = /datum/preference/toggle/erp
- /// What preference you need enabled for effects on overdose
- var/overdose_pref_datum = /datum/preference/toggle/erp
-
- /// The amount to adjust the mob's arousal by
- var/arousal_adjust_amount = 1
- /// The amount to adjust the mob's pleasure by
- var/pleasure_adjust_amount = 0
- /// The amount to adjust the mob's pain by
- var/pain_adjust_amount = 0
-
- // Vars for enlargement chems from here on
- /// Counts up to a threshold before triggering enlargement effects
- var/enlargement_amount = 0
- /// How much to increase the count by each process
- var/enlarger_increase_step = 5
- /// Count to reach before effects
- var/enlargement_threshold = 100
-
- /// % chance for damage to be applied when the organ is too large and the mob is clothed
- var/damage_chance = 20
-
- /// Largest length the chem can make a mob's penis
- var/penis_max_length = PENIS_MAX_LENGTH
- /// Smallest size the chem can make a mob's penis
- var/penis_min_length = PENIS_MIN_LENGTH
- /// How much the penis is increased in size each time it's run
- var/penis_length_increase_step = 1
- /// How much the penis is increased in girth each time it's run
- var/penis_girth_increase_step = 1
- /// Largest girth the chem can make a mob's penis
- var/penis_max_girth = PENIS_MAX_GIRTH
- /// Smallest girth the chem can make a mob's penis
- var/penis_minimum_girth = 2
- /// How much to reduce the size of the penis each time it's run
- var/penis_size_reduction_step = 2
- /// How much to reduce the girth of the penis each time it's run
- var/penis_girth_reduction_step = 2
-
- /// Largest size the chem can make a mob's breasts
- var/max_breast_size = 16
- /// How much breasts are increased in size each time it's run
- var/breast_size_increase_step = 1
- /// Smallest size the chem can make a mob's breasts
- var/breast_minimum_size = 2
- /// How much to reduce the size of the breasts each time it's run
- var/breast_size_reduction_step = 1
-
-/// Runs on life after preference checks. Use this instead of on_mob_life
-/datum/reagent/drug/aphrodisiac/proc/life_effects(mob/living/carbon/human/exposed_mob)
- return
-
-/// Runs on OD process after preference checks. Use this instead of overdose_process
-/datum/reagent/drug/aphrodisiac/proc/overdose_effects(mob/living/carbon/human/exposed_mob)
- return
-
-/datum/reagent/drug/aphrodisiac/on_mob_life(mob/living/carbon/human/exposed_mob)
- . = ..()
- if(life_pref_datum && exposed_mob.client?.prefs.read_preference(life_pref_datum) && ishuman(exposed_mob))
- life_effects(exposed_mob)
-
-/datum/reagent/drug/aphrodisiac/overdose_process(mob/living/carbon/human/exposed_mob)
- . = ..()
- if(overdose_pref_datum && exposed_mob.client?.prefs.read_preference(overdose_pref_datum) && ishuman(exposed_mob))
- overdose_effects(exposed_mob)
-
-/*
-* APHRODISIACS
-*/
-
-///To notify, but not harm the player in terms of mood. Used for camphor, pentacamphor, succubus milk, and incubus draft.
-/datum/mood_event/minor_overdose
- timeout = 5 MINUTES
-
-/datum/mood_event/minor_overdose/add_effects(drug_name)
- description = "I think I took a bit too much [drug_name]...\n"
-
-//Crocin. Basic aphrodisiac with no consequences
-/datum/reagent/drug/aphrodisiac/crocin
- name = "crocin"
- description = "Naturally found in the crocus and gardenia flowers, this drug acts as a natural and safe aphrodisiac."
- taste_description = "strawberries"
- color = "#FFADFF"
- life_pref_datum = /datum/preference/toggle/erp/aphro
- arousal_adjust_amount = 1
- pleasure_adjust_amount = 0
- pain_adjust_amount = 0
-
- /// Probability of the chem triggering an emote, as a %, run on mob life
- var/emote_probability = 3
- /// Probability of the chem triggering a to_chat, as a %, run on mob life
- var/thought_probability = 2
-
- /// A list of possible emotes the chem is able to trigger
- var/list/possible_aroused_emotes = list("moan", "blush")
-
- /// A list of possible to_chat messages the chem is able to trigger
- var/list/possible_aroused_thoughts = list("You feel frisky.", "You're having trouble suppressing your urges.", "You feel in the mood.")
-
-
-/datum/reagent/drug/aphrodisiac/crocin/life_effects(mob/living/carbon/human/exposed_mob)
- if(prob(emote_probability))
- exposed_mob.emote(pick(possible_aroused_emotes))
- if(prob(thought_probability))
- var/displayed_thought = pick(possible_aroused_thoughts)
- to_chat(exposed_mob, span_notice("[displayed_thought]"))
-
- exposed_mob.adjustArousal(arousal_adjust_amount)
- exposed_mob.adjustPleasure(pleasure_adjust_amount)
- exposed_mob.adjustPain(pain_adjust_amount)
-
- for(var/obj/item/organ/external/genital/mob_genitals in exposed_mob.external_organs)
- if(!mob_genitals.aroused == AROUSAL_CANT)
- mob_genitals.aroused = AROUSAL_FULL
- mob_genitals.update_sprite_suffix()
- exposed_mob.update_body()
-
-//Hexacrocin. Advanced aphrodisiac that can cause brain traumas.
-/datum/reagent/drug/aphrodisiac/crocin/hexacrocin
- name = "hexacrocin"
- description = "Chemically condensed form of basic crocin. This aphrodisiac is extremely powerful and addictive for most animals.\
- Addiction withdrawals can cause brain damage and shortness of breath. Overdose can lead to brain traumas."
- taste_description = "liquid desire"
- color = "#FF2BFF"
- overdose_threshold = 25
- overdose_pref_datum = /datum/preference/toggle/erp/bimbofication
-
- emote_probability = 5
- thought_probability = 5
- arousal_adjust_amount = 2
- pleasure_adjust_amount = 1.5
- pain_adjust_amount = 0.2
- possible_aroused_thoughts = list("You feel a bit hot.", "You feel strong sexual urges.", "You feel in the mood.", "You're ready to go down on someone.")
-
- /// A list of possible to_chat messages the chem is able to trigger after enough cycles in the mobs system
- var/list/extreme_aroused_thoughts = list("You need to fuck someone!", "You're bursting with sexual tension!", "You can't get sex off your mind!")
- /// How many cycles the chem has to be in the mob's system before triggering extreme effects
- var/extreme_thought_threshold = 25
-
-/datum/reagent/drug/aphrodisiac/crocin/hexacrocin/life_effects(mob/living/carbon/human/exposed_mob)
- . = ..()
- if(prob(thought_probability) && current_cycle >= extreme_thought_threshold)
- var/displayed_extreme_thought = pick(extreme_aroused_thoughts)
- to_chat(exposed_mob, span_purple("[displayed_extreme_thought]"))
-
-/datum/reagent/drug/aphrodisiac/crocin/hexacrocin/overdose_effects(mob/living/carbon/human/exposed_mob)
- if(prob(95) || HAS_TRAIT(exposed_mob, TRAIT_BIMBO))
- return ..()
-
- to_chat(exposed_mob, span_purple("Your libido is going haywire! It feels like speaking is much harder..."))
- exposed_mob.gain_trauma(/datum/brain_trauma/very_special/bimbo, TRAUMA_RESILIENCE_BASIC)
- ADD_TRAIT(exposed_mob, TRAIT_BIMBO, LEWDCHEM_TRAIT)
-
-//Dopamine. Generates in character after orgasm.
-/datum/reagent/drug/aphrodisiac/dopamine
- name = "dopamine...?"
- description = "Pure happiness"
- taste_description = "an indescribable, slightly sour taste. Something in it relaxes you, filling you with pleasure."
- color = "#97ffee"
- glass_name = "dopamine"
- glass_desc = "Delicious flavored reagent. You feel happy even looking at it."
- reagent_state = LIQUID
- overdose_threshold = 10
- life_pref_datum = /datum/preference/toggle/erp/aphro
- overdose_pref_datum = /datum/preference/toggle/erp/aphro
- arousal_adjust_amount = 0.5
- pleasure_adjust_amount = 0.3
- pain_adjust_amount = -0.5
-
- /// How druggy the chem will make the mob
- var/drugginess_amount = 5 SECONDS
- /// How likely the drug is to make the mob druggy per life process
- var/drugginess_chance = 7
-
-/datum/reagent/drug/aphrodisiac/dopamine/on_mob_add(mob/living/carbon/human/exposed_mob)
- if(!(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/aphro)))
- return ..()
- exposed_mob.add_mood_event("[type]_start", /datum/mood_event/orgasm, name)
- ..()
-
-/datum/reagent/drug/aphrodisiac/dopamine/life_effects(mob/living/carbon/human/exposed_mob)
- exposed_mob.set_drugginess(drugginess_amount)
- if(prob(drugginess_chance))
- exposed_mob.emote(pick("twitch", "drool", "moan", "giggle", "shaking"))
-
-/datum/reagent/drug/aphrodisiac/dopamine/overdose_start(mob/living/carbon/human/exposed_mob)
- . = ..()
- to_chat(exposed_mob, span_purple("You feel so happy!"))
- exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/overgasm, name)
-
-/datum/reagent/drug/aphrodisiac/dopamine/overdose_effects(mob/living/carbon/human/exposed_mob)
- if(!(exposed_mob.hallucination < volume && prob(20)))
- return ..()
- exposed_mob.adjustArousal(arousal_adjust_amount)
- exposed_mob.adjustPleasure(pleasure_adjust_amount)
- exposed_mob.adjustPain(pain_adjust_amount)
- if(prob(2))
- exposed_mob.emote(pick("moan", "twitch_s"))
-
-/*
-* ANAPHRODISIACS
-*/
-
-//Camphor. Used to reduce libido.
-///Able to reset one's genital size back to normal upon OD, or alternatively, if the user sets the size and then disables the organ, that size.
-/datum/reagent/drug/aphrodisiac/camphor
- name = "Camphor"
- description = "Naturally found in some species of evergreen trees, camphor is a waxy substance. When ingested by most animals it acts as an anaphrodisiac, \
- reducing libido and calming them. Non-habit forming and non-addictive."
- taste_description = "dull bitterness"
- taste_mult = 2
- color = "#D9D9D9"//rgb(157, 157, 157)
- reagent_state = SOLID
- overdose_threshold = 25 ///OD will reset sizes of genitals back to normal.
- life_pref_datum = /datum/preference/toggle/erp/aphro
- arousal_adjust_amount = -12
- pleasure_adjust_amount = -3
- overdose_pref_datum = /datum/preference/toggle/erp ///This will work without having aphrodisiacs enabled.
-
-/datum/reagent/drug/aphrodisiac/camphor/life_effects(mob/living/carbon/human/exposed_mob)
- var/old_arousal = exposed_mob.arousal
- exposed_mob.adjustArousal(arousal_adjust_amount)
- exposed_mob.adjustPleasure(pleasure_adjust_amount)
- if(exposed_mob.arousal <= 0 && old_arousal > 0)
- to_chat(exposed_mob, span_notice("You no longer feel aroused."))
-
-/datum/reagent/drug/aphrodisiac/camphor/overdose_effects(mob/living/carbon/human/exposed_mob)
- ///Grows and shrinks organs depending on prefs, returning to normal. Offers a way of resetting succubus milk / incubus draft sizes, but not organs.
-
- ///Resets gender if prefs enabled.
- if(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/gender_change))
- if(exposed_mob.gender != exposed_mob.client?.prefs?.read_preference(/datum/preference/choiced/gender))
- exposed_mob.set_gender(exposed_mob.client?.prefs?.read_preference(/datum/preference/choiced/gender))
-
- if(exposed_mob.getorganslot(ORGAN_SLOT_BREASTS))
- var/obj/item/organ/external/genital/breasts/mob_breasts = exposed_mob.getorganslot(ORGAN_SLOT_BREASTS)
- if(exposed_mob.client?.prefs.read_preference(/datum/preference/numeric/breasts_size))
- var/original_breast_size = exposed_mob.client?.prefs.read_preference(/datum/preference/numeric/breasts_size)
- if(mob_breasts?.genital_size > original_breast_size)
- mob_breasts.genital_size -= breast_size_reduction_step
- mob_breasts.update_sprite_suffix()
- exposed_mob.update_body()
- return
- if(mob_breasts?.genital_size < original_breast_size)
- mob_breasts.genital_size += breast_size_increase_step
- mob_breasts.update_sprite_suffix()
- exposed_mob.update_body()
- return
- if(exposed_mob.getorganslot(ORGAN_SLOT_PENIS))
- var/obj/item/organ/external/genital/penis/mob_penis = exposed_mob.getorganslot(ORGAN_SLOT_PENIS)
- if(exposed_mob.client?.prefs?.read_preference(/datum/preference/numeric/penis_length))
- var/original_penis_length = exposed_mob.client?.prefs.read_preference(/datum/preference/numeric/penis_length)
- var/original_penis_girth = exposed_mob.client?.prefs.read_preference(/datum/preference/numeric/penis_girth)
- ///Run to go through girth first.
- if(mob_penis?.girth > original_penis_girth)
- mob_penis.girth -= penis_girth_reduction_step
- mob_penis.update_sprite_suffix()
- exposed_mob.update_body()
- if(mob_penis?.girth < original_penis_girth)
- mob_penis.girth += penis_girth_increase_step
- mob_penis.update_sprite_suffix()
- exposed_mob.update_body()
- ///No returns in order to continue code.
- if(mob_penis?.genital_size > original_penis_length)
- mob_penis.genital_size -= penis_size_reduction_step
- mob_penis.update_sprite_suffix()
- exposed_mob.update_body()
- return
- if(mob_penis?.genital_size < original_penis_length)
- mob_penis.genital_size += penis_length_increase_step
- mob_penis.update_sprite_suffix()
- exposed_mob.update_body()
- return
- if(exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES))
- var/obj/item/organ/external/genital/testicles/mob_testicles = exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES)
- if(exposed_mob.client?.prefs?.read_preference(/datum/preference/numeric/balls_size))
- var/original_ball_size = exposed_mob.client?.prefs.read_preference(/datum/preference/numeric/balls_size)
- if(mob_testicles?.genital_size > original_ball_size)
- mob_testicles.genital_size -= 1
- mob_testicles.update_sprite_suffix()
- exposed_mob.update_body()
- return
- if(mob_testicles?.genital_size < original_ball_size)
- mob_testicles.genital_size += 1
- mob_testicles.update_sprite_suffix()
- exposed_mob.update_body()
- return
-
-// Pentacamphor. Used to purge crocin and hexacrocin. Can permanently disable arousal or cure bimbofication on overdose.
-/datum/reagent/drug/aphrodisiac/camphor/pentacamphor
- name = "Pentacamphor"
- description = "Chemically condensed camphor. Causes an extreme reduction in libido and a permanent one if overdosed. Non-addictive."
- taste_description = "tranquil celibacy"
- color = "#D9D9D9"//rgb(255, 255, 255)
- reagent_state = SOLID
- overdose_threshold = 20
- arousal_adjust_amount = -18
- overdose_pref_datum = /datum/preference/toggle/erp/aphro
-
- /// How much of the given reagent to remove per operation
- var/reagent_reduction_amount = 20
-
-/datum/reagent/drug/aphrodisiac/camphor/pentacamphor/life_effects(mob/living/carbon/human/exposed_mob)
- . = ..()
- if(exposed_mob.reagents.has_reagent(/datum/reagent/drug/aphrodisiac/crocin))
- exposed_mob.reagents.remove_reagent(/datum/reagent/drug/aphrodisiac/crocin, reagent_reduction_amount)
- if(exposed_mob.reagents.has_reagent(/datum/reagent/drug/aphrodisiac/crocin/hexacrocin))
- exposed_mob.reagents.remove_reagent(/datum/reagent/drug/aphrodisiac/crocin/hexacrocin, reagent_reduction_amount)
-
-/datum/reagent/drug/aphrodisiac/camphor/pentacamphor/overdose_start(mob/living/carbon/human/exposed_mob)
- if(!HAS_TRAIT(exposed_mob, TRAIT_NEVERBONER))
- to_chat(exposed_mob, span_notice("You feel like you'll never feel aroused again..."))
- ADD_TRAIT(exposed_mob, TRAIT_NEVERBONER, LEWDCHEM_TRAIT)
- if(!HAS_TRAIT(exposed_mob, TRAIT_BIMBO))
- return
- exposed_mob.cure_trauma_type(/datum/brain_trauma/very_special/bimbo, TRAUMA_RESILIENCE_ABSOLUTE)
- to_chat(exposed_mob, span_notice("Your mind is free. Your thoughts are pure and innocent once more."))
- REMOVE_TRAIT(exposed_mob, TRAIT_BIMBO, LEWDCHEM_TRAIT)
-
-/*
-* GENITAL ENLARGEMENT CHEMICALS
-*/
-
-/*
-* BREAST ENLARGEMENT
-* Normal function increases the player's breast size.
-* If the player's breasts are near or at the maximum size and they're wearing clothing, they press against the player's clothes and causes brute and oxygen damage.
-* If gender change preference is enabled: Overdosing makes you female if male, makes the player grow breasts, sets the player's testicles to the minimum size, and shrinks the player's penis to a minimum size.
-* If the gender change preference is not enabled: Overdosing will just make you grow breasts if you don't have any.
-* Credit to Citadel for the original code before modification
-*/
-
-/datum/reagent/drug/aphrodisiac/breast_enlarger
- name = "succubus milk"
- description = "A volatile collodial mixture derived from milk that encourages mammary production via a potent estrogen mix."
- color = "#E60584" // rgb: 96, 0, 255
- taste_description = "a milky ice cream like flavour" /// Edited so it doesn't trail off and act strangely with other taste descriptions.
- overdose_threshold = 20
- metabolization_rate = 0.25
- life_pref_datum = /datum/preference/toggle/erp/breast_enlargement
- overdose_pref_datum = /datum/preference/toggle/erp ///Changed from breast_enlargement in order to have gender swapping as a separate feature within overdose.
-
-/datum/reagent/drug/aphrodisiac/breast_enlarger/life_effects(mob/living/carbon/human/exposed_mob) //Increases breast size
- var/obj/item/organ/external/genital/breasts/mob_breasts = exposed_mob.getorganslot(ORGAN_SLOT_BREASTS)
- enlargement_amount += enlarger_increase_step
- /// Adds a check for breasts in the first place. I HATE ERRORS.
- if(!mob_breasts)
- return
- if(enlargement_amount >= enlargement_threshold)
- if(mob_breasts?.genital_size >= max_breast_size)
- return
- mob_breasts.genital_size += breast_size_increase_step
- mob_breasts.update_sprite_suffix()
- exposed_mob.update_body()
- enlargement_amount = 0
-
- /// Words for the breasts when huge.
- var/static/list/words_for_bigger = list("huge", "massive", "squishy", "gigantic", "rather large", "jiggly", "hefty", )
- /// Synonyms for breasts.
- var/static/list/boob_text_list = list("boobs", "tits", "breasts", )
- /// Synonyms for the chest.
- var/static/list/covered_boobs_list = list("bust", "chest", "bosom", )
- /// Synonyms for bigger breasts.
- var/static/list/bigger_boob_text_list = list("jigglies", "melons", "jugs", "boobies", "milkers", "boobs", "tits", "breasts", )
- /// Wording chosen to expand the breasts, shown only to the mob.
- var/static/list/action_text_list = list("expand outward to ", "grow out to ", "begin to enlarge, growing to ", "suddenly expand to ", "swell out to ", )
- /// Wording chosen to be seen by other mobs, regardless of whether mob is clothed/unclothed.
- var/static/list/public_bigger_action_text_list = list("expand and jiggle outward.", "grow a bit larger, bouncing about.", "seem a bit bigger than they were before.", "bounce and jiggle as they suddenly expand.", )
- /// Wording chosen to be seen by other mobs, while mob is unclothed.
- var/static/list/public_action_text_list = list("expand outward.", "seem to grow a bit larger.", "appear a bit bigger than they were before.", "bounce and jiggle as they suddenly expand.", )
- /// Wording chosen to be seen by other mobs, while mob is clothed.
- var/static/list/notice_boobs = list("seems to be a bit tighter.", "appears to be a bit bigger.", "seems to swell outward a bit.", )
- /// Checks for cup size.
- var/translation = mob_breasts.breasts_size_to_cup(mob_breasts.genital_size)
-
- if(mob_breasts.visibility_preference == GENITAL_ALWAYS_SHOW || exposed_mob.is_topless())
- switch(translation)
- if("Flatchested")
- return
- if("beyond measurement")
- exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(words_for_bigger)] [pick(bigger_boob_text_list)] [pick(public_bigger_action_text_list)]"))
- to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger)] [pick(bigger_boob_text_list)] [pick(action_text_list)]about [mob_breasts.genital_size] inches in diameter."))
- return
- else
- if(mob_breasts?.genital_size >= (max_breast_size - 2))
- exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(words_for_bigger)] [pick(bigger_boob_text_list)] [pick(public_bigger_action_text_list)]"))
- to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger)] [pick(bigger_boob_text_list)] [pick(action_text_list)]about [translation]-cups."))
- return
- else
- exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(boob_text_list)] [pick(public_action_text_list)]"))
- to_chat(exposed_mob, span_purple("Your [pick(boob_text_list)] [pick(action_text_list)]about [translation]-cups."))
- return
- else
- switch(translation)
- if("Flatchested")
- return
- if("beyond measurement")
- exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(boob_text_list)] [pick(public_bigger_action_text_list)]"))
- to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger)] [pick(bigger_boob_text_list)] [pick(action_text_list)]about [mob_breasts.genital_size] inches in diameter."))
- return
- else
- if(mob_breasts?.genital_size >= (max_breast_size - 2))
- exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(boob_text_list)] [pick(public_bigger_action_text_list)]"))
- to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger)] [pick(bigger_boob_text_list)] [pick(action_text_list)]about [translation]-cups."))
- return
- else
- exposed_mob.visible_message(span_notice("The area around [exposed_mob]'s [pick(covered_boobs_list)] [pick(notice_boobs)]"))
- to_chat(exposed_mob, span_purple("Your [pick(boob_text_list)] [pick(action_text_list)]about [translation]-cups."))
- return
-
- if((mob_breasts?.genital_size >= (max_breast_size - 2)) && (exposed_mob.w_uniform || exposed_mob.wear_suit))
- var/target_bodypart = exposed_mob.get_bodypart(BODY_ZONE_CHEST)
- if(prob(damage_chance))
- to_chat(exposed_mob, span_danger("Your breasts begin to strain against your clothes!"))
- exposed_mob.adjustOxyLoss(5, 0)
- exposed_mob.apply_damage(1, BRUTE, target_bodypart)
-
-/datum/reagent/drug/aphrodisiac/breast_enlarger/overdose_effects(mob/living/carbon/human/exposed_mob) //Turns you into a female if character is male. Also supposed to add breasts but enlargement_amount'm too dumb to figure out how to make it work
- var/obj/item/organ/external/genital/penis/mob_penis = exposed_mob.getorganslot(ORGAN_SLOT_PENIS)
- var/obj/item/organ/external/genital/testicles/mob_testicles = exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES)
- if(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/breast_enlargement))
- /// Makes above comment actually work.
- if(!exposed_mob.getorganslot(ORGAN_SLOT_BREASTS))
- var/obj/item/organ/path = /obj/item/organ/external/genital/breasts
- exposed_mob.dna.mutant_bodyparts["breasts"][MUTANT_INDEX_NAME] = "Pair"
- path = new /obj/item/organ/external/genital/breasts
- path.build_from_dna(exposed_mob.dna, "breasts")
- path.Insert(exposed_mob, 0, FALSE)
- var/obj/item/organ/external/genital/new_breasts = exposed_mob.getorganslot(ORGAN_SLOT_BREASTS)
- new_breasts.genital_size = 2
- new_breasts.update_sprite_suffix()
- exposed_mob.update_body()
- enlargement_amount = 0
- if(new_breasts.visibility_preference == GENITAL_ALWAYS_SHOW || exposed_mob.is_topless())
- exposed_mob.visible_message(span_notice("[exposed_mob]'s bust suddenly expands!"))
- to_chat(exposed_mob, span_purple("Your chest feels warm, tingling with sensitivity as it expands outward."))
- return
- else
- exposed_mob.visible_message(span_notice("The area around [exposed_mob]'s chest suddenly bounces a bit."))
- to_chat(exposed_mob, span_purple("Your chest feels warm, tingling with sensitivity as it strains against your clothes."))
- return
- ///Separates gender change stuff from breast growth.
- if(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/gender_change))
- if(exposed_mob.gender == MALE)
- exposed_mob.set_gender(FEMALE)
- exposed_mob.physique = exposed_mob.gender
- exposed_mob.update_body()
- exposed_mob.update_mutations_overlay()
- if(!mob_penis)
- return
- if(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/penis_enlargement)) ///To do cock shrinkage, check if prefs allow for this.
- if(mob_penis.genital_size > penis_min_length)
- mob_penis.genital_size -= penis_size_reduction_step
- mob_penis.update_sprite_suffix()
- exposed_mob.update_body()
- if(mob_penis.girth > penis_minimum_girth)
- mob_penis.girth -= penis_girth_reduction_step
- mob_penis.update_sprite_suffix()
- exposed_mob.update_body()
- if(!mob_testicles || mob_testicles.genital_size <= 1)
- return
- mob_testicles.genital_size -= 1
- return
-
-/*
-* PENIS ENLARGEMENT
-* Normal function increases the player's penis size.
-* If the player's penis is near or at the maximum size and they're wearing clothing, it presses against the player's clothes and causes brute damage.
-* If gender change preference is enabled: Overdosing makes you male if female, makes the player grow a cock, and shrinks the player's breasts to a minimum size.
-* If the gender change preference is not enabled: Overdosing will just make you grow a cock if you don't have one.
-*/
-
-/datum/reagent/drug/aphrodisiac/penis_enlarger
- name = "incubus draft"
- description = "A volatile collodial mixture derived from various masculine solutions that encourages a larger gentleman's package via a potent testosterone mix."
- color = "#888888"
- taste_description = "chinese dragon powder"
- overdose_threshold = 20 ///ODing makes you male and shrinks female genitals if gender change prefs are enabled. Otherwise, grows a cock.
- metabolization_rate = 0.25
- life_pref_datum = /datum/preference/toggle/erp/penis_enlargement
- overdose_pref_datum = /datum/preference/toggle/erp ///Changed from penis_enlargement in order to have gender swapping as a separate feature within overdose.
-
-/datum/reagent/drug/aphrodisiac/penis_enlarger/life_effects(mob/living/carbon/human/exposed_mob)
- var/obj/item/organ/external/genital/penis/mob_penis = exposed_mob.getorganslot(ORGAN_SLOT_PENIS)
- enlargement_amount += enlarger_increase_step
- /// Add yet another check because I hate errors!!
- if(!mob_penis)
- return
- if(enlargement_amount >= enlargement_threshold)
- if(mob_penis?.genital_size >= penis_max_length)
- return ..()
- mob_penis.genital_size += penis_length_increase_step
- ///Improvision to girth to not make it random chance.
- if(mob_penis?.girth < penis_max_girth) ///Because any higher is ridiculous. However, should still allow for regular penis growth.
- mob_penis.girth = round(mob_penis.girth + (mob_penis.genital_size/mob_penis.girth))
- mob_penis.update_sprite_suffix()
- exposed_mob.update_body()
- enlargement_amount = 0
- /// Words for the cock when huge.
- var/static/list/words_for_bigger_cock = list("huge", "massive", "gigantic", "rather lengthy", "colossal", "hefty", )
- /// Synonyms for cock.
- var/static/list/cock_text_list = list("cock", "penis", "dick", "member", "richard", "johnston", "johnson", )
- /// Synonyms for bigger cock.
- var/static/list/bigger_cock_text_list = list("rod", "shaft", "cock", "penis", "dick", "member", "richard", "johnston", "johnson", )
- /// Wording chosen to extend the cock, shown only to the mob.
- var/static/list/cock_action_text_list = list("extends to ", "grows out to ", "begins to enlarge, growing to ", "suddenly expands to ", "lengthens out to ", )
- /// Wording chosen to be seen by other mobs, while mob is unclothed.
- var/static/list/public_cock_action_text_list = list("expands by an inch or so.", "appears to grow a bit longer.", "seems a bit bigger than it was before.", "suddenly lengthens about an inch or two.", )
-
- if(mob_penis.visibility_preference == GENITAL_ALWAYS_SHOW || exposed_mob.is_bottomless())
- if(mob_penis?.genital_size >= (penis_max_length - 2))
- if(exposed_mob.dna.features["penis_sheath"] == SHEATH_SLIT)
- if(mob_penis.aroused != AROUSAL_FULL)
- to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger_cock)] [pick(bigger_cock_text_list)] [pick(cock_action_text_list)]about [mob_penis.genital_size] inches long, and [mob_penis.girth] inches in circumference."))
- return
- exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(words_for_bigger_cock)] [pick(bigger_cock_text_list)] [pick(public_cock_action_text_list)]"))
- to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger_cock)] [pick(bigger_cock_text_list)] [pick(cock_action_text_list)]about [mob_penis.genital_size] inches long, and [mob_penis.girth] inches in circumference."))
- return
- else
- if(exposed_mob.dna.features["penis_sheath"] == SHEATH_SLIT)
- if(mob_penis.aroused != AROUSAL_FULL)
- to_chat(exposed_mob, span_purple("Your [pick(cock_text_list)] [pick(cock_action_text_list)]about [mob_penis.genital_size] inches long, and [mob_penis.girth] inches in circumference."))
- return
- exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(cock_text_list)] [pick(public_cock_action_text_list)]"))
- to_chat(exposed_mob, span_purple("Your [pick(cock_text_list)] [pick(cock_action_text_list)]about [mob_penis.genital_size] inches long, and [mob_penis.girth] inches in circumference."))
- return
- else
- if(mob_penis?.genital_size >= (penis_max_length - 2))
- to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger_cock)] [pick(bigger_cock_text_list)] [pick(cock_action_text_list)]about [mob_penis.genital_size] inches long, and [mob_penis.girth] inches in circumference."))
- return
- else
- to_chat(exposed_mob, span_purple("Your [pick(cock_text_list)] [pick(cock_action_text_list)]about [mob_penis.genital_size] inches long, and [mob_penis.girth] inches in circumference."))
- return
-
- if((mob_penis?.genital_size >= (penis_max_length - 2)) && (exposed_mob.w_uniform || exposed_mob.wear_suit))
- var/target_bodypart = exposed_mob.get_bodypart(BODY_ZONE_PRECISE_GROIN)
- if(prob(damage_chance))
- to_chat(exposed_mob, span_danger("You feel a tightness in your pants!"))
- exposed_mob.apply_damage(1, BRUTE, target_bodypart)
-
- return ..()
-
-/datum/reagent/drug/aphrodisiac/penis_enlarger/overdose_effects(mob/living/carbon/human/exposed_mob)
- if(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/penis_enlargement))
- if(!exposed_mob.getorganslot(ORGAN_SLOT_PENIS))
- ///Check if human. If not do messy code. (This only supports lizards and human penises (for now))
- exposed_mob.dna.features["penis_sheath"] = SHEATH_NONE
- exposed_mob.dna.mutant_bodyparts["penis"][MUTANT_INDEX_NAME] = "Human"
- exposed_mob.dna.mutant_bodyparts["testicles"][MUTANT_INDEX_NAME] = "Pair"
- if((exposed_mob.dna.species.id == SPECIES_HUMAN) && (exposed_mob.dna.species.id != SPECIES_LIZARD) && (exposed_mob.dna.species.id != SPECIES_LIZARD_ASH))
- if (!exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES))
- var/obj/item/organ/balls_path = /obj/item/organ/external/genital/testicles
- balls_path = new /obj/item/organ/external/genital/testicles
- balls_path.build_from_dna(exposed_mob.dna, "testicles")
- balls_path.Insert(exposed_mob, 0, FALSE)
- var/obj/item/organ/external/genital/new_balls = exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES)
- new_balls.genital_size = 1
- new_balls.update_sprite_suffix()
- if((exposed_mob.dna.species.id == SPECIES_LIZARD) || (exposed_mob.dna.species.id == SPECIES_LIZARD_ASH))
- exposed_mob.dna.features["penis_sheath"] = SHEATH_SLIT
- exposed_mob.dna.mutant_bodyparts["penis"][MUTANT_INDEX_NAME] = "Flared"
- exposed_mob.dna.mutant_bodyparts["penis"][MUTANT_INDEX_COLOR_LIST] = list("#FFB6C1")
- exposed_mob.dna.mutant_bodyparts["testicles"][MUTANT_INDEX_NAME] = "Internal"
- if (!exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES))
- var/obj/item/organ/balls_path = /obj/item/organ/external/genital/testicles
- balls_path = new /obj/item/organ/external/genital/testicles
- balls_path.build_from_dna(exposed_mob.dna, "testicles")
- balls_path.Insert(exposed_mob, 0, FALSE)
- var/obj/item/organ/external/genital/new_balls = exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES)
- new_balls.genital_size = 1
- new_balls.update_sprite_suffix()
- var/obj/item/organ/cock_path = /obj/item/organ/external/genital/penis
- cock_path = new /obj/item/organ/external/genital/penis
- cock_path.build_from_dna(exposed_mob.dna, "penis")
- cock_path.Insert(exposed_mob, 0, FALSE)
- var/obj/item/organ/external/genital/penis/new_penis = exposed_mob.getorganslot(ORGAN_SLOT_PENIS)
- new_penis.genital_size = 4
- new_penis.girth = 3
- new_penis.update_sprite_suffix()
- exposed_mob.update_body()
- to_chat(exposed_mob, span_purple("Your crotch feels warm as something suddenly sprouts between your legs."))
- ///Makes the balls bigger if they're small.
- var/obj/item/organ/external/genital/testicles/mob_testicles = exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES)
- if(mob_testicles)
- if(mob_testicles.genital_size > 2)
- return
- mob_testicles.genital_size = 2
- ///Separates gender change stuff from cock growth.
- var/obj/item/organ/external/genital/breasts/mob_breasts = exposed_mob.getorganslot(ORGAN_SLOT_BREASTS)
- if(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/gender_change))
- if(exposed_mob.gender == FEMALE)
- exposed_mob.set_gender(MALE)
- exposed_mob.physique = exposed_mob.gender
- exposed_mob.update_body()
- exposed_mob.update_mutations_overlay()
- if(!mob_breasts)
- return
- if(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/breast_enlargement)) ///To do breast shrinkage, check if prefs allow for this.
- if(mob_breasts.genital_size > breast_minimum_size)
- mob_breasts.genital_size -= breast_size_reduction_step
- mob_breasts.update_sprite_suffix()
- exposed_mob.update_body()
- return
-
-/*
-* CHEMICAL REACTIONS
-*/
-
-/datum/chemical_reaction
- /// Will this reaction be disabled by the ERP config being turned off?
- var/erp_reaction = FALSE
-
-/datum/chemical_reaction/crocin
- results = list(/datum/reagent/drug/aphrodisiac/crocin = 6)
- required_reagents = list(/datum/reagent/carbon = 2, /datum/reagent/hydrogen = 2, /datum/reagent/oxygen = 2, /datum/reagent/water = 1)
- required_temp = 400
- mix_message = "The mixture boils off a pink vapor..."
- erp_reaction = TRUE
-
-/datum/chemical_reaction/hexacrocin
- results = list(/datum/reagent/drug/aphrodisiac/crocin/hexacrocin = 1)
- required_reagents = list(/datum/reagent/drug/aphrodisiac/crocin = 6, /datum/reagent/phenol = 1)
- required_temp = 600
- mix_message = "The mixture rapidly condenses and darkens in color..."
- erp_reaction = TRUE
-
-/datum/chemical_reaction/camphor
- results = list(/datum/reagent/drug/aphrodisiac/camphor = 6)
- required_reagents = list(/datum/reagent/carbon = 2, /datum/reagent/hydrogen = 2, /datum/reagent/oxygen = 2, /datum/reagent/sulfur = 1)
- required_temp = 400
- mix_message = "The mixture boils off a yellow, smelly vapor..."
- erp_reaction = TRUE
-
-/datum/chemical_reaction/pentacamphor
- results = list(/datum/reagent/drug/aphrodisiac/camphor/pentacamphor = 1)
- required_reagents = list(/datum/reagent/drug/aphrodisiac/camphor = 5, /datum/reagent/acetone = 1)
- required_temp = 500
- mix_message = "The mixture thickens and heats up slighty..."
- erp_reaction = TRUE
-
-/datum/chemical_reaction/cum
- results = list(/datum/reagent/consumable/cum = 5)
- required_reagents = list(/datum/reagent/blood = 2, /datum/reagent/consumable/milk = 2, /datum/reagent/consumable/salt = 1)
- mix_message = "The mixture turns into a gooey, musky white liquid..."
- erp_reaction = TRUE
-
-/datum/chemical_reaction/breast_enlarger
- results = list(/datum/reagent/drug/aphrodisiac/breast_enlarger = 8)
- required_reagents = list(/datum/reagent/medicine/salglu_solution = 1, /datum/reagent/consumable/milk = 1, /datum/reagent/medicine/c2/synthflesh = 2, /datum/reagent/silicon = 3, /datum/reagent/drug/aphrodisiac/crocin = 3)
- mix_message = "the reaction gives off a mist of milk."
- erp_reaction = TRUE
-
-/datum/chemical_reaction/penis_enlarger
- results = list(/datum/reagent/drug/aphrodisiac/penis_enlarger = 8)
- required_reagents = list(/datum/reagent/blood = 5, /datum/reagent/medicine/c2/synthflesh = 2, /datum/reagent/carbon = 2, /datum/reagent/drug/aphrodisiac/crocin = 2, /datum/reagent/medicine/salglu_solution = 1)
- mix_message = "the reaction gives off a mist of milk."
- erp_reaction = TRUE
-
-/*
-* PREMADE CONTAINERS
-*/
-
-// BOTTLES
-
-/obj/item/reagent_containers/cup/bottle/crocin
- name = "crocin bottle"
- desc = "A bottle of mild aphrodisiac. Increases libido."
- list_reagents = list(/datum/reagent/drug/aphrodisiac/crocin = 30)
-
-/obj/item/reagent_containers/cup/bottle/hexacrocin
- name = "hexacrocin bottle"
- desc = "A bottle of strong aphrodisiac. Increases libido. Potentially dangerous."
- list_reagents = list(/datum/reagent/drug/aphrodisiac/crocin/hexacrocin = 30)
-
-/obj/item/reagent_containers/cup/bottle/dopamine
- name = "dopamine bottle"
- desc = "Pure pleasure and happines in a bottle."
- list_reagents = list(/datum/reagent/drug/aphrodisiac/dopamine = 30)
-
-/obj/item/reagent_containers/cup/bottle/camphor
- name = "camphor bottle"
- desc = "A bottle of mild anaphrodisiac. Reduces libido."
- list_reagents = list(/datum/reagent/drug/aphrodisiac/camphor = 30)
-
-/obj/item/reagent_containers/cup/bottle/pentacamphor
- name = "pentacamphor bottle"
- desc = "A bottle of strong anaphrodisiac. Reduces libido."
- list_reagents = list(/datum/reagent/drug/aphrodisiac/camphor/pentacamphor = 30)
-
-/obj/item/reagent_containers/cup/bottle/breast_enlarger
- name = "succubus milk bottle"
- desc = "A bottle of strong breast enlargement reagent."
- list_reagents = list(/datum/reagent/drug/aphrodisiac/breast_enlarger = 30)
-
-/obj/item/reagent_containers/cup/bottle/penis_enlarger
- name = "incubus draft bottle"
- desc = "A bottle of strong penis enlargement reagent."
- list_reagents = list(/datum/reagent/drug/aphrodisiac/penis_enlarger = 30)
-
-
-// PILLS
-
-/obj/item/reagent_containers/pill/crocin
- name = "crocin pill (10u)"
- desc = "I've fallen, and I can't get it up!"
- icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_pills.dmi'
- icon_state = "crocin"
- list_reagents = list(/datum/reagent/drug/aphrodisiac/crocin = 10)
-
-/obj/item/reagent_containers/pill/hexacrocin
- name = "hexacrocin pill (10u)"
- desc = "Pill in creepy heart form."
- icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_pills.dmi'
- icon_state = "hexacrocin"
- list_reagents = list(/datum/reagent/drug/aphrodisiac/crocin/hexacrocin = 10)
-
-/obj/item/reagent_containers/pill/dopamine
- name = "dopamine pill (5u)"
- desc = "Feelings of orgasm, contained in a pill... Weird."
- icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_pills.dmi'
- icon_state = "dopamine"
- list_reagents = list(/datum/reagent/drug/aphrodisiac/dopamine = 5)
-
-/obj/item/reagent_containers/pill/camphor
- name = "camphor pill (10u)"
- desc = "For the early bird."
- icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_pills.dmi'
- icon_state = "camphor"
- list_reagents = list(/datum/reagent/drug/aphrodisiac/camphor = 10)
-
-/obj/item/reagent_containers/pill/pentacamphor
- name = "pentacamphor pill (10u)"
- desc = "The chemical equivalent of horny jail."
- icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_pills.dmi'
- icon_state = "pentacamphor"
- list_reagents = list(/datum/reagent/drug/aphrodisiac/camphor/pentacamphor = 10)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/items/bottles.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/items/bottles.dm
new file mode 100644
index 00000000000..ac51f3d5a9b
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/items/bottles.dm
@@ -0,0 +1,34 @@
+/obj/item/reagent_containers/cup/bottle/crocin
+ name = "crocin bottle"
+ desc = "A bottle of mild aphrodisiac. Increases libido."
+ list_reagents = list(/datum/reagent/drug/aphrodisiac/crocin = 30)
+
+/obj/item/reagent_containers/cup/bottle/hexacrocin
+ name = "hexacrocin bottle"
+ desc = "A bottle of strong aphrodisiac. Increases libido. Potentially dangerous."
+ list_reagents = list(/datum/reagent/drug/aphrodisiac/crocin/hexacrocin = 30)
+
+/obj/item/reagent_containers/cup/bottle/dopamine
+ name = "dopamine bottle"
+ desc = "Pure pleasure and happines in a bottle."
+ list_reagents = list(/datum/reagent/drug/aphrodisiac/dopamine = 30)
+
+/obj/item/reagent_containers/cup/bottle/camphor
+ name = "camphor bottle"
+ desc = "A bottle of mild anaphrodisiac. Reduces libido."
+ list_reagents = list(/datum/reagent/drug/aphrodisiac/camphor = 30)
+
+/obj/item/reagent_containers/cup/bottle/pentacamphor
+ name = "pentacamphor bottle"
+ desc = "A bottle of strong anaphrodisiac. Reduces libido."
+ list_reagents = list(/datum/reagent/drug/aphrodisiac/camphor/pentacamphor = 30)
+
+/obj/item/reagent_containers/cup/bottle/succubus_milk
+ name = "succubus milk bottle"
+ desc = "A bottle of strong breast enlargement reagent."
+ list_reagents = list(/datum/reagent/drug/aphrodisiac/succubus_milk = 30)
+
+/obj/item/reagent_containers/cup/bottle/incubus_draft
+ name = "incubus draft bottle"
+ desc = "A bottle of strong penis enlargement reagent."
+ list_reagents = list(/datum/reagent/drug/aphrodisiac/incubus_draft = 30)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/items/pills.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/items/pills.dm
new file mode 100644
index 00000000000..b2a6a8e2e6d
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/items/pills.dm
@@ -0,0 +1,34 @@
+/obj/item/reagent_containers/pill/crocin
+ name = "crocin pill (10u)"
+ desc = "I've fallen, and I can't get it up!"
+ icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_pills.dmi'
+ icon_state = "crocin"
+ list_reagents = list(/datum/reagent/drug/aphrodisiac/crocin = 10)
+
+/obj/item/reagent_containers/pill/hexacrocin
+ name = "hexacrocin pill (10u)"
+ desc = "Pill in creepy heart form."
+ icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_pills.dmi'
+ icon_state = "hexacrocin"
+ list_reagents = list(/datum/reagent/drug/aphrodisiac/crocin/hexacrocin = 10)
+
+/obj/item/reagent_containers/pill/dopamine
+ name = "dopamine pill (5u)"
+ desc = "Feelings of orgasm, contained in a pill... Weird."
+ icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_pills.dmi'
+ icon_state = "dopamine"
+ list_reagents = list(/datum/reagent/drug/aphrodisiac/dopamine = 5)
+
+/obj/item/reagent_containers/pill/camphor
+ name = "camphor pill (10u)"
+ desc = "For the early bird."
+ icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_pills.dmi'
+ icon_state = "camphor"
+ list_reagents = list(/datum/reagent/drug/aphrodisiac/camphor = 10)
+
+/obj/item/reagent_containers/pill/pentacamphor
+ name = "pentacamphor pill (10u)"
+ desc = "The chemical equivalent of horny jail."
+ icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_pills.dmi'
+ icon_state = "pentacamphor"
+ list_reagents = list(/datum/reagent/drug/aphrodisiac/camphor/pentacamphor = 10)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/mood_events.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/mood_events.dm
new file mode 100644
index 00000000000..b0e72c57800
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/mood_events.dm
@@ -0,0 +1,6 @@
+///To notify, but not harm the player in terms of mood. Used for camphor, pentacamphor, succubus milk, and incubus draft.
+/datum/mood_event/minor_overdose
+ timeout = 5 MINUTES
+
+/datum/mood_event/minor_overdose/add_effects(drug_name)
+ description = "I think I took a bit too much [drug_name]...\n"
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/_aphrodisiac.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/_aphrodisiac.dm
new file mode 100644
index 00000000000..3637d5bbde3
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/_aphrodisiac.dm
@@ -0,0 +1,71 @@
+// The base ERP chem. It handles pref and human type checks for you, so ALL chems relating to ERP should be subtypes of this.
+/datum/reagent/drug/aphrodisiac
+ name = "liquid ERP"
+ description = "ERP in its liquified form. Complain to a coder."
+
+ /// What preference you need enabled for effects on life
+ var/life_pref_datum = /datum/preference/toggle/erp
+ /// What preference you need enabled for effects on overdose
+ var/overdose_pref_datum = /datum/preference/toggle/erp
+
+ /// The amount to adjust the mob's arousal by
+ var/arousal_adjust_amount = 1
+ /// The amount to adjust the mob's pleasure by
+ var/pleasure_adjust_amount = 0
+ /// The amount to adjust the mob's pain by
+ var/pain_adjust_amount = 0
+
+ // Vars for enlargement chems from here on
+ /// Counts up to a threshold before triggering enlargement effects
+ var/enlargement_amount = 0
+ /// How much to increase the count by each process
+ var/enlarger_increase_step = 5
+ /// Count to reach before effects
+ var/enlargement_threshold = 100
+
+ /// % chance for damage to be applied when the organ is too large and the mob is clothed
+ var/damage_chance = 20
+
+ /// Largest length the chem can make a mob's penis
+ var/penis_max_length = PENIS_MAX_LENGTH
+ /// Smallest size the chem can make a mob's penis
+ var/penis_min_length = PENIS_MIN_LENGTH
+ /// How much the penis is increased in size each time it's run
+ var/penis_length_increase_step = 1
+ /// How much the penis is increased in girth each time it's run
+ var/penis_girth_increase_step = 1
+ /// Largest girth the chem can make a mob's penis
+ var/penis_max_girth = PENIS_MAX_GIRTH
+ /// Smallest girth the chem can make a mob's penis
+ var/penis_minimum_girth = 2
+ /// How much to reduce the size of the penis each time it's run
+ var/penis_size_reduction_step = 2
+ /// How much to reduce the girth of the penis each time it's run
+ var/penis_girth_reduction_step = 2
+
+ /// Largest size the chem can make a mob's breasts
+ var/max_breast_size = 16
+ /// How much breasts are increased in size each time it's run
+ var/breast_size_increase_step = 1
+ /// Smallest size the chem can make a mob's breasts
+ var/breast_minimum_size = 2
+ /// How much to reduce the size of the breasts each time it's run
+ var/breast_size_reduction_step = 1
+
+/// Runs on life after preference checks. Use this instead of on_mob_life
+/datum/reagent/drug/aphrodisiac/proc/life_effects(mob/living/carbon/human/exposed_mob)
+ return
+
+/// Runs on OD process after preference checks. Use this instead of overdose_process
+/datum/reagent/drug/aphrodisiac/proc/overdose_effects(mob/living/carbon/human/exposed_mob)
+ return
+
+/datum/reagent/drug/aphrodisiac/on_mob_life(mob/living/carbon/human/exposed_mob)
+ . = ..()
+ if(life_pref_datum && exposed_mob.client?.prefs.read_preference(life_pref_datum) && ishuman(exposed_mob))
+ life_effects(exposed_mob)
+
+/datum/reagent/drug/aphrodisiac/overdose_process(mob/living/carbon/human/exposed_mob)
+ . = ..()
+ if(overdose_pref_datum && exposed_mob.client?.prefs.read_preference(overdose_pref_datum) && ishuman(exposed_mob))
+ overdose_effects(exposed_mob)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/_chemical_reaction.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/_chemical_reaction.dm
new file mode 100644
index 00000000000..9ef83b8afa5
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/_chemical_reaction.dm
@@ -0,0 +1,3 @@
+/datum/chemical_reaction
+ /// Will this reaction be disabled by the ERP config being turned off?
+ var/erp_reaction = FALSE
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/breast_milk.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/breast_milk.dm
new file mode 100644
index 00000000000..b104b6c879b
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/breast_milk.dm
@@ -0,0 +1,9 @@
+/datum/reagent/consumable/breast_milk
+ name = "breast milk"
+ description = "This looks familiar... Wait, it's milk!"
+ taste_description = "warm and creamy"
+ color = "#ffffffff"
+ glass_icon_state = "glass_white"
+ glass_name = "glass of breast milk"
+ glass_desc = "almost like normal milk."
+ reagent_state = LIQUID
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/camphor.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/camphor.dm
new file mode 100644
index 00000000000..a93679181b0
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/camphor.dm
@@ -0,0 +1,94 @@
+// Camphor. Used to reduce libido.
+/// Able to reset one's genital size back to normal upon OD, or alternatively, if the user sets the size and then disables the organ, that size.
+/datum/reagent/drug/aphrodisiac/camphor
+ name = "Camphor"
+ description = "Naturally found in some species of evergreen trees, camphor is a waxy substance. When ingested by most animals it acts as an anaphrodisiac, \
+ reducing libido and calming them. Non-habit forming and non-addictive. This is not an aphrodisiac."
+ taste_description = "dull bitterness"
+ taste_mult = 2
+ color = "#D9D9D9"
+ reagent_state = SOLID
+ overdose_threshold = 25 // OD will reset sizes of genitals back to normal.
+ life_pref_datum = /datum/preference/toggle/erp/aphro
+ arousal_adjust_amount = -12
+ pleasure_adjust_amount = -3
+ overdose_pref_datum = /datum/preference/toggle/erp // This will work without having aphrodisiacs enabled.
+
+/datum/reagent/drug/aphrodisiac/camphor/life_effects(mob/living/carbon/human/exposed_mob)
+ var/old_arousal = exposed_mob.arousal
+ exposed_mob.adjust_arousal(arousal_adjust_amount)
+ exposed_mob.adjust_pleasure(pleasure_adjust_amount)
+ if(exposed_mob.arousal <= 0 && old_arousal > 0)
+ to_chat(exposed_mob, span_notice("You no longer feel aroused."))
+
+/datum/reagent/drug/aphrodisiac/camphor/overdose_effects(mob/living/carbon/human/exposed_mob)
+ var/modified_genitals = FALSE
+ // Grows and shrinks organs depending on prefs, returning to normal. Offers a way of resetting succubus milk / incubus draft sizes, but not organs.
+
+ // Resets gender if prefs enabled.
+ if(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/gender_change) && exposed_mob.gender != exposed_mob.client?.prefs?.read_preference(/datum/preference/choiced/gender))
+ exposed_mob.set_gender(exposed_mob.client?.prefs?.read_preference(/datum/preference/choiced/gender))
+
+ if(exposed_mob.getorganslot(ORGAN_SLOT_BREASTS))
+ var/obj/item/organ/external/genital/breasts/mob_breasts = exposed_mob.getorganslot(ORGAN_SLOT_BREASTS)
+ var/original_breast_size = GLOB.breast_size_to_number[exposed_mob.client?.prefs.read_preference(/datum/preference/choiced/breasts_size)]
+ if(original_breast_size)
+ if(mob_breasts?.genital_size > original_breast_size)
+ mob_breasts.genital_size -= breast_size_reduction_step
+ mob_breasts.update_sprite_suffix()
+ modified_genitals = TRUE
+ if(mob_breasts?.genital_size < original_breast_size)
+ mob_breasts.genital_size += breast_size_increase_step
+ mob_breasts.update_sprite_suffix()
+ modified_genitals = TRUE
+
+ if(exposed_mob.getorganslot(ORGAN_SLOT_PENIS))
+ var/obj/item/organ/external/genital/penis/mob_penis = exposed_mob.getorganslot(ORGAN_SLOT_PENIS)
+ if(exposed_mob.client?.prefs?.read_preference(/datum/preference/numeric/penis_length))
+ var/original_penis_length = exposed_mob.client?.prefs.read_preference(/datum/preference/numeric/penis_length)
+ var/original_penis_girth = exposed_mob.client?.prefs.read_preference(/datum/preference/numeric/penis_girth)
+ // Run to go through girth first.
+ if(mob_penis?.girth > original_penis_girth)
+ mob_penis.girth -= penis_girth_reduction_step
+ mob_penis.update_sprite_suffix()
+ modified_genitals = TRUE
+ if(mob_penis?.girth < original_penis_girth)
+ mob_penis.girth += penis_girth_increase_step
+ mob_penis.update_sprite_suffix()
+ modified_genitals = TRUE
+ if(mob_penis?.genital_size > original_penis_length)
+ mob_penis.genital_size -= penis_size_reduction_step
+ mob_penis.update_sprite_suffix()
+ modified_genitals = TRUE
+ if(mob_penis?.genital_size < original_penis_length)
+ mob_penis.genital_size += penis_length_increase_step
+ mob_penis.update_sprite_suffix()
+ modified_genitals = TRUE
+
+ if(exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES))
+ var/obj/item/organ/external/genital/testicles/mob_testicles = exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES)
+ if(exposed_mob.client?.prefs?.read_preference(/datum/preference/numeric/balls_size))
+ var/original_ball_size = exposed_mob.client?.prefs.read_preference(/datum/preference/numeric/balls_size)
+ if(mob_testicles?.genital_size > original_ball_size)
+ mob_testicles.genital_size -= 1
+ mob_testicles.update_sprite_suffix()
+ modified_genitals = TRUE
+ if(mob_testicles?.genital_size < original_ball_size)
+ mob_testicles.genital_size += 1
+ mob_testicles.update_sprite_suffix()
+ modified_genitals = TRUE
+
+ if(modified_genitals)
+ exposed_mob.update_body()
+
+// Notify the user that they're overdosing. Doesn't affect their mood.
+/datum/reagent/drug/aphrodisiac/camphor/overdose_process(mob/living/carbon/human/exposed_mob)
+ to_chat(exposed_mob, span_userdanger("You feel like you took too much [name]!"))
+ exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/minor_overdose, name)
+
+/datum/chemical_reaction/camphor
+ results = list(/datum/reagent/drug/aphrodisiac/camphor = 6)
+ required_reagents = list(/datum/reagent/carbon = 2, /datum/reagent/hydrogen = 2, /datum/reagent/oxygen = 2, /datum/reagent/sulfur = 1)
+ required_temp = 400
+ mix_message = "The mixture boils off a yellow, smelly vapor..."
+ erp_reaction = TRUE
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/crocin.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/crocin.dm
new file mode 100644
index 00000000000..09499beead6
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/crocin.dm
@@ -0,0 +1,53 @@
+// Crocin. Basic aphrodisiac with no consequences
+/datum/reagent/drug/aphrodisiac/crocin
+ name = "crocin"
+ description = "Naturally found in the crocus and gardenia flowers, this drug acts as a natural and safe aphrodisiac."
+ taste_description = "strawberries"
+ color = "#FFADFF"
+ life_pref_datum = /datum/preference/toggle/erp/aphro
+ arousal_adjust_amount = 1
+ pleasure_adjust_amount = 0
+ pain_adjust_amount = 0
+
+ /// Probability of the chem triggering an emote, as a %, run on mob life
+ var/emote_probability = 3
+ /// Probability of the chem triggering a to_chat, as a %, run on mob life
+ var/thought_probability = 2
+
+ /// A list of possible emotes the chem is able to trigger
+ var/list/possible_aroused_emotes = list("moan", "blush")
+
+ /// A list of possible to_chat messages the chem is able to trigger
+ var/list/possible_aroused_thoughts = list(
+ "You feel frisky.",
+ "You're having trouble suppressing your urges.",
+ "You feel in the mood.",
+ )
+
+
+/datum/reagent/drug/aphrodisiac/crocin/life_effects(mob/living/carbon/human/exposed_mob)
+ if(prob(emote_probability))
+ exposed_mob.emote(pick(possible_aroused_emotes))
+ if(prob(thought_probability))
+ var/displayed_thought = pick(possible_aroused_thoughts)
+ to_chat(exposed_mob, span_notice("[displayed_thought]"))
+
+ exposed_mob.adjust_arousal(arousal_adjust_amount)
+ exposed_mob.adjust_pleasure(pleasure_adjust_amount)
+ exposed_mob.adjust_pain(pain_adjust_amount)
+
+ var/modified_genitals = FALSE
+ for(var/obj/item/organ/external/genital/mob_genitals in exposed_mob.external_organs)
+ if(!mob_genitals.aroused == AROUSAL_CANT)
+ mob_genitals.aroused = AROUSAL_FULL
+ mob_genitals.update_sprite_suffix()
+ modified_genitals = TRUE
+ if (modified_genitals)
+ exposed_mob.update_body()
+
+/datum/chemical_reaction/crocin
+ results = list(/datum/reagent/drug/aphrodisiac/crocin = 6)
+ required_reagents = list(/datum/reagent/carbon = 2, /datum/reagent/hydrogen = 2, /datum/reagent/oxygen = 2, /datum/reagent/water = 1)
+ required_temp = 400
+ mix_message = "The mixture boils off a pink vapor..."
+ erp_reaction = TRUE
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/cum.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/cum.dm
new file mode 100644
index 00000000000..77214a0ebc5
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/cum.dm
@@ -0,0 +1,25 @@
+/datum/reagent/consumable/femcum
+ name = "femcum"
+ description = "Uhh... Someone had fun."
+ taste_description = "astringent and sweetish"
+ color = "#ffffffb0"
+ glass_name = "glass of girlcum"
+ glass_desc = "A strange white liquid... Ew!"
+ reagent_state = LIQUID
+ shot_glass_icon_state = "shotglasswhite"
+
+/datum/reagent/consumable/cum
+ name = "cum"
+ description = "A fluid secreted by the sexual organs of many species."
+ taste_description = "musky and salty"
+ color = "#ffffffff"
+ glass_name = "glass of cum"
+ glass_desc = "O-oh, my...~"
+ reagent_state = LIQUID
+ shot_glass_icon_state = "shotglasswhite"
+
+/datum/chemical_reaction/cum
+ results = list(/datum/reagent/consumable/cum = 5)
+ required_reagents = list(/datum/reagent/blood = 2, /datum/reagent/consumable/milk = 2, /datum/reagent/consumable/salt = 1) // Iiiinteresting...
+ mix_message = "The mixture turns into a gooey, musky white liquid..."
+ erp_reaction = TRUE
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/dopamine.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/dopamine.dm
new file mode 100644
index 00000000000..7712ee530b7
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/dopamine.dm
@@ -0,0 +1,49 @@
+// Dopamine. Generates in character after orgasm.
+/datum/reagent/drug/aphrodisiac/dopamine
+ name = "dopamine...?"
+ description = "Pure happiness"
+ taste_description = "an indescribable, slightly sour taste. Something in it relaxes you, filling you with pleasure."
+ color = "#97ffee"
+ glass_name = "dopamine"
+ glass_desc = "Delicious flavored reagent. You feel happy even looking at it."
+ reagent_state = LIQUID
+ overdose_threshold = 10
+ life_pref_datum = /datum/preference/toggle/erp/aphro
+ overdose_pref_datum = /datum/preference/toggle/erp/aphro
+ arousal_adjust_amount = 0.5
+ pleasure_adjust_amount = 0.3
+ pain_adjust_amount = -0.5
+
+ /// How druggy the chem will make the mob
+ var/drugginess_amount = 5 SECONDS
+ /// How likely the drug is to make the mob druggy per life process
+ var/drugginess_chance = 7
+
+/datum/reagent/drug/aphrodisiac/dopamine/on_mob_add(mob/living/carbon/human/exposed_mob)
+ if(!(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/aphro)))
+ return ..()
+ exposed_mob.add_mood_event("[type]_start", /datum/mood_event/orgasm, name)
+ return ..()
+
+/datum/reagent/drug/aphrodisiac/dopamine/life_effects(mob/living/carbon/human/exposed_mob)
+ exposed_mob.set_drugginess(drugginess_amount)
+ if(prob(drugginess_chance))
+ exposed_mob.emote(pick("twitch", "drool", "moan", "giggle", "shaking"))
+
+/datum/reagent/drug/aphrodisiac/dopamine/overdose_start(mob/living/carbon/human/exposed_mob)
+ . = ..()
+ to_chat(exposed_mob, span_purple("You feel so happy!"))
+ exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/overgasm, name)
+
+/datum/reagent/drug/aphrodisiac/dopamine/overdose_effects(mob/living/carbon/human/exposed_mob)
+ if(!(exposed_mob.hallucination < volume && prob(20)))
+ return ..()
+ exposed_mob.adjust_arousal(arousal_adjust_amount)
+ exposed_mob.adjust_pleasure(pleasure_adjust_amount)
+ exposed_mob.adjust_pain(pain_adjust_amount)
+ if(prob(2))
+ exposed_mob.emote(pick("moan", "twitch_s"))
+
+// This chem shouldn't affect user's mood negatively.
+/datum/reagent/drug/aphrodisiac/dopamine/overdose_process(mob/living/carbon/human/exposed_mob)
+ return
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/hexacrocin.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/hexacrocin.dm
new file mode 100644
index 00000000000..5323a8ddc86
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/hexacrocin.dm
@@ -0,0 +1,55 @@
+#define OD_CHANCE_NO_TRAUMA 95
+
+// Hexacrocin. Advanced aphrodisiac that can cause brain traumas.
+/datum/reagent/drug/aphrodisiac/crocin/hexacrocin
+ name = "hexacrocin"
+ description = "Chemically condensed form of basic crocin. This aphrodisiac is extremely powerful and addictive for most animals.\
+ Addiction withdrawals can cause brain damage and shortness of breath. Overdose can lead to brain traumas."
+ taste_description = "liquid desire"
+ color = "#FF2BFF"
+ overdose_threshold = 25
+ overdose_pref_datum = /datum/preference/toggle/erp/bimbofication
+
+ emote_probability = 5
+ thought_probability = 5
+ arousal_adjust_amount = 2
+ pleasure_adjust_amount = 1.5
+ pain_adjust_amount = 0.2
+ possible_aroused_thoughts = list(
+ "You feel a bit hot.",
+ "You feel strong sexual urges.",
+ "You feel in the mood.",
+ "You're ready to go down on someone.",
+ )
+
+ /// A list of possible to_chat messages the chem is able to trigger after enough cycles in the mobs system
+ var/list/extreme_aroused_thoughts = list(
+ "You need to fuck someone!",
+ "You're bursting with sexual tension!",
+ "You can't get sex off your mind!",
+ )
+ /// How many cycles the chem has to be in the mob's system before triggering extreme effects
+ var/extreme_thought_threshold = 25
+
+/datum/reagent/drug/aphrodisiac/crocin/hexacrocin/life_effects(mob/living/carbon/human/exposed_mob)
+ . = ..()
+ if(prob(thought_probability) && current_cycle >= extreme_thought_threshold)
+ var/displayed_extreme_thought = pick(extreme_aroused_thoughts)
+ to_chat(exposed_mob, span_purple("[displayed_extreme_thought]"))
+
+/datum/reagent/drug/aphrodisiac/crocin/hexacrocin/overdose_effects(mob/living/carbon/human/exposed_mob)
+ if(prob(OD_CHANCE_NO_TRAUMA) || HAS_TRAIT(exposed_mob, TRAIT_BIMBO))
+ return ..()
+
+ to_chat(exposed_mob, span_purple("Your libido is going haywire! It feels like speaking is much harder..."))
+ exposed_mob.gain_trauma(/datum/brain_trauma/very_special/bimbo, TRAUMA_RESILIENCE_BASIC)
+ ADD_TRAIT(exposed_mob, TRAIT_BIMBO, LEWDCHEM_TRAIT)
+
+/datum/chemical_reaction/hexacrocin
+ results = list(/datum/reagent/drug/aphrodisiac/crocin/hexacrocin = 1)
+ required_reagents = list(/datum/reagent/drug/aphrodisiac/crocin = 6, /datum/reagent/phenol = 1)
+ required_temp = 600
+ mix_message = "The mixture rapidly condenses and darkens in color..."
+ erp_reaction = TRUE
+
+#undef OD_CHANCE_NO_TRAUMA
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/incubus_draft.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/incubus_draft.dm
new file mode 100644
index 00000000000..98998af059b
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/incubus_draft.dm
@@ -0,0 +1,200 @@
+/*
+* PENIS ENLARGEMENT
+* Normal function increases the player's penis size.
+* If the player's penis is near or at the maximum size and they're wearing clothing, it presses against the player's clothes and causes brute damage.
+* If gender change preference is enabled: Overdosing makes you male if female, makes the player grow a cock, and shrinks the player's breasts to a minimum size.
+* If the gender change preference is not enabled: Overdosing will just make you grow a cock if you don't have one.
+*/
+
+/datum/reagent/drug/aphrodisiac/incubus_draft
+ name = "incubus draft"
+ description = "A volatile collodial mixture derived from various masculine solutions that encourages a larger gentleman's package via a potent testosterone mix."
+ color = "#888888"
+ taste_description = "chinese dragon powder"
+ overdose_threshold = 20 // ODing makes you male and shrinks female genitals if gender change prefs are enabled. Otherwise, grows a cock.
+ metabolization_rate = 0.25
+ life_pref_datum = /datum/preference/toggle/erp/penis_enlargement
+ overdose_pref_datum = /datum/preference/toggle/erp
+
+ // Not important at all, really, but I don't want folk complaining about a removed feature.
+ var/static/list/species_to_penis = list(
+ SPECIES_HUMAN = list(
+ "sheath" = SHEATH_NONE,
+ "mutant_index" = "Human",
+ "balls" = "Pair"
+ ),
+ SPECIES_LIZARD = list(
+ "sheath" = SHEATH_SLIT,
+ "color" = "#FFB6C1",
+ "mutant_index" = "Flared",
+ "balls" = "Internal"
+ ),
+ SPECIES_LIZARD_ASH = list(
+ "sheath" = SHEATH_SLIT,
+ "color" = "#FFB6C1",
+ "mutant_index" = "Flared",
+ "balls" = "Internal"
+ ),
+ )
+
+ /// Words for the cock when huge.
+ var/static/list/words_for_bigger_cock = list(
+ "huge",
+ "massive",
+ "gigantic",
+ "rather lengthy",
+ "colossal",
+ "hefty",
+ )
+ /// Synonyms for cock.
+ var/static/list/cock_text_list = list(
+ "cock",
+ "penis",
+ "dick",
+ "member",
+ "richard",
+ "johnston",
+ "johnson",
+ )
+ /// Synonyms for bigger cock.
+ var/static/list/bigger_cock_text_list = list(
+ "rod",
+ "shaft",
+ "cock",
+ "penis",
+ "dick",
+ "member",
+ "richard",
+ "johnston",
+ "johnson",
+ )
+ /// Wording chosen to extend the cock, shown only to the mob.
+ var/static/list/cock_action_text_list = list(
+ "extends to ",
+ "grows out to ",
+ "begins to enlarge, growing to ",
+ "suddenly expands to ",
+ "lengthens out to ",
+ )
+ /// Wording chosen to be seen by other mobs, while mob is unclothed.
+ var/static/list/public_cock_action_text_list = list(
+ "expands by an inch or so.",
+ "appears to grow a bit longer.",
+ "seems a bit bigger than it was before.",
+ "suddenly lengthens about an inch or two.",
+ )
+
+/datum/reagent/drug/aphrodisiac/incubus_draft/life_effects(mob/living/carbon/human/exposed_mob)
+ var/obj/item/organ/external/genital/penis/mob_penis = exposed_mob.getorganslot(ORGAN_SLOT_PENIS)
+ enlargement_amount += enlarger_increase_step
+ // Add yet another check because I hate errors!!
+ if(!mob_penis)
+ return
+ if(enlargement_amount >= enlargement_threshold)
+ if(mob_penis?.genital_size >= penis_max_length)
+ return ..()
+ mob_penis.genital_size += penis_length_increase_step
+ // Improvision to girth to not make it random chance.
+ if(mob_penis?.girth < penis_max_girth) // Because any higher is ridiculous. However, should still allow for regular penis growth.
+ mob_penis.girth = round(mob_penis.girth + (mob_penis.genital_size/mob_penis.girth))
+ mob_penis.update_sprite_suffix()
+ exposed_mob.update_body()
+ enlargement_amount = 0
+
+ if(mob_penis.visibility_preference == GENITAL_ALWAYS_SHOW || exposed_mob.is_bottomless())
+ if(mob_penis?.genital_size >= (penis_max_length - 2))
+ if(exposed_mob.dna.features["penis_sheath"] == SHEATH_SLIT)
+ if(mob_penis.aroused != AROUSAL_FULL)
+ to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger_cock)] [pick(bigger_cock_text_list)] [pick(cock_action_text_list)]about [mob_penis.genital_size] inches long, and [mob_penis.girth] inches in circumference."))
+ return
+ exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(words_for_bigger_cock)] [pick(bigger_cock_text_list)] [pick(public_cock_action_text_list)]"))
+ to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger_cock)] [pick(bigger_cock_text_list)] [pick(cock_action_text_list)]about [mob_penis.genital_size] inches long, and [mob_penis.girth] inches in circumference."))
+ return
+ else
+ if(exposed_mob.dna.features["penis_sheath"] == SHEATH_SLIT)
+ if(mob_penis.aroused != AROUSAL_FULL)
+ to_chat(exposed_mob, span_purple("Your [pick(cock_text_list)] [pick(cock_action_text_list)]about [mob_penis.genital_size] inches long, and [mob_penis.girth] inches in circumference."))
+ return
+ exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(cock_text_list)] [pick(public_cock_action_text_list)]"))
+ to_chat(exposed_mob, span_purple("Your [pick(cock_text_list)] [pick(cock_action_text_list)]about [mob_penis.genital_size] inches long, and [mob_penis.girth] inches in circumference."))
+ return
+ else
+ if(mob_penis?.genital_size >= (penis_max_length - 2))
+ to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger_cock)] [pick(bigger_cock_text_list)] [pick(cock_action_text_list)]about [mob_penis.genital_size] inches long, and [mob_penis.girth] inches in circumference."))
+ return
+ else
+ to_chat(exposed_mob, span_purple("Your [pick(cock_text_list)] [pick(cock_action_text_list)]about [mob_penis.genital_size] inches long, and [mob_penis.girth] inches in circumference."))
+ return
+
+ if((mob_penis?.genital_size >= (penis_max_length - 2)) && (exposed_mob.w_uniform || exposed_mob.wear_suit))
+ var/target_bodypart = exposed_mob.get_bodypart(BODY_ZONE_PRECISE_GROIN)
+ if(prob(damage_chance))
+ to_chat(exposed_mob, span_danger("You feel a tightness in your pants!"))
+ exposed_mob.apply_damage(1, BRUTE, target_bodypart)
+
+ return ..()
+
+/datum/reagent/drug/aphrodisiac/incubus_draft/overdose_effects(mob/living/carbon/human/exposed_mob)
+ if(exposed_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/penis_enlargement))
+ if(!exposed_mob.getorganslot(ORGAN_SLOT_PENIS) && exposed_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/new_genitalia_growth))
+ var/list/data = species_to_penis[exposed_mob.dna.species.id]
+ if(!data)
+ data = species_to_penis[SPECIES_HUMAN]
+
+ exposed_mob.dna.features["penis_sheath"] = data["sheath"]
+ exposed_mob.dna.mutant_bodyparts[ORGAN_SLOT_PENIS][MUTANT_INDEX_NAME] = data["index_name"]
+ exposed_mob.dna.mutant_bodyparts[ORGAN_SLOT_TESTICLES][MUTANT_INDEX_NAME] = data["balls"]
+ var/colour = data["colour"]
+ if(colour)
+ exposed_mob.dna.mutant_bodyparts[ORGAN_SLOT_PENIS][MUTANT_INDEX_COLOR_LIST] = list(colour)
+
+ if(!exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES))
+ var/obj/item/organ/balls_path = /obj/item/organ/external/genital/testicles
+ balls_path = new /obj/item/organ/external/genital/testicles
+ balls_path.build_from_dna(exposed_mob.dna, ORGAN_SLOT_TESTICLES)
+ balls_path.Insert(exposed_mob, 0, FALSE)
+ var/obj/item/organ/external/genital/new_balls = exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES)
+ new_balls.genital_size = 1
+ new_balls.update_sprite_suffix()
+
+ var/obj/item/organ/external/genital/penis/new_penis = new /obj/item/organ/external/genital/penis
+ new_penis.build_from_dna(exposed_mob.dna, ORGAN_SLOT_PENIS)
+ new_penis.Insert(exposed_mob, 0, FALSE)
+ new_penis.genital_size = 4
+ new_penis.girth = 3
+ new_penis.update_sprite_suffix()
+ exposed_mob.update_body()
+ to_chat(exposed_mob, span_purple("Your crotch feels warm as something suddenly sprouts between your legs."))
+ // Makes the balls bigger if they're small.
+ var/obj/item/organ/external/genital/testicles/mob_testicles = exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES)
+ if(mob_testicles)
+ if(mob_testicles.genital_size > 2)
+ return
+ mob_testicles.genital_size = 2
+ // Separates gender change stuff from cock growth.
+ var/obj/item/organ/external/genital/breasts/mob_breasts = exposed_mob.getorganslot(ORGAN_SLOT_BREASTS)
+ if(exposed_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/gender_change))
+ if(exposed_mob.gender == FEMALE)
+ exposed_mob.set_gender(MALE)
+ exposed_mob.physique = exposed_mob.gender
+ exposed_mob.update_body()
+ exposed_mob.update_mutations_overlay()
+ if(!mob_breasts)
+ return
+ if(exposed_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/breast_enlargement)) // To do breast shrinkage, check if prefs allow for this.
+ if(mob_breasts.genital_size > breast_minimum_size)
+ mob_breasts.genital_size -= breast_size_reduction_step
+ mob_breasts.update_sprite_suffix()
+ exposed_mob.update_body()
+ return
+
+// Notify the user that they're overdosing. Doesn't affect their mood.
+/datum/reagent/drug/aphrodisiac/incubus_draft/overdose_process(mob/living/carbon/human/exposed_mob)
+ to_chat(exposed_mob, span_userdanger("You feel like you took too much [name]!"))
+ exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/minor_overdose, name)
+
+/datum/chemical_reaction/incubus_draft
+ results = list(/datum/reagent/drug/aphrodisiac/incubus_draft = 8)
+ required_reagents = list(/datum/reagent/blood = 5, /datum/reagent/medicine/c2/synthflesh = 2, /datum/reagent/carbon = 2, /datum/reagent/drug/aphrodisiac/crocin = 2, /datum/reagent/medicine/salglu_solution = 1)
+ mix_message = "the reaction gives off a mist of milk."
+ erp_reaction = TRUE
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/pentacamphor.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/pentacamphor.dm
new file mode 100644
index 00000000000..2c2e6083c8c
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/pentacamphor.dm
@@ -0,0 +1,37 @@
+// Pentacamphor. Used to purge crocin and hexacrocin. Can permanently disable arousal or cure bimbofication on overdose.
+/datum/reagent/drug/aphrodisiac/camphor/pentacamphor
+ name = "Pentacamphor"
+ description = "Chemically condensed camphor. Causes an extreme reduction in libido and a permanent one if overdosed. Non-addictive."
+ taste_description = "tranquil celibacy"
+ color = "#D9D9D9"//rgb(255, 255, 255)
+ reagent_state = SOLID
+ overdose_threshold = 20
+ arousal_adjust_amount = -18
+ overdose_pref_datum = /datum/preference/toggle/erp/aphro
+
+ /// How much of the given reagent to remove per operation
+ var/reagent_reduction_amount = 20
+
+/datum/reagent/drug/aphrodisiac/camphor/pentacamphor/life_effects(mob/living/carbon/human/exposed_mob)
+ . = ..()
+ if(exposed_mob.reagents.has_reagent(/datum/reagent/drug/aphrodisiac/crocin))
+ exposed_mob.reagents.remove_reagent(/datum/reagent/drug/aphrodisiac/crocin, reagent_reduction_amount)
+ if(exposed_mob.reagents.has_reagent(/datum/reagent/drug/aphrodisiac/crocin/hexacrocin))
+ exposed_mob.reagents.remove_reagent(/datum/reagent/drug/aphrodisiac/crocin/hexacrocin, reagent_reduction_amount)
+
+/datum/reagent/drug/aphrodisiac/camphor/pentacamphor/overdose_start(mob/living/carbon/human/exposed_mob)
+ if(HAS_TRAIT(exposed_mob, TRAIT_BIMBO))
+ exposed_mob.cure_trauma_type(/datum/brain_trauma/very_special/bimbo, TRAUMA_RESILIENCE_ABSOLUTE)
+ to_chat(exposed_mob, span_notice("Your mind is free. Your thoughts are pure and innocent once more."))
+ REMOVE_TRAIT(exposed_mob, TRAIT_BIMBO, LEWDCHEM_TRAIT)
+ return
+ if(!HAS_TRAIT(exposed_mob, TRAIT_NEVERBONER))
+ to_chat(exposed_mob, span_notice("You feel like you'll never feel aroused again..."))
+ ADD_TRAIT(exposed_mob, TRAIT_NEVERBONER, LEWDCHEM_TRAIT)
+
+/datum/chemical_reaction/pentacamphor
+ results = list(/datum/reagent/drug/aphrodisiac/camphor/pentacamphor = 1)
+ required_reagents = list(/datum/reagent/drug/aphrodisiac/camphor = 5, /datum/reagent/acetone = 1)
+ required_temp = 500
+ mix_message = "The mixture thickens and heats up slighty..."
+ erp_reaction = TRUE
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/succubus_milk.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/succubus_milk.dm
new file mode 100644
index 00000000000..3efcd2fea4a
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_chemistry/reagents/succubus_milk.dm
@@ -0,0 +1,196 @@
+/*
+* BREAST ENLARGEMENT
+* Normal function increases the player's breast size.
+* If the player's breasts are near or at the maximum size and they're wearing clothing, they press against the player's clothes and causes brute and oxygen damage.
+* If gender change preference is enabled: Overdosing makes you female if male, makes the player grow breasts, sets the player's testicles to the minimum size, and shrinks the player's penis to a minimum size.
+* If the gender change preference is not enabled: Overdosing will just make you grow breasts if you don't have any.
+* Credit to Citadel for the original code before modification
+*/
+
+/datum/reagent/drug/aphrodisiac/succubus_milk
+ name = "succubus milk"
+ description = "A volatile collodial mixture derived from milk that encourages mammary production via a potent estrogen mix."
+ color = "#E60584"
+ taste_description = "a milky ice cream like flavour"
+ overdose_threshold = 20
+ metabolization_rate = 0.25
+ life_pref_datum = /datum/preference/toggle/erp/breast_enlargement
+ overdose_pref_datum = /datum/preference/toggle/erp
+
+ /// Words for the breasts when huge.
+ var/static/list/words_for_bigger = list(
+ "huge",
+ "massive",
+ "squishy",
+ "gigantic",
+ "rather large",
+ "jiggly",
+ "hefty",
+ )
+ /// Synonyms for breasts.
+ var/static/list/boob_text_list = list(
+ "boobs",
+ "tits",
+ "breasts",
+ )
+ /// Synonyms for the chest.
+ var/static/list/covered_boobs_list = list(
+ "bust",
+ "chest",
+ "bosom",
+ )
+ /// Synonyms for bigger breasts.
+ var/static/list/bigger_boob_text_list = list(
+ "jigglies",
+ "melons",
+ "jugs",
+ "boobies",
+ "milkers",
+ "boobs",
+ "tits",
+ "breasts",
+ )
+ /// Wording chosen to expand the breasts,shown only to the mob.
+ var/static/list/action_text_list = list(
+ "expand outward to ",
+ "grow out to ",
+ "begin to enlarge, growing to ",
+ "suddenly expand to ",
+ "swell out to ",
+ )
+ /// Wording chosen to be seen by other mobs, regardless of whether mob is clothed/unclothed.
+ var/static/list/public_bigger_action_text_list = list(
+ "expand and jiggle outward.",
+ "grow a bit larger, bouncing about.",
+ "seem a bit bigger than they were before.",
+ "bounce and jiggle as they suddenly expand.",
+ )
+ /// Wording chosen to be seen by other mobs, while mob is unclothed.
+ var/static/list/public_action_text_list = list(
+ "expand outward.",
+ "seem to grow a bit larger.",
+ "appear a bit bigger than they were before.",
+ "bounce and jiggle as they suddenly expand.",
+ )
+ /// Wording chosen to be seen by other mobs, while mob is clothed.
+ var/static/list/notice_boobs = list(
+ "seems to be a bit tighter.",
+ "appears to be a bit bigger.",
+ "seems to swell outward a bit.",
+ )
+
+/datum/reagent/drug/aphrodisiac/succubus_milk/life_effects(mob/living/carbon/human/exposed_mob) //Increases breast size
+ var/obj/item/organ/external/genital/breasts/mob_breasts = exposed_mob.getorganslot(ORGAN_SLOT_BREASTS)
+ enlargement_amount += enlarger_increase_step
+ // Adds a check for breasts in the first place.
+ if(!mob_breasts)
+ return
+ if(enlargement_amount >= enlargement_threshold)
+ if(mob_breasts?.genital_size >= max_breast_size)
+ return
+ mob_breasts.genital_size += breast_size_increase_step
+ mob_breasts.update_sprite_suffix()
+ exposed_mob.update_body()
+ enlargement_amount = 0
+
+ // Checks for cup size.
+ var/translation = mob_breasts.breasts_size_to_cup(mob_breasts.genital_size)
+
+ if(mob_breasts.visibility_preference == GENITAL_ALWAYS_SHOW || exposed_mob.is_topless())
+ switch(translation)
+ if(BREAST_SIZE_FLATCHESTED)
+ return
+ if(BREAST_SIZE_BEYOND_MEASUREMENT)
+ exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(words_for_bigger)] [pick(bigger_boob_text_list)] [pick(public_bigger_action_text_list)]"))
+ to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger)] [pick(bigger_boob_text_list)] [pick(action_text_list)]about [mob_breasts.genital_size] inches in diameter."))
+ return
+ else
+ if(mob_breasts?.genital_size >= (max_breast_size - 2))
+ exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(words_for_bigger)] [pick(bigger_boob_text_list)] [pick(public_bigger_action_text_list)]"))
+ to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger)] [pick(bigger_boob_text_list)] [pick(action_text_list)]about [translation]-cups."))
+ return
+ else
+ exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(boob_text_list)] [pick(public_action_text_list)]"))
+ to_chat(exposed_mob, span_purple("Your [pick(boob_text_list)] [pick(action_text_list)]about [translation]-cups."))
+ return
+ else
+ switch(translation)
+ if(BREAST_SIZE_FLATCHESTED)
+ return
+ if(BREAST_SIZE_BEYOND_MEASUREMENT)
+ exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(boob_text_list)] [pick(public_bigger_action_text_list)]"))
+ to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger)] [pick(bigger_boob_text_list)] [pick(action_text_list)]about [mob_breasts.genital_size] inches in diameter."))
+ return
+ else
+ if(mob_breasts?.genital_size >= (max_breast_size - 2))
+ exposed_mob.visible_message(span_notice("[exposed_mob]'s [pick(boob_text_list)] [pick(public_bigger_action_text_list)]"))
+ to_chat(exposed_mob, span_purple("Your [pick(words_for_bigger)] [pick(bigger_boob_text_list)] [pick(action_text_list)]about [translation]-cups."))
+ return
+ else
+ exposed_mob.visible_message(span_notice("The area around [exposed_mob]'s [pick(covered_boobs_list)] [pick(notice_boobs)]"))
+ to_chat(exposed_mob, span_purple("Your [pick(boob_text_list)] [pick(action_text_list)]about [translation]-cups."))
+ return
+
+ if((mob_breasts?.genital_size >= (max_breast_size - 2)) && (exposed_mob.w_uniform || exposed_mob.wear_suit))
+ if(prob(damage_chance))
+ to_chat(exposed_mob, span_danger("Your breasts begin to strain against your clothes!"))
+ exposed_mob.adjustOxyLoss(5)
+ exposed_mob.apply_damage(1, BRUTE, exposed_mob.get_bodypart(BODY_ZONE_CHEST))
+
+// Turns you into a female if character is male. Also adds breasts.
+/datum/reagent/drug/aphrodisiac/succubus_milk/overdose_effects(mob/living/carbon/human/exposed_mob)
+ if(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/breast_enlargement))
+ if(!exposed_mob.getorganslot(ORGAN_SLOT_BREASTS) && exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/new_genitalia_growth))
+ var/obj/item/organ/path = /obj/item/organ/external/genital/breasts
+ exposed_mob.dna.mutant_bodyparts[ORGAN_SLOT_BREASTS][MUTANT_INDEX_NAME] = "Pair"
+ path = new /obj/item/organ/external/genital/breasts
+ path.build_from_dna(exposed_mob.dna, ORGAN_SLOT_BREASTS)
+ path.Insert(exposed_mob, FALSE, FALSE)
+ var/obj/item/organ/external/genital/new_breasts = exposed_mob.getorganslot(ORGAN_SLOT_BREASTS)
+ new_breasts.genital_size = 2
+ new_breasts.update_sprite_suffix()
+ exposed_mob.update_body()
+ enlargement_amount = 0
+ if(new_breasts.visibility_preference == GENITAL_ALWAYS_SHOW || exposed_mob.is_topless())
+ exposed_mob.visible_message(span_notice("[exposed_mob]'s bust suddenly expands!"))
+ to_chat(exposed_mob, span_purple("Your chest feels warm, tingling with sensitivity as it expands outward."))
+ return
+ else
+ exposed_mob.visible_message(span_notice("The area around [exposed_mob]'s chest suddenly bounces a bit."))
+ to_chat(exposed_mob, span_purple("Your chest feels warm, tingling with sensitivity as it strains against your clothes."))
+ return
+ // Separates gender change stuff from breast growth.
+ if(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/gender_change))
+ var/obj/item/organ/external/genital/penis/mob_penis = exposed_mob.getorganslot(ORGAN_SLOT_PENIS)
+ var/obj/item/organ/external/genital/testicles/mob_testicles = exposed_mob.getorganslot(ORGAN_SLOT_TESTICLES)
+ if(exposed_mob.gender == MALE)
+ exposed_mob.set_gender(FEMALE)
+ exposed_mob.physique = exposed_mob.gender
+ exposed_mob.update_body()
+ exposed_mob.update_mutations_overlay()
+ if(!mob_penis)
+ return
+ if(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/penis_enlargement)) // To do cock shrinkage, check if prefs allow for this.
+ if(mob_penis.genital_size > penis_min_length)
+ mob_penis.genital_size -= penis_size_reduction_step
+ mob_penis.update_sprite_suffix()
+ exposed_mob.update_body()
+ if(mob_penis.girth > penis_minimum_girth)
+ mob_penis.girth -= penis_girth_reduction_step
+ mob_penis.update_sprite_suffix()
+ exposed_mob.update_body()
+ if(!mob_testicles || mob_testicles.genital_size <= 1)
+ return
+ mob_testicles.genital_size -= 1
+ return
+
+// Notify the user that they're overdosing. Doesn't affect their mood.
+/datum/reagent/drug/aphrodisiac/succubus_milk/overdose_process(mob/living/carbon/human/exposed_mob)
+ to_chat(exposed_mob, span_userdanger("You feel like you took too much [name]!"))
+ exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/minor_overdose, name)
+
+/datum/chemical_reaction/succubus_milk
+ results = list(/datum/reagent/drug/aphrodisiac/succubus_milk = 8)
+ required_reagents = list(/datum/reagent/medicine/salglu_solution = 1, /datum/reagent/consumable/milk = 1, /datum/reagent/medicine/c2/synthflesh = 2, /datum/reagent/silicon = 3, /datum/reagent/drug/aphrodisiac/crocin = 3)
+ mix_message = "the reaction gives off a mist of milk."
+ erp_reaction = TRUE
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/erp_belt.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/erp_belt.dm
index db2fc7eaacb..c9cf41361f6 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/erp_belt.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/erp_belt.dm
@@ -82,8 +82,8 @@
//chems
/obj/item/reagent_containers/pill/crocin,
/obj/item/reagent_containers/pill/camphor,
- /obj/item/reagent_containers/cup/bottle/breast_enlarger,
- /obj/item/reagent_containers/cup/bottle/penis_enlarger,
+ /obj/item/reagent_containers/cup/bottle/succubus_milk,
+ /obj/item/reagent_containers/cup/bottle/incubus_draft,
/obj/item/reagent_containers/pill/hexacrocin,
/obj/item/reagent_containers/pill/pentacamphor,
/obj/item/reagent_containers/cup/bottle/hexacrocin,
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/latex_catsuit.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/latex_catsuit.dm
index bf5ec6c3a75..f8690a49c9f 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/latex_catsuit.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/latex_catsuit.dm
@@ -92,6 +92,6 @@
/obj/item/clothing/under/misc/latex_catsuit/Initialize(mapload)
. = ..()
breasts_overlay = mutable_appearance('modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_clothing/lewd_uniform/lewd_uniform.dmi', "none", ABOVE_MOB_LAYER)
- breasts_overlay.icon_state = "breasts"
+ breasts_overlay.icon_state = ORGAN_SLOT_BREASTS
breasts_icon_overlay = mutable_appearance('modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_clothing/lewd_uniform.dmi', "none")
- breasts_icon_overlay.icon_state = "breasts"
+ breasts_icon_overlay.icon_state = ORGAN_SLOT_BREASTS
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shibari_worn_uniform.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shibari_worn_uniform.dm
index c117f081c5c..a4115f079d0 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shibari_worn_uniform.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shibari_worn_uniform.dm
@@ -87,11 +87,11 @@
src.dropped(hooman)
return PROCESS_KILL
if(tightness == SHIBARI_TIGHTNESS_LOW && hooman.arousal < 15)
- hooman.adjustArousal(0.6 * delta_time)
+ hooman.adjust_arousal(0.6 * delta_time)
if(tightness == SHIBARI_TIGHTNESS_MED && hooman.arousal < 25)
- hooman.adjustArousal(0.6 * delta_time)
+ hooman.adjust_arousal(0.6 * delta_time)
if(tightness == SHIBARI_TIGHTNESS_HIGH && hooman.arousal < 30)
- hooman.adjustArousal(0.6 * delta_time)
+ hooman.adjust_arousal(0.6 * delta_time)
//stuff to apply processing on equip and add mood event for perverts
/obj/item/clothing/under/shibari/equipped(mob/user, slot)
@@ -133,7 +133,7 @@
return PROCESS_KILL
var/mob/living/carbon/human/hooman = loc
if(tightness == SHIBARI_TIGHTNESS_HIGH && hooman.pain < 25)
- hooman.adjustPain(0.6 * delta_time)
+ hooman.adjust_pain(0.6 * delta_time)
/obj/item/clothing/under/shibari/groin
name = "crotch rope shibari"
@@ -161,11 +161,11 @@
return PROCESS_KILL
var/mob/living/carbon/human/hooman = loc
if(tightness == SHIBARI_TIGHTNESS_LOW && hooman.pleasure < 20)
- hooman.adjustPleasure(0.6 * delta_time)
+ hooman.adjust_pleasure(0.6 * delta_time)
if(tightness == SHIBARI_TIGHTNESS_MED && hooman.pleasure < 60)
- hooman.adjustPleasure(0.6 * delta_time)
+ hooman.adjust_pleasure(0.6 * delta_time)
if(tightness == SHIBARI_TIGHTNESS_HIGH)
- hooman.adjustPleasure(0.6 * delta_time)
+ hooman.adjust_pleasure(0.6 * delta_time)
/obj/item/clothing/under/shibari/full
name = "shibari fullbody ropes"
@@ -188,13 +188,13 @@
return PROCESS_KILL
var/mob/living/carbon/human/hooman = loc
if(tightness == SHIBARI_TIGHTNESS_LOW && hooman.pleasure< 20)
- hooman.adjustPleasure(0.6 * delta_time)
+ hooman.adjust_pleasure(0.6 * delta_time)
if(tightness == SHIBARI_TIGHTNESS_MED && hooman.pleasure < 60)
- hooman.adjustPleasure(0.6 * delta_time)
+ hooman.adjust_pleasure(0.6 * delta_time)
if(tightness == SHIBARI_TIGHTNESS_HIGH)
- hooman.adjustPleasure(0.6 * delta_time)
+ hooman.adjust_pleasure(0.6 * delta_time)
if(hooman.pain < 40)
- hooman.adjustPain(0.6 * delta_time)
+ hooman.adjust_pain(0.6 * delta_time)
#undef SHIBARI_TIGHTNESS_LOW
#undef SHIBARI_TIGHTNESS_MED
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shockcollar.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shockcollar.dm
index 804eb2a7318..34bd9975408 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shockcollar.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shockcollar.dm
@@ -49,7 +49,7 @@
created_sparks.start()
affected_mob.Paralyze(30)
- affected_mob.adjustPain(10)
+ affected_mob.adjust_pain(10)
affected_mob.adjust_stutter(30 SECONDS)
if(master)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/strapon.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/strapon.dm
index 60f4dfa91ac..d9d947e10cb 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/strapon.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/strapon.dm
@@ -140,7 +140,7 @@
return
else if(held == null)
- if(unheld.name =="strapon" && unheld.item_flags == ABSTRACT | HAND_ITEM)
+ if(istype(unheld, /obj/item/strapon_dildo) && unheld.item_flags == ABSTRACT | HAND_ITEM)
if(src == user.belt)
qdel(unheld)
//CODE FOR PUTTING STRAPON IN HANDS
@@ -201,8 +201,8 @@
if(vagina)
if(hit_mob.is_bottomless() || vagina.visibility_preference == GENITAL_ALWAYS_SHOW)
message = pick("delicately rubs [hit_mob]'s vagina with [src]", "uses [src] to fuck [hit_mob]'s vagina", "jams [hit_mob]'s pussy with [src]", "teases [hit_mob]'s pussy with [src]")
- hit_mob.adjustArousal(6)
- hit_mob.adjustPleasure(8)
+ hit_mob.adjust_arousal(6)
+ hit_mob.adjust_pleasure(8)
if(prob(40))
hit_mob.try_lewd_autoemote(pick("twitch_s", "moan"))
user.visible_message(span_purple("[user] [message]!"))
@@ -222,8 +222,8 @@
if(BODY_ZONE_HEAD, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_PRECISE_EYES) //Mouth only. Sorry, perverts. No eye/ear penetration for you today.
if(!hit_mob.is_mouth_covered())
message = pick("fucks [hit_mob]'s mouth with [src]", "chokes [hit_mob] by inserting [src] into [hit_mob.p_their()] throat", "forces [hit_mob] to suck [src]", "inserts [src] into [hit_mob]'s throat")
- hit_mob.adjustArousal(4)
- hit_mob.adjustPleasure(1)
+ hit_mob.adjust_arousal(4)
+ hit_mob.adjust_pleasure(1)
hit_mob.adjustOxyLoss(1.5)
if(prob(70))
hit_mob.try_lewd_autoemote(pick("gasp", "moan"))
@@ -242,8 +242,8 @@
else
if(hit_mob.is_bottomless())
message = pick("fucks [hit_mob]'s ass with [src]", "uses [src] to fuck [hit_mob]'s anus", "jams [hit_mob]'s ass with [src]", "roughly fucks [hit_mob]'s ass with [src], causing their eyes to roll back")
- hit_mob.adjustArousal(5)
- hit_mob.adjustPleasure(5)
+ hit_mob.adjust_arousal(5)
+ hit_mob.adjust_pleasure(5)
if(prob(60))
hit_mob.try_lewd_autoemote(pick("twitch_s", "moan", "shiver"))
user.visible_message(span_purple("[user] [message]!"))
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/appearance.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/appearance.dm
new file mode 100644
index 00000000000..15c4b2c895e
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/appearance.dm
@@ -0,0 +1,76 @@
+/// Helper proc for adding overrides to bodyparts to hide them if a catsuit or sleeping bag is worn. Allows for overrides for each, in case a part shouldn't be hidden.
+/datum/sprite_accessory/proc/lewd_is_hidden(mob/living/carbon/human/target_human, hide_if_catsuit = TRUE, hide_if_sleeping_bag = TRUE)
+ if(target_human.wear_suit && istype(target_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
+ var/obj/item/clothing/suit/straight_jacket/kinky_sleepbag/sleeping_bag = target_human.wear_suit
+ if(sleeping_bag.state_thing == "inflated")
+ return hide_if_sleeping_bag
+ return FALSE
+ else if(target_human.w_uniform && istype(target_human.w_uniform, /obj/item/clothing/under/misc/latex_catsuit))
+ return hide_if_catsuit
+ return FALSE
+
+// Extends default proc check for hidden ears for supporting our sleepbag and catsuit too
+/datum/sprite_accessory/ears/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/bodypart)
+ // First lets proc default code
+ . = ..()
+ if(!.) // If true, ears already hidden
+ return lewd_is_hidden(target_human)
+
+// Extends default proc check for hidden frills for supporting our sleepbag and catsuit too
+/datum/sprite_accessory/frills/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/bodypart)
+ . = ..()
+ if(!.) // If true, frills already hidden
+ return lewd_is_hidden(target_human)
+
+// Extends default proc check for hidden head accessory for supporting our sleepbag and catsuit too
+/datum/sprite_accessory/head_accessory/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/bodypart)
+ . = ..()
+ if(!.) // If true, head accessory already hidden
+ return lewd_is_hidden(target_human)
+
+// Extends default proc check for hidden horns for supporting our sleepbag and catsuit too
+/datum/sprite_accessory/horns/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/bodypart)
+ . = ..()
+ if(!.) // If true, horns already hidden
+ return lewd_is_hidden(target_human)
+
+// Extends default proc check for hidden antenna for supporting our sleepbag and catsuit too
+/datum/sprite_accessory/antenna/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/bodypart)
+ . = ..()
+ if(!.) // If true, antenna already hidden
+ return lewd_is_hidden(target_human)
+
+// Extends default proc check for hidden moth antenna for supporting our sleepbag and catsuit too
+/datum/sprite_accessory/moth_antennae/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/bodypart)
+ . = ..()
+ if(!.) // If true, moth antenna already hidden
+ return lewd_is_hidden(target_human)
+
+// Extends default proc check for hidden skrell hair for supporting our sleepbag and catsuit too
+/datum/sprite_accessory/skrell_hair/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/bodypart)
+ . = ..()
+ if(!.) // If true, skrell hair already hidden
+ return lewd_is_hidden(target_human)
+
+// Extends default proc check for hidden skrell hair for supporting our sleepbag and catsuit too
+/datum/sprite_accessory/tails/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/bodypart)
+ . = ..()
+ if(!.) // If true, tail already hidden
+ return lewd_is_hidden(target_human, hide_if_catsuit = FALSE)
+
+/datum/sprite_accessory/xenodorsal/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/bodypart)
+ . = ..()
+ if(!.)
+ return lewd_is_hidden(target_human)
+
+/datum/sprite_accessory/xenohead/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/bodypart)
+ . = ..()
+ if(!.)
+ return lewd_is_hidden(target_human, hide_if_catsuit = FALSE, hide_if_sleeping_bag = FALSE)
+
+// Extends default proc check for hidden wings for supporting our sleepbag and catsuit too
+/datum/sprite_accessory/wings/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/bodypart)
+ . = ..()
+ if(!.)
+ return lewd_is_hidden(target_human)
+
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/carbon.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/carbon.dm
new file mode 100644
index 00000000000..fae48e53f14
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/carbon.dm
@@ -0,0 +1,5 @@
+// Let's not force lewd emotes from folk who don't want them, mmm~?
+/mob/living/carbon/proc/try_lewd_autoemote(emote)
+ if(!client?.prefs?.read_preference(/datum/preference/toggle/erp/autoemote))
+ return
+ emote(emote)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm
new file mode 100644
index 00000000000..9f5b51f1bbf
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm
@@ -0,0 +1,378 @@
+/mob/living/carbon/human
+ var/arousal = 0
+ var/pleasure = 0
+ var/pain = 0
+
+ var/pain_limit = 0
+ var/arousal_status = AROUSAL_NONE
+
+ // Add variables for slots to the human class
+ var/obj/item/vagina = null
+ var/obj/item/anus = null
+ var/obj/item/nipples = null
+ var/obj/item/penis = null
+
+ var/has_penis = FALSE
+ var/has_vagina = FALSE
+ var/has_breasts = FALSE
+ var/has_anus = FALSE
+
+
+// For tracking arousal and fluid regen.
+/mob/living/carbon/human/Initialize(mapload)
+ . = ..()
+ if(!istype(src, /mob/living/carbon/human/species/monkey))
+ apply_status_effect(/datum/status_effect/aroused)
+ apply_status_effect(/datum/status_effect/body_fluid_regen)
+
+/*
+* This code needed to determine if the human is naked in that part of body or not
+* You can use this for your own stuff if you want, haha.
+*/
+
+/// Are we wearing something that covers our chest?
+/mob/living/carbon/human/proc/is_topless()
+ return (!(wear_suit) || !(wear_suit.body_parts_covered & CHEST)) && (!(w_uniform) || !(w_uniform.body_parts_covered & CHEST))
+
+/// Are we wearing something that covers our groin?
+/mob/living/carbon/human/proc/is_bottomless()
+ return (!(wear_suit) || !(wear_suit.body_parts_covered & GROIN)) && (!(w_uniform) || !(w_uniform.body_parts_covered & GROIN))
+
+/// Are we wearing something that covers our shoes?
+/mob/living/carbon/human/proc/is_barefoot()
+ return (!(wear_suit) || !(wear_suit.body_parts_covered & GROIN)) && (!(shoes) || !(shoes.body_parts_covered & FEET))
+
+/mob/living/carbon/human/proc/is_hands_uncovered()
+ return (gloves?.body_parts_covered & ARMS)
+
+/mob/living/carbon/human/proc/is_head_uncovered()
+ return (head?.body_parts_covered & HEAD)
+
+/// Returns true if the human has an accessible penis for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines.
+/mob/living/carbon/human/proc/has_penis(required_state = REQUIRE_GENITAL_ANY)
+ var/obj/item/organ/external/genital/genital = getorganslot(ORGAN_SLOT_PENIS)
+ if(!genital)
+ return FALSE
+
+ switch(required_state)
+ if(REQUIRE_GENITAL_ANY)
+ return TRUE
+ if(REQUIRE_GENITAL_EXPOSED)
+ return genital.visibility_preference == GENITAL_ALWAYS_SHOW || is_bottomless()
+ if(REQUIRE_GENITAL_UNEXPOSED)
+ return genital.visibility_preference != GENITAL_ALWAYS_SHOW && !is_bottomless()
+ else
+ return TRUE
+
+/// Returns true if the human has a accessible balls for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines.
+/mob/living/carbon/human/proc/has_balls(required_state = REQUIRE_GENITAL_ANY)
+ var/obj/item/organ/external/genital/genital = getorganslot(ORGAN_SLOT_TESTICLES)
+ if(!genital)
+ return FALSE
+
+ switch(required_state)
+ if(REQUIRE_GENITAL_ANY)
+ return TRUE
+ if(REQUIRE_GENITAL_EXPOSED)
+ return genital.visibility_preference == GENITAL_ALWAYS_SHOW || is_bottomless()
+ if(REQUIRE_GENITAL_UNEXPOSED)
+ return genital.visibility_preference != GENITAL_ALWAYS_SHOW && !is_bottomless()
+ else
+ return TRUE
+
+/// Returns true if the human has an accessible vagina for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines.
+/mob/living/carbon/human/proc/has_vagina(required_state = REQUIRE_GENITAL_ANY)
+ var/obj/item/organ/external/genital/genital = getorganslot(ORGAN_SLOT_VAGINA)
+ if(!genital)
+ return FALSE
+
+ switch(required_state)
+ if(REQUIRE_GENITAL_ANY)
+ return TRUE
+ if(REQUIRE_GENITAL_EXPOSED)
+ return genital.visibility_preference == GENITAL_ALWAYS_SHOW || is_bottomless()
+ if(REQUIRE_GENITAL_UNEXPOSED)
+ return genital.visibility_preference != GENITAL_ALWAYS_SHOW && !is_bottomless()
+ else
+ return TRUE
+
+/// Returns true if the human has a accessible breasts for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines.
+/mob/living/carbon/human/proc/has_breasts(required_state = REQUIRE_GENITAL_ANY)
+ var/obj/item/organ/external/genital/genital = getorganslot(ORGAN_SLOT_BREASTS)
+ if(!genital)
+ return FALSE
+
+ switch(required_state)
+ if(REQUIRE_GENITAL_ANY)
+ return TRUE
+ if(REQUIRE_GENITAL_EXPOSED)
+ return genital.visibility_preference == GENITAL_ALWAYS_SHOW || is_topless()
+ if(REQUIRE_GENITAL_UNEXPOSED)
+ return genital.visibility_preference != GENITAL_ALWAYS_SHOW && !is_topless()
+ else
+ return TRUE
+
+/// Returns true if the human has an accessible anus for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines.
+/mob/living/carbon/human/proc/has_anus(required_state = REQUIRE_GENITAL_ANY)
+ if(issilicon(src))
+ return TRUE
+ var/obj/item/organ/external/genital/genital = getorganslot(ORGAN_SLOT_ANUS)
+ if(!genital)
+ return FALSE
+
+ switch(required_state)
+ if(REQUIRE_GENITAL_ANY)
+ return TRUE
+ if(REQUIRE_GENITAL_EXPOSED)
+ return genital.visibility_preference == GENITAL_ALWAYS_SHOW || is_bottomless()
+ if(REQUIRE_GENITAL_UNEXPOSED)
+ return genital.visibility_preference != GENITAL_ALWAYS_SHOW && !is_bottomless()
+ else
+ return TRUE
+
+/// Returns true if the human has a accessible feet for the parameter, returning the number of feet the human has if they do. Accepts any of the `REQUIRE_GENITAL_` defines.
+/mob/living/carbon/human/proc/has_arms(required_state = REQUIRE_GENITAL_ANY)
+ var/hand_count = 0
+ var/covered = 0
+ var/is_covered = FALSE
+ for(var/obj/item/bodypart/l_arm/left_arm in bodyparts)
+ hand_count++
+ for(var/obj/item/bodypart/r_arm/right_arm in bodyparts)
+ hand_count++
+ if(get_item_by_slot(ITEM_SLOT_HANDS))
+ var/obj/item/clothing/gloves/worn_gloves = get_item_by_slot(ITEM_SLOT_HANDS)
+ covered = worn_gloves.body_parts_covered
+ if(covered & HANDS)
+ is_covered = TRUE
+ switch(required_state)
+ if(REQUIRE_GENITAL_ANY)
+ return hand_count
+ if(REQUIRE_GENITAL_EXPOSED)
+ if(is_covered)
+ return FALSE
+ else
+ return hand_count
+ if(REQUIRE_GENITAL_UNEXPOSED)
+ if(!is_covered)
+ return FALSE
+ else
+ return hand_count
+ else
+ return hand_count
+
+/// Returns true if the human has a accessible feet for the parameter, returning the number of feet the human has if they do. Accepts any of the `REQUIRE_GENITAL_` defines.
+/mob/living/carbon/human/proc/has_feet(required_state = REQUIRE_GENITAL_ANY)
+ var/feet_count = 0
+
+ for(var/obj/item/bodypart/l_leg/left_leg in bodyparts)
+ feet_count++
+ for(var/obj/item/bodypart/r_leg/right_leg in bodyparts)
+ feet_count++
+
+ switch(required_state)
+ if(REQUIRE_GENITAL_ANY)
+ return feet_count
+ if(REQUIRE_GENITAL_EXPOSED)
+ if(!is_barefoot())
+ return FALSE
+ else
+ return feet_count
+ if(REQUIRE_GENITAL_UNEXPOSED)
+ if(is_barefoot())
+ return FALSE
+ else
+ return feet_count
+ else
+ return feet_count
+
+/// Gets the number of feet the human has.
+/mob/living/carbon/human/proc/get_num_feet()
+ return has_feet(REQUIRE_GENITAL_ANY)
+
+/// Returns true if the human has a accessible ears for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines.
+/mob/living/carbon/human/proc/has_ears(required_state = REQUIRE_GENITAL_ANY)
+ var/obj/item/organ/genital = getorganslot(ORGAN_SLOT_EARS)
+ if(!genital)
+ return FALSE
+
+ switch(required_state)
+ if(REQUIRE_GENITAL_ANY)
+ return TRUE
+ if(REQUIRE_GENITAL_EXPOSED)
+ return !get_item_by_slot(ITEM_SLOT_EARS)
+ if(REQUIRE_GENITAL_UNEXPOSED)
+ return get_item_by_slot(ITEM_SLOT_EARS)
+ else
+ return TRUE
+
+/// Returns true if the human has accessible eyes for the parameter. Accepts any of the `REQUIRE_GENITAL_` defines.
+/mob/living/carbon/human/proc/has_eyes(required_state = REQUIRE_GENITAL_ANY)
+ var/obj/item/organ/genital = getorganslot(ORGAN_SLOT_EYES)
+ if(!genital)
+ return FALSE
+
+ switch(required_state)
+ if(REQUIRE_GENITAL_ANY)
+ return TRUE
+ if(REQUIRE_GENITAL_EXPOSED)
+ return !get_item_by_slot(ITEM_SLOT_EYES)
+ if(REQUIRE_GENITAL_UNEXPOSED)
+ return get_item_by_slot(ITEM_SLOT_EYES)
+ else
+ return TRUE
+
+
+/*
+* This code needed for changing character's gender by chems
+*/
+
+/// Sets the gender of the human, respecting prefs unless it's forced. Do not force in non-admin operations.
+/mob/living/carbon/human/proc/set_gender(ngender = NEUTER, silent = FALSE, update_icon = TRUE, forced = FALSE)
+ var/bender = gender != ngender
+ if((!client?.prefs?.read_preference(/datum/preference/toggle/erp/gender_change) && !forced) || !dna || !bender)
+ return FALSE
+
+ if(ngender == MALE || ngender == FEMALE)
+ dna.features["body_model"] = ngender
+ if(!silent)
+ var/adj = ngender == MALE ? "masculine" : "feminine"
+ visible_message(span_boldnotice("[src] suddenly looks more [adj]!"), span_boldwarning("You suddenly feel more [adj]!"))
+ else if(ngender == NEUTER)
+ dna.features["body_model"] = MALE
+ gender = ngender
+ if(update_icon)
+ update_body()
+
+/*
+* ICON UPDATING EXTENTION
+*/
+
+/// Updating vagina slot
+/mob/living/carbon/human/proc/update_inv_vagina()
+ // on_mob stuff
+ remove_overlay(VAGINA_LAYER)
+
+ var/obj/item/clothing/sextoy/sex_toy = vagina
+
+ if(wear_suit && (wear_suit.flags_inv & HIDESEXTOY)) // You can add proper flags here if required
+ return
+
+ var/icon_file = vagina?.worn_icon
+ var/mutable_appearance/vagina_overlay
+
+ if(!vagina_overlay)
+ vagina_overlay = sex_toy?.build_worn_icon(default_layer = VAGINA_LAYER, default_icon_file = 'icons/mob/clothing/under/default.dmi', isinhands = FALSE, override_file = icon_file)
+
+ if(OFFSET_UNIFORM in dna.species.offset_features)
+ vagina_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1]
+ vagina_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2]
+ overlays_standing[VAGINA_LAYER] = vagina_overlay
+
+ apply_overlay(VAGINA_LAYER)
+ update_mutant_bodyparts()
+
+/// Updating anus slot
+/mob/living/carbon/human/proc/update_inv_anus()
+ // on_mob stuff
+ remove_overlay(ANUS_LAYER)
+
+ var/obj/item/clothing/sextoy/sex_toy = anus
+
+ if(wear_suit && (wear_suit.flags_inv & HIDESEXTOY)) // You can add proper flags here if required
+ return
+
+ var/icon_file = anus?.worn_icon
+ var/mutable_appearance/anus_overlay
+
+ if(!anus_overlay)
+ anus_overlay = sex_toy?.build_worn_icon(default_layer = ANUS_LAYER, default_icon_file = 'icons/mob/clothing/under/default.dmi', isinhands = FALSE, override_file = icon_file)
+
+ if(OFFSET_UNIFORM in dna.species.offset_features)
+ anus_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1]
+ anus_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2]
+ overlays_standing[ANUS_LAYER] = anus_overlay
+
+ apply_overlay(ANUS_LAYER)
+ update_mutant_bodyparts()
+
+/// Updating nipples slot
+/mob/living/carbon/human/proc/update_inv_nipples()
+ // on_mob stuff
+ remove_overlay(NIPPLES_LAYER)
+
+ var/obj/item/clothing/sextoy/sex_toy = nipples
+
+ if(wear_suit && (wear_suit.flags_inv & HIDESEXTOY)) // You can add proper flags here if required
+ return
+
+ var/icon_file = nipples?.worn_icon
+ var/mutable_appearance/nipples_overlay
+
+ if(!nipples_overlay)
+ nipples_overlay = sex_toy?.build_worn_icon(default_layer = NIPPLES_LAYER, default_icon_file = 'icons/mob/clothing/under/default.dmi', isinhands = FALSE, override_file = icon_file)
+
+ if(OFFSET_UNIFORM in dna.species.offset_features)
+ nipples_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1]
+ nipples_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2]
+ overlays_standing[NIPPLES_LAYER] = nipples_overlay
+
+ apply_overlay(NIPPLES_LAYER)
+ update_mutant_bodyparts()
+
+/// Updating penis slot
+/mob/living/carbon/human/proc/update_inv_penis()
+ // on_mob stuff
+ remove_overlay(PENIS_LAYER)
+
+ var/obj/item/clothing/sextoy/sex_toy = penis
+
+ if(wear_suit && (wear_suit.flags_inv & HIDESEXTOY)) // You can add proper flags here if required
+ return
+
+ var/icon_file = penis?.worn_icon
+ var/mutable_appearance/penis_overlay
+
+ if(!penis_overlay)
+ penis_overlay = sex_toy?.build_worn_icon(default_layer = PENIS_LAYER, default_icon_file = 'icons/mob/clothing/under/default.dmi', isinhands = FALSE, override_file = icon_file)
+
+ if(OFFSET_UNIFORM in dna.species.offset_features)
+ penis_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1]
+ penis_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2]
+ overlays_standing[PENIS_LAYER] = penis_overlay
+
+ apply_overlay(PENIS_LAYER)
+ update_mutant_bodyparts()
+
+/// Helper proc for calling all the lewd slot update_inv_ procs.
+/mob/living/carbon/human/proc/update_inv_lewd()
+ update_inv_vagina()
+ update_inv_anus()
+ update_inv_nipples()
+ update_inv_penis()
+
+/*
+* MISC LOGIC
+*/
+
+// Handles breaking out of gloves that restrain people.
+/mob/living/carbon/human/resist_restraints()
+ if(gloves?.breakouttime)
+ changeNext_move(CLICK_CD_BREAKOUT)
+ last_special = world.time + CLICK_CD_BREAKOUT
+ cuff_resist(gloves)
+ else
+ ..()
+
+/// Checks if the human is wearing a condom, and also hasn't broken it.
+/mob/living/carbon/human/proc/is_wearing_condom()
+ if(!penis || !istype(penis, /obj/item/clothing/sextoy/condom))
+ return FALSE
+
+ var/obj/item/clothing/sextoy/condom/condom = penis
+ return condom.condom_state == CONDOM_BROKEN
+
+// For handling things that don't already have handcuff handlers.
+/mob/living/carbon/human/set_handcuffed(new_value)
+ if(wear_suit && istype(wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
+ return FALSE
+ ..()
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/misc.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/misc.dm
new file mode 100644
index 00000000000..3d9ee0d297c
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/misc.dm
@@ -0,0 +1,62 @@
+/*
+* Looping sound for vibrating stuff
+*/
+
+/datum/looping_sound/vibrator
+ start_sound = 'modular_skyrat/modules/modular_items/lewd_items/sounds/bzzz-loop-1.ogg'
+ start_length = 1
+ mid_sounds = 'modular_skyrat/modules/modular_items/lewd_items/sounds/bzzz-loop-1.ogg'
+ mid_length = 1
+ end_sound = 'modular_skyrat/modules/modular_items/lewd_items/sounds/bzzz-loop-1.ogg'
+ falloff_distance = 1
+ falloff_exponent = 5
+ extra_range = SILENCED_SOUND_EXTRARANGE
+ ignore_walls = FALSE
+
+/datum/looping_sound/vibrator/low
+ volume = 80
+
+/datum/looping_sound/vibrator/medium
+ volume = 90
+
+/datum/looping_sound/vibrator/high
+ volume = 100
+
+/*
+* Dancing pole code.
+*/
+
+/atom
+ var/pseudo_z_axis
+
+/atom/proc/get_fake_z()
+ return pseudo_z_axis
+
+/obj/structure/table
+ pseudo_z_axis = 8
+
+/turf/open/get_fake_z()
+ var/objschecked
+ for(var/obj/structure/structurestocheck in contents)
+ objschecked++
+ if(structurestocheck.pseudo_z_axis)
+ return structurestocheck.pseudo_z_axis
+ if(objschecked >= 25)
+ break
+ return pseudo_z_axis
+
+/mob/living/Move(atom/newloc, direct)
+ . = ..()
+ if(.)
+ pseudo_z_axis = newloc.get_fake_z()
+ pixel_z = pseudo_z_axis
+
+/// Used to add a cum decal to the floor while transferring viruses and DNA to it
+/mob/living/proc/add_cum_splatter_floor(turf/the_turf, female = FALSE)
+ if(!the_turf)
+ the_turf = get_turf(src)
+
+ var/selected_type = female ? /obj/effect/decal/cleanable/cum/femcum : /obj/effect/decal/cleanable/cum
+ var/atom/stain = new selected_type(the_turf, get_static_viruses())
+
+ stain.transfer_mob_blood_dna(src) //I'm not adding a new forensics category for cumstains
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/organs.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/organs.dm
new file mode 100644
index 00000000000..a90348ca396
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/organs.dm
@@ -0,0 +1,6 @@
+/obj/item/organ/internal/brain/on_life(delta_time, times_fired) //All your horny is here *points to the head*
+ . = ..()
+ var/mob/living/carbon/human/brain_owner = owner
+ if(istype(brain_owner, /mob/living/carbon/human) && brain_owner.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
+ if(!(organ_flags & ORGAN_FAILING))
+ brain_owner.dna.species.handle_arousal(brain_owner, delta_time, times_fired)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/species.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/species.dm
new file mode 100644
index 00000000000..69a63d89d15
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/species.dm
@@ -0,0 +1,91 @@
+#define AROUSED_ALERT "aroused"
+#define AROUSED_SMALL "small"
+#define AROUSED_MEDIUM "medium"
+#define AROUSED_HIGH "high"
+#define AROUSED_MAX "max"
+
+/// Sends an icon to the screen that gives an approximate indication of the mob's arousal.
+/datum/species/proc/throw_arousal_alert(level, atom/movable/screen/alert/aroused/arousal_alert, mob/living/carbon/human/targeted_human)
+ targeted_human.throw_alert(AROUSED_ALERT, /atom/movable/screen/alert/aroused)
+ arousal_alert?.icon_state = "arousal_[level]"
+ arousal_alert?.update_icon()
+
+/// Sends an icon to the screen that gives an approximate indication of the mob's pain. Looks like spikes/barbed wire.
+/datum/species/proc/overlay_pain(level, atom/movable/screen/alert/aroused/arousal_alert)
+ arousal_alert?.cut_overlay(arousal_alert.pain_overlay)
+ arousal_alert?.pain_level = level
+ arousal_alert?.pain_overlay = arousal_alert.update_pain()
+ arousal_alert?.add_overlay(arousal_alert.pain_overlay)
+ arousal_alert?.update_overlays()
+
+/// Sends an icon to the screen that gives an approximate indication of the mob's pleasure. Looks like a pink-white border on the arousal alert heart.
+/datum/species/proc/overlay_pleasure(level, atom/movable/screen/alert/aroused/arousal_alert)
+ arousal_alert?.cut_overlay(arousal_alert.pleasure_overlay)
+ arousal_alert?.pleasure_level = level
+ arousal_alert?.pleasure_overlay = arousal_alert.update_pleasure()
+ arousal_alert?.add_overlay(arousal_alert.pleasure_overlay)
+ arousal_alert?.update_overlays()
+
+/// Handles throwing the arousal alerts to screen.
+/datum/species/proc/handle_arousal(mob/living/carbon/human/target_human, atom/movable/screen/alert/aroused)
+ if(!target_human.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
+ return
+
+ var/atom/movable/screen/alert/aroused/arousal_alert = target_human.alerts[AROUSED_ALERT]
+
+ var/alert_state
+ switch(target_human.arousal)
+ if(-INFINITY to AROUSAL_MINIMUM_DETECTABLE)
+ target_human.clear_alert(AROUSED_ALERT, /atom/movable/screen/alert/aroused)
+ if(target_human.arousal < AROUSAL_MINIMUM)
+ target_human.arousal = AROUSAL_MINIMUM // To prevent massively negative values that break the lewd system for some.
+ if(AROUSAL_MINIMUM_DETECTABLE to AROUSAL_LOW)
+ alert_state = AROUSED_SMALL
+ if(AROUSAL_LOW to AROUSAL_MEDIUM)
+ alert_state = AROUSED_MEDIUM
+ if(AROUSAL_HIGH to AROUSAL_AUTO_CLIMAX_THRESHOLD)
+ alert_state = AROUSED_HIGH
+ if(AROUSAL_AUTO_CLIMAX_THRESHOLD to INFINITY) //to prevent that 101 arousal that can make icon disappear or something.
+ alert_state = AROUSED_MAX
+ if(alert_state)
+ throw_arousal_alert(alert_state, arousal_alert, target_human)
+ alert_state = null
+
+ switch(target_human.pain)
+ if(-INFINITY to AROUSAL_MINIMUM_DETECTABLE) //to prevent same thing with pain
+ arousal_alert?.cut_overlay(arousal_alert.pain_overlay)
+ if(target_human.pain < AROUSAL_MINIMUM)
+ target_human.pain = AROUSAL_MINIMUM // To prevent massively negative values that break the lewd system for some.
+ if(AROUSAL_MINIMUM_DETECTABLE to AROUSAL_LOW)
+ alert_state = AROUSED_SMALL
+ if(AROUSAL_LOW to AROUSAL_MEDIUM)
+ alert_state = AROUSED_MEDIUM
+ if(AROUSAL_HIGH to AROUSAL_AUTO_CLIMAX_THRESHOLD)
+ alert_state = AROUSED_HIGH
+ if(AROUSAL_AUTO_CLIMAX_THRESHOLD to INFINITY)
+ alert_state = AROUSED_MAX
+ if(alert_state)
+ overlay_pain(alert_state, arousal_alert)
+ alert_state = null
+
+ switch(target_human.pleasure)
+ if(-INFINITY to AROUSAL_MINIMUM_DETECTABLE) //to prevent same thing with pleasure
+ arousal_alert?.cut_overlay(arousal_alert.pleasure_overlay)
+ if(target_human.pleasure < AROUSAL_MINIMUM)
+ target_human.pleasure = AROUSAL_MINIMUM // To prevent massively negative values that break the lewd system for some.
+ if(AROUSAL_MINIMUM_DETECTABLE to AROUSAL_LOW)
+ alert_state = AROUSED_SMALL
+ if(AROUSAL_LOW to AROUSAL_MEDIUM)
+ alert_state = AROUSED_MEDIUM
+ if(AROUSAL_HIGH to AROUSAL_AUTO_CLIMAX_THRESHOLD)
+ alert_state = AROUSED_HIGH
+ if(AROUSAL_AUTO_CLIMAX_THRESHOLD to INFINITY)
+ alert_state = AROUSED_MAX
+ if(alert_state)
+ overlay_pleasure(alert_state, arousal_alert)
+
+#undef AROUSED_ALERT
+#undef AROUSED_SMALL
+#undef AROUSED_MEDIUM
+#undef AROUSED_HIGH
+#undef AROUSED_MAX
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_remove_erp_items.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_erp_disabled_item_enforcement.dm
similarity index 100%
rename from modular_skyrat/modules/modular_items/lewd_items/code/lewd_remove_erp_items.dm
rename to modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_erp_disabled_item_enforcement.dm
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_kits.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_kits.dm
new file mode 100644
index 00000000000..62b5a8d84a6
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_kits.dm
@@ -0,0 +1,56 @@
+
+// Milking machine
+/obj/item/storage/box/milking_kit
+ name = "DIY milking machine kit"
+ desc = "Contains everything you need to build your own milking machine!"
+
+/obj/item/storage/box/milking_kit/PopulateContents()
+ var/static/items_inside = list(
+ /obj/item/milking_machine/constructionkit = 1)
+ generate_items_inside(items_inside, src)
+
+// X-Stand
+/obj/item/storage/box/xstand_kit
+ name = "DIY x-stand kit"
+ desc = "Contains everything you need to build your own X-stand!"
+
+/obj/item/storage/box/xstand_kit/PopulateContents()
+ var/static/items_inside = list(
+ /obj/item/x_stand_kit = 1)
+ generate_items_inside(items_inside, src)
+
+// BDSM bed
+/obj/item/storage/box/bdsmbed_kit
+ name = "DIY BDSM bed kit"
+ desc = "Contains everything you need to build your own BDSM bed!"
+
+/obj/item/storage/box/bdsmbed_kit/PopulateContents()
+ var/static/items_inside = list(
+ /obj/item/bdsm_bed_kit = 1)
+ generate_items_inside(items_inside, src)
+
+// Striptease pole
+/obj/item/storage/box/strippole_kit
+ name = "DIY stripper pole kit"
+ desc = "Contains everything you need to build your own stripper pole!"
+
+/obj/item/storage/box/strippole_kit/PopulateContents()
+ var/static/items_inside = list(
+ /obj/item/polepack = 1)
+ generate_items_inside(items_inside, src)
+
+// Shibari stand
+/obj/item/storage/box/shibari_stand
+ name = "DIY shibari stand kit"
+ desc = "Contains everything you need to build your own shibari stand!"
+
+/obj/item/storage/box/shibari_stand/PopulateContents()
+ var/static/items_inside = list(
+ /obj/item/shibari_stand_kit = 1,
+ /obj/item/paper/shibari_kit_instructions = 1)
+ generate_items_inside(items_inside, src)
+
+// Paper instructions for shibari kit
+
+/obj/item/paper/shibari_kit_instructions
+ default_raw_text = "Hello! Congratulations on your purchase of the shibari kit by LustWish! Some newbies may get confused by our ropes, so we prepared a small instructions for you! First of all, you have to have a wrench to construct the stand itself. Secondly, you can use screwdrivers to change the color of your shibari stand. Just replace the plastic fittings! Thirdly, if you want to tie somebody to a bondage stand you need to fully tie their body, on both groin and chest!. To do that you need to use rope on body and then on groin of character, then you can just buckle them to the stand like any chair. Don't forget to have some ropes on your hand to actually tie them to the stand, as there's no ropes included with it! And that's it!"
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_masturbation_item.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_masturbation_item.dm
new file mode 100644
index 00000000000..a6010e2d229
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_masturbation_item.dm
@@ -0,0 +1,63 @@
+#define CUM_VOLUME_MULTIPLIER 5 + 5
+
+/obj/item/hand_item/coom
+ name = "cum"
+ desc = "C-can I watch...?"
+ icon = 'icons/obj/hydroponics/harvest.dmi'
+ icon_state = "eggplant"
+ inhand_icon_state = "nothing"
+
+// Jerk off into bottles and onto people.
+/obj/item/hand_item/coom/afterattack(obj/target, mob/user, proximity)
+ . = ..()
+ do_masturbate(target, user, proximity)
+
+/// Handles masturbation onto a living mob, or an atom.
+/// Attempts to fill the atom's reagent container, if it has one, and it isn't full.
+/obj/item/hand_item/coom/proc/do_masturbate(obj/target, mob/user, proximity)
+ if (CONFIG_GET(flag/disable_erp_preferences) || !proximity || user.stat >= DEAD)
+ return
+
+ var/mob/living/carbon/human/affected_human = user
+ var/obj/item/organ/external/genital/testicles/testicles = affected_human.getorganslot(ORGAN_SLOT_TESTICLES)
+ var/obj/item/organ/external/genital/penis/penis = affected_human.getorganslot(ORGAN_SLOT_PENIS)
+ var/datum/sprite_accessory/genital/penis_sprite = GLOB.sprite_accessories[ORGAN_SLOT_PENIS][affected_human.dna.species.mutant_bodyparts[ORGAN_SLOT_PENIS][MUTANT_INDEX_NAME]]
+ if(penis_sprite.is_hidden(affected_human))
+ to_chat(user, span_notice("You need to expose yourself in order to masturbate."))
+ return
+ else if(penis.aroused != AROUSAL_FULL)
+ to_chat(user, span_notice("You need to be aroused in order to masturbate."))
+ return
+ var/cum_volume = testicles.genital_size * CUM_VOLUME_MULTIPLIER
+ if(target == user)
+ user.visible_message(span_warning("[user] starts masturbating onto [target.p_them()]self!"), span_danger("You start masturbating onto yourself!"))
+
+ else if(target.is_refillable() && target.is_drainable())
+ if(target.reagents.holder_full())
+ to_chat(user, span_warning("[target] is full."))
+ return
+ user.visible_message(span_warning("[user] starts masturbating into [target]!"), span_danger("You start masturbating into [target]!"))
+ else
+ user.visible_message(span_warning("[user] starts masturbating onto [target]!"), span_danger("You start masturbating onto [target]!"))
+
+ if(do_after(user, 6 SECONDS, target))
+ if(target == user)
+ user.visible_message(span_warning("[user] cums on [target.p_them()]self!"), span_danger("You cum on yourself!"))
+
+ else if(target.is_refillable() && target.is_drainable())
+ var/datum/reagents/applied_reagents = new/datum/reagents(50)
+ applied_reagents.add_reagent(/datum/reagent/consumable/cum, cum_volume)
+ user.visible_message(span_warning("[user] cums into [target]!"), span_danger("You cum into [target]!"))
+ playsound(target, SFX_DESECRATION, 50, TRUE, ignore_walls = FALSE)
+ applied_reagents.trans_to(target, cum_volume)
+ else
+ user.visible_message(span_warning("[user] cums on [target]!"), span_danger("You cum on [target]!"))
+ playsound(target, SFX_DESECRATION, 50, TRUE, ignore_walls = FALSE)
+ affected_human.add_cum_splatter_floor(get_turf(target))
+
+ log_combat(user, target, "came on")
+ if(prob(40))
+ affected_human.try_lewd_autoemote("moan")
+ qdel(src)
+
+#undef CUM_VOLUME_MULTIPLIER
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_sex_toy.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_sex_toy.dm
new file mode 100644
index 00000000000..9ad53e50828
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_sex_toy.dm
@@ -0,0 +1,34 @@
+/obj/item/clothing/sextoy
+ name = "sextoy"
+ icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_items.dmi'
+ worn_icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_items/lewd_items.dmi'
+ equip_sound = 'modular_skyrat/modules/modular_items/lewd_items/sounds/bang1.ogg'
+ drop_sound = 'modular_skyrat/modules/modular_items/lewd_items/sounds/bang2.ogg'
+ pickup_sound = 'sound/items/handling/cloth_pickup.ogg'
+ /// This is used to decide what lewd slot a toy should be able to be inserted into.
+ /// The currently accepted defines are all prefixes with LEWD_SLOT_, and there is one for each lewd organ.
+ /// See code/__DEFINES/~skyrat_defines/inventory.dm for the full list.
+ var/lewd_slot_flags = NONE
+
+/obj/item/clothing/sextoy/dropped(mob/user)
+ ..()
+
+ update_appearance()
+ if(!ishuman(loc))
+ return
+
+ var/mob/living/carbon/human/holder = loc
+ holder.update_inv_lewd()
+ holder.fan_hud_set_fandom()
+
+/// A check to confirm if you can open the toy's color/design radial menu
+/obj/item/clothing/sextoy/proc/check_menu(mob/living/user)
+ if(!istype(user))
+ return FALSE
+ if(user.incapacitated())
+ return FALSE
+ return TRUE
+
+/// Returns if the item is inside a lewd slot.
+/obj/item/clothing/sextoy/proc/is_inside_lewd_slot(mob/living/carbon/human/target)
+ return (src == target.penis || src == target.vagina || src == target.anus || src == target.nipples)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/attachable_vibrator.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/attachable_vibrator.dm
index fc255a5ad91..bd6b1e48f9b 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/attachable_vibrator.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/attachable_vibrator.dm
@@ -10,7 +10,7 @@
icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_items.dmi'
lefthand_file = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_inhands/lewd_inhand_left.dmi'
righthand_file = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_inhands/lewd_inhand_right.dmi'
- slot_flags = ITEM_SLOT_PENIS | ITEM_SLOT_VAGINA | ITEM_SLOT_NIPPLES | ITEM_SLOT_ANUS
+ lewd_slot_flags = LEWD_SLOT_PENIS | LEWD_SLOT_VAGINA | LEWD_SLOT_NIPPLES | LEWD_SLOT_ANUS
clothing_flags = INEDIBLE_CLOTHING
/// Is the toy currently on
var/toy_on = FALSE
@@ -113,7 +113,7 @@
. = ..()
if(!istype(user))
return
- if(is_in_genital(user))
+ if(is_inside_lewd_slot(user))
START_PROCESSING(SSobj, src)
/obj/item/clothing/sextoy/eggvib/dropped(mob/user, silent)
@@ -127,14 +127,14 @@
if(!istype(target))
return
if(vibration_mode == "low")
- target.adjustArousal(0.5 * delta_time)
- target.adjustPleasure(0.5 * delta_time)
+ target.adjust_arousal(0.5 * delta_time)
+ target.adjust_pleasure(0.5 * delta_time)
if(vibration_mode == "medium")
- target.adjustArousal(0.6 * delta_time)
- target.adjustPleasure(0.6 * delta_time)
+ target.adjust_arousal(0.6 * delta_time)
+ target.adjust_pleasure(0.6 * delta_time)
if(vibration_mode == "high")
- target.adjustArousal(0.7 * delta_time)
- target.adjustPleasure(0.7 * delta_time)
+ target.adjust_arousal(0.7 * delta_time)
+ target.adjust_pleasure(0.7 * delta_time)
/*
* SIGNALLER CONTROLLED EGG
@@ -249,7 +249,7 @@
to_chat(vibrated, span_purple("You feel pleasant vibrations deep below..."))
else if(src == vibrated.nipples)
to_chat(vibrated, span_purple("You feel pleasant stimulation in your nipples."))
- else if(!toy_on && is_in_genital(vibrated))
+ else if(!toy_on && is_inside_lewd_slot(vibrated))
to_chat(vibrated, span_purple("The vibrating toy no longer drives you mad."))
if(!master)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/buttplug.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/buttplug.dm
index 766347e739b..1dc74c3f811 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/buttplug.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/buttplug.dm
@@ -7,7 +7,7 @@
icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_items.dmi'
lefthand_file = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_inhands/lewd_inhand_left.dmi'
righthand_file = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_inhands/lewd_inhand_right.dmi'
- slot_flags = ITEM_SLOT_ANUS|ITEM_SLOT_VAGINA
+ lewd_slot_flags = LEWD_SLOT_ANUS | LEWD_SLOT_VAGINA
/// Current color of the toy, can be changed, affects sprite
var/current_color = "pink"
/// Current size of the toy, can be changed, affects sprite and arousal
@@ -106,14 +106,14 @@
return
// I tried using switch here, but it need static value, and u.arousal can't be it. So fuck switches. Reject it, embrace the IFs
if(current_size == "small" && target.arousal < 30)
- target.adjustArousal(0.6 * delta_time)
- target.adjustPleasure(0.7 * delta_time)
+ target.adjust_arousal(0.6 * delta_time)
+ target.adjust_pleasure(0.7 * delta_time)
else if(current_size == "medium" && target.arousal < 40)
- target.adjustArousal(0.8 * delta_time)
- target.adjustPleasure(0.8 * delta_time)
+ target.adjust_arousal(0.8 * delta_time)
+ target.adjust_pleasure(0.8 * delta_time)
else if(current_size == "big" && target.arousal < 50)
- target.adjustArousal(1 * delta_time)
- target.adjustPleasure(1 * delta_time)
+ target.adjust_arousal(delta_time)
+ target.adjust_pleasure(delta_time)
if(!(target.pain < 22.5)) //yeah, this will cause pain. No buttplug gib intended, sry
return
- target.adjustPain(target*delta_time)
+ target.adjust_pain(target * delta_time)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/clamps.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/clamps.dm
index 412cf8b6ce3..81632b61a68 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/clamps.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/clamps.dm
@@ -6,7 +6,7 @@
icon_state = "clamps"
icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_items.dmi'
w_class = WEIGHT_CLASS_TINY
- slot_flags = ITEM_SLOT_NIPPLES
+ lewd_slot_flags = LEWD_SLOT_NIPPLES
/// What kind are the wearer's breasts?
var/breast_type = null
/// What size are the wearer's breasts?
@@ -73,12 +73,12 @@
var/obj/item/organ/external/genital/breasts/target_breast = target.getorganslot(ORGAN_SLOT_BREASTS)
if(!target || !target_breast)
return
- target.adjustArousal(1 * delta_time)
+ target.adjust_arousal(1 * delta_time)
if(target.pain < 27.5) //To prevent maxing pain by just pair of clamps.
- target.adjustPain(1 * delta_time)
+ target.adjust_pain(1 * delta_time)
if(target.arousal < 15)
- target.adjustArousal(1 * delta_time)
+ target.adjust_arousal(1 * delta_time)
if(target_breast.aroused != AROUSAL_CANT)
target_breast.aroused = AROUSAL_FULL //Clamps keeping nipples aroused
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/condom.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/condom.dm
index 75f4b028640..0658c211bd7 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/condom.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/condom.dm
@@ -53,7 +53,7 @@
w_class = WEIGHT_CLASS_TINY
var/current_color = "pink"
var/condom_state = "unused"
- slot_flags = ITEM_SLOT_PENIS
+ lewd_slot_flags = LEWD_SLOT_PENIS
/obj/item/clothing/sextoy/condom/Initialize(mapload)
. = ..()
@@ -72,20 +72,20 @@
condom_state = "dirty"
if(prob(10)) //chance of condom to break on first time.
name = "broken condom"
- condom_state = "broken"
+ condom_state = CONDOM_BROKEN
update_icon_state()
update_icon()
if("dirty")
name = "broken condom"
- condom_state = "broken"
+ condom_state = CONDOM_BROKEN
update_icon_state()
update_icon()
//When condom equipped we doing stuff
/obj/item/clothing/sextoy/condom/equipped(mob/user, slot, initial)
. = ..()
- if((slot & ITEM_SLOT_PENIS) && condom_state == "unused")
+ if((slot == LEWD_SLOT_PENIS) && condom_state == "unused")
condom_state = "used"
update_icon_state()
update_icon()
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/dildo.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/dildo.dm
index 5b92cedd37f..063ae27dca7 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/dildo.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/dildo.dm
@@ -27,7 +27,7 @@
/// If the toy can have its sprite changed
var/change_sprite = TRUE
w_class = WEIGHT_CLASS_TINY
- slot_flags = ITEM_SLOT_ANUS|ITEM_SLOT_VAGINA
+ lewd_slot_flags = LEWD_SLOT_ANUS | LEWD_SLOT_VAGINA
clothing_flags = INEDIBLE_CLOTHING
/// Create an assoc list of designs for the radial color/design menu
@@ -81,14 +81,14 @@
if(!istype(user))
return
if(poly_size == "small" && user.arousal < (AROUSAL_REGULAR_THRESHOLD * 0.8))
- user.adjustArousal(0.6 * delta_time)
- user.adjustPleasure(0.6 * delta_time)
+ user.adjust_arousal(0.6 * delta_time)
+ user.adjust_pleasure(0.6 * delta_time)
else if(poly_size == "medium" && user.arousal < AROUSAL_REGULAR_THRESHOLD)
- user.adjustArousal(0.8 * delta_time)
- user.adjustPleasure(0.8 * delta_time)
+ user.adjust_arousal(0.8 * delta_time)
+ user.adjust_pleasure(0.8 * delta_time)
else if(poly_size == "big" && user.arousal < (AROUSAL_REGULAR_THRESHOLD * 1.2))
- user.adjustArousal(1 * delta_time)
- user.adjustPleasure(1 * delta_time)
+ user.adjust_arousal(1 * delta_time)
+ user.adjust_pleasure(1 * delta_time)
/obj/item/clothing/sextoy/dildo/attack(mob/living/carbon/human/target, mob/living/carbon/human/user)
. = ..()
@@ -110,32 +110,32 @@
return
message = (user == target) ? pick("rubs [target.p_their()] vagina with [src]", "gently jams [target.p_their()] pussy with [src]", "fucks [target.p_their()] vagina with a [src]") : pick("delicately rubs [target]'s vagina with [src]", "uses [src] to fuck [target]'s vagina", "jams [target]'s pussy with [src]", "teasing [target]'s pussy with [src]")
if(poly_size == "small")
- target.adjustArousal(4)
- target.adjustPleasure(5)
+ target.adjust_arousal(4)
+ target.adjust_pleasure(5)
if(prob(20) && (target.stat != DEAD))
target.try_lewd_autoemote("moan")
else if(poly_size == "medium")
- target.adjustArousal(6)
- target.adjustPleasure(8)
+ target.adjust_arousal(6)
+ target.adjust_pleasure(8)
if(prob(40) && (target.stat != DEAD))
target.try_lewd_autoemote(pick("twitch_s", "moan"))
else if(poly_size == "big")
- target.adjustArousal(8)
- target.adjustPleasure(10)
- target.adjustPain(2)
+ target.adjust_arousal(8)
+ target.adjust_pleasure(10)
+ target.adjust_pain(2)
if(prob(60) && (target.stat != DEAD))
target.try_lewd_autoemote(pick("twitch_s", "moan", "gasp"))
if(side_double)
- user.adjustArousal(6)
- user.adjustPleasure(8)
+ user.adjust_arousal(6)
+ user.adjust_pleasure(8)
if(BODY_ZONE_HEAD, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_PRECISE_EYES) //Mouth only. Sorry, perverts. No eye/ear penetration for you today.
if(!target.is_mouth_covered())
to_chat(user, span_danger("Looks like [target]'s mouth is covered!"))
return
message = (user == target) ? pick("licks [src] erotically", "sucks on [src], slowly inserting it into [target.p_their()] throat") : pick("fucks [target]'s mouth with [src]", "inserts [src] into [target]'s throat, choking [target.p_them()]", "forces [target] to suck [src]", "inserts [src] into [target]'s throat")
- target.adjustArousal(4)
- target.adjustPleasure(1)
+ target.adjust_arousal(4)
+ target.adjust_pleasure(1)
if(prob(70) && (target.stat != DEAD))
target.try_lewd_autoemote(pick("gasp", "moan"))
@@ -145,8 +145,8 @@
to_chat(user, span_danger("[target]'s anus is covered!"))
return
message = (user == target) ? pick("puts [src] into [target.p_their()] anus", "slowly inserts [src] into [target.p_their()] ass") : pick("fucks [target]'s ass with [src]", "uses [src] to fuck [target]'s anus", "jams [target]'s ass with [src]", "roughly fucks [target]'s ass with [src], making [target.p_their()] eyes roll back")
- target.adjustArousal(5)
- target.adjustPleasure(5)
+ target.adjust_arousal(5)
+ target.adjust_pleasure(5)
if(prob(60) && (target.stat != DEAD))
target.try_lewd_autoemote(pick("twitch_s", "moan", "shiver"))
@@ -190,7 +190,7 @@ GLOBAL_LIST_INIT(dildo_colors, list(//mostly neon colors
var/static/list/dildo_sizes = list()
w_class = WEIGHT_CLASS_TINY
- slot_flags = ITEM_SLOT_ANUS|ITEM_SLOT_VAGINA
+ lewd_slot_flags = LEWD_SLOT_ANUS | LEWD_SLOT_VAGINA
/obj/item/clothing/sextoy/dildo/custom_dildo/populate_dildo_designs()
dildo_sizes = list(
@@ -256,7 +256,7 @@ GLOBAL_LIST_INIT(dildo_colors, list(//mostly neon colors
lefthand_file = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_inhands/lewd_inhand_left.dmi'
righthand_file = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_inhands/lewd_inhand_right.dmi'
w_class = WEIGHT_CLASS_TINY
- slot_flags = ITEM_SLOT_ANUS|ITEM_SLOT_VAGINA
+ lewd_slot_flags = LEWD_SLOT_ANUS | LEWD_SLOT_VAGINA
actions_types = list(/datum/action/item_action/take_dildo)
/// If one end of the toy is in your hand
var/in_hands = FALSE
@@ -370,8 +370,8 @@ GLOBAL_LIST_INIT(dildo_colors, list(//mostly neon colors
if(!istype(user))
return
if(user.arousal < AROUSAL_REGULAR_THRESHOLD)
- user.adjustArousal(0.8 * delta_time)
- user.adjustPleasure(0.8 * delta_time)
+ user.adjust_arousal(0.8 * delta_time)
+ user.adjust_pleasure(0.8 * delta_time)
/obj/item/clothing/sextoy/dildo/double_dildo/dropped(mob/living/carbon/human/user)
. = ..()
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/feather.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/feather.dm
index 89b2049c89b..26b633a9f80 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/feather.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/feather.dm
@@ -69,7 +69,7 @@
target.do_jitter_animation()
target.adjustStaminaLoss(4)
target.add_mood_event("tickled", /datum/mood_event/tickled)
- target.adjustArousal(3)
+ target.adjust_arousal(3)
user.visible_message(span_purple("[user] [message]!"))
playsound(loc, pick('sound/items/handling/cloth_drop.ogg', // I duplicate this part of code because im useless shitcoder that can't make it work properly without tons of repeating code blocks
'sound/items/handling/cloth_pickup.ogg', // If you can make it better - go ahead, modify it, please.
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/fleshlight.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/fleshlight.dm
index ac428f7824d..0f504c1a660 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/fleshlight.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/fleshlight.dm
@@ -72,8 +72,8 @@
if(!(prob(40) && (target.stat != DEAD)))
return
target.try_lewd_autoemote(pick("twitch_s", "moan", "blush"))
- target.adjustArousal(6)
- target.adjustPleasure(9)
+ target.adjust_arousal(6)
+ target.adjust_pleasure(9)
user.visible_message(span_purple("[user] [message]!"))
playsound(loc, pick('modular_skyrat/modules/modular_items/lewd_items/sounds/bang1.ogg',
'modular_skyrat/modules/modular_items/lewd_items/sounds/bang2.ogg',
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/kinky_shocker.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/kinky_shocker.dm
index 258c9f14374..5a9b2609113 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/kinky_shocker.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/kinky_shocker.dm
@@ -283,5 +283,5 @@
target.try_lewd_autoemote(pick("twitch", "twitch_s", "shiver", "scream"))
target.do_jitter_animation()
target.adjustStaminaLoss(3)
- target.adjustPain(9)
+ target.adjust_pain(9)
target.adjust_stutter(30 SECONDS)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/leather_whip.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/leather_whip.dm
index 55253696d4f..3d8c0c0e196 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/leather_whip.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/leather_whip.dm
@@ -176,7 +176,7 @@
if(prob(10))
target.apply_status_effect(/datum/status_effect/subspace)
target.Paralyze(1)//don't touch it. It's domination tool, it should have ability to put someone on kneels. I already inserted check for PREF YOU CAN'T ABUSE THIS ITEM
- target.adjustPain(5)
+ target.adjust_pain(5)
playsound(loc, 'sound/weapons/whip.ogg', 100)
else
message = (user == target) ? pick("knocks [target.p_them()]self down with [src]", "gently uses [src] to knock [target.p_them()]self on the ground") : pick("drops [target] to the ground with [src]", "uses [src] to put [target] on [target.p_their()] knees")
@@ -186,7 +186,7 @@
if(prob(10))
target.apply_status_effect(/datum/status_effect/subspace)
target.Paralyze(1)
- target.adjustPain(3)
+ target.adjust_pain(3)
playsound(loc, 'sound/weapons/whip.ogg', 60)
if(BODY_ZONE_R_LEG)
@@ -202,7 +202,7 @@
if(prob(10))
target.apply_status_effect(/datum/status_effect/subspace)
target.Paralyze(1)//don't touch it. It's domination tool, it should have ability to put someone on kneels. I already inserted check for PREF YOU CAN'T ABUSE THIS ITEM
- target.adjustPain(5)
+ target.adjust_pain(5)
playsound(loc, 'sound/weapons/whip.ogg', 100)
else
message = (user == target) ? pick("Knocks [target.p_them()]self down with [src]", "gently uses [src] to knock [target.p_them()]self on the ground") : pick("drops [target] to the ground with [src]", "uses [src] to put [target] on [target.p_their()] knees")
@@ -212,7 +212,7 @@
if(prob(10))
target.apply_status_effect(/datum/status_effect/subspace)
target.Paralyze(1)
- target.adjustPain(3)
+ target.adjust_pain(3)
playsound(loc, 'sound/weapons/whip.ogg', 60)
if(BODY_ZONE_HEAD)
@@ -220,8 +220,8 @@
message = (user == target) ? pick("wraps [src] around [target.p_their()] neck, choking [target.p_them()]self", "chokes [target.p_them()]self with [src]") : pick("chokes [target] with [src]", "twines [src] around [target]'s neck!")
if(prob(70) && (target.stat != DEAD))
target.try_lewd_autoemote(pick("gasp", "choke", "moan"))
- target.adjustArousal(3)
- target.adjustPain(5)
+ target.adjust_arousal(3)
+ target.adjust_pain(5)
playsound(loc, 'modular_skyrat/modules/modular_items/lewd_items/sounds/latex.ogg', 80)
if(BODY_ZONE_PRECISE_GROIN)
@@ -236,8 +236,8 @@
target.try_lewd_autoemote(pick("moan", "twitch"))
if(prob(10))
target.apply_status_effect(/datum/status_effect/subspace)
- target.adjustArousal(5)
- target.adjustPain(5)
+ target.adjust_arousal(5)
+ target.adjust_pain(5)
target.apply_status_effect(/datum/status_effect/spanked)
if(HAS_TRAIT(target, TRAIT_MASOCHISM || TRAIT_BIMBO))
target.add_mood_event("pervert spanked", /datum/mood_event/perv_spanked)
@@ -250,8 +250,8 @@
target.try_lewd_autoemote(pick("moan", "twitch", "twitch_s", "scream"))
if(prob(10))
target.apply_status_effect(/datum/status_effect/subspace)
- target.adjustArousal(3)
- target.adjustPain(8)
+ target.adjust_arousal(3)
+ target.adjust_pain(8)
target.apply_status_effect(/datum/status_effect/spanked)
if(HAS_TRAIT(target, TRAIT_MASOCHISM || TRAIT_BIMBO))
target.add_mood_event("pervert spanked", /datum/mood_event/perv_spanked)
@@ -265,7 +265,7 @@
if(prob(10))
target.apply_status_effect(/datum/status_effect/subspace)
target.do_jitter_animation()
- target.adjustPain(7)
+ target.adjust_pain(7)
playsound(loc, 'sound/weapons/whip.ogg', 100)
else
@@ -276,8 +276,8 @@
if(prob(10))
target.apply_status_effect(/datum/status_effect/subspace)
target.do_jitter_animation()
- target.adjustPain(4)
- target.adjustArousal(5)
+ target.adjust_pain(4)
+ target.adjust_arousal(5)
playsound(loc, 'sound/weapons/whip.ogg', 60)
if(!targetedsomewhere)
return
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/magic_wand.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/magic_wand.dm
index dedbbb86ad1..706d4568f65 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/magic_wand.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/magic_wand.dm
@@ -23,7 +23,7 @@
/// Mutable appearance for the human overlay of this itme
var/mutable_appearance/magicwand_overlay
w_class = WEIGHT_CLASS_TINY
- slot_flags = ITEM_SLOT_VAGINA|ITEM_SLOT_PENIS
+ lewd_slot_flags = LEWD_SLOT_VAGINA | LEWD_SLOT_PENIS
clothing_flags = INEDIBLE_CLOTHING
//some stuff for making overlay of this item. Why? Because.
@@ -74,14 +74,14 @@
return
// I tried using switch here, but it need static value, and u.arousal can't be it. So fuck switches. Reject it, embrace the IFs
if(vibration_mode == "low" && vibrated.arousal < 30)
- vibrated.adjustArousal(0.6 * delta_time)
- vibrated.adjustPleasure(0.7 * delta_time)
+ vibrated.adjust_arousal(0.6 * delta_time)
+ vibrated.adjust_pleasure(0.7 * delta_time)
else if(vibration_mode == "medium" && vibrated.arousal < 60)
- vibrated.adjustArousal(0.8 * delta_time)
- vibrated.adjustPleasure(0.8 * delta_time)
+ vibrated.adjust_arousal(0.8 * delta_time)
+ vibrated.adjust_pleasure(0.8 * delta_time)
else if(vibration_mode == "hard")
- vibrated.adjustArousal(1 * delta_time)
- vibrated.adjustPleasure(1 * delta_time)
+ vibrated.adjust_arousal(1 * delta_time)
+ vibrated.adjust_pleasure(1 * delta_time)
/obj/item/clothing/sextoy/magic_wand/attack(mob/living/carbon/human/target, mob/living/carbon/human/user)
. = ..()
@@ -124,17 +124,18 @@
to_chat(user, span_danger("Looks like [target]'s groin is covered!"))
return
message = (user == target) ? pick("massages their pussy with the [src]", "[vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] teases their pussy with [src]") : pick("[vibration_mode == "low" ? "delicately" : ""][vibration_mode = "hard" ? "aggressively" : ""] massages [target]'s pussy with [src]", "uses [src] to [vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] massage [target]'s pussy", "leans the vibrator against [target]'s pussy")
- target.adjustArousal((vibration_mode == "low" ? 4 : (vibration_mode == "hard" ? 8 : 5)))
- target.adjustPleasure((vibration_mode == "low" ? 2 : (vibration_mode == "hard" ? 10 : 5)))
+ target.adjust_arousal((vibration_mode == "low" ? 4 : (vibration_mode == "hard" ? 8 : 5)))
+ target.adjust_pleasure((vibration_mode == "low" ? 2 : (vibration_mode == "hard" ? 10 : 5)))
if(BODY_ZONE_CHEST)
var/obj/item/organ/external/genital/breasts = target.getorganslot(ORGAN_SLOT_BREASTS)
if(!(target.is_topless() || breasts.visibility_preference == GENITAL_ALWAYS_SHOW))
to_chat(user, span_danger("Looks like [target]'s chest is covered!"))
return
- message = (user == target) ? pick("massages their [breasts ? "breasts" : "nipples"] with the [src]", "[vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] teases their [breasts ? "tits" : "nipples"] with [src]") : pick("[vibration_mode == "low" ? "delicately" : ""][vibration_mode = "hard" ? "aggressively" : ""] teases [target]'s [breasts ? "breasts" : "nipples"] with [src]", "uses [src] to[vibration_mode == "low" ? " slowly" : ""] massage [target]'s [breasts ? "tits" : "nipples"]", "uses [src] to tease [target]'s [breasts ? "boobs" : "nipples"]")
- target.adjustArousal((vibration_mode == "low" ? 3 : (vibration_mode == "hard" ? 7 : 4)))
- target.adjustPleasure((vibration_mode == "low" ? 1 : (vibration_mode == "hard" ? 9 : 4)))
+ var/breasts_or_nipples = breasts ? ORGAN_SLOT_BREASTS : ORGAN_SLOT_NIPPLES
+ message = (user == target) ? pick("massages their [breasts_or_nipples] with the [src]", "[vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] teases their [breasts ? "tits" : ORGAN_SLOT_NIPPLES] with [src]") : pick("[vibration_mode == "low" ? "delicately" : ""][vibration_mode = "hard" ? "aggressively" : ""] teases [target]'s [breasts_or_nipples] with [src]", "uses [src] to[vibration_mode == "low" ? " slowly" : ""] massage [target]'s [breasts ? "tits" : ORGAN_SLOT_NIPPLES]", "uses [src] to tease [target]'s [breasts ? "boobs" : ORGAN_SLOT_NIPPLES]")
+ target.adjust_arousal((vibration_mode == "low" ? 3 : (vibration_mode == "hard" ? 7 : 4)))
+ target.adjust_pleasure((vibration_mode == "low" ? 1 : (vibration_mode == "hard" ? 9 : 4)))
if(prob(30) && (target.stat != DEAD))
target.try_lewd_autoemote(pick("twitch_s", "moan"))
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/spanking_pad.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/spanking_pad.dm
index 7c58533aa3a..3bb885d041b 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/spanking_pad.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/spanking_pad.dm
@@ -72,8 +72,8 @@
message = (user == target) ? pick("spanks themselves with [src]", "uses [src] to slap their hips") : pick("slaps [target]'s hips with [src]", "uses [src] to slap [target]'s butt", "spanks [target] with [src], making a loud slapping noise", "slaps [target]'s thighs with [src]")
if(prob(40) && (target.stat != DEAD))
target.try_lewd_autoemote(pick("twitch_s", "moan", "blush", "gasp"))
- target.adjustArousal(2)
- target.adjustPain(4)
+ target.adjust_arousal(2)
+ target.adjust_pain(4)
target.apply_status_effect(/datum/status_effect/spanked)
if(HAS_TRAIT(target, TRAIT_MASOCHISM || TRAIT_BIMBO))
target.add_mood_event("pervert spanked", /datum/mood_event/perv_spanked)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/torture_candle.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/torture_candle.dm
index 7f27870b28b..0b9a8fdb491 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/torture_candle.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/torture_candle.dm
@@ -152,7 +152,7 @@
"drips hot wax from the [src] onto [attacked]'s genitalia, causing [attacked.p_them()] to shiver",
"tilts [src], dripping wax right onto [attacked]'s genitals, causing [attacked.p_them()] to moan",
"drips some wax onto [attacked]'s genitals, making [attacked.p_them()] moan in pleasure")
- attacked.adjustPain(PAIN_DEFAULT)
+ attacked.adjust_pain(PAIN_DEFAULT)
else if(penis && (attacked.is_bottomless() || penis.visibility_preference == GENITAL_ALWAYS_SHOW))
message = (user == attacked) ? pick("drips some wax on [attacked.p_their()] penis, causing [attacked.p_them()] to moan in pleasure",
@@ -160,15 +160,15 @@
"drips wax right on [attacked]'s penis. It slightly itches.",
"drips hot wax from the [src] on the [attacked]'s penis, he slightly shivers.",
"tilts the candle. Drops of wax, dripping right from [src] right on the [attacked]'s penis, made him moan.")
- attacked.adjustPain(PAIN_DEFAULT)
+ attacked.adjust_pain(PAIN_DEFAULT)
else if(vagina && (attacked.is_bottomless() || vagina.visibility_preference == GENITAL_ALWAYS_SHOW))
message = (user == attacked) ? pick("drips some wax on themselves, letting it reach his vagina. He moans in pleasure.", "drips some wax on the [attacked]'s pussy, he moans in pleasure") : pick("drips some wax on the [attacked]'s vagina, he moans in pleasure", "tilts the candle. Wax slowly goes down, reaching the [attacked]'s vagina.", "tilts the candle. Drops of wax, dripping right from [src] right on the [attacked]'s pussy, made him moan.")
- attacked.adjustPain(PAIN_DEFAULT)
+ attacked.adjust_pain(PAIN_DEFAULT)
else if(attacked.is_bottomless())
message = (user == attacked) ? pick("drips some wax on themselves, letting it reach his belly. He moans in pleasure.", "drips some wax on the [attacked]'s tummy, he moans in pleasure") : pick("drips some wax on the [attacked]'s belly, he moans in pleasure", "tilts the candle. Wax slowly goes down, reaching the [attacked]'s tummy.", "tilts the candle. Drops of wax, dripping right from [src] right on the [attacked]'s groin, made him moan.")
- attacked.adjustPain(PAIN_DEFAULT)
+ attacked.adjust_pain(PAIN_DEFAULT)
else
to_chat(user, span_danger("Looks like [attacked]'s groin is covered!"))
@@ -178,8 +178,9 @@
targeted_somewhere = TRUE
var/obj/item/organ/external/genital/breasts = attacked.getorganslot(ORGAN_SLOT_BREASTS)
if(attacked.is_topless() || breasts.visibility_preference == GENITAL_ALWAYS_SHOW)
- message = (user == attacked) ? pick("drips some wax on [attacked.p_their()] [breasts ? "breasts" : "nipples"], releasing all [attacked.p_their()] lustness", "drips some wax right on [attacked.p_their()] [breasts ? "tits" : "chest"], making [attacked.p_their()] feel faint.") : pick("pours the wax that is slowly dripping from [src] onto [attacked]'s [breasts ? "breasts" : "nipples"], [attacked.p_they()] shows pure enjoyment.", "tilts the candle. Right in the moment when wax drips on [attacked]'s [breasts ? "breasts" : "nipples"], [attacked.p_they()] shivers", "tilts the candle. Just when hot drops of wax fell on [attacked]'s [breasts ? "breasts" : "nipples"], [attacked.p_they()] quietly moans in pleasure")
- attacked.adjustPain(PAIN_DEFAULT * 0.66)
+ var/breasts_or_nipples = breasts ? ORGAN_SLOT_BREASTS : ORGAN_SLOT_NIPPLES
+ message = (user == attacked) ? pick("drips some wax on [attacked.p_their()] [breasts_or_nipples], releasing all [attacked.p_their()] lustness", "drips some wax right on [attacked.p_their()] [breasts ? "tits" : "chest"], making [attacked.p_their()] feel faint.") : pick("pours the wax that is slowly dripping from [src] onto [attacked]'s [breasts_or_nipples], [attacked.p_they()] shows pure enjoyment.", "tilts the candle. Right in the moment when wax drips on [attacked]'s [breasts_or_nipples], [attacked.p_they()] shivers", "tilts the candle. Just when hot drops of wax fell on [attacked]'s [breasts_or_nipples], [attacked.p_they()] quietly moans in pleasure")
+ attacked.adjust_pain(PAIN_DEFAULT * 0.66)
else
to_chat(user, span_danger("Looks like [attacked]'s chest is covered!"))
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibrator.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibrator.dm
index 92fd8b4087a..2277e464181 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibrator.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibrator.dm
@@ -10,7 +10,7 @@
icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_items.dmi'
lefthand_file = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_inhands/lewd_inhand_left.dmi'
righthand_file = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_inhands/lewd_inhand_right.dmi'
- slot_flags = ITEM_SLOT_VAGINA | ITEM_SLOT_ANUS
+ lewd_slot_flags = LEWD_SLOT_VAGINA | LEWD_SLOT_ANUS
/// If the toy is on or not
var/toy_on = FALSE
/// Current color of the toy, can be changed and affects sprite
@@ -94,17 +94,17 @@
return
if(toy_on)
if(vibration_mode == "low" && user.arousal < 40) //prevent non-stop cumming from wearing this thing
- user.adjustArousal(0.7 * delta_time)
- user.adjustPleasure(0.7 * delta_time)
+ user.adjust_arousal(0.7 * delta_time)
+ user.adjust_pleasure(0.7 * delta_time)
if(vibration_mode == "medium" && user.arousal < 70)
- user.adjustArousal(1 * delta_time)
- user.adjustPleasure(1 * delta_time)
+ user.adjust_arousal(1 * delta_time)
+ user.adjust_pleasure(1 * delta_time)
if(vibration_mode == "hard") //no mercy
- user.adjustArousal(1.5 * delta_time)
- user.adjustPleasure(1.5 * delta_time)
+ user.adjust_arousal(1.5 * delta_time)
+ user.adjust_pleasure(1.5 * delta_time)
else if(!toy_on && user.arousal < 30)
- user.adjustArousal(0.5 * delta_time)
- user.adjustPleasure(0.5 * delta_time)
+ user.adjust_arousal(0.5 * delta_time)
+ user.adjust_pleasure(0.5 * delta_time)
//SHITCODESHITCODESHITCODESHITCODESHITCODESHITCODESHITCODESHITCODESHITCODESHITCODESHITCODESHITCODESHITCODESHITCODESHITCODESHITCODE
/obj/item/clothing/sextoy/vibrator/attack(mob/living/carbon/human/target, mob/living/carbon/human/user)
@@ -128,18 +128,18 @@
var/obj/item/organ/external/genital/vagina = target.getorganslot(ORGAN_SLOT_VAGINA)
if((vagina && penis) && (vagina.visibility_preference == GENITAL_ALWAYS_SHOW && penis.visibility_preference == GENITAL_ALWAYS_SHOW || target.is_bottomless()))
message = (user == target) ? pick("massages their vagina with the [src]", "[vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] teases their pussy with [src]", "massages their penis with the [src]", "[vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] teases their penis with [src]") : pick("[vibration_mode == "low" ? "delicately" : ""][vibration_mode = "hard" ? "aggressively" : ""] massages [target]'s vagina with [src]", "uses [src] to [vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] massage [target]'s crotch", "leans the massager against [target]'s pussy", "[vibration_mode == "low" ? "delicately" : ""][vibration_mode = "hard" ? "aggressively" : ""] massages [target]'s penis with [src]", "uses [src] to [vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] massage [target]'s penis", "leans the massager against [target]'s penis")
- target.adjustArousal(DEFAULT_AROUSAL_INCREASE)
- target.adjustPleasure(DEFAULT_PLEASURE_INCREASE)
+ target.adjust_arousal(DEFAULT_AROUSAL_INCREASE)
+ target.adjust_pleasure(DEFAULT_PLEASURE_INCREASE)
else if(vagina && (vagina.visibility_preference == GENITAL_ALWAYS_SHOW || target.is_bottomless()))
message = (user == target) ? pick("massages their vagina with the [src]", "[vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] teases their pussy with [src]") : pick("[vibration_mode == "low" ? "delicately" : ""][vibration_mode = "hard" ? "aggressively" : ""] massages [target]'s vagina with [src]", "uses [src] to [vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] massage [target]'s crotch", "leans the massager against [target]'s pussy")
- target.adjustArousal(DEFAULT_AROUSAL_INCREASE)
- target.adjustPleasure(DEFAULT_PLEASURE_INCREASE)
+ target.adjust_arousal(DEFAULT_AROUSAL_INCREASE)
+ target.adjust_pleasure(DEFAULT_PLEASURE_INCREASE)
else if(penis && (penis.visibility_preference == GENITAL_ALWAYS_SHOW || target.is_bottomless()))
message = (user == target) ? pick("massages their penis with the [src]", "[vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] teases their penis with [src]") : pick("[vibration_mode == "low" ? "delicately" : ""][vibration_mode = "hard" ? "aggressively" : ""] massages [target]'s penis with [src]", "uses [src] to [vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] massage [target]'s penis", "leans the massager against [target]'s penis")
- target.adjustArousal(DEFAULT_AROUSAL_INCREASE)
- target.adjustPleasure(DEFAULT_PLEASURE_INCREASE)
+ target.adjust_arousal(DEFAULT_AROUSAL_INCREASE)
+ target.adjust_pleasure(DEFAULT_PLEASURE_INCREASE)
else
to_chat(user, span_danger("Looks like [target]'s groin is covered!"))
@@ -152,9 +152,10 @@
targetedsomewhere = TRUE
var/obj/item/organ/external/genital/breasts = target.getorganslot(ORGAN_SLOT_BREASTS)
if(target.is_topless() || breasts.visibility_preference == GENITAL_ALWAYS_SHOW)
- message = (user == target) ? pick("massages their [breasts ? "breasts" : "nipples"] with the [src]", "[vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] teases their tits with [src]") : pick("[vibration_mode == "low" ? "delicately" : ""][vibration_mode = "hard" ? "aggressively" : ""] teases [target]'s [breasts ? "breasts" : "nipples"] with [src]", "uses [src] to[vibration_mode == "low" ? " slowly" : ""] massage [target]'s [breasts ? "tits" : "nipples"]", "uses [src] to tease [target]'s [breasts ? "boobs" : "nipples"]", "rubs [target]'s [breasts ? "tits" : "nipples"] with [src]")
- target.adjustArousal(DEFAULT_AROUSAL_INCREASE)
- target.adjustPleasure(DEFAULT_PLEASURE_INCREASE * 0.5)
+ var/breasts_or_nipples = breasts ? ORGAN_SLOT_BREASTS : ORGAN_SLOT_NIPPLES
+ message = (user == target) ? pick("massages their [breasts_or_nipples] with the [src]", "[vibration_mode == "low" ? "gently" : ""][vibration_mode = "hard" ? "roughly" : ""] teases their tits with [src]") : pick("[vibration_mode == "low" ? "delicately" : ""][vibration_mode = "hard" ? "aggressively" : ""] teases [target]'s [breasts_or_nipples] with [src]", "uses [src] to[vibration_mode == "low" ? " slowly" : ""] massage [target]'s [breasts ? "tits" : ORGAN_SLOT_NIPPLES]", "uses [src] to tease [target]'s [breasts ? "boobs" : ORGAN_SLOT_NIPPLES]", "rubs [target]'s [breasts ? "tits" : ORGAN_SLOT_NIPPLES] with [src]")
+ target.adjust_arousal(DEFAULT_AROUSAL_INCREASE)
+ target.adjust_pleasure(DEFAULT_PLEASURE_INCREASE * 0.5)
if(prob(30) && (target.stat != DEAD))
target.try_lewd_autoemote(pick("twitch_s", "moan"))
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibroring.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibroring.dm
index 75cadfcfd11..4d0ab49232b 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibroring.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibroring.dm
@@ -14,7 +14,7 @@
/// Looping sound called on process
var/datum/looping_sound/vibrator/medium/soundloop
w_class = WEIGHT_CLASS_TINY
- slot_flags = ITEM_SLOT_PENIS
+ lewd_slot_flags = LEWD_SLOT_PENIS
clothing_flags = INEDIBLE_CLOTHING
/obj/item/clothing/sextoy/vibroring/attack_self(mob/user)
@@ -84,7 +84,7 @@
var/obj/item/organ/external/genital/testicles/balls = user.getorganslot(ORGAN_SLOT_PENIS)
if(!toy_on || !balls)
return
- user.adjustArousal(1 * delta_time)
- user.adjustPleasure(1 * delta_time)
+ user.adjust_arousal(1 * delta_time)
+ user.adjust_pleasure(1 * delta_time)
if(balls.aroused != AROUSAL_CANT)
balls.aroused = AROUSAL_FULL //Vibroring keep penis erected.
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/lustwish.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm
similarity index 95%
rename from modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/lustwish.dm
rename to modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm
index 52c558cea8e..98eeed47403 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/lustwish.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm
@@ -97,8 +97,8 @@
/obj/item/reagent_containers/pill/camphor = 10,
/obj/item/reagent_containers/cup/bottle/crocin = 6,
/obj/item/reagent_containers/cup/bottle/camphor = 3,
- /obj/item/reagent_containers/cup/bottle/breast_enlarger = 6, //Those are legal 'cause you can just turn off prefs in round in "CLOWN SMOKE MACHINE+PENIS ENLARGEMENT CHEMICAL CASE". Yes, i have special code-phrase for this. I've seen some shit.
- /obj/item/reagent_containers/cup/bottle/penis_enlarger = 6,
+ /obj/item/reagent_containers/cup/bottle/succubus_milk = 6, //Those are legal 'cause you can just turn off prefs in round in "CLOWN SMOKE MACHINE+PENIS ENLARGEMENT CHEMICAL CASE". Yes, i have special code-phrase for this. I've seen some shit.
+ /obj/item/reagent_containers/cup/bottle/incubus_draft = 6,
//special
/obj/item/clothing/glasses/nice_goggles = 1, //easter egg, don't touch plz)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/milking_machine.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/milking_machine.dm
index 39f41cf8873..f6a7acbefd6 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/milking_machine.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/milking_machine.dm
@@ -530,13 +530,6 @@
used_item.play_tool_sound(src, 50)
deconstruct(TRUE)
-// // Object disassembly handler by wrench
-// /obj/structure/chair/milking_machine/default_unfasten_wrench(mob/user, obj/item/used_item, time = 20)
-// . = !(flags_1 & NODECONSTRUCT_1) && used_item.tool_behaviour == TOOL_WRENCH
-// if(.)
-// used_item.play_tool_sound(src, 50)
-// deconstruct(TRUE)
-
// Machine Workflow Processor
/obj/structure/chair/milking_machine/process(delta_time)
@@ -546,8 +539,6 @@
pump_state = pump_state_list[1]
update_all_visuals()
return
-// if(current_mode == mode_list[1] && pump_state == pump_state_list[1])
-// cell.give(charge_rate * delta_time)
// Check if the machine should work
if(!current_mob)
@@ -600,17 +591,17 @@
if(istype(current_selected_organ, /obj/item/organ/external/genital/breasts))
if(current_selected_organ.reagents.total_volume > 0)
- current_selected_organ.internal_fluids.trans_to(milk_vessel, milk_retrive_amount[current_mode] * fluid_multiplier * delta_time)
+ current_selected_organ.transfer_internal_fluid(milk_vessel, milk_retrive_amount[current_mode] * fluid_multiplier * delta_time)
else
return
else if (istype(current_selected_organ, /obj/item/organ/external/genital/vagina))
if(current_selected_organ.reagents.total_volume > 0)
- current_selected_organ.internal_fluids.trans_to(girlcum_vessel, girlcum_retrive_amount[current_mode] * fluid_multiplier * delta_time)
+ current_selected_organ.transfer_internal_fluid(girlcum_vessel, girlcum_retrive_amount[current_mode] * fluid_multiplier * delta_time)
else
return
else if (istype(current_selected_organ, /obj/item/organ/external/genital/testicles))
if(current_selected_organ.reagents.total_volume > 0)
- current_selected_organ.internal_fluids.trans_to(semen_vessel, semen_retrive_amount[current_mode] * fluid_multiplier * delta_time)
+ current_selected_organ.transfer_internal_fluid(semen_vessel, semen_retrive_amount[current_mode] * fluid_multiplier * delta_time)
else
return
else
@@ -619,9 +610,9 @@
// Handling the process of the impact of the machine on the organs of the mob
/obj/structure/chair/milking_machine/proc/increase_current_mob_arousal(delta_time)
- src.current_mob.adjustArousal(src.arousal_amounts[src.current_mode] * delta_time)
- src.current_mob.adjustPleasure(src.pleasure_amounts[src.current_mode] * delta_time)
- src.current_mob.adjustPain(src.pain_amounts[src.current_mode] * delta_time)
+ current_mob.adjust_arousal(arousal_amounts[current_mode] * delta_time)
+ current_mob.adjust_pleasure(pleasure_amounts[current_mode] * delta_time)
+ current_mob.adjust_pain(pain_amounts[current_mode] * delta_time)
// Energy consumption processor
/obj/structure/chair/milking_machine/proc/draw_power_from_cell(delta_time)
@@ -765,7 +756,7 @@
organ_overlay.icon_state = organ_overlay_new_icon_state
if(istype(current_selected_organ, /obj/item/organ/external/genital/testicles))
- current_selected_organ_type = "penis"
+ current_selected_organ_type = ORGAN_SLOT_PENIS
current_selected_organ_size = current_selected_organ.genital_size
if(current_mode == mode_list[1])
pump_state = pump_state_list[1]
@@ -779,7 +770,7 @@
organ_overlay.icon_state = organ_overlay_new_icon_state
if(istype(current_selected_organ, /obj/item/organ/external/genital/vagina))
- current_selected_organ_type = "vagina"
+ current_selected_organ_type = ORGAN_SLOT_VAGINA
current_selected_organ_size = current_selected_organ.genital_size
if(current_mode == mode_list[1])
pump_state = pump_state_list[1]
@@ -859,12 +850,6 @@
/obj/structure/chair/milking_machine/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
- /* Standard behavior. Uncomment for UI debugging
- if(!ui)
- ui = new(user, src, "MilkingMachine", name)
- ui.open()
- */
-
//Block the interface if we are in the machine. Use in production
if(LAZYLEN(buckled_mobs))
if(user != src.buckled_mobs[1])
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_organs/_genital.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_organs/_genital.dm
new file mode 100644
index 00000000000..5e199453db0
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_organs/_genital.dm
@@ -0,0 +1,30 @@
+/obj/item/organ/external/genital
+ /// The fluid count of the genital.
+ var/internal_fluid_count = 0
+
+ /// The maximum amount of fluid that can be stored in the genital.
+ var/internal_fluid_maximum = 0
+
+ /// The datum to be used for the tracked fluid, should it need to be added to a fluid container.
+ var/internal_fluid_datum
+
+ /// The currently inserted sex toy.
+ var/obj/item/inserted_item
+
+/// Helper proc for checking if internal fluids are full or not.
+/obj/item/organ/external/genital/proc/internal_fluid_full()
+ return internal_fluid_count >= internal_fluid_maximum
+
+/// Adds the given amount to the internal fluid count, clamping it between 0 and internal_fluid_maximum.
+/obj/item/organ/external/genital/proc/adjust_internal_fluid(amount)
+ internal_fluid_count = clamp(internal_fluid_count + amount, 0, internal_fluid_maximum)
+
+/// Tries to add the specified amount to the target reagent container. Keeps in mind internal_fluid_count.
+/obj/item/organ/external/genital/proc/transfer_internal_fluid(datum/reagents/reagent_container, attempt_amount)
+ if(!internal_fluid_datum || !internal_fluid_count || !internal_fluid_maximum)
+ return
+
+ attempt_amount = clamp(attempt_amount, 0, internal_fluid_count)
+ reagent_container.add_reagent(internal_fluid_datum, attempt_amount)
+ internal_fluid_count -= attempt_amount
+
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_organs/breasts.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_organs/breasts.dm
new file mode 100644
index 00000000000..8899db9d003
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_organs/breasts.dm
@@ -0,0 +1,18 @@
+/obj/item/organ/external/genital/breasts
+ internal_fluid_datum = /datum/reagent/consumable/breast_milk
+
+/obj/item/organ/external/genital/breasts/build_from_dna(datum/dna/DNA, associated_key)
+ . = ..()
+ var/breasts_capacity = 0
+ var/size = 0.5
+ if(DNA.features["breasts_size"] > 0)
+ size = DNA.features["breasts_size"]
+
+ switch(genital_type)
+ if("pair")
+ breasts_capacity = 2
+ if("quad")
+ breasts_capacity = 2.5
+ if("sextuple")
+ breasts_capacity = 3
+ internal_fluid_maximum = size * breasts_capacity * 60 // This seems like it could balloon drastically out of proportion with larger breast sizes.
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_organs/testicles.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_organs/testicles.dm
new file mode 100644
index 00000000000..e9864538174
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_organs/testicles.dm
@@ -0,0 +1,10 @@
+/obj/item/organ/external/genital/testicles
+ internal_fluid_datum = /datum/reagent/consumable/cum
+
+/obj/item/organ/external/genital/testicles/build_from_dna(datum/dna/DNA, associated_key)
+ . = ..()
+ var/size = 0.5
+ if(DNA.features["balls_size"] > 0)
+ size = DNA.features["balls_size"]
+
+ internal_fluid_maximum = size * 20
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_organs/vagina.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_organs/vagina.dm
new file mode 100644
index 00000000000..c74995845a1
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_organs/vagina.dm
@@ -0,0 +1,6 @@
+/obj/item/organ/external/genital/vagina
+ internal_fluid_datum = /datum/reagent/consumable/femcum
+
+/obj/item/organ/external/genital/vagina/build_from_dna(datum/dna/DNA, associated_key)
+ . = ..()
+ internal_fluid_maximum = 10
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_quirks.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_quirks.dm
index ac9faa8f5cb..ea66041e467 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_quirks.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_quirks.dm
@@ -93,9 +93,9 @@
else
stress = clamp(stress + 1, 0, 300)
- human_owner.adjustArousal(10)
+ human_owner.adjust_arousal(10)
if(human_owner.pleasure < 80)
- human_owner.adjustPleasure(5)
+ human_owner.adjust_pleasure(5)
//Anything beyond this obeys a cooldown system because we don't want to spam it
if(!COOLDOWN_FINISHED(src, desire_cooldown))
@@ -260,7 +260,7 @@
/datum/brain_trauma/very_special/sadism/on_life(delta_time, times_fired)
var/mob/living/carbon/human/affected_mob = owner
if(someone_suffering() && affected_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp))
- affected_mob.adjustArousal(2)
+ affected_mob.adjust_arousal(2)
owner.add_mood_event("sadistic", /datum/mood_event/sadistic)
else
owner.clear_mood_event("sadistic")
@@ -327,19 +327,22 @@
/mob/living/carbon/human/examine(mob/user)
. = ..()
var/mob/living/examiner = user
- if(stat != DEAD && !HAS_TRAIT(src, TRAIT_FAKEDEATH) && src != examiner)
- if(src != user)
- if(HAS_TRAIT(examiner, TRAIT_EMPATH))
- switch(arousal)
- if(11 to 21)
- . += span_purple("[p_they()] [p_are()] excited.") + "\n"
- if(21.01 to 41)
- . += span_purple("[p_they()] [p_are()] slightly blushed.") + "\n"
- if(41.01 to 51)
- . += span_purple("[p_they()] [p_are()] quite aroused and seems to be stirring up lewd thoughts in [p_their()] head.") + "\n"
- if(51.01 to 61)
- . += span_purple("[p_they()] [p_are()] very aroused and [p_their()] movements are seducing.") + "\n"
- if(61.01 to 91)
- . += span_purple("[p_they()] [p_are()] aroused as hell.") + "\n"
- if(91.01 to INFINITY)
- . += span_purple("[p_they()] [p_are()] extremely excited, exhausting from entolerable desire.") + "\n"
+ if(stat >= DEAD || HAS_TRAIT(src, TRAIT_FAKEDEATH) || src == examiner || !HAS_TRAIT(examiner, TRAIT_EMPATH))
+ return
+
+ if(examiner.client?.prefs?.read_preference(/datum/preference/toggle/erp))
+ var/arousal_message
+ switch(arousal)
+ if(AROUSAL_MINIMUM_DETECTABLE to AROUSAL_LOW)
+ arousal_message = span_purple("[p_they()] [p_are()] slightly blushed.") + "\n"
+ if(AROUSAL_LOW to AROUSAL_MEDIUM)
+ arousal_message = span_purple("[p_they()] [p_are()] quite aroused and seems to be stirring up lewd thoughts in [p_their()] head.") + "\n"
+ if(AROUSAL_HIGH to AROUSAL_AUTO_CLIMAX_THRESHOLD)
+ arousal_message = span_purple("[p_they()] [p_are()] aroused as hell.") + "\n"
+ if(AROUSAL_AUTO_CLIMAX_THRESHOLD to INFINITY)
+ arousal_message = span_purple("[p_they()] [p_are()] extremely excited, exhausting from entolerable desire.") + "\n"
+ if(arousal_message)
+ . += arousal_message
+ else if(arousal > AROUSAL_MINIMUM_DETECTABLE)
+ . += span_purple("[p_they()] [p_are()] slightly blushed.") + "\n"
+
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/screen_icons.dm b/modular_skyrat/modules/modular_items/lewd_items/code/screen_icons.dm
new file mode 100644
index 00000000000..63aa2c1452c
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/screen_icons.dm
@@ -0,0 +1,24 @@
+//screen alert
+
+/atom/movable/screen/alert/aroused
+ name = "Aroused"
+ desc = "It's a little hot in here"
+ icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_icons.dmi'
+ icon_state = "arousal_small"
+ var/mutable_appearance/pain_overlay
+ var/mutable_appearance/pleasure_overlay
+ var/pain_level = "small"
+ var/pleasure_level = "small"
+
+/atom/movable/screen/alert/aroused/Initialize(mapload)
+ .=..()
+ pain_overlay = update_pain()
+ pleasure_overlay = update_pleasure()
+
+/atom/movable/screen/alert/aroused/proc/update_pain()
+ if(pain_level)
+ return mutable_appearance(icon, "pain_[pain_level]")
+
+/atom/movable/screen/alert/aroused/proc/update_pleasure()
+ if(pleasure_level)
+ return mutable_appearance(icon, "pleasure_[pleasure_level]")
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/technical_stuff_for_lewd.dm b/modular_skyrat/modules/modular_items/lewd_items/code/technical_stuff_for_lewd.dm
deleted file mode 100644
index b9f5dbb14b8..00000000000
--- a/modular_skyrat/modules/modular_items/lewd_items/code/technical_stuff_for_lewd.dm
+++ /dev/null
@@ -1,1206 +0,0 @@
-// Moved from my old interactions file 'cause skyrats already did interactions
-
-#define REQUIRE_NONE 0
-#define REQUIRE_EXPOSED 1
-#define REQUIRE_UNEXPOSED 2
-#define REQUIRE_ANY 3
-
-/mob/living/carbon/human
- var/has_penis = FALSE
- var/has_vagina = FALSE
- var/has_breasts = FALSE
- var/has_anus = FALSE
-
-/*
-* Looping sound for vibrating stuff
-*/
-
-/datum/looping_sound/vibrator
- start_sound = 'modular_skyrat/modules/modular_items/lewd_items/sounds/bzzz-loop-1.ogg'
- start_length = 1
- mid_sounds = 'modular_skyrat/modules/modular_items/lewd_items/sounds/bzzz-loop-1.ogg'
- mid_length = 1
- end_sound = 'modular_skyrat/modules/modular_items/lewd_items/sounds/bzzz-loop-1.ogg'
- falloff_distance = 1
- falloff_exponent = 5
- extra_range = SILENCED_SOUND_EXTRARANGE
- ignore_walls = FALSE
-
-/datum/looping_sound/vibrator/low
- volume = 80
-
-/datum/looping_sound/vibrator/medium
- volume = 90
-
-/datum/looping_sound/vibrator/high
- volume = 100
-
-/*
-* Boxes for vending machine, to spawn stuff with important cheap tools in pack
-*/
-
-// Milking machine
-/obj/item/storage/box/milking_kit
- name = "DIY milking machine kit"
- desc = "Contains everything you need to build your own milking machine!"
-
-/obj/item/storage/box/milking_kit/PopulateContents()
- var/static/items_inside = list(
- /obj/item/milking_machine/constructionkit = 1)
- generate_items_inside(items_inside, src)
-
-// X-Stand
-/obj/item/storage/box/xstand_kit
- name = "DIY x-stand kit"
- desc = "Contains everything you need to build your own X-stand!"
-
-/obj/item/storage/box/xstand_kit/PopulateContents()
- var/static/items_inside = list(
- /obj/item/x_stand_kit = 1)
- generate_items_inside(items_inside, src)
-
-// BDSM bed
-/obj/item/storage/box/bdsmbed_kit
- name = "DIY BDSM bed kit"
- desc = "Contains everything you need to build your own BDSM bed!"
-
-/obj/item/storage/box/bdsmbed_kit/PopulateContents()
- var/static/items_inside = list(
- /obj/item/bdsm_bed_kit = 1)
- generate_items_inside(items_inside, src)
-
-// Striptease pole
-/obj/item/storage/box/strippole_kit
- name = "DIY stripper pole kit"
- desc = "Contains everything you need to build your own stripper pole!"
-
-/obj/item/storage/box/strippole_kit/PopulateContents()
- var/static/items_inside = list(
- /obj/item/polepack = 1)
- generate_items_inside(items_inside, src)
-
-// Shibari stand
-/obj/item/storage/box/shibari_stand
- name = "DIY shibari stand kit"
- desc = "Contains everything you need to build your own shibari stand!"
-
-/obj/item/storage/box/shibari_stand/PopulateContents()
- var/static/items_inside = list(
- /obj/item/shibari_stand_kit = 1,
- /obj/item/paper/shibari_kit_instructions = 1)
- generate_items_inside(items_inside, src)
-
-// Paper instructions for shibari kit
-
-/obj/item/paper/shibari_kit_instructions
- default_raw_text = "Hello! Congratulations on your purchase of the shibari kit by LustWish! Some newbies may get confused by our ropes, so we prepared a small instructions for you! First of all, you have to have a wrench to construct the stand itself. Secondly, you can use screwdrivers to change the color of your shibari stand. Just replace the plastic fittings! Thirdly, if you want to tie somebody to a bondage stand you need to fully tie their body, on both groin and chest!. To do that you need to use rope on body and then on groin of character, then you can just buckle them to the stand like any chair. Don't forget to have some ropes on your hand to actually tie them to the stand, as there's no ropes included with it! And that's it!"
-
-/*
-* This code is supposed to be placed in "code/modules/mob/living/carbon/human/inventory.dm"
-* If you are nice person you can transfer this part of code to it, but i didn't for modularisation reasons
-*/
-
-/mob/living/carbon/human/resist_restraints()
- if(gloves?.breakouttime)
- changeNext_move(CLICK_CD_BREAKOUT)
- last_special = world.time + CLICK_CD_BREAKOUT
- cuff_resist(gloves)
- else
- ..()
-
-/*
-* I needed this code for ballgag, because it doesn't muzzle, it kinda voxbox
-* wearer for moaning. So i really need it, don't touch or whole ballgag will be broken
-* for ballgag mute audible emotes
-* adding is_ballgagged() proc here. Hope won't break anything important.
-* This is kinda shitcode, but they said don't touch main code or they will break my knees.
-* i love my knees, please merge.
-* more shitcode can be found in code/datums/emotes.dm
-* in /datum/emote/proc/select_message_type(mob/user, intentional) proc. Sorry for that, i had no other choise.
-*/
-
-//false for default
-/mob/proc/is_ballgagged()
- return FALSE
-
-/mob/living/carbon/is_ballgagged()
- return(istype(src.wear_mask, /obj/item/clothing/mask/ballgag) || istype(src.wear_mask, /obj/item/clothing/head/helmet/space/deprivation_helmet))
-
-//proc for condoms. Need to prevent cum appearing on the floor.
-/mob/proc/wear_condom()
- return FALSE
-
-/mob/living/carbon/human/wear_condom()
- . = ..()
- if(.)
- return TRUE
- if(penis != null && istype(penis, /obj/item/clothing/sextoy/condom))
- return TRUE
- return FALSE
-
-/*
-* This shouldn't be put anywhere, get your dirty hands off!
-* For dancing pole
-*/
-
-/atom
- var/pseudo_z_axis
-
-/atom/proc/get_fake_z()
- return pseudo_z_axis
-
-/obj/structure/table
- pseudo_z_axis = 8
-
-/turf/open/get_fake_z()
- var/objschecked
- for(var/obj/structure/structurestocheck in contents)
- objschecked++
- if(structurestocheck.pseudo_z_axis)
- return structurestocheck.pseudo_z_axis
- if(objschecked >= 25)
- break
- return pseudo_z_axis
-
-/mob/living/Move(atom/newloc, direct)
- . = ..()
- if(.)
- pseudo_z_axis = newloc.get_fake_z()
- pixel_z = pseudo_z_axis
-
-/*
-* This code needed to determine if the human is naked in that part of body or not
-* You can you for your own stuff if you want, haha.
-*/
-
-
-/// Are we wearing something that covers our chest?
-/mob/living/carbon/human/proc/is_topless()
- return (!(wear_suit) || !(wear_suit.body_parts_covered & CHEST)) && (!(w_uniform) || !(w_uniform.body_parts_covered & CHEST))
-
-/// Are we wearing something that covers our groin?
-/mob/living/carbon/human/proc/is_bottomless()
- return (!(wear_suit) || !(wear_suit.body_parts_covered & GROIN)) && (!(w_uniform) || !(w_uniform.body_parts_covered & GROIN))
-
-/// Are we wearing something that covers our shoes?
-/mob/living/carbon/human/proc/is_barefoot()
- return (!(wear_suit) || !(wear_suit.body_parts_covered & GROIN)) && (!(shoes) || !(shoes.body_parts_covered & FEET))
-
-/mob/living/carbon/human/proc/is_hands_uncovered()
- return (gloves?.body_parts_covered & ARMS)
-
-/mob/living/carbon/human/proc/is_head_uncovered()
- return (head?.body_parts_covered & HEAD)
-
-/mob/living/carbon/human/proc/has_penis(required_state = REQUIRE_ANY)
- if(issilicon(src) && has_penis)
- return TRUE
- var/obj/item/organ/external/genital/peepee = getorganslot(ORGAN_SLOT_PENIS)
- if(peepee)
- switch(required_state)
- if(REQUIRE_ANY)
- return TRUE
- if(REQUIRE_EXPOSED)
- if(peepee.visibility_preference == GENITAL_ALWAYS_SHOW || is_bottomless())
- return TRUE
- else
- return FALSE
- if(REQUIRE_UNEXPOSED)
- if(peepee.visibility_preference != GENITAL_ALWAYS_SHOW && !is_bottomless())
- return TRUE
- else
- return FALSE
- else
- return TRUE
- return FALSE
-
-/mob/living/carbon/human/proc/has_balls(required_state = REQUIRE_ANY)
- var/obj/item/organ/external/genital/peepee = getorganslot(ORGAN_SLOT_TESTICLES)
- if(peepee)
- switch(required_state)
- if(REQUIRE_ANY)
- return TRUE
- if(REQUIRE_EXPOSED)
- if(peepee.visibility_preference == GENITAL_ALWAYS_SHOW || is_bottomless())
- return TRUE
- else
- return FALSE
- if(REQUIRE_UNEXPOSED)
- if(peepee.visibility_preference != GENITAL_ALWAYS_SHOW && !is_bottomless())
- return TRUE
- else
- return FALSE
- else
- return TRUE
- return FALSE
-
-/mob/living/carbon/human/proc/has_vagina(required_state = REQUIRE_ANY)
- if(issilicon(src) && has_vagina)
- return TRUE
- var/obj/item/organ/external/genital/peepee = getorganslot(ORGAN_SLOT_VAGINA)
- if(peepee)
- switch(required_state)
- if(REQUIRE_ANY)
- return TRUE
- if(REQUIRE_EXPOSED)
- if(peepee.visibility_preference == GENITAL_ALWAYS_SHOW || is_bottomless())
- return TRUE
- else
- return FALSE
- if(REQUIRE_UNEXPOSED)
- if(peepee.visibility_preference != GENITAL_ALWAYS_SHOW && !is_bottomless())
- return TRUE
- else
- return FALSE
- else
- return TRUE
- return FALSE
-
-/mob/living/carbon/human/proc/has_breasts(required_state = REQUIRE_ANY)
- var/obj/item/organ/external/genital/peepee = getorganslot(ORGAN_SLOT_BREASTS)
- if(peepee)
- switch(required_state)
- if(REQUIRE_ANY)
- return TRUE
- if(REQUIRE_EXPOSED)
- if(peepee.visibility_preference == GENITAL_ALWAYS_SHOW || is_topless())
- return TRUE
- else
- return FALSE
- if(REQUIRE_UNEXPOSED)
- if(peepee.visibility_preference != GENITAL_ALWAYS_SHOW && !is_topless())
- return TRUE
- else
- return FALSE
- else
- return TRUE
- return FALSE
-
-/mob/living/carbon/human/proc/has_anus(required_state = REQUIRE_ANY)
- if(issilicon(src))
- return TRUE
- var/obj/item/organ/external/genital/peepee = getorganslot(ORGAN_SLOT_ANUS)
- if(peepee)
- switch(required_state)
- if(REQUIRE_ANY)
- return TRUE
- if(REQUIRE_EXPOSED)
- if(peepee.visibility_preference == GENITAL_ALWAYS_SHOW || is_bottomless())
- return TRUE
- else
- return FALSE
- if(REQUIRE_UNEXPOSED)
- if(peepee.visibility_preference != GENITAL_ALWAYS_SHOW && !is_bottomless())
- return TRUE
- else
- return FALSE
- else
- return TRUE
-
-/mob/living/carbon/human/proc/has_arms(required_state = REQUIRE_ANY)
- var/hand_count = 0
- var/covered = 0
- var/is_covered = FALSE
- for(var/obj/item/bodypart/l_arm/left_arm in bodyparts)
- hand_count++
- for(var/obj/item/bodypart/r_arm/right_arm in bodyparts)
- hand_count++
- if(get_item_by_slot(ITEM_SLOT_HANDS))
- var/obj/item/clothing/gloves/worn_gloves = get_item_by_slot(ITEM_SLOT_HANDS)
- covered = worn_gloves.body_parts_covered
- if(covered & HANDS)
- is_covered = TRUE
- switch(required_state)
- if(REQUIRE_ANY)
- return hand_count
- if(REQUIRE_EXPOSED)
- if(is_covered)
- return FALSE
- else
- return hand_count
- if(REQUIRE_UNEXPOSED)
- if(!is_covered)
- return FALSE
- else
- return hand_count
- else
- return hand_count
-
-/mob/living/carbon/human/proc/has_feet(required_state = REQUIRE_ANY)
- var/feet_count = 0
- var/covered = 0
- var/is_covered = FALSE
- for(var/obj/item/bodypart/l_leg/left_leg in bodyparts)
- feet_count++
- for(var/obj/item/bodypart/r_leg/right_leg in bodyparts)
- feet_count++
- if(!is_barefoot())
- covered = TRUE
- if(covered)
- is_covered = TRUE
- switch(required_state)
- if(REQUIRE_ANY)
- return feet_count
- if(REQUIRE_EXPOSED)
- if(is_covered)
- return FALSE
- else
- return feet_count
- if(REQUIRE_UNEXPOSED)
- if(!is_covered)
- return FALSE
- else
- return feet_count
- else
- return feet_count
-
-/mob/living/carbon/human/proc/get_num_feet()
- return has_feet(REQUIRE_ANY)
-
-// Weird procs go here
-/mob/living/carbon/human/proc/has_ears(required_state = REQUIRE_ANY)
- var/obj/item/organ/peepee = getorganslot(ORGAN_SLOT_EARS)
- if(peepee)
- switch(required_state)
- if(REQUIRE_ANY)
- return TRUE
- if(REQUIRE_EXPOSED)
- if(get_item_by_slot(ITEM_SLOT_EARS))
- return FALSE
- else
- return TRUE
- if(REQUIRE_UNEXPOSED)
- if(!get_item_by_slot(ITEM_SLOT_EARS))
- return FALSE
- else
- return TRUE
- else
- return TRUE
- return FALSE
-
-/mob/living/carbon/human/proc/has_earsockets(required_state = REQUIRE_ANY)
- var/obj/item/organ/peepee = getorganslot(ORGAN_SLOT_EARS)
- if(!peepee)
- switch(required_state)
- if(REQUIRE_ANY)
- return TRUE
- if(REQUIRE_EXPOSED)
- if(get_item_by_slot(ITEM_SLOT_EARS))
- return FALSE
- else
- return TRUE
- if(REQUIRE_UNEXPOSED)
- if(!get_item_by_slot(ITEM_SLOT_EARS))
- return FALSE
- else
- return TRUE
- else
- return TRUE
- return FALSE
-
-/mob/living/carbon/human/proc/has_eyes(required_state = REQUIRE_ANY)
- var/obj/item/organ/peepee = getorganslot(ORGAN_SLOT_EYES)
- if(peepee)
- switch(required_state)
- if(REQUIRE_ANY)
- return TRUE
- if(REQUIRE_EXPOSED)
- if(get_item_by_slot(ITEM_SLOT_EYES))
- return FALSE
- else
- return TRUE
- if(REQUIRE_UNEXPOSED)
- if(!get_item_by_slot(ITEM_SLOT_EYES))
- return FALSE
- else
- return TRUE
- else
- return TRUE
- return FALSE
-
-/mob/living/carbon/human/proc/has_eyesockets(required_state = REQUIRE_ANY)
- var/obj/item/organ/peepee = getorganslot(ORGAN_SLOT_EYES)
- if(!peepee)
- switch(required_state)
- if(REQUIRE_ANY)
- return TRUE
- if(REQUIRE_EXPOSED)
- if(get_item_by_slot(ITEM_SLOT_EYES))
- return FALSE
- else
- return TRUE
- if(REQUIRE_UNEXPOSED)
- if(!get_item_by_slot(ITEM_SLOT_EYES))
- return FALSE
- else
- return TRUE
- else
- return TRUE
- return FALSE
-
-
-/*
-* This code needed for changing character's gender by chems
-*/
-
-/mob/living/carbon/human/proc/set_gender(ngender = NEUTER, silent = FALSE, update_icon = TRUE, forced = FALSE)
- if(forced || (!ckey || client?.prefs.read_preference(/datum/preference/toggle/erp/gender_change)))
- gender = ngender
- return TRUE
- return FALSE
-
-/mob/living/carbon/human/set_gender(ngender = NEUTER, silent = FALSE, update_icon = TRUE, forced = FALSE)
- var/bender = !(gender == ngender)
- . = ..()
- if(!.)
- return
- if(dna && bender)
- if(ngender == MALE || ngender == FEMALE)
- dna.features["body_model"] = ngender
- if(!silent)
- var/adj = ngender == MALE ? "masculine" : "feminine"
- visible_message(span_boldnotice("[src] suddenly looks more [adj]!"), span_boldwarning("You suddenly feel more [adj]!"))
- else if(ngender == NEUTER)
- dna.features["body_model"] = MALE
- if(update_icon)
- update_body()
-
-/*
-* INVENTORY SYSTEM EXTENTION
-*/
-
-/*
-* ERP INVENTORY ITEM SLOTS
-*/
-
-/*
-/// Vagina slot
-#define ITEM_SLOT_VAGINA (1<<21)
-
-/// Anus slot
-#define ITEM_SLOT_ANUS (1<<22)
-
-/// Nipples slot
-#define ITEM_SLOT_NIPPLES (1<<23)
-
-/// Penis slot
-#define ITEM_SLOT_PENIS (1<<20)
-*/
-
-// SLOT GROUP HELPERS
-#define ITEM_SLOT_ERP_INSERTABLE (ITEM_SLOT_VAGINA|ITEM_SLOT_ANUS)
-#define ITEM_SLOT_ERP_ATTACHABLE (ITEM_SLOT_NIPPLES|ITEM_SLOT_PENIS)
-
-// Strippable Defines
-#define ERP_SLOT_EQUIP_DELAY (5 SECONDS) // Lamella TODO: delay need to be balanced
-
-#define STRIPPABLE_ITEM_VAGINA "vagina"
-#define STRIPPABLE_ITEM_ANUS "anus"
-#define STRIPPABLE_ITEM_NIPPLES "nipples"
-#define STRIPPABLE_ITEM_PEINS "penis"
-
-/*
-* OUTFIT SYSTEM ERP SLOT SUPPORT
-*/
-
-// Variables for ERP slots
-/datum/outfit
- /// Type path of item for vagina slot
- var/vagina = null
- /// Type path of item for anus slot
- var/anus = null
- /// Type path of item for nipples slot
- var/nipples = null
- /// Type path of item for penis slot
- var/penis = null
-
-// Complementing the equipment procedure
-/datum/outfit/equip(mob/living/carbon/human/target, visualsOnly = FALSE)
- . = ..()
- if(.)
- var/need_updating = FALSE
- if(vagina)
- target.equip_to_slot_or_del(new vagina(target), ITEM_SLOT_VAGINA, TRUE)
- need_updating = TRUE
-
- if(anus)
- target.equip_to_slot_or_del(new anus(target), ITEM_SLOT_ANUS, TRUE)
- need_updating = TRUE
-
- if(nipples)
- target.equip_to_slot_or_del(new nipples(target), ITEM_SLOT_NIPPLES, TRUE)
- need_updating = TRUE
-
- if(penis)
- target.equip_to_slot_or_del(new penis(target), ITEM_SLOT_PENIS, TRUE)
- need_updating = TRUE
-
- if(need_updating)
- target.update_body()
- target?.hud_used?.hidden_inventory_update(target)
-
- return TRUE
-
- return .
-
-
-// Support fingerprints when working with ERP slots
-/datum/outfit/apply_fingerprints(mob/living/carbon/human/target)
- . = ..()
- if(.)
- if(!istype(target))
- return
- if(target.vagina)
- target.vagina.add_fingerprint(target, 1)
- if(target.anus)
- target.anus.add_fingerprint(target, 1)
- if(target.nipples)
- target.nipples.add_fingerprint(target, 1)
- if(target.penis)
- target.penis.add_fingerprint(target, 1)
- return 1
-
-// Supplementing the data structure with ERP slot data
-/datum/outfit/get_json_data()
- var/list/genital_list = ..()
-
- genital_list["vagina"] = vagina
- genital_list["anus"] = anus
- genital_list["nipples"] = nipples
- genital_list["penis"] = penis
-
-// Supplementing the data structure with ERP slot data
-/datum/outfit/load_from(list/outfit_data)
- vagina = text2path(outfit_data["vagina"])
- anus = text2path(outfit_data["anus"])
- nipples = text2path(outfit_data["nipples"])
- penis = text2path(outfit_data["penis"])
- . = ..()
-
-// Just by analogy with the TG code. No ideas for what this is.
-/mob/proc/update_inv_vagina()
- return
-/mob/proc/update_inv_anus()
- return
-/mob/proc/update_inv_nipples()
- return
-/mob/proc/update_inv_penis()
- return
-
-// Add variables for slots to the Carbon class
-/mob/living/carbon/human
- var/obj/item/vagina = null
- var/obj/item/anus = null
- var/obj/item/nipples = null
- var/obj/item/penis = null
-
-/*
-* SEXTOY CLOTH TYPE
-*/
-
-/obj/item/clothing/sextoy
- name = "sextoy"
- icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_items.dmi'
- worn_icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_items/lewd_items.dmi'
- equip_sound = 'modular_skyrat/modules/modular_items/lewd_items/sounds/bang1.ogg'
- drop_sound = 'modular_skyrat/modules/modular_items/lewd_items/sounds/bang2.ogg'
- pickup_sound = 'sound/items/handling/cloth_pickup.ogg'
- slot_flags = ITEM_SLOT_VAGINA | ITEM_SLOT_ANUS | ITEM_SLOT_PENIS | ITEM_SLOT_NIPPLES
-
-/obj/item/clothing/sextoy/dropped(mob/user)
- ..()
-
- update_appearance()
- if(!ishuman(loc))
- return
-
- var/mob/living/carbon/human/holder = loc
- holder.update_inv_vagina()
- holder.update_inv_anus()
- holder.update_inv_nipples()
- holder.update_inv_penis()
- holder.fan_hud_set_fandom()
-
-/// A check to confirm if you can open the toy's color/design radial menu
-/obj/item/clothing/sextoy/proc/check_menu(mob/living/user)
- if(!istype(user))
- return FALSE
- if(user.incapacitated())
- return FALSE
- return TRUE
-
-/*
-* ICON UPDATING EXTENTION
-*/
-
-// Regenerate ERP icons to
-/mob/living/carbon/human/regenerate_icons()
- .=..()
- update_inv_vagina()
- update_inv_anus()
- update_inv_nipples()
- update_inv_penis()
-
-// Updating vagina slot
-/mob/living/carbon/human/update_inv_vagina()
- if(client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- if(client && hud_used && hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_VAGINA) + 1])
- var/atom/movable/screen/inventory/inv = hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_VAGINA) + 1]
- inv.update_icon()
-
- if(vagina)
- if(hud_used.inventory_shown && hud_used)
- vagina?.screen_loc = ui_vagina
- else
- vagina?.screen_loc = ui_vagina_down
- if(hud_used.hud_shown)
- client.screen += vagina
- update_observer_view(vagina)
- hud_used.hidden_inventory_update(src)
-
- // on_mob stuff
- remove_overlay(VAGINA_LAYER)
-
- var/obj/item/clothing/sextoy/sex_toy = vagina
-
- if(wear_suit && (wear_suit.flags_inv & HIDESEXTOY)) // You can add proper flags here if required
- return
-
- var/icon_file = vagina?.worn_icon
- var/mutable_appearance/vagina_overlay
-
- if(!vagina_overlay)
- vagina_overlay = sex_toy?.build_worn_icon(default_layer = VAGINA_LAYER, default_icon_file = 'icons/mob/clothing/under/default.dmi', isinhands = FALSE, override_file = icon_file)
-
- if(OFFSET_UNIFORM in dna.species.offset_features)
- vagina_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1]
- vagina_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2]
- overlays_standing[VAGINA_LAYER] = vagina_overlay
-
- apply_overlay(VAGINA_LAYER)
- update_mutant_bodyparts()
-
-// Updating anus slot
-/mob/living/carbon/human/update_inv_anus()
- if(client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- if(client && hud_used && hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_ANUS) + 1])
- var/atom/movable/screen/inventory/inv = hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_ANUS) + 1]
- inv.update_icon()
-
- if(anus)
- if(hud_used.inventory_shown && hud_used)
- anus?.screen_loc = ui_anus
- else
- anus?.screen_loc = ui_anus_down
- if(hud_used.hud_shown)
- client.screen += anus
- update_observer_view(anus)
- hud_used.hidden_inventory_update(src)
-
- // on_mob stuff
- remove_overlay(ANUS_LAYER)
-
- var/obj/item/clothing/sextoy/sex_toy = anus
-
- if(wear_suit && (wear_suit.flags_inv & HIDESEXTOY)) // You can add proper flags here if required
- return
-
- var/icon_file = anus?.worn_icon
- var/mutable_appearance/anus_overlay
-
- if(!anus_overlay)
- anus_overlay = sex_toy?.build_worn_icon(default_layer = ANUS_LAYER, default_icon_file = 'icons/mob/clothing/under/default.dmi', isinhands = FALSE, override_file = icon_file)
-
- if(OFFSET_UNIFORM in dna.species.offset_features)
- anus_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1]
- anus_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2]
- overlays_standing[ANUS_LAYER] = anus_overlay
-
- apply_overlay(ANUS_LAYER)
- update_mutant_bodyparts()
-
-// Updating nipples slot
-/mob/living/carbon/human/update_inv_nipples()
- if(client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- if(client && hud_used && hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_NIPPLES) + 1])
- var/atom/movable/screen/inventory/inv = hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_NIPPLES) + 1]
- inv.update_icon()
-
- if(nipples)
- if(hud_used.inventory_shown && hud_used)
- nipples?.screen_loc = ui_nipples
- else
- nipples?.screen_loc = ui_nipples_down
- if(hud_used.hud_shown)
- client.screen += nipples
- update_observer_view(nipples)
- hud_used.hidden_inventory_update(src)
-
- // on_mob stuff
- remove_overlay(NIPPLES_LAYER)
-
- var/obj/item/clothing/sextoy/sex_toy = nipples
-
- if(wear_suit && (wear_suit.flags_inv & HIDESEXTOY)) // You can add proper flags here if required
- return
-
- var/icon_file = nipples?.worn_icon
- var/mutable_appearance/nipples_overlay
-
- if(!nipples_overlay)
- nipples_overlay = sex_toy?.build_worn_icon(default_layer = NIPPLES_LAYER, default_icon_file = 'icons/mob/clothing/under/default.dmi', isinhands = FALSE, override_file = icon_file)
-
- if(OFFSET_UNIFORM in dna.species.offset_features)
- nipples_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1]
- nipples_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2]
- overlays_standing[NIPPLES_LAYER] = nipples_overlay
-
- apply_overlay(NIPPLES_LAYER)
- update_mutant_bodyparts()
-
-// Updating penis slot
-/mob/living/carbon/human/update_inv_penis()
- if(client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- if(client && hud_used && hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_PENIS) + 1])
- var/atom/movable/screen/inventory/inv = hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_PENIS) + 1]
- inv.update_icon()
-
- if(penis)
- if(hud_used.inventory_shown && hud_used)
- penis?.screen_loc = ui_penis
- else
- penis?.screen_loc = ui_penis_down
- if(hud_used.hud_shown)
- client.screen += penis
- update_observer_view(penis)
- hud_used.hidden_inventory_update(src)
-
- // on_mob stuff
- remove_overlay(PENIS_LAYER)
-
- var/obj/item/clothing/sextoy/sex_toy = penis
-
- if(wear_suit && (wear_suit.flags_inv & HIDESEXTOY)) // You can add proper flags here if required
- return
-
- var/icon_file = penis?.worn_icon
- var/mutable_appearance/penis_overlay
-
- if(!penis_overlay)
- penis_overlay = sex_toy?.build_worn_icon(default_layer = PENIS_LAYER, default_icon_file = 'icons/mob/clothing/under/default.dmi', isinhands = FALSE, override_file = icon_file)
-
- if(OFFSET_UNIFORM in dna.species.offset_features)
- penis_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1]
- penis_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2]
- overlays_standing[PENIS_LAYER] = penis_overlay
-
- apply_overlay(PENIS_LAYER)
- update_mutant_bodyparts()
-
-// Updating vagina hud slot
-/mob/living/carbon/human/update_hud_vagina(obj/item/contained_item)
- contained_item.screen_loc = ui_vagina
- if(client && src.hud_used?.hud_shown)
- if(src.hud_used.inventory_shown)
- client.screen += contained_item
- update_observer_view(contained_item, 1)
-
-// Updating anus hud slot
-/mob/living/carbon/human/update_hud_anus(obj/item/contained_item)
- contained_item.screen_loc = ui_anus
- if(client && src.hud_used?.hud_shown)
- if(src.hud_used.inventory_shown)
- client.screen += contained_item
- update_observer_view(contained_item, 1)
-
-// Updating nipples hud slot
-/mob/living/carbon/human/update_hud_nipples(obj/item/contained_item)
- contained_item.screen_loc = ui_nipples
- if(client && src.hud_used?.hud_shown)
- if(src.hud_used.inventory_shown)
- client.screen += contained_item
- update_observer_view(contained_item, 1)
-
-// Updating penis hud slot
-/mob/living/carbon/human/update_hud_penis(obj/item/contained_item)
- contained_item.screen_loc = ui_penis
- if(client && src.hud_used?.hud_shown)
- if(src.hud_used.inventory_shown)
- client.screen += contained_item
- update_observer_view(contained_item, 1)
-
-// Update whether our back item appears on our hud.
-/mob/living/carbon/proc/update_hud_vagina(obj/item/contained_item)
- return
-
-// Update whether our back item appears on our hud.
-/mob/living/carbon/proc/update_hud_anus(obj/item/contained_item)
- return
-
-// Update whether our back item appears on our hud.
-/mob/living/carbon/proc/update_hud_nipples(obj/item/contained_item)
- return
-
-// Update whether our back item appears on our hud.
-/mob/living/carbon/proc/update_hud_penis(obj/item/contained_item)
- return
-
-/*
-* UI CONSTRUCTION AND HANDLING
-*/
-
-// Add to hud class additional ERP variable boolean for check inventiry status (equipped or not)
-/datum/hud
- /// The screen ERP objects which can be hidden
- var/list/erp_toggleable_inventory = list()
- /// Equipped item ERP inventory
- var/ERP_inventory_shown = FALSE
-
-// Define additional button for ERP hud slots for expand/collapse like default inventory
-/atom/movable/screen/human/erp_toggle
- name = "erp_toggle"
- icon_state = "toggle"
-
-// ERP inventory button logic. Just expand/collapse
-/atom/movable/screen/human/erp_toggle/Click()
-
- var/mob/target_mob = usr
-
- if(isobserver(usr))
- if(ishuman(usr.client.eye) && (usr.client.eye != usr))
- var/mob/target_eye = usr.client.eye
- target_mob = target_eye
-
- if(usr.hud_used.ERP_inventory_shown && target_mob.hud_used)
- usr.hud_used.ERP_inventory_shown = FALSE
- usr.client.screen -= target_mob.hud_used.erp_toggleable_inventory
- else
- usr.hud_used.ERP_inventory_shown = TRUE
- usr.client.screen += target_mob.hud_used.erp_toggleable_inventory
-
- target_mob.hud_used.hidden_inventory_update(usr)
-
-/*
-* STRIPPING ERP SYSTEM EXTENTION
-*/
-
-// Extend stripping menus with ERP slots
-/datum/strippable_item/mob_item_slot/vagina
- key = STRIPPABLE_ITEM_VAGINA
- item_slot = ITEM_SLOT_VAGINA
-
-/datum/strippable_item/mob_item_slot/anus
- key = STRIPPABLE_ITEM_ANUS
- item_slot = ITEM_SLOT_ANUS
-
-/datum/strippable_item/mob_item_slot/nipples
- key = STRIPPABLE_ITEM_NIPPLES
- item_slot = ITEM_SLOT_NIPPLES
-
-/datum/strippable_item/mob_item_slot/penis
- key = STRIPPABLE_ITEM_PEINS
- item_slot = ITEM_SLOT_PENIS
-
-// Obscuring for ERP slots
-/datum/strippable_item/mob_item_slot/vagina/get_obscuring(atom/source)
- var/mob/source_mob = source
- if(source_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- return isnull(get_item(source)) \
- ? STRIPPABLE_OBSCURING_NONE \
- : STRIPPABLE_OBSCURING_HIDDEN
- else
- return STRIPPABLE_OBSCURING_COMPLETELY
-// Obscuring for ERP slots
-/datum/strippable_item/mob_item_slot/anus/get_obscuring(atom/source)
- var/mob/source_mob = source
- if(source_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- return isnull(get_item(source)) \
- ? STRIPPABLE_OBSCURING_NONE \
- : STRIPPABLE_OBSCURING_HIDDEN
- else
- return STRIPPABLE_OBSCURING_COMPLETELY
-// Obscuring for ERP slots
-/datum/strippable_item/mob_item_slot/nipples/get_obscuring(atom/source)
- var/mob/source_mob = source
- if(source_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- return isnull(get_item(source)) \
- ? STRIPPABLE_OBSCURING_NONE \
- : STRIPPABLE_OBSCURING_HIDDEN
- else
- return STRIPPABLE_OBSCURING_COMPLETELY
-// Obscuring for ERP slots
-/datum/strippable_item/mob_item_slot/penis/get_obscuring(atom/source)
- var/mob/source_mob = source
- if(source_mob.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- return isnull(get_item(source)) \
- ? STRIPPABLE_OBSCURING_NONE \
- : STRIPPABLE_OBSCURING_HIDDEN
- else
- return STRIPPABLE_OBSCURING_COMPLETELY
-
-// Strippable ERP items slot list
-GLOBAL_LIST_INIT(strippable_human_erp_items, create_erp_strippable_list(list(
- /datum/strippable_item/mob_item_slot/vagina,
- /datum/strippable_item/mob_item_slot/anus,
- /datum/strippable_item/mob_item_slot/nipples,
- /datum/strippable_item/mob_item_slot/penis,
-)))
-
-// This list is only needed in order to immediately add the necessary elements to a typical global list
-/proc/create_erp_strippable_list(types)
- var/list/strippable_items = list()
-
- for (var/strippable_type in types)
- var/datum/strippable_item/strippable_item = new strippable_type
- strippable_items[strippable_item.key] = strippable_item
- GLOB.strippable_human_items += strippable_items
- return strippable_items
-
-//Disables ERP strippable inventory depending on config
-/datum/element/strippable/Attach(datum/target, list/items, should_strip_proc_path)
- . = ..()
- if(CONFIG_GET(flag/disable_erp_preferences))
- src.items -= GLOB.strippable_human_erp_items
-
-/*
-* EXTENTIONS FOR SPRITE_ACCESSORY IS_HIDDEN CHECKS FOR ERP STUFF
-*/
-
-// Extends default proc check for hidden ears for supporting our sleepbag and catsuit to
-/datum/sprite_accessory/ears/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/HD)
- /*
- // Default proc code
- if(target_human.head && (target_human.head.flags_inv & HIDEHAIR) || (target_human.wear_mask && (target_human.wear_mask.flags_inv & HIDEHAIR)) || !HD)
- //return TRUE
- return FALSE
- */
-
- // First lets proc default code
- . = ..()
- if(!.) // If true, ears already hidden
- if(target_human.wear_suit && istype(target_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
- var/obj/item/clothing/suit/straight_jacket/kinky_sleepbag/sleeping_bag = target_human.wear_suit
- if(sleeping_bag.state_thing == "inflated")
- return TRUE
- return FALSE
- else if(target_human.w_uniform && istype(target_human.w_uniform, /obj/item/clothing/under/misc/latex_catsuit/))
- return FALSE
- return FALSE
- return TRUE // Return TRUE if superfuncitons already retuns TRUE
-
-// Extends default proc check for hidden frills for supporting our sleepbag and catsuit to
-/datum/sprite_accessory/frills/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/HD)
- /*
- // Default proc code
- if(target_human.head && (target_human.try_hide_mutant_parts || (target_human.head.flags_inv & HIDEEARS) || !HD || HD.status == BODYPART_ROBOTIC))
- //return TRUE
- return FALSE
- */
-
- . = ..()
- if(!.) // If true, frills already hidden
- if(target_human.wear_suit && istype(target_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
- var/obj/item/clothing/suit/straight_jacket/kinky_sleepbag/sleeping_bag = target_human.wear_suit
- if(sleeping_bag.state_thing == "inflated")
- return TRUE
- return FALSE
- else if(target_human.w_uniform && istype(target_human.w_uniform, /obj/item/clothing/under/misc/latex_catsuit/))
- return FALSE
- return FALSE
- return TRUE // Return TRUE if superfuncitons already retuns TRUE
-
-// Extends default proc check for hidden head accessory for supporting our sleepbag and catsuit to
-/datum/sprite_accessory/head_accessory/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/HD)
- /*
- // Default proc code
- if(target_human.head && (target_human.head.flags_inv & HIDEHAIR) || (target_human.wear_mask && (target_human.wear_mask.flags_inv & HIDEHAIR)))
- //return TRUE
- return FALSE
- */
-
- . = ..()
- if(!.) // If true, head accessory already hidden
- if(target_human.wear_suit && istype(target_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
- var/obj/item/clothing/suit/straight_jacket/kinky_sleepbag/sleeping_bag = target_human.wear_suit
- if(sleeping_bag.state_thing == "inflated")
- return TRUE
- return FALSE
- else if(target_human.w_uniform && istype(target_human.w_uniform, /obj/item/clothing/under/misc/latex_catsuit/))
- return FALSE
- return FALSE
- return TRUE // Return TRUE if superfuncitons already retuns TRUE
-
-// Extends default proc check for hidden horns for supporting our sleepbag and catsuit to
-/datum/sprite_accessory/horns/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/HD)
- /*
- // Default proc code
- if(target_human.head && (target_human.head.flags_inv & HIDEHAIR) || (target_human.wear_mask && (target_human.wear_mask.flags_inv & HIDEHAIR)) || !HD)
- return TRUE
- return FALSE
- */
-
- . = ..()
- if(!.) // If true, horns already hidden
- if(target_human.wear_suit && istype(target_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
- var/obj/item/clothing/suit/straight_jacket/kinky_sleepbag/sleeping_bag = target_human.wear_suit
- if(sleeping_bag.state_thing == "inflated")
- return TRUE
- return FALSE
- else if(target_human.w_uniform && istype(target_human.w_uniform, /obj/item/clothing/under/misc/latex_catsuit/))
- return FALSE
- return FALSE
- return TRUE // Return TRUE if superfuncitons already retuns TRUE
-
-// Extends default proc check for hidden antenna for supporting our sleepbag and catsuit to
-/datum/sprite_accessory/antenna/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/HD)
- /*
- // Default proc code
- if(target_human.head && (target_human.head.flags_inv & HIDEHAIR) || (target_human.wear_mask && (target_human.wear_mask.flags_inv & HIDEHAIR)) || !HD)
- //return TRUE
- return FALSE
- */
-
- . = ..()
- if(!.) // If true, antenna already hidden
- if(target_human.wear_suit && istype(target_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
- var/obj/item/clothing/suit/straight_jacket/kinky_sleepbag/sleeping_bag = target_human.wear_suit
- if(sleeping_bag.state_thing == "inflated")
- return TRUE
- return FALSE
- else if(target_human.w_uniform && istype(target_human.w_uniform, /obj/item/clothing/under/misc/latex_catsuit/))
- return FALSE
- return FALSE
- return TRUE // Return TRUE if superfuncitons already retuns TRUE
-
-// Extends default proc check for hidden moth antenna for supporting our sleepbag and catsuit to
-/datum/sprite_accessory/moth_antennae/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/HD)
- /*
- // Default proc code
- if(target_human.head && (target_human.head.flags_inv & HIDEHAIR) || (target_human.wear_mask && (target_human.wear_mask.flags_inv & HIDEHAIR)) || !HD)
- //return TRUE
- return FALSE
- */
-
- . = ..()
- if(!.) // If true, moth antenna already hidden
- if(target_human.wear_suit && istype(target_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
- var/obj/item/clothing/suit/straight_jacket/kinky_sleepbag/sleeping_bag = target_human.wear_suit
- if(sleeping_bag.state_thing == "inflated")
- return TRUE
- return FALSE
- else if(target_human.w_uniform && istype(target_human.w_uniform, /obj/item/clothing/under/misc/latex_catsuit/))
- return FALSE
- return FALSE
- return TRUE // Return TRUE if superfuncitons already retuns TRUE
-
-// Extends default proc check for hidden skrell hair for supporting our sleepbag and catsuit to
-/datum/sprite_accessory/skrell_hair/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/HD)
- /*
- // Default proc code
- if(target_human.head && (target_human.head.flags_inv & HIDEHAIR) || (target_human.wear_mask && (target_human.wear_mask.flags_inv & HIDEHAIR)))
- return TRUE
- return FALSE
- */
-
- . = ..()
- if(!.) // If true, skrell hair already hidden
- if(target_human.wear_suit && istype(target_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
- var/obj/item/clothing/suit/straight_jacket/kinky_sleepbag/sleeping_bag = target_human.wear_suit
- if(sleeping_bag.state_thing == "inflated")
- return TRUE
- return FALSE
- else if(target_human.w_uniform && istype(target_human.w_uniform, /obj/item/clothing/under/misc/latex_catsuit/))
- return FALSE
- return FALSE
- return TRUE // Return TRUE if superfuncitons already retuns TRUE
-
-// Extends default proc check for hidden skrell hair for supporting our sleepbag and catsuit to
-/datum/sprite_accessory/tails/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/HD)
-
- . = ..()
- if(!.) // If true, tail already hidden
- if(target_human.wear_suit && istype(target_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
- // var/obj/item/clothing/suit/straight_jacket/kinky_sleepbag/sleeping_bag = target_human.wear_suit
- // if(sleeping_bag.state_thing == "inflated")
- // return TRUE
- return TRUE /* return FALSE */
- else if(target_human.w_uniform && istype(target_human.w_uniform, /obj/item/clothing/under/misc/latex_catsuit/))
- return TRUE
- return FALSE
- return TRUE // Return TRUE if superfuncitons already retuns TRUE
-
-// Extends default proc check for hidden wings for supporting our sleepbag and catsuit to
-/datum/sprite_accessory/wings/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/HD)
- . = ..()
- if(.)
- return TRUE
- if(target_human.wear_suit && istype(target_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
- return TRUE
- return FALSE
-
-/mob/living/carbon/human/set_handcuffed(new_value)
- if(wear_suit && istype(wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
- return FALSE
- ..()
-
-/datum/sprite_accessory/xenodorsal/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/HD)
- if(target_human.wear_suit && istype(target_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
- return TRUE
- else if(target_human.w_uniform && istype(target_human.w_uniform, /obj/item/clothing/under/misc/latex_catsuit/))
- return TRUE
- return FALSE
-
-/datum/sprite_accessory/xenohead/is_hidden(mob/living/carbon/human/target_human, obj/item/bodypart/HD)
- if(target_human.wear_suit && istype(target_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag))
- var/obj/item/clothing/suit/straight_jacket/kinky_sleepbag/sleeping_bag = target_human.wear_suit
- if(sleeping_bag.state_thing == "inflated")
- return TRUE
- return FALSE
- else if(target_human.w_uniform && istype(target_human.w_uniform, /obj/item/clothing/under/misc/latex_catsuit/))
- return FALSE
- return FALSE
-
-/datum/preference/toggle/erp/sex_toy/apply_to_client_updated(client/client, value)
- . = ..()
- if(client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy))
- if(client.mob.hud_used)
- for(var/atom/movable/screen/human/erp_toggle/toggle in client.mob.hud_used.static_inventory)
- if(istype(toggle, /atom/movable/screen/human/erp_toggle))
- toggle.invisibility = 0
- else
- if(ishuman(client.mob))
- var/mob/living/carbon/human/target = client.mob
- if(target.vagina != null)
- target.dropItemToGround(target.vagina, TRUE, target.loc, TRUE, FALSE, TRUE)
- if(target.anus != null)
- target.dropItemToGround(target.anus, TRUE, target.loc, TRUE, FALSE, TRUE)
- if(target.nipples != null)
- target.dropItemToGround(target.nipples, TRUE, target.loc, TRUE, FALSE, TRUE)
- if(target.penis != null)
- target.dropItemToGround(target.penis, TRUE, target.loc, TRUE, FALSE, TRUE)
- if(client.mob.hud_used)
- if(client.mob.hud_used.ERP_inventory_shown)
- client.mob.hud_used.ERP_inventory_shown = FALSE
- client.screen -= client.mob.hud_used.erp_toggleable_inventory
-
- for(var/atom/movable/screen/human/erp_toggle/erp_button in client.mob.hud_used.static_inventory)
- if(istype(erp_button, /atom/movable/screen/human/erp_toggle))
- erp_button.invisibility = 100
-
-
- client.mob.hud_used.hidden_inventory_update(client.mob)
- client.mob.hud_used.persistent_inventory_update(client.mob)
-
-/// If the item this is called in, is in a genital slot of the target
-/obj/item/proc/is_in_genital(mob/living/carbon/human/the_guy)
- return (src == the_guy.penis || src == the_guy.vagina || src == the_guy.anus || src == the_guy.nipples)
-
-/// Used to add a cum decal to the floor while transferring viruses and DNA to it
-/mob/living/proc/add_cum_splatter_floor(turf/the_turf, female = FALSE)
- if(!the_turf)
- the_turf = get_turf(src)
-
- var/selected_type = female ? /obj/effect/decal/cleanable/cum/femcum : /obj/effect/decal/cleanable/cum
- var/atom/stain = new selected_type(the_turf, get_static_viruses())
-
- stain.transfer_mob_blood_dna(src) //I'm not adding a new forensics category for cumstains
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/verbs.dm b/modular_skyrat/modules/modular_items/lewd_items/code/verbs.dm
new file mode 100644
index 00000000000..4edd2ccf3ed
--- /dev/null
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/verbs.dm
@@ -0,0 +1,18 @@
+/mob/living/carbon/human/verb/climax_verb()
+ set name = "Climax"
+ set category = "IC"
+
+ if(!has_status_effect(/datum/status_effect/climax_cooldown))
+ if(tgui_alert(usr, "Are you sure you want to cum?", "Climax", list("Yes", "No")) == "Yes")
+ if(stat != CONSCIOUS)
+ to_chat(usr, span_warning("You can't climax right now..."))
+ return
+ else
+ climax(TRUE)
+ else
+ to_chat(src, span_warning("You can't cum right now!"))
+
+/mob/living/carbon/human/Initialize(mapload)
+ . = ..()
+ if(CONFIG_GET(flag/disable_erp_preferences))
+ verbs -= /mob/living/carbon/human/verb/climax_verb
diff --git a/modular_skyrat/modules/modular_items/readme.md b/modular_skyrat/modules/modular_items/readme.md
index e42eed6c332..b55bb62b4be 100644
--- a/modular_skyrat/modules/modular_items/readme.md
+++ b/modular_skyrat/modules/modular_items/readme.md
@@ -29,3 +29,4 @@ A modular folder for various single-item additions that don't deserve their own
Ranged66 - Material pouch ported
KathrinBailey - Pretty much a whole bunch of clothes.
mel-byond - Ported Baystation12 Aviators
+RimiNosha - Fixing up code no one else would touch.
diff --git a/tgstation.dme b/tgstation.dme
index 858aca6407e..1d267fe8b3e 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -325,6 +325,7 @@
#include "code\__DEFINES\~skyrat_defines\jobs.dm"
#include "code\__DEFINES\~skyrat_defines\keybindings.dm"
#include "code\__DEFINES\~skyrat_defines\layers.dm"
+#include "code\__DEFINES\~skyrat_defines\lewd_defines.dm"
#include "code\__DEFINES\~skyrat_defines\liquids.dm"
#include "code\__DEFINES\~skyrat_defines\loadouts.dm"
#include "code\__DEFINES\~skyrat_defines\logging.dm"
@@ -4906,14 +4907,15 @@
#include "modular_skyrat\master_files\code\modules\client\preferences\_preference.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\auto_dementor.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\be_antag.dm"
+#include "modular_skyrat\master_files\code\modules\client\preferences\body_size.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\clothing.dm"
-#include "modular_skyrat\master_files\code\modules\client\preferences\cursed_shit.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\delete_sparks.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\emote_overlay.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\emote_panel.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\erp_preferences.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\face_cursor_combat_mode.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\flavor_text.dm"
+#include "modular_skyrat\master_files\code\modules\client\preferences\genitals.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\headshot.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\laugh.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\loadout_override_preference.dm"
@@ -5864,11 +5866,34 @@
#include "modular_skyrat\modules\modular_items\code\summon_beacon.dm"
#include "modular_skyrat\modules\modular_items\code\tailoring.dm"
#include "modular_skyrat\modules\modular_items\code\thieving_gloves.dm"
-#include "modular_skyrat\modules\modular_items\lewd_items\code\arousal_system.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\decals.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_quirks.dm"
-#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_remove_erp_items.dm"
-#include "modular_skyrat\modules\modular_items\lewd_items\code\technical_stuff_for_lewd.dm"
-#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\chemistry_for_ERP.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\screen_icons.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\verbs.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_arousal\arousal.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_arousal\climax.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_arousal\mood_events.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_arousal\pain.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_arousal\pleasure.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_arousal\status_effects\aroused.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_arousal\status_effects\climax.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_arousal\status_effects\fluid_generation.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_arousal\status_effects\ropebunny.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_arousal\status_effects\spank_related.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\mood_events.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\items\bottles.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\items\pills.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\reagents\_aphrodisiac.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\reagents\_chemical_reaction.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\reagents\breast_milk.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\reagents\camphor.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\reagents\crocin.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\reagents\cum.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\reagents\dopamine.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\reagents\hexacrocin.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\reagents\incubus_draft.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\reagents\pentacamphor.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_chemistry\reagents\succubus_milk.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_clothing\ballgag.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_clothing\bdsm_mask.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_clothing\corset.dm"
@@ -5893,6 +5918,16 @@
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_clothing\shockcollar.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_clothing\strapon.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_clothing\stripper_outfit.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_helpers\appearance.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_helpers\carbon.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_helpers\human.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_helpers\misc.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_helpers\organs.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_helpers\species.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_items\_erp_disabled_item_enforcement.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_items\_kits.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_items\_masturbation_item.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_items\_sex_toy.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_items\attachable_vibrator.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_items\buttplug.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_items\clamps.dm"
@@ -5912,10 +5947,14 @@
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_items\torture_candle.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_items\vibrator.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_items\vibroring.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_machinery\lustwish.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_machinery\milking_machine.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_organs\_genital.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_organs\breasts.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_organs\testicles.dm"
+#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_organs\vagina.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_structures\bdsm_furniture.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_structures\dancing_pole.dm"
-#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_structures\lustwish.dm"
#include "modular_skyrat\modules\modular_items\lewd_items\code\lewd_structures\pillow.dm"
#include "modular_skyrat\modules\modular_reagents\code\alert.dm"
#include "modular_skyrat\modules\modular_reagents\code\reagents\medicine.dm"
diff --git a/tgui/packages/tgui/interfaces/InteractionMenu.js b/tgui/packages/tgui/interfaces/InteractionMenu.js
deleted file mode 100644
index c6f01b0a9ab..00000000000
--- a/tgui/packages/tgui/interfaces/InteractionMenu.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { useBackend } from '../backend';
-import { Button, NoticeBox, Section } from '../components';
-import { Window } from '../layouts';
-
-export const InteractionMenu = (props, context) => {
- const { act, data } = useBackend(context);
- const { categories, interactions, descriptions, colors } = data;
- const { self, ref_self, ref_user } = data;
- const { block_interact } = data;
-
- return (
-
-
- {(block_interact && Unable to Interact) || (
- Able to Interact
- )}
-
- {categories.map((category) => (
-
- {interactions[category].map((interaction) => (
-
-
-
-
- ))}
-
- ))}
-
-
-
- );
-};
diff --git a/tgui/packages/tgui/interfaces/InteractionMenu.tsx b/tgui/packages/tgui/interfaces/InteractionMenu.tsx
new file mode 100644
index 00000000000..c41e611ddb4
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/InteractionMenu.tsx
@@ -0,0 +1,123 @@
+import { useBackend } from '../backend';
+import { Button, NoticeBox, Section, Icon, Box, Stack } from '../components';
+import { Window } from '../layouts';
+
+class Interaction {
+ categories;
+ interactions;
+ descriptions;
+ colors;
+ lewd_slots: LewdSlot[];
+ self;
+ ref_self;
+ ref_user;
+ block_interact;
+}
+
+class LewdSlot {
+ img;
+ name;
+}
+
+export const InteractionMenu = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ categories,
+ interactions,
+ descriptions,
+ colors,
+ lewd_slots,
+ self,
+ ref_self,
+ ref_user,
+ block_interact,
+ } = data;
+
+ return (
+
+
+ {(block_interact && Unable to Interact) || (
+ Able to Interact
+ )}
+
+ {categories.map((category) => (
+
+ {interactions[category].map((interaction) => (
+
+
+ ))}
+
+ ))}
+
+ {lewd_slots.length > 0 ? (
+
+
+ {lewd_slots.map((element: LewdSlot) => {
+ return (
+
+
+
+ );
+ })}
+
+
+ ) : (
+ ''
+ )}
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skyrat/cursed_shit.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skyrat/genitals.tsx
similarity index 93%
rename from tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skyrat/cursed_shit.tsx
rename to tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skyrat/genitals.tsx
index 43ffd2b3a63..5ffd9f414e6 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skyrat/cursed_shit.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skyrat/genitals.tsx
@@ -106,38 +106,38 @@ export const feature_womb: Feature = {
};
export const feature_breasts: Feature = {
- name: 'Breasts Choice',
+ name: 'Breast Choice',
component: FeatureDropdownInput,
};
export const breasts_skin_tone: FeatureToggle = {
- name: 'Breasts uses Skin Tone',
+ name: 'Breasts use Skin Tone',
component: CheckboxInput,
};
export const breasts_skin_color: FeatureToggle = {
- name: 'Breasts uses Skin Color',
+ name: 'Breasts use Skin Color',
component: CheckboxInput,
};
export const breasts_color: Feature = {
- name: 'Breasts Color',
+ name: 'Breast Color',
component: FeatureTriColorInput,
};
export const breasts_emissive: Feature = {
- name: 'Breasts Emissives',
+ name: 'Breast Emissives',
component: FeatureTriBoolInput,
};
export const breasts_lactation_toggle: FeatureToggle = {
- name: 'Breasts Lactation',
+ name: 'Breast Lactation',
component: CheckboxInput,
};
-export const breasts_size: FeatureNumeric = {
- name: 'Breasts Size',
- component: FeatureNumberInput,
+export const breasts_size: Feature = {
+ name: 'Breast Size',
+ component: FeatureDropdownInput,
};
export const feature_anus: Feature = {
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/skyrat/erp_preferences.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/skyrat/erp_preferences.tsx
index 8cb3b1b8099..d5d5f378ea9 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/skyrat/erp_preferences.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/skyrat/erp_preferences.tsx
@@ -83,3 +83,11 @@ export const erp_sexuality_pref: FeatureChoiced = {
'Determines what sexual content you see, limited use. None will show all content.',
component: FeatureDropdownInput,
};
+
+export const new_genitalia_growth_pref: FeatureToggle = {
+ name: 'ERP New Genitalia Growth',
+ category: 'ERP',
+ description:
+ 'If checked, allows drugs to grow new genitalia on your character.',
+ component: CheckboxInput,
+};