diff --git a/GainStation13/code/datums/traits.dm b/GainStation13/code/datums/traits.dm
index 8f4877fc8f..7196ca9423 100644
--- a/GainStation13/code/datums/traits.dm
+++ b/GainStation13/code/datums/traits.dm
@@ -57,3 +57,20 @@
/datum/quirk/slimespeaker/remove()
var/mob/living/M = quirk_holder
M?.remove_language(/datum/language/slime)
+
+/datum/quirk/SpawnWithWheelchair
+ name = "Mobility Assistance"
+ desc = "After your last failed fitness test, you were advised to start using a hoverchair"
+
+/datum/quirk/SpawnWithWheelchair/on_spawn()
+ if(quirk_holder.buckled) // Handle late joins being buckled to arrival shuttle chairs.
+ quirk_holder.buckled.unbuckle_mob(quirk_holder)
+
+ var/turf/T = get_turf(quirk_holder)
+ var/obj/structure/chair/spawn_chair = locate() in T
+
+ var/obj/vehicle/ridden/wheelchair/wheels = new(T)
+ if(spawn_chair) // Makes spawning on the arrivals shuttle more consistent looking
+ wheels.setDir(spawn_chair.dir)
+
+ wheels.buckle_mob(quirk_holder)
diff --git a/GainStation13/code/mechanics/den abductors/console.dm b/GainStation13/code/mechanics/den abductors/console.dm
index 4293c5e203..a0a967a3b6 100644
--- a/GainStation13/code/mechanics/den abductors/console.dm
+++ b/GainStation13/code/mechanics/den abductors/console.dm
@@ -26,7 +26,7 @@
var/credits = linked_scale?.credits
dat += "Gear Credits: [credits]
"
- dat += "Transfer credits in exchange for supplies:
"
+ dat += "Transfer credits in exchange for supplies:
"
for(var/goodie in subtypesof(/datum/feeders_den_goodie))
var/datum/feeders_den_goodie/temp_goodie = new goodie()
dat += "[temp_goodie.name] (Cost: [temp_goodie.credit_cost])
"
@@ -64,7 +64,7 @@
say("Unable to purchase more!")
return FALSE
- if(!Dispense(item_path, price))
+ if(!Dispense(item_path, price))
return FALSE
if(buy_counts[goodie_datum.name] == null)
@@ -146,7 +146,7 @@
return TRUE
return ..()
-
+
/obj/structure/scale/credits
name = "tracking scale"
desc = "A upgraded scale that tracks to weight of all of those that have stepped on it. Using this will add credits to the feeder console"
@@ -155,7 +155,7 @@
/// How much credits do we currently have?
var/credits = 0
/// How many credits are we going to reward per pound gained?
- var/credits_per_fatness = 0.25
+ var/credits_per_fatness = 0.25
/// A list containing all of the people we've scanned and their maximum weight.
var/list/scanned_people = list()
/// What is the current team number?
@@ -201,7 +201,7 @@
var/credit_total = max((credits_to_add - credits_to_remove), 0)
if(credit_total > 0)
say("[credit_total] credits have been deposited into the console.")
-
+
credits += credit_total
scanned_people[fatty] += credit_total
@@ -214,6 +214,7 @@
team_number = 27
vest_mode_action = null
vest_disguise_action = null
+ check_if_abductor = FALSE
/obj/machinery/computer/camera_advanced/abductor/feeder/IsScientist(mob/living/carbon/human/H)
return TRUE
@@ -245,4 +246,4 @@
prepare(target,user)
return TRUE
-
+
diff --git a/GainStation13/code/mechanics/fatrousal.dm b/GainStation13/code/mechanics/fatrousal.dm
index c31b6054af..2a93a8cad0 100644
--- a/GainStation13/code/mechanics/fatrousal.dm
+++ b/GainStation13/code/mechanics/fatrousal.dm
@@ -16,14 +16,14 @@
var/mob/living/carbon/C = quirk_holder
C.hider_remove(src)
-/*
+
/datum/quirk/fatrousal/proc/fat_hide(var/mob/living/carbon/user)
if(iscarbon(quirk_holder))
var/mob/living/carbon/C = quirk_holder
- return C.getArousalLoss()*35
+ return C.getArousal()*35
return FALSE
-*/
+
///mob/living/adjust_arousal(amount, updating_arousal=1)
// if(HAS_TRAIT(src, TRAIT_FATROUSAL))
diff --git a/GainStation13/code/mobs/emote.dm b/GainStation13/code/mobs/emote.dm
index 647908b79e..2fa204ac0c 100644
--- a/GainStation13/code/mobs/emote.dm
+++ b/GainStation13/code/mobs/emote.dm
@@ -1,7 +1,3 @@
-/datum/emote/proc/get_sound(mob/living/user)
- return sound //by default just return this var.
-
-
/datum/emote/speen
key = "speen"
key_third_person = "speeeeens!"
@@ -31,3 +27,19 @@
playsound(user, 'GainStation13/sound/voice/speen.ogg', 50, 1, -1)
. = ..()
+
+/datum/emote/living/cackle
+ key = "cackle"
+ key_third_person = "cackles"
+ message = "cackles hysterically!"
+ emote_type = EMOTE_AUDIBLE
+ muzzle_ignore = FALSE
+ restraint_check = FALSE
+
+/datum/emote/living/cackle/run_emote(mob/living/user, params)
+ if(ishuman(user))
+ if(user.nextsoundemote >= world.time)
+ return
+ user.nextsoundemote = world.time + 7
+ playsound(user, 'GainStation13/sound/voice//cackle_yeen.ogg', 50, 1, -1)
+ . = ..()
diff --git a/GainStation13/code/modules/arousal/arousal.dm b/GainStation13/code/modules/arousal/arousal.dm
new file mode 100644
index 0000000000..11b2474e0d
--- /dev/null
+++ b/GainStation13/code/modules/arousal/arousal.dm
@@ -0,0 +1,19 @@
+//GS13 Port
+/mob/living/proc/mob_climax(forced_climax = FALSE, cause = "none")//This is just so I can test this shit without being forced to add actual content to get rid of arousal. Will be a very basic proc for a while.
+ set name = "Masturbate"
+ set category = "IC"
+ if(canbearoused && !restrained() && !stat)
+ if(mb_cd_timer <= world.time)
+ //start the cooldown even if it fails
+ mb_cd_timer = world.time + mb_cd_length
+ if(getArousal() >= ((max_arousal / 100) * 33))//33% arousal or greater required
+ src.visible_message("[src] starts masturbating!", \
+ "You start masturbating.")
+ if(do_after(src, 30, target = src))
+ src.visible_message("[src] relieves [p_them()]self!", \
+ "You have relieved yourself.")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ setArousal(min_arousal)
+ else
+ to_chat(src, "You aren't aroused enough for that.")
+
diff --git a/GainStation13/code/modules/mapping/ghost_roles.dm b/GainStation13/code/modules/mapping/ghost_roles.dm
index 21d5f16cc5..f37da590dd 100644
--- a/GainStation13/code/modules/mapping/ghost_roles.dm
+++ b/GainStation13/code/modules/mapping/ghost_roles.dm
@@ -12,6 +12,7 @@
flavour_text = "After you've sold your soul to corporate overlords, your contract obliged you to enter cryostasis. \
Finally, after God knows how long, the cryopod system have awakened you with only a single sentence of information - welcome and lure in new guests into the freshly opened GATO restaurant!"
assignedrole = "Restaurant worker"
+ mirrorcanloadappearance = TRUE
/obj/effect/mob_spawn/human/fastfoodmanager
name = "Corporate cryostasis pod"
@@ -27,6 +28,7 @@
flavour_text = "After you've sold your soul to corporate overlords, your contract obliged you to enter cryostasis. \
Finally, after God knows how long, the cryopod system have awakened you with only a single sentence of information - make sure to keep the best care of GATO's restaurant, currently under your management! You have a higher say over your workers, but do not abuse this power."
assignedrole = "Restaurant manager"
+ mirrorcanloadappearance = TRUE
/obj/effect/mob_spawn/human/fastfood/Initialize(mapload)
. = ..()
@@ -108,6 +110,7 @@
death = FALSE
roundstart = FALSE
mob_species = /datum/species/human
+ mirrorcanloadappearance = TRUE
/datum/outfit/feeders_den/fanatic
name = "Feeder Fanatic"
@@ -152,6 +155,7 @@
death = FALSE
roundstart = FALSE
mob_species = /datum/species/human
+ mirrorcanloadappearance = TRUE
/datum/outfit/feeders_den/victim
name = "Den Victim"
diff --git a/GainStation13/code/modules/mob/living/belly.dm b/GainStation13/code/modules/mob/living/belly.dm
index 796b0407d4..6ff2363f2b 100644
--- a/GainStation13/code/modules/mob/living/belly.dm
+++ b/GainStation13/code/modules/mob/living/belly.dm
@@ -45,6 +45,7 @@
else
color = "#[D.features["belly_color"]]"
size = D.features["belly_size"]
+ inflatable = D.features["inflatable_belly"]
toggle_visibility(D.features["belly_visibility"], FALSE)
diff --git a/GainStation13/sound/voice/cackle_yeen.ogg b/GainStation13/sound/voice/cackle_yeen.ogg
new file mode 100644
index 0000000000..8d76cda277
Binary files /dev/null and b/GainStation13/sound/voice/cackle_yeen.ogg differ
diff --git a/code/__DEFINES/citadel_defines.dm b/code/__DEFINES/citadel_defines.dm
index b3b5ece36d..de25ec52e9 100644
--- a/code/__DEFINES/citadel_defines.dm
+++ b/code/__DEFINES/citadel_defines.dm
@@ -67,9 +67,14 @@
#define BUTT_SIZE_DEF 1
#define BUTT_SIZE_MAX 10 //butt genitals are special in that they have caps. if there's the event there's even bigger butt sprites, raise this number.
-#define BELLY_SIZE_DEF 0
+#define BELLY_SIZE_DEF 1
#define BELLY_SIZE_MAX 10
+//GS13 Port - Add back Arousal
+#define AROUSAL_MINIMUM_DEFAULT 0
+#define AROUSAL_MAXIMUM_DEFAULT 100
+#define AROUSAL_START_VALUE 1
+
//visibility toggles defines to avoid errors typos code errors.
#define GEN_VISIBLE_ALWAYS "Always visible"
#define GEN_VISIBLE_NO_CLOTHES "Hidden by clothes"
diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm
index 14a3b7dc49..4842a38716 100644
--- a/code/__DEFINES/hud.dm
+++ b/code/__DEFINES/hud.dm
@@ -98,6 +98,7 @@
#define ui_health "EAST-1:28,CENTER-1:15"
#define ui_internal "EAST-1:28,CENTER+1:19"//CIT CHANGE - moves internal icon up a little bit to accommodate for the stamina meter
#define ui_mood "EAST-1:28,CENTER-3:10"
+#define ui_arousal "EAST-1:28,CENTER-4:8"
// #define ui_spacesuit "EAST-1:28,CENTER-4:10"
//Pop-up inventory
diff --git a/code/__DEFINES/research/stock_parts.dm b/code/__DEFINES/research/stock_parts.dm
index 457e3b632e..e493df242e 100644
--- a/code/__DEFINES/research/stock_parts.dm
+++ b/code/__DEFINES/research/stock_parts.dm
@@ -2,4 +2,4 @@
/// Efficiency scaling for stock part level to material usage. All code concerning lathing and production from raw material sheet should be using this.
#define STANDARD_PART_LEVEL_LATHE_COEFFICIENT(level) clamp(1 - (level * 0.1), 0, 1)
/// Efficiency scaling for stock part level to ore factor. All code concerning lathing and production from raw ores to raw material sheets should be using this.
-#define STANDARD_PART_LEVEL_ORE_COEFFICIENT(level) clamp(1 + (level * 0.125), 1, 10)
+#define STANDARD_PART_LEVEL_ORE_COEFFICIENT(level) clamp(1 + (level * 0.25), 1, 10) //GS13 EDIT, original level mult 0.125
diff --git a/code/__HELPERS/_cit_helpers.dm b/code/__HELPERS/_cit_helpers.dm
index de006de90e..c885a5b996 100644
--- a/code/__HELPERS/_cit_helpers.dm
+++ b/code/__HELPERS/_cit_helpers.dm
@@ -144,6 +144,17 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE)
if(I.body_parts_covered & GROIN)
return FALSE
return TRUE
+//GS13 Port - Arousal
+/mob/living/carbon/proc/is_butt_exposed(var/list/L)
+ if(!L)
+ L = get_equipped_items()
+ for(var/obj/item/I in L)
+ if(I.body_parts_covered & GROIN)
+ if(!I.do_not_cover_butt)
+ return FALSE
+ else
+ return TRUE
+ return TRUE
/mob/living/carbon/proc/is_chest_exposed(list/L)
if(!L)
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index e2f76e1952..27a028d40f 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -215,6 +215,7 @@
"butt_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"),
// GS13 EDIT START - BELLY
"has_belly" = FALSE,
+ "belly_size" = BELLY_SIZE_DEF,
"hide_belly" = FALSE,
"inflatable_belly" = FALSE,
"belly_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"),
@@ -225,6 +226,7 @@
"cock_visibility" = GEN_VISIBLE_NO_UNDIES,
"vag_visibility" = GEN_VISIBLE_NO_UNDIES,
"butt_visibility" = GEN_VISIBLE_NO_UNDIES,
+ "belly_visibility" = GEN_VISIBLE_NO_UNDIES,
"ipc_screen" = snowflake_ipc_antenna_list ? pick(snowflake_ipc_antenna_list) : "None",
"ipc_antenna" = "None",
"flavor_text" = "",
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 53d69fe01e..61d99e29f3 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -88,6 +88,9 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
var/atom/movable/screen/healthdoll
var/atom/movable/screen/internals
+ //GS13 Port - Add back Arousal
+ var/atom/movable/screen/arousal
+
var/atom/movable/screen/wanted/wanted_lvl
// subtypes can override this to force a specific UI style
var/ui_style
@@ -144,6 +147,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
healthdoll = null
wanted_lvl = null
internals = null
+ arousal = null
lingchemdisplay = null
devilsouldisplay = null
lingstingdisplay = null
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 27989f3bb1..bea9e564f2 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -430,6 +430,12 @@
infodisplay += staminabuffer
//END OF CIT CHANGES
+ //GS13 Port - adds arousal and stamina to hud
+ arousal = new /atom/movable/screen/arousal()
+ arousal.icon_state = (owner.canbearoused == 1 ? "arousal0" : "")
+ arousal.hud = src
+ infodisplay += arousal
+
healthdoll = new /atom/movable/screen/healthdoll()
healthdoll.hud = src
infodisplay += healthdoll
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index c05d04ede8..5c810bed2a 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -169,6 +169,8 @@
var/mob/living/L = new_character
if(L.client?.prefs && L.client.prefs.auto_ooc && L.client.prefs.chat_toggles & CHAT_OOC)
L.client.prefs.chat_toggles &= ~(CHAT_OOC)
+ L.canbearoused = L.client?.prefs?.arousable //Technically this should make taking over a character mean the body gain the new minds setting...
+ L.update_arousal_hud() //Removes the old icon
hide_ckey = current.client?.prefs?.hide_ckey
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 0eaba60ba8..ae025b355a 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -94,6 +94,7 @@
var/datum/wires/wires = null
var/icon/blood_splatter_icon
+ var/icon/cum_splatter_icon
var/list/fingerprints
var/list/fingerprintshidden
var/list/blood_DNA
diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm
index 5395dd6fee..fc4823cbc8 100644
--- a/code/game/machinery/computer/camera_advanced.dm
+++ b/code/game/machinery/computer/camera_advanced.dm
@@ -102,7 +102,7 @@
return TRUE
/obj/machinery/computer/camera_advanced/abductor/can_use(mob/user)
- if(!isabductor(user))
+ if(check_if_abductor && !isabductor(user))
return FALSE
return ..()
diff --git a/code/game/machinery/quantum_pad.dm b/code/game/machinery/quantum_pad.dm
index 1ae2692da9..1eeec0fd74 100644
--- a/code/game/machinery/quantum_pad.dm
+++ b/code/game/machinery/quantum_pad.dm
@@ -139,7 +139,7 @@
to_chat(user, "[src] is unpowered!")
teleporting = FALSE
return
- if(!target_pad || QDELETED(target_pad) || (needs_power && (target_pad.machine_stat & NOPOWER))) // GS13 EDIT
+ if(!target_pad || QDELETED(target_pad) || (target_pad.needs_power && (target_pad.machine_stat & NOPOWER))) // GS13 EDIT
to_chat(user, "Linked pad is not responding to ping. Teleport aborted.")
teleporting = FALSE
return
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index e437ac5c8b..9db3090d66 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -181,6 +181,9 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
/// Used if we want to have a custom verb text for throwing. "John Spaceman flicks the ciggerate" for example.
var/throw_verb
+ //GS13 Port - Hyper, for clothes that reveal your behind! butt stuff, you know how it is.
+ var/do_not_cover_butt = FALSE
+
/obj/item/Initialize(mapload)
if(attack_verb)
diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm
index ffb48ce633..a88d250eb7 100644
--- a/code/game/objects/structures/mirror.dm
+++ b/code/game/objects/structures/mirror.dm
@@ -21,6 +21,7 @@
if(ishuman(user))
var/mob/living/carbon/human/H = user
+ H.checkloadappearance() // GS13 EDIT
//see code/modules/mob/dead/new_player/preferences.dm at approx line 545 for comments!
//this is largely copypasted from there.
diff --git a/code/modules/antagonists/abductor/machinery/camera.dm b/code/modules/antagonists/abductor/machinery/camera.dm
index 4fb6bf5645..9069130a7d 100644
--- a/code/modules/antagonists/abductor/machinery/camera.dm
+++ b/code/modules/antagonists/abductor/machinery/camera.dm
@@ -11,6 +11,8 @@
var/obj/machinery/abductor/console/console
lock_override = TRUE
+ var/check_if_abductor = TRUE //GS13 EDIT
+
icon = 'icons/obj/abductor.dmi'
icon_state = "camera"
icon_keyboard = null
@@ -24,7 +26,7 @@
/obj/machinery/computer/camera_advanced/abductor/Initialize(mapload)
. = ..()
-
+
if(tele_in_action)
actions += new tele_in_action(src)
if(tele_out_action)
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index 9c0abf70b5..1313d21312 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -56,6 +56,12 @@
if(send_to_spawnpoint)
move_to_spawnpoint()
+ //GS13 EDIT START
+ var/mob/living/carbon/human/H = owner.current
+ H.checkloadappearance()
+ //GS13 EDIT END
+
+
/datum/antagonist/nukeop/get_team()
return nuke_team
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 5747e5190d..65988577b7 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -30,6 +30,11 @@
create_objectives()
if(move_to_lair)
send_to_lair()
+ //GS13 EDIT START
+ var/mob/living/carbon/human/H = owner.current
+ H.mirrorcanloadappearance = TRUE
+ H.checkloadappearance()
+ //GS13 EDIT END
. = ..()
if(allow_rename)
rename_wizard()
diff --git a/code/modules/arousal/arousal.dm b/code/modules/arousal/arousal.dm
index 4e0f37d529..d918d0cb19 100644
--- a/code/modules/arousal/arousal.dm
+++ b/code/modules/arousal/arousal.dm
@@ -1,15 +1,36 @@
+//GS13 Port - Oh boy this has changed
/mob/living
+ var/arousal = 0 //How aroused the mob is.
+ var/min_arousal = AROUSAL_MINIMUM_DEFAULT //The lowest this mobs arousal will get. default = 0
+ var/max_arousal = AROUSAL_MAXIMUM_DEFAULT //The highest this mobs arousal will get. default = 100
+ var/arousal_rate = AROUSAL_START_VALUE //The base rate that arousal will increase in this mob.
+ var/arousal_loss_rate = AROUSAL_START_VALUE //How easily arousal can be relieved for this mob.
+ var/canbearoused = FALSE //Mob-level disabler for arousal. Starts off and can be enabled as features are added for different mob types.
var/mb_cd_length = 5 SECONDS //5 second cooldown for masturbating because fuck spam.
var/mb_cd_timer = 0 //The timer itself
/mob/living/carbon/human
+ canbearoused = TRUE
+
var/saved_underwear = ""//saves their underwear so it can be toggled later
var/saved_undershirt = ""
var/saved_socks = ""
var/hidden_underwear = FALSE
var/hidden_undershirt = FALSE
var/hidden_socks = FALSE
- var/arousal_rate = 1
+
+/mob/living/carbon/human/New()
+ ..()
+ saved_underwear = underwear
+ saved_undershirt = undershirt
+
+//Species vars
+/datum/species
+ var/arousal_gain_rate = AROUSAL_START_VALUE //Rate at which this species becomes aroused
+ var/arousal_lose_rate = AROUSAL_START_VALUE //Multiplier for how easily arousal can be relieved
+ var/list/cum_fluids = list(/datum/reagent/consumable/semen)
+ var/list/milk_fluids = list(/datum/reagent/consumable/milk)
+ var/list/femcum_fluids = list(/datum/reagent/consumable/semen/femcum)
//Mob procs
/mob/living/carbon/human/verb/underwear_toggle()
@@ -40,6 +61,162 @@
update_body(TRUE)
+/mob/living/proc/handle_arousal()
+
+
+/mob/living/carbon/handle_arousal()
+ if(canbearoused && dna)
+ var/datum/species/S
+ S = dna.species
+ if(S)
+ if(!(SSmobs.times_fired % 36))
+ if(getArousal() < max_arousal)
+ //if(S && !(SSmobs.times_fired % 36) && getArousal() < max_arousal)//Totally stolen from breathing code. Do this every 36 ticks.
+ adjustArousal(arousal_rate * S.arousal_gain_rate)
+ if(dna.features["exhibitionist"] && client)
+ var/amt_nude = 0
+ if(is_chest_exposed() && (getorganslot("breasts")))
+ amt_nude++
+ if(is_groin_exposed())
+ if(getorganslot("penis"))
+ amt_nude++
+ if(getorganslot("vagina"))
+ amt_nude++
+ if(is_butt_exposed())
+ if(getorganslot("anus"))
+ amt_nude++
+ if(amt_nude)
+ var/watchers = 0
+ for(var/mob/_M in view(world.view, src))
+ var/mob/living/M = _M
+ if(!istype(M))
+ continue
+ if(M.client && !M.stat && !M.eye_blind && (locate(src) in viewers(world.view,M)))
+ watchers++
+ if(watchers)
+ adjustArousal((amt_nude * watchers) + S.arousal_gain_rate)
+
+/mob/living/proc/getArousal()
+ return arousal
+
+/mob/living/proc/adjustArousal(amount, updating_arousal=1)
+ if(status_flags & GODMODE || !canbearoused)
+ return FALSE
+ arousal = clamp(arousal + amount, min_arousal, max_arousal)
+ if(updating_arousal)
+ updatearousal()
+
+/mob/living/proc/setArousal(amount, updating_arousal=1)
+ if(status_flags & GODMODE || !canbearoused)
+ return FALSE
+ arousal = clamp(amount, min_arousal, max_arousal)
+ if(updating_arousal)
+ updatearousal()
+
+/mob/living/proc/getPercentAroused()
+ var/percentage = ((100 / max_arousal) * arousal)
+ return percentage
+
+/mob/living/proc/isPercentAroused(percentage)//returns true if the mob's arousal (measured in a percent of 100) is greater than the arg percentage.
+ if(!isnum(percentage) || percentage > 100 || percentage < 0)
+ CRASH("Provided percentage is invalid")
+ if(getPercentAroused() >= percentage)
+ return TRUE
+ return FALSE
+
+//H U D//
+/mob/living/proc/updatearousal()
+ update_arousal_hud()
+
+/mob/living/carbon/updatearousal()
+ . = ..()
+
+ for(var/obj/item/organ/genital/G in internal_organs)
+ if(istype(G))
+ var/datum/sprite_accessory/S
+ switch(G.type)
+ if(/obj/item/organ/genital/penis)
+ S = GLOB.cock_shapes_list[G.shape]
+ if(/obj/item/organ/genital/testicles)
+ S = GLOB.balls_shapes_list[G.shape]
+ if(/obj/item/organ/genital/vagina)
+ S = GLOB.vagina_shapes_list[G.shape]
+ if(/obj/item/organ/genital/breasts)
+ S = GLOB.breasts_shapes_list[G.shape]
+ if(S?.alt_aroused)
+ G.aroused_state = isPercentAroused(G.aroused_amount)
+ if(getArousal() >= ((max_arousal / 100) * 33))
+ G.aroused_state = TRUE
+ else
+ G.aroused_state = FALSE
+ G.update_appearance()
+/mob/living/proc/update_arousal_hud()
+ return FALSE
+
+/datum/species/proc/update_arousal_hud(mob/living/carbon/human/H)
+ return FALSE
+
+/mob/living/carbon/human/update_arousal_hud()
+ if(!client || !hud_used)
+ return FALSE
+ if(dna.species.update_arousal_hud())
+ return FALSE
+ if(!canbearoused)
+ hud_used.arousal.icon_state = ""
+ return FALSE
+ else
+ if(hud_used.arousal)
+ if(stat == DEAD)
+ hud_used.arousal.icon_state = "arousal0"
+ return TRUE
+ if(getArousal() == max_arousal)
+ hud_used.arousal.icon_state = "arousal100"
+ return TRUE
+ if(getArousal() >= (max_arousal / 100) * 90)//M O D U L A R , W O W
+ hud_used.arousal.icon_state = "arousal90"
+ return TRUE
+ if(getArousal() >= (max_arousal / 100) * 80)//M O D U L A R , W O W
+ hud_used.arousal.icon_state = "arousal80"
+ return TRUE
+ if(getArousal() >= (max_arousal / 100) * 70)//M O D U L A R , W O W
+ hud_used.arousal.icon_state = "arousal70"
+ return TRUE
+ if(getArousal() >= (max_arousal / 100) * 60)//M O D U L A R , W O W
+ hud_used.arousal.icon_state = "arousal60"
+ return TRUE
+ if(getArousal() >= (max_arousal / 100) * 50)//M O D U L A R , W O W
+ hud_used.arousal.icon_state = "arousal50"
+ return TRUE
+ if(getArousal() >= (max_arousal / 100) * 40)//M O D U L A R , W O W
+ hud_used.arousal.icon_state = "arousal40"
+ return TRUE
+ if(getArousal() >= (max_arousal / 100) * 30)//M O D U L A R , W O W
+ hud_used.arousal.icon_state = "arousal30"
+ return TRUE
+ if(getArousal() >= (max_arousal / 100) * 20)//M O D U L A R , W O W
+ hud_used.arousal.icon_state = "arousal10"
+ return TRUE
+ if(getArousal() >= (max_arousal / 100) * 10)//M O D U L A R , W O W
+ hud_used.arousal.icon_state = "arousal10"
+ return TRUE
+ else
+ hud_used.arousal.icon_state = "arousal0"
+
+//GS13 Port - Arousal menu
+/atom/movable/screen/arousal
+ name = "arousal"
+ icon_state = "arousal0"
+ icon = 'icons/obj/genitals/hud.dmi'
+ screen_loc = ui_arousal
+
+/atom/movable/screen/arousal/Click()
+ if(!isliving(usr))
+ return FALSE
+ if(isobserver(usr))
+ return
+ var/mob/living/M = usr
+ if(M.canbearoused)
+ ui_interact(usr)
/mob/living/carbon/human/proc/adjust_arousal(strength, cause = "manual toggle", aphro = FALSE,maso = FALSE) // returns all genitals that were adjust
var/list/obj/item/organ/genital/genit_list = list()
@@ -63,207 +240,543 @@
if(!. && !silent)
to_chat(H, "Your [name] is unable to produce it's own fluids, it's missing the organs for it.")
-/mob/living/carbon/human/proc/do_climax(datum/reagents/R, atom/target, obj/item/organ/genital/G, spill = TRUE)
- if(!G)
- return
- if(!target || !R)
- return
- var/turfing = isturf(target)
- G.generate_fluid(R)
- log_message("Climaxed using [G] with [target]", LOG_EMOTE)
- if(spill && R.total_volume >= 5)
- R.reaction(turfing ? target : target.loc, TOUCH, 1, 0)
- if(!turfing)
- R.trans_to(target, R.total_volume * (spill ? G.fluid_transfer_factor : 1), log = TRUE)
- G.last_orgasmed = world.time
- R.clear_reagents()
+//These are various procs that we'll use later, split up for readability instead of having one, huge proc.
+//For all of these, we assume the arguments given are proper and have been checked beforehand.
+/mob/living/carbon/human/proc/mob_masturbate(obj/item/organ/genital/G, mb_time = 30) //Masturbation, keep it gender-neutral
+ var/total_fluids = 0
+ var/datum/reagents/fluid_source = null
-/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30) //This is used for forced orgasms and other hands-free climaxes
- var/datum/reagents/fluid_source = G.climaxable(src, TRUE)
- if(!fluid_source)
- to_chat(src,"Your [G.name] cannot cum.")
- return
- if(mb_time) //as long as it's not instant, give a warning
- to_chat(src,"You feel yourself about to orgasm.")
- if(!do_after(src, mb_time, target = src) || !G.climaxable(src, TRUE))
+ if(G.producing) //Can it produce its own fluids, such as breasts?
+ fluid_source = G.reagents
+ else
+ if(!G.linked_organ)
+ to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.")
return
- to_chat(src,"You climax[isturf(loc) ? " onto [loc]" : ""] with your [G.name].")
- do_climax(fluid_source, loc, G)
+ fluid_source = G.linked_organ.reagents
+ total_fluids = fluid_source.total_volume
+ if(mb_time)
+ src.visible_message("[src] starts to [G.masturbation_verb] [p_their()] [G.name].", \
+ "You start to [G.masturbation_verb] your [G.name].", \
+ "You start to [G.masturbation_verb] your [G.name].")
-/mob/living/carbon/human/proc/mob_climax_partner(obj/item/organ/genital/G, mob/living/L, spillage = TRUE, mb_time = 30) //Used for climaxing with any living thing
- var/datum/reagents/fluid_source = G.climaxable(src)
- if(!fluid_source)
- return
- if(mb_time) //Skip warning if this is an instant climax.
- to_chat(src,"You're about to climax with [L]!")
- to_chat(L,"[src] is about to climax with you!")
- if(!do_after(src, mb_time, target = src) || !in_range(src, L) || !G.climaxable(src, TRUE))
+ if(do_after(src, mb_time, target = src))
+ if(total_fluids > 5)
+ fluid_source.reaction(src.loc, TOUCH, 1, 0)
+ fluid_source.clear_reagents()
+
+ src.visible_message("[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!", \
+ "You cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""].", \
+ "You have relieved yourself.")
+
+
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ if(G.can_climax)
+ setArousal(min_arousal)
+
+
+/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30, spillage = TRUE) //This is used for forced orgasms and other hands-free climaxes
+ var/total_fluids = 0
+ var/datum/reagents/fluid_source = null
+ var/unable_to_come = FALSE
+
+ if(G.producing) //Can it produce its own fluids, such as breasts?
+ fluid_source = G.reagents
+ total_fluids = fluid_source.total_volume
+ else
+ if(!G.linked_organ)
+ unable_to_come = TRUE
+ else
+ fluid_source = G.linked_organ.reagents
+ total_fluids = fluid_source.total_volume
+
+ if(unable_to_come)
+ src.visible_message("[src] shudders, their [G.name] unable to cum.", \
+ "Your [G.name] cannot cum, giving no relief.", \
+ "Your [G.name] cannot cum, giving no relief.")
+ else
+ total_fluids = fluid_source.total_volume
+ if(mb_time) //as long as it's not instant, give a warning
+ src.visible_message("[src] looks like they're about to cum.", \
+ "You feel yourself about to orgasm.", \
+ "You feel yourself about to orgasm.")
+ if(do_after(src, mb_time, target = src))
+ if(spillage)
+ if(total_fluids > 5)
+ fluid_source.reaction(src.loc, TOUCH, 1, 0)
+
+ fluid_source.clear_reagents()
+ src.visible_message("[src] orgasms[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""], with [p_their()] [G.name]!", \
+ "You climax[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""] with your [G.name].", \
+ "You climax using your [G.name].")
+ else //Else from spillage check, also note subtle text change
+ src.visible_message("[src] orgasms with [p_their()] [G.name]!", \
+ "You climax with your [G.name].", \
+ "You climax using your [G.name].")
+
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ if(G.can_climax)
+ setArousal(min_arousal)
+
+
+/mob/living/carbon/human/proc/mob_climax_partner(obj/item/organ/genital/G, mob/living/L, spillage = TRUE, impreg = FALSE,cover = FALSE,remote = FALSE, mb_time = 30) //Used for climaxing with any living thing
+ var/total_fluids = 0
+ var/datum/reagents/fluid_source = null
+
+ if(G.producing) //Can it produce its own fluids, such as breasts?
+ fluid_source = G.reagents
+ else
+ if(!G.linked_organ)
+ to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.")
return
- if(spillage)
- to_chat(src,"You orgasm with [L], spilling out of them, using your [G.name].")
- to_chat(L,"[src] climaxes with you, overflowing and spilling, using [p_their()] [G.name]!")
- else //knots and other non-spilling orgasms
- to_chat(src,"You climax with [L], your [G.name] spilling nothing.")
- to_chat(L,"[src] climaxes with you, [p_their()] [G.name] spilling nothing!")
- SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
- do_climax(fluid_source, spillage ? loc : L, G, spillage)
+ fluid_source = G.linked_organ.reagents
+ total_fluids = fluid_source.total_volume
+
+ if(mb_time && !remote) //Skip warning if this is an instant climax.
+ src.visible_message("[src] is about to climax with [L]!", \
+ "You're about to climax with [L]!", \
+ "You're preparing to climax with something!")
+ if(remote)
+ src.visible_message("[src] is about to climax with someone!", \
+ "You're about to climax with someone!", \
+ "You're preparing to climax with something!")
+
+ if(cover)//covering the partner in cum, this overrides other options.
+ if(do_after(src, mb_time, target = src) && in_range(src, L))
+ fluid_source.trans_to(L, total_fluids*G.fluid_transfer_factor)
+ total_fluids -= total_fluids*G.fluid_transfer_factor
+ if(total_fluids > 80) // now thats a big cum!
+ if(isliving(L))
+ var/mutable_appearance/cumoverlaylarge = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi')
+ cumoverlaylarge.icon_state = "cum_large"
+ L.add_overlay(cumoverlaylarge)
+
+ if(total_fluids > 5)
+ fluid_source.reaction(L.loc, TOUCH, 1, 0)
+ var/mob/living/carbon/human/H = L
+ if(H)
+ H.cumdrip_rate += rand(5,10)
+ fluid_source.clear_reagents()
+ src.visible_message("[src] climaxes over [L], using [p_their()] [G.name]!", \
+ "You orgasm over [L], using your [G.name].", \
+ "You have climaxed over something, using your [G.name].")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+
+ if(isliving(L))
+ var/mutable_appearance/cumoverlay = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi')
+ cumoverlay.icon_state = "cum_normal"
+ L.add_overlay(cumoverlay)
+ else
+ L.add_cum_overlay()
+
+ setArousal(min_arousal)
+ if(G.can_climax)
+ setArousal(min_arousal)
+
+ if(spillage && !cover)
+ if(do_after(src, mb_time, target = src))
+ if(!in_range(src, L) && !remote)
+ return
+ fluid_source.trans_to(L, total_fluids*G.fluid_transfer_factor)
+ total_fluids -= total_fluids*G.fluid_transfer_factor
+ if(total_fluids > 5)
+ fluid_source.reaction(L.loc, TOUCH, 1, 0)
+ var/mob/living/carbon/human/H = L
+ if(H)
+ H.cumdrip_rate += rand(5,10)
+ fluid_source.clear_reagents()
+ src.visible_message("[src] climaxes with [L][spillage ? ", overflowing and spilling":""], using [p_their()] [G.name]!", \
+ "You orgasm with [L][spillage ? ", spilling out of them":""], using your [G.name].", \
+ "You have climaxed with someone[spillage ? ", spilling out of them":""], using your [G.name].")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+
+ if(G.can_climax)
+ setArousal(min_arousal)
+
+ else //knots, portal fleshlights, and other non-spilling orgasms
+ if(!cover)
+ if(!remote && !in_range(src, L))
+ return
+ if(do_after(src, mb_time, target = src))
+
+ if(!spillage) //hyper inflation
+ var/obj/item/organ/genital/belly/B = L.getorganslot("belly")
+ if(B)
+ if(B.inflatable && total_fluids > 80) //requires a big cumshot to expand.
+ if(B.size < 3)
+ B.size += 1
+ to_chat(L, "You feel your belly expand.")
+ else
+ to_chat(L, "You feel your belly strain.")
+
+ fluid_source.trans_to(L, total_fluids)
+ total_fluids = 0
+ if(!remote)
+ src.visible_message("[src] climaxes with [L], [p_their()] [G.name] spilling nothing!", \
+ "You ejaculate with [L], your [G.name] spilling nothing.", \
+ "You have climaxed inside someone, your [G.name] spilling nothing.")
+ else
+ src.visible_message("[src] climaxes with someone, using [p_their()] [G.name]!", \
+ "You ejaculate with someone, using your [G.name].", \
+ "You have climaxed inside someone, using your [G.name].")
+ to_chat(L, "You feel someone ejaculate inside you.")
+
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+
+ if(G.can_climax)
+ setArousal(min_arousal)
+
+ //Hyper - antag code
+ /*if(src.mind.special_role == ROLE_LEWD_TRAITOR)
+ for(var/datum/objective/obj in src.mind.objectives)
+ if (L.mind == obj.target)
+ L.mind.sexed = TRUE //sexed
+ to_chat(src, "You feel deep satisfaction with yourself.")
+ */
+ //Hyper - impreg
+ // if(impreg)
+ // //Role them odds, only people with the dicks can send the chance to the person with the settings enabled at the momment.
+ // if(prob(L.impregchance))
+ // var/obj/item/organ/genital/womb/W = L.getorganslot("womb")
+ // if(W) //check if they have a womb.
+ // if (L.breedable == 1 && W.pregnant == 0) //Dont get pregnant again, if you are pregnant.
+ // log_game("Debug: [L] has been impregnated by [src]")
+ // to_chat(L, "You feel your hormones change, and a motherly instinct take over.") //leting them know magic has happened.
+ // W.pregnant = 1
+ // if (HAS_TRAIT(L, TRAIT_HEAT))
+ // SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "heat", /datum/mood_event/heat) //well done you perv.
+ // REMOVE_TRAIT(L, TRAIT_HEAT, ROUNDSTART_TRAIT) //take the heat away, you satisfied it!
+
+ // //Make breasts produce quicker.
+ // var/obj/item/organ/genital/breasts/B = L.getorganslot("breasts")
+ // if (B.fluid_mult < 0.5 && B)
+ // B.fluid_mult = 0.5
+
/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/G, obj/item/reagent_containers/container, mb_time = 30) //For beaker-filling, beware the bartender
- var/datum/reagents/fluid_source = G.climaxable(src)
- if(!fluid_source)
- return
- if(mb_time)
- to_chat(src,"You start to [G.masturbation_verb] your [G.name] over [container].")
- if(!do_after(src, mb_time, target = src) || !in_range(src, container) || !G.climaxable(src, TRUE))
- return
- to_chat(src,"You used your [G.name] to fill [container].")
- message_admins("[ADMIN_LOOKUPFLW(src)] used their [G.name] to fill [container].")
- log_consent("[key_name(src)] used their [G.name] to fill [container].")
- do_climax(fluid_source, container, G, FALSE)
+ var/total_fluids = 0
+ var/datum/reagents/fluid_source = null
-/mob/living/carbon/human/proc/pick_climax_genitals(silent = FALSE)
- var/list/genitals_list
+ if(G.producing) //Can it produce its own fluids, such as breasts?
+ fluid_source = G.reagents
+ else
+ if(!G.linked_organ)
+ to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.")
+ return
+ fluid_source = G.linked_organ.reagents
+ total_fluids = fluid_source.total_volume
+
+ if(!container) //Something weird happened
+ to_chat(src, "You need a container to do this!")
+ return
+
+ src.visible_message("[src] starts to [G.masturbation_verb] their [G.name] over [container].", \
+ "You start to [G.masturbation_verb] your [G.name] over [container].", \
+ "You start to [G.masturbation_verb] your [G.name] over something.")
+ if(do_after(src, mb_time, target = src) && in_range(src, container))
+ fluid_source.trans_to(container, total_fluids)
+ src.visible_message("[src] uses [p_their()] [G.name] to fill [container]!", \
+ "You used your [G.name] and fill [container] with a total of [total_fluids]u's.", \
+ "You have relieved some pressure.")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ container.add_cum_overlay() //your aim is bad...
+ if(G.can_climax)
+ setArousal(min_arousal)
+
+
+/mob/living/carbon/human/proc/pick_masturbate_genitals()
+ var/obj/item/organ/genital/ret_organ
+ var/list/genitals_list = list()
var/list/worn_stuff = get_equipped_items()
for(var/obj/item/organ/genital/G in internal_organs)
- if((G.genital_flags & CAN_CLIMAX_WITH) && G.is_exposed(worn_stuff)) //filter out what you can't masturbate with
- LAZYADD(genitals_list, G)
- if(LAZYLEN(genitals_list))
- var/obj/item/organ/genital/ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list
+ if(G.genital_flags & CAN_MASTURBATE_WITH) //filter out what you can't masturbate with
+ if(G.is_exposed(worn_stuff)) //Nude or through_clothing
+ if(!G.dontlist)
+ genitals_list += G
+ if(genitals_list.len)
+ ret_organ = input(src, "with what?", "Masturbate", null) as null|obj in genitals_list
return ret_organ
- else if(!silent)
- to_chat(src, "You cannot climax without available genitals.")
+ return null //error stuff
-/mob/living/carbon/human/proc/pick_partner(silent = FALSE)
+/mob/living/carbon/human/proc/target_genitals(mob/living/carbon/human/T) //used for targeting others
+ var/obj/item/organ/genital/ret_organ
+ var/list/genitals_list = list()
+ var/list/worn_stuff = get_equipped_items()
+
+ for(var/obj/item/organ/genital/G in T.internal_organs)
+ if(G.is_exposed(worn_stuff)) //Nude or through_clothing
+ if(!G.dontlist)
+ genitals_list += G
+ if(genitals_list.len)
+ ret_organ = input(src, "", "Genitals", null) as null|obj in genitals_list
+ return ret_organ
+ return null //error stuff
+
+/mob/living/carbon/human/proc/pick_climax_genitals()
+ var/obj/item/organ/genital/ret_organ
+ var/list/genitals_list = list()
+ var/list/worn_stuff = get_equipped_items()
+
+ for(var/obj/item/organ/genital/G in internal_organs)
+ if(G.can_climax) //filter out what you can't masturbate with
+ if(G.is_exposed(worn_stuff)) //Nude or through_clothing
+ if(!G.dontlist)
+ genitals_list += G
+ if(genitals_list.len)
+ ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list
+ return ret_organ
+ return null //error stuff
+
+/mob/living/carbon/human/proc/pick_partner_overide() //used for cumming on people without genitals exposed
var/list/partners = list()
- if(pulling)
- partners += pulling
- if(pulledby)
- partners += pulledby
+ if(src.pulling)
+ partners += src.pulling //Yes, even objects for now
+ if(src.pulledby)
+ partners += src.pulledby
//Now we got both of them, let's check if they're proper
- for(var/mob/living/L in partners)
- if(!L.client || !L.mind) // can't consent, not a partner
- partners -= L
- if(iscarbon(L))
- var/mob/living/carbon/C = L
- if(!C.exposed_genitals.len && !C.is_groin_exposed() && !C.is_chest_exposed() && C.is_mouth_covered()) //Nothing through_clothing, no proper partner.
- partners -= C
+ for(var/I in partners)
+ if(isliving(I))
+ else
+ partners -= I //No fucking objects
//NOW the list should only contain correct partners
if(!partners.len)
- if(!silent)
- to_chat(src, "You cannot do this alone.")
- return //No one left.
- var/mob/living/target = input(src, "With whom?", "Sexual partner", null) as null|anything in partners //pick one, default to null
- if(target && in_range(src, target))
- to_chat(src,"Waiting for consent...")
- var/consenting = input(target, "Do you want [src] to climax with you?","Climax mechanics","No") in list("Yes","No")
- if(consenting == "Yes")
- return target
- else
- message_admins("[ADMIN_LOOKUPFLW(src)] tried to climax with [target], but [target] did not consent.")
- log_consent("[key_name(src)] tried to climax with [target], but [target] did not consent.")
+ return null //No one left.
+ return input(src, "With whom?", "Sexual partner", null) in partners //pick one, default to null
-/mob/living/carbon/human/proc/pick_climax_container(silent = FALSE)
+/mob/living/carbon/human/proc/pick_partner()
+ var/list/partners = list()
+ if(src.pulling)
+ partners += src.pulling //Yes, even objects for now
+ if(src.pulledby)
+ partners += src.pulledby
+ //Now we got both of them, let's check if they're proper
+ for(var/I in partners)
+ if(isliving(I))
+ if(iscarbon(I))
+ var/mob/living/carbon/C = I
+ if(!C.exposed_genitals.len) //Nothing through_clothing
+ if(!C.is_groin_exposed()) //No pants undone
+ if(!C.is_chest_exposed()) //No chest exposed
+ partners -= I //Then not proper, remove them
+ else
+ partners -= I //No fucking objects
+ //NOW the list should only contain correct partners
+ if(!partners.len)
+ return null //No one left.
+ return input(src, "With whom?", "Sexual partner", null) in partners //pick one, default to null
+
+/mob/living/carbon/human/proc/pick_climax_container()
+ var/obj/item/reagent_containers/SC = null
var/list/containers_list = list()
- for(var/obj/item/reagent_containers/C in held_items)
- if(C.is_open_container() || istype(C, /obj/item/reagent_containers/food/snacks))
- containers_list += C
- for(var/obj/item/reagent_containers/C in range(1, src))
- if((C.is_open_container() || istype(C, /obj/item/reagent_containers/food/snacks)) && CanReach(C))
- containers_list += C
+ for(var/obj/item/reagent_containers/container in held_items)
+ if(container.is_open_container() || istype(container, /obj/item/reagent_containers/food/snacks))
+ containers_list += container
if(containers_list.len)
- var/obj/item/reagent_containers/SC = input(src, "Into or onto what?(Cancel for nowhere)", null) as null|obj in containers_list
- if(SC && CanReach(SC))
- return SC
- else if(!silent)
- to_chat(src, "You cannot do this without an appropriate container.")
+ SC = input(src, "Into or onto what?(Cancel for nowhere)", null) as null|obj in containers_list
+ if(SC)
+ if(in_range(src, SC))
+ return SC
+ return null //If nothing correct, give null.
-/mob/living/carbon/human/proc/available_rosie_palms(silent = FALSE, list/whitelist_typepaths = list(/obj/item/dildo))
- if(restrained(TRUE)) //TRUE ignores grabs
- if(!silent)
- to_chat(src, "You can't do that while restrained!")
- return FALSE
- if(!get_num_arms() || !get_empty_held_indexes())
- if(whitelist_typepaths)
- if(!islist(whitelist_typepaths))
- whitelist_typepaths = list(whitelist_typepaths)
- for(var/path in whitelist_typepaths)
- if(is_holding_item_of_type(path))
- return TRUE
- if(!silent)
- to_chat(src, "You need at least one free arm.")
- return FALSE
- return TRUE
//Here's the main proc itself
-/mob/living/carbon/human/proc/mob_climax(forced_climax=FALSE,cause = "") //Forced is instead of the other proc, makes you cum if you have the tools for it, ignoring restraints
- set waitfor = FALSE
+/mob/living/carbon/human/mob_climax(forced_climax=FALSE, cause = "none") //Forced is instead of the other proc, makes you cum if you have the tools for it, ignoring restraints
if(mb_cd_timer > world.time)
if(!forced_climax) //Don't spam the message to the victim if forced to come too fast
to_chat(src, "You need to wait [DisplayTimeText((mb_cd_timer - world.time), TRUE)] before you can do that again!")
return
+ mb_cd_timer = (world.time + mb_cd_length)
- if(!client?.prefs.arousable || !has_dna())
- return
- if(stat == DEAD)
- if(!forced_climax)
+
+ if(canbearoused && has_dna())
+ if(stat==2)
to_chat(src, "You can't do that while dead!")
- return
- if(forced_climax) //Something forced us to cum, this is not a masturbation thing and does not progress to the other checks
- log_message("was forced to climax by [cause]",LOG_EMOTE)
- for(var/obj/item/organ/genital/G in internal_organs)
- if(!(G.genital_flags & CAN_CLIMAX_WITH)) //Skip things like wombs and testicles
- continue
- mob_climax_outside(G, mb_time = 0) //removed climax timer for sudden, forced orgasms
- //Now all genitals that could climax, have.
- //Since this was a forced climax, we do not need to continue with the other stuff
- mb_cd_timer = world.time + mb_cd_length
- return
- //If we get here, then this is not a forced climax and we gotta check a few things.
+ return
+ if(forced_climax) //Something forced us to cum, this is not a masturbation thing and does not progress to the other checks
+ for(var/obj/item/organ/O in internal_organs)
+ if(istype(O, /obj/item/organ/genital))
+ var/obj/item/organ/genital/G = O
+ if(!G.can_climax) //Skip things like wombs and testicles
+ continue
+ var/mob/living/partner
+ var/check_target
+ var/list/worn_stuff = get_equipped_items()
- if(stat == UNCONSCIOUS) //No sleep-masturbation, you're unconscious.
- to_chat(src, "You must be conscious to do that!")
- return
+ if(G.is_exposed(worn_stuff))
+ if(src.pulling) //Are we pulling someone? Priority target, we can't be making option menus for this, has to be quick
+ if(isliving(src.pulling)) //Don't fuck objects
+ check_target = src.pulling
+ if(src.pulledby && !check_target) //prioritise pulled over pulledby
+ if(isliving(src.pulledby))
+ check_target = src.pulledby
+ //Now we should have a partner, or else we have to come alone
+ if(check_target)
+ if(iscarbon(check_target)) //carbons can have clothes
+ var/mob/living/carbon/C = check_target
+ if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) //Are they naked enough?
+ partner = C
+ else //A cat is fine too
+ partner = check_target
+ if(partner) //Did they pass the clothing checks?
+ mob_climax_partner(G, partner, mb_time = 0) //Instant climax due to forced
+ continue //You've climaxed once with this organ, continue on
+ //not exposed OR if no partner was found while exposed, climax alone
+ mob_climax_outside(G, mb_time = 0) //removed climax timer for sudden, forced orgasms
+ //Now all genitals that could climax, have.
+ //Since this was a forced climax, we do not need to continue with the other stuff
+ return
+ //If we get here, then this is not a forced climax and we gotta check a few things.
- //Ok, now we check what they want to do.
- var/choice = input(src, "Select sexual activity", "Sexual activity:") as null|anything in list("Climax alone","Climax with partner", "Fill container")
- if(!choice)
- return
+ if(stat==1) //No sleep-masturbation, you're unconscious.
+ to_chat(src, "You must be conscious to do that!")
+ return
+ if(getArousal() < 33) //flat number instead of percentage
+ to_chat(src, "You aren't aroused enough for that!")
+ return
- switch(choice)
- if("Climax alone")
- if(!available_rosie_palms())
- return
- var/obj/item/organ/genital/picked_organ = pick_climax_genitals()
- if(picked_organ && available_rosie_palms(TRUE))
- mob_climax_outside(picked_organ)
- if("Climax with partner")
- //We need no hands, we can be restrained and so on, so let's pick an organ
- var/obj/item/organ/genital/picked_organ = pick_climax_genitals()
- if(picked_organ)
- var/mob/living/partner = pick_partner() //Get someone
- if(partner)
- var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as null|anything in list("Yes", "No")
- if(spillage && in_range(src, partner))
- mob_climax_partner(picked_organ, partner, spillage == "Yes" ? TRUE : FALSE)
- if("Fill container")
- //We'll need hands and no restraints.
- if(!available_rosie_palms(FALSE, /obj/item/reagent_containers))
- return
- //We got hands, let's pick an organ
- var/obj/item/organ/genital/picked_organ
- picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
- if(picked_organ)
- //Good, got an organ, time to pick a container
- var/obj/item/reagent_containers/fluid_container = pick_climax_container()
- if(fluid_container && available_rosie_palms(TRUE, /obj/item/reagent_containers))
- mob_fill_container(picked_organ, fluid_container)
- mb_cd_timer = world.time + mb_cd_length
+ //Ok, now we check what they want to do.
+ var/choice = input(src, "Select sexual activity", "Sexual activity:") in list("Masturbate", "Climax alone", "Climax with partner","Climax over partner", "Fill container")
+
+ switch(choice)
+
+ if("Masturbate")
+ if(restrained(TRUE)) //TRUE ignores grabs
+ to_chat(src, "You can't do that while restrained!")
+ return
+ var/free_hands = get_num_arms()
+ if(!free_hands)
+ to_chat(src, "You need at least one free arm.")
+ return
+ for(var/helditem in held_items)//how many hands are free
+ if(isobj(helditem))
+ free_hands--
+ if(free_hands <= 0)
+ to_chat(src, "You're holding too many things.")
+ return
+ //We got hands, let's pick an organ
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_masturbate_genitals()
+ if(picked_organ)
+ mob_masturbate(picked_organ)
+ return
+ else //They either lack organs that can masturbate, or they didn't pick one.
+ to_chat(src, "You cannot masturbate without choosing genitals.")
+ return
+
+ if("Climax alone")
+ if(restrained(TRUE)) //TRUE ignores grabs
+ to_chat(src, "You can't do that while restrained!")
+ return
+ var/free_hands = get_num_arms()
+ if(!free_hands)
+ to_chat(src, "You need at least one free arm.")
+ return
+ for(var/helditem in held_items)//how many hands are free
+ if(isobj(helditem))
+ free_hands--
+ if(free_hands <= 0)
+ to_chat(src, "You're holding too many things.")
+ return
+ //We got hands, let's pick an organ
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals()
+ if(picked_organ)
+ mob_climax_outside(picked_organ)
+ return
+ else //They either lack organs that can masturbate, or they didn't pick one.
+ to_chat(src, "You cannot climax without choosing genitals.")
+ return
+
+ if("Climax with partner")
+ //We need no hands, we can be restrained and so on, so let's pick an organ
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals()
+ if(picked_organ)
+ var/mob/living/partner = pick_partner() //Get someone
+ if(partner)
+ /*GS13 Port - De we need breeding? For now disable it for inital porting
+ var/obj/item/organ/genital/penis/P = picked_organ
+ if(partner.breedable == 1 && picked_organ.name == "penis")
+ var/impreg = input(src, "Would this action carry the risk of pregnancy?", "Choose a option", "Yes") as anything in list("Yes", "No")
+ if(impreg == "Yes") //If we are impregging
+ var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
+ if(spillage == "Yes")
+ mob_climax_partner(picked_organ, partner, TRUE, TRUE, FALSE)
+ else
+ mob_climax_partner(picked_organ, partner, FALSE, TRUE, FALSE)
+ else
+ var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
+ if(spillage == "Yes")
+ mob_climax_partner(picked_organ, partner, TRUE, FALSE, FALSE)
+ else
+ mob_climax_partner(picked_organ, partner, FALSE, FALSE, FALSE) //Wow, im trash at coding, I need to find a better way of coding this, ill rewrite it later.-quote
+ return
+
+ else //If we arent impregging
+ var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
+ if(spillage == "Yes")
+ mob_climax_partner(picked_organ, partner, TRUE, FALSE, FALSE)
+ else
+ mob_climax_partner(picked_organ, partner, FALSE, FALSE, FALSE)
+ return
+ */
+ var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
+ if(spillage == "Yes")
+ mob_climax_partner(picked_organ, partner, TRUE, FALSE, FALSE)
+ else
+ mob_climax_partner(picked_organ, partner, FALSE, FALSE, FALSE)
+ return
+
+ else
+ to_chat(src, "You cannot do this alone.")
+ return
+ else //They either lack organs that can masturbate, or they didn't pick one.
+ to_chat(src, "You cannot climax without choosing genitals.")
+ return
+ if("Climax over partner")
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals()
+ if(picked_organ)
+ var/mob/living/partner = pick_partner_overide() //Get your partner, clothed or not.
+ if(partner)
+ mob_climax_partner(picked_organ, partner, FALSE, FALSE, TRUE)
+ else
+ to_chat(src, "You cannot do this alone.")
+
+ if("Fill container")
+ //We'll need hands and no restraints.
+ if(restrained(TRUE)) //TRUE ignores grabs
+ to_chat(src, "You can't do that while restrained!")
+ return
+ var/free_hands = get_num_arms()
+ if(!free_hands)
+ to_chat(src, "You need at least one free arm.")
+ return
+ for(var/helditem in held_items)//how many hands are free
+ if(isobj(helditem))
+ free_hands--
+ if(free_hands <= 0)
+ to_chat(src, "You're holding too many things.")
+ return
+ //We got hands, let's pick an organ
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
+ if(picked_organ)
+ //Good, got an organ, time to pick a container
+ var/obj/item/reagent_containers/fluid_container = pick_climax_container()
+ if(fluid_container)
+ mob_fill_container(picked_organ, fluid_container)
+ return
+ else
+ to_chat(src, "You cannot do this without anything to fill.")
+ return
+ else //They either lack organs that can climax, or they didn't pick one.
+ to_chat(src, "You cannot fill anything without choosing genitals.")
+ return
+ else //Somehow another option was taken, maybe something interrupted the selection or it was cancelled
+ return //Just end it in that case.
-/mob/living/carbon/human/verb/climax_verb()
- set category = "IC"
- set name = "Climax"
- set desc = "Lets you choose a couple ways to ejaculate."
- mob_climax()
diff --git a/code/modules/arousal/genitals.dm b/code/modules/arousal/genitals.dm
index e4b9f4107c..de892c050f 100644
--- a/code/modules/arousal/genitals.dm
+++ b/code/modules/arousal/genitals.dm
@@ -9,6 +9,7 @@
var/orgasm_verb = "cumming" //present continous
var/arousal_verb = "You feel aroused"
var/unarousal_verb = "You no longer feel aroused"
+ var/can_climax = FALSE
var/fluid_transfer_factor = 0 //How much would a partner get in them if they climax using this?
var/size = 2 //can vary between num or text, just used in icon_state strings
var/datum/reagent/fluid_id = null
@@ -16,11 +17,22 @@
var/fluid_efficiency = 1
var/fluid_rate = CUM_RATE
var/fluid_mult = 1
+ var/producing = FALSE
var/last_orgasmed = 0
- var/aroused_state = FALSE //Boolean used in icon_state strings
+ var/aroused_state = FALSE //Boolean used in icon_state strings
+ var/aroused_amount = 50 //This is a num from 0 to 100 for arousal percentage for when to use arousal state icons.
var/obj/item/organ/genital/linked_organ
var/linked_organ_slot //used for linking an apparatus' organ to its other half on update_link().
var/layer_index = GENITAL_LAYER_INDEX //Order should be very important. FIRST vagina, THEN testicles, THEN penis, as this affects the order they are rendered in.
+ var/through_clothes = FALSE
+ var/internal = FALSE
+ var/hidden = FALSE
+ var/colourtint = ""
+ var/mode = ""
+ var/obj/item/equipment //for fun stuff that goes on the gentials/maybe rings down the line
+ var/dontlist = FALSE
+ var/nochange = FALSE //stops people changing visablity.
+
/obj/item/organ/genital/Initialize(mapload, do_update = TRUE)
. = ..()
@@ -33,8 +45,14 @@
linked_organ = null
. = ..()
+//GS13 Port - Make gentials actually produce "reagents"
/obj/item/organ/genital/on_life()
- return
+ if(QDELETED(src))
+ return
+ if(!reagents || !owner)
+ return
+
+ generate_fluid(reagents)
/obj/item/organ/genital/proc/set_aroused_state(new_state,cause = "manual toggle")
if(!(genital_flags & GENITAL_CAN_AROUSE))
@@ -89,17 +107,21 @@
if(owner)
owner.log_message("Exposed their [src]",LOG_EMOTE)
owner.exposed_genitals += src
+ mode = GEN_VISIBLE_ALWAYS
if(GEN_VISIBLE_NO_CLOTHES)
if(owner)
owner.log_message("Hid their [src] under clothes only",LOG_EMOTE)
+ mode = GEN_VISIBLE_NO_CLOTHES
if(GEN_VISIBLE_NO_UNDIES)
genital_flags |= GENITAL_UNDIES_HIDDEN
if(owner)
owner.log_message("Hid their [src] under underwear",LOG_EMOTE)
+ mode = GEN_VISIBLE_NO_UNDIES
if(GEN_VISIBLE_NEVER)
genital_flags |= GENITAL_HIDDEN
if(owner)
owner.log_message("Hid their [src] completely",LOG_EMOTE)
+ mode = GEN_VISIBLE_NEVER
if(update && owner && ishuman(owner)) //recast to use update genitals proc
var/mob/living/carbon/human/H = owner
diff --git a/code/modules/arousal/organs/breasts.dm b/code/modules/arousal/organs/breasts.dm
index d7a8c67adb..69020da4f3 100644
--- a/code/modules/arousal/organs/breasts.dm
+++ b/code/modules/arousal/organs/breasts.dm
@@ -11,9 +11,11 @@
size = BREASTS_SIZE_DEF // "c". Refer to the breast_values static list below for the cups associated number values
fluid_id = /datum/reagent/consumable/milk
fluid_rate = MILK_RATE
+ producing = TRUE
shape = DEF_BREASTS_SHAPE
genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_FUID_PRODUCTION|GENITAL_CAN_AROUSE|UPDATE_OWNER_APPEARANCE|GENITAL_UNDIES_HIDDEN
masturbation_verb = "massage"
+ can_climax = TRUE
arousal_verb = "Your breasts start feeling sensitive"
unarousal_verb = "Your breasts no longer feel sensitive"
orgasm_verb = "leaking"
diff --git a/code/modules/arousal/organs/penis.dm b/code/modules/arousal/organs/penis.dm
index e1025094c3..7e0f31ed43 100644
--- a/code/modules/arousal/organs/penis.dm
+++ b/code/modules/arousal/organs/penis.dm
@@ -6,6 +6,7 @@
zone = BODY_ZONE_PRECISE_GROIN
slot = ORGAN_SLOT_PENIS
masturbation_verb = "stroke"
+ can_climax = TRUE
arousal_verb = "You pop a boner"
unarousal_verb = "Your boner goes down"
genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_CAN_AROUSE|UPDATE_OWNER_APPEARANCE|GENITAL_UNDIES_HIDDEN|GENITAL_CAN_TAUR
diff --git a/code/modules/arousal/organs/testicles.dm b/code/modules/arousal/organs/testicles.dm
index bfb5c99a55..2fdb0fb2c2 100644
--- a/code/modules/arousal/organs/testicles.dm
+++ b/code/modules/arousal/organs/testicles.dm
@@ -13,7 +13,9 @@
var/size_name = "average"
shape = DEF_BALLS_SHAPE
fluid_id = /datum/reagent/consumable/semen
+ producing = TRUE
masturbation_verb = "massage"
+ can_climax = TRUE
layer_index = TESTICLES_LAYER_INDEX
/obj/item/organ/genital/testicles/generate_fluid()
diff --git a/code/modules/arousal/organs/vagina.dm b/code/modules/arousal/organs/vagina.dm
index 5c8bbeea98..aaeb0fdf99 100644
--- a/code/modules/arousal/organs/vagina.dm
+++ b/code/modules/arousal/organs/vagina.dm
@@ -9,6 +9,7 @@
shape = DEF_VAGINA_SHAPE
genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_CAN_AROUSE|GENITAL_UNDIES_HIDDEN
masturbation_verb = "finger"
+ can_climax = TRUE
arousal_verb = "You feel wetness on your crotch"
unarousal_verb = "You no longer feel wet"
fluid_transfer_factor = 0.1 //Yes, some amount is exposed to you, go get your AIDS
diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm
index 46e80b3255..041dd4be96 100644
--- a/code/modules/awaymissions/corpse.dm
+++ b/code/modules/awaymissions/corpse.dm
@@ -32,6 +32,7 @@
var/banType = "lavaland"
var/ghost_usable = TRUE
var/skip_reentry_check = FALSE //Skips the ghost role blacklist time for people who ghost/suicide/cryo
+ var/mirrorcanloadappearance = TRUE //GS13 EDIT
///override this to add special spawn conditions to a ghost role
/obj/effect/mob_spawn/proc/allow_spawn(mob/user, silent = FALSE)
@@ -139,6 +140,7 @@
M.mind.assigned_role = assignedrole
special(M, name)
MM.name = M.real_name
+ M.checkloadappearance() // GS13 EDIT
if(uses > 0)
uses--
if(!permanent && !uses)
@@ -251,6 +253,10 @@
W.assignment = id_job
W.registered_name = H.real_name
W.update_label()
+ //GS13 EDIT START
+ if (mirrorcanloadappearance)
+ H.mirrorcanloadappearance = TRUE
+ //GS13 EDIT END
//Instant version - use when spawning corpses during runtime
/obj/effect/mob_spawn/human/corpse
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index cf5b9fd913..cf81e47337 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -132,7 +132,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/eye_type = DEFAULT_EYES_TYPE //Eye type
var/split_eye_colors = FALSE
var/datum/species/pref_species = new /datum/species/human() //Mutant race
- var/list/features = list("mcolor" = "FFFFFF", "mcolor2" = "FFFFFF", "mcolor3" = "FFFFFF", "tail_lizard" = "Smooth", "tail_human" = "None", "snout" = "Round", "horns" = "None", "horns_color" = "85615a", "ears" = "None", "wings" = "None", "wings_color" = "FFF", "frills" = "None", "deco_wings" = "None", "spines" = "None", "legs" = "Plantigrade", "insect_wings" = "Plain", "insect_fluff" = "None", "insect_markings" = "None", "arachnid_legs" = "Plain", "arachnid_spinneret" = "Plain", "arachnid_mandibles" = "Plain", "mam_body_markings" = list(), "mam_ears" = "None", "mam_snouts" = "None", "mam_tail" = "None", "mam_tail_animated" = "None", "xenodorsal" = "Standard", "xenohead" = "Standard", "xenotail" = "Xenomorph Tail", "taur" = "None", "genitals_use_skintone" = FALSE, "has_cock" = FALSE, "cock_shape" = DEF_COCK_SHAPE, "cock_length" = COCK_SIZE_DEF, "cock_diameter_ratio" = COCK_DIAMETER_RATIO_DEF, "cock_color" = "ffffff", "cock_taur" = FALSE, "has_balls" = FALSE, "balls_color" = "ffffff", "balls_shape" = DEF_BALLS_SHAPE, "balls_size" = BALLS_SIZE_DEF, "balls_cum_rate" = CUM_RATE, "balls_cum_mult" = CUM_RATE_MULT, "balls_efficiency" = CUM_EFFICIENCY, "has_breasts" = FALSE, "breasts_color" = "ffffff", "breasts_size" = BREASTS_SIZE_DEF, "breasts_shape" = DEF_BREASTS_SHAPE, "breasts_fluid" = /datum/reagent/consumable/milk, "breasts_producing" = FALSE, "has_vag" = FALSE, "vag_shape" = DEF_VAGINA_SHAPE, "vag_color" = "ffffff", "has_womb" = FALSE, "has_butt" = FALSE, "butt_color" = "ffffff", "butt_size" = BUTT_SIZE_DEF, "balls_visibility" = GEN_VISIBLE_NO_UNDIES, "breasts_visibility"= GEN_VISIBLE_NO_UNDIES, "cock_visibility" = GEN_VISIBLE_NO_UNDIES, "vag_visibility" = GEN_VISIBLE_NO_UNDIES, "butt_visibility" = GEN_VISIBLE_NO_UNDIES, "ipc_screen" = "Sunburst", "ipc_antenna" = "None", "flavor_text" = "", "silicon_flavor_text" = "", "ooc_notes" = "", "meat_type" = "Mammalian", "body_model" = MALE, "body_size" = RESIZE_DEFAULT_SIZE, "color_scheme" = OLD_CHARACTER_COLORING)
+ var/list/features = list("mcolor" = "FFFFFF", "mcolor2" = "FFFFFF", "mcolor3" = "FFFFFF", "tail_lizard" = "Smooth", "tail_human" = "None", "snout" = "Round", "horns" = "None", "horns_color" = "85615a", "ears" = "None", "wings" = "None", "wings_color" = "FFF", "frills" = "None", "deco_wings" = "None", "spines" = "None", "legs" = "Plantigrade", "insect_wings" = "Plain", "insect_fluff" = "None", "insect_markings" = "None", "arachnid_legs" = "Plain", "arachnid_spinneret" = "Plain", "arachnid_mandibles" = "Plain", "mam_body_markings" = list(), "mam_ears" = "None", "mam_snouts" = "None", "mam_tail" = "None", "mam_tail_animated" = "None", "xenodorsal" = "Standard", "xenohead" = "Standard", "xenotail" = "Xenomorph Tail", "taur" = "None", "genitals_use_skintone" = FALSE, "has_cock" = FALSE, "cock_shape" = DEF_COCK_SHAPE, "cock_length" = COCK_SIZE_DEF, "cock_diameter_ratio" = COCK_DIAMETER_RATIO_DEF, "cock_color" = "ffffff", "cock_taur" = FALSE, "has_balls" = FALSE, "balls_color" = "ffffff", "balls_shape" = DEF_BALLS_SHAPE, "balls_size" = BALLS_SIZE_DEF, "balls_cum_rate" = CUM_RATE, "balls_cum_mult" = CUM_RATE_MULT, "balls_efficiency" = CUM_EFFICIENCY, "has_breasts" = FALSE, "breasts_color" = "ffffff", "breasts_size" = BREASTS_SIZE_DEF, "breasts_shape" = DEF_BREASTS_SHAPE, "breasts_fluid" = /datum/reagent/consumable/milk, "breasts_producing" = FALSE, "has_vag" = FALSE, "vag_shape" = DEF_VAGINA_SHAPE, "vag_color" = "ffffff", "has_womb" = FALSE, "has_butt" = FALSE, "butt_color" = "ffffff", "butt_size" = BUTT_SIZE_DEF, "balls_visibility" = GEN_VISIBLE_NO_UNDIES, "breasts_visibility"= GEN_VISIBLE_NO_UNDIES, "cock_visibility" = GEN_VISIBLE_NO_UNDIES, "vag_visibility" = GEN_VISIBLE_NO_UNDIES, "butt_visibility" = GEN_VISIBLE_NO_UNDIES, "ipc_screen" = "Sunburst", "ipc_antenna" = "None", "flavor_text" = "", "silicon_flavor_text" = "", "ooc_notes" = "", "meat_type" = "Mammalian", "body_model" = MALE, "body_size" = RESIZE_DEFAULT_SIZE, "color_scheme" = OLD_CHARACTER_COLORING, "belly_size" = BELLY_SIZE_DEF, "inflatable_belly" = FALSE, "belly_visibility" = GEN_VISIBLE_NO_UNDIES)
var/custom_speech_verb = "default" //if your say_mod is to be something other than your races
var/custom_tongue = "default" //if your tongue is to be something other than your races
@@ -845,7 +845,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "Belly Size: [features["belly_size"]]"
dat += "Belly Visibility:[features["belly_visibility"]]"
// GS13: tweak inflation description
- //dat += "Inflation (climax with and manual belly size change in arousal menu):[features["inflatable_belly"] == 1 ? "Yes" : "No"]"
+ dat += "Inflation (climax with and manual belly size change in arousal menu):[features["inflatable_belly"] == 1 ? "Yes" : "No"]"
+
dat += ""
dat += ""
@@ -3050,6 +3051,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
features["has_butt"] = !features["has_butt"]
if("has_belly")
features["has_belly"] = !features["has_belly"]
+ if("inflatable_belly")
+ features["inflatable_belly"] = !features["inflatable_belly"]
if("widescreenpref")
widescreenpref = !widescreenpref
user.client.view_size.setDefault(getScreenSize(widescreenpref))
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 488f167981..e4d86720f5 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -704,7 +704,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
var/savefile/S = new /savefile(path)
if(!S)
return FALSE
- features = list("mcolor" = "FFFFFF", "mcolor2" = "FFFFFF", "mcolor3" = "FFFFFF", "tail_lizard" = "Smooth", "tail_human" = "None", "snout" = "Round", "horns" = "None", "horns_color" = "85615a", "ears" = "None", "wings" = "None", "wings_color" = "FFF", "frills" = "None", "deco_wings" = "None", "spines" = "None", "legs" = "Plantigrade", "insect_wings" = "Plain", "insect_fluff" = "None", "insect_markings" = "None", "arachnid_legs" = "Plain", "arachnid_spinneret" = "Plain", "arachnid_mandibles" = "Plain", "mam_body_markings" = "Plain", "mam_ears" = "None", "mam_snouts" = "None", "mam_tail" = "None", "mam_tail_animated" = "None", "xenodorsal" = "Standard", "xenohead" = "Standard", "xenotail" = "Xenomorph Tail", "taur" = "None", "genitals_use_skintone" = FALSE, "has_cock" = FALSE, "cock_shape" = DEF_COCK_SHAPE, "cock_length" = COCK_SIZE_DEF, "cock_diameter_ratio" = COCK_DIAMETER_RATIO_DEF, "cock_color" = "ffffff", "cock_taur" = FALSE, "has_balls" = FALSE, "balls_color" = "ffffff", "balls_shape" = DEF_BALLS_SHAPE, "balls_size" = BALLS_SIZE_DEF, "balls_cum_rate" = CUM_RATE, "balls_cum_mult" = CUM_RATE_MULT, "balls_efficiency" = CUM_EFFICIENCY, "has_breasts" = FALSE, "breasts_color" = "ffffff", "breasts_size" = BREASTS_SIZE_DEF, "breasts_shape" = DEF_BREASTS_SHAPE, "breasts_producing" = FALSE, "has_vag" = FALSE, "vag_shape" = DEF_VAGINA_SHAPE, "vag_color" = "ffffff", "has_womb" = FALSE, "has_butt" = FALSE, "butt_color" = "ffffff", "butt_size" = BUTT_SIZE_DEF, "balls_visibility" = GEN_VISIBLE_NO_UNDIES, "breasts_visibility"= GEN_VISIBLE_NO_UNDIES, "cock_visibility" = GEN_VISIBLE_NO_UNDIES, "vag_visibility" = GEN_VISIBLE_NO_UNDIES, "butt_visibility" = GEN_VISIBLE_NO_UNDIES, "belly_visibility" = GEN_VISIBLE_NO_UNDIES, "ipc_screen" = "Sunburst", "ipc_antenna" = "None", "flavor_text" = "", "silicon_flavor_text" = "", "ooc_notes" = "", "meat_type" = "Mammalian", "body_model" = MALE, "body_size" = RESIZE_DEFAULT_SIZE, "color_scheme" = OLD_CHARACTER_COLORING)
+ features = list("mcolor" = "FFFFFF", "mcolor2" = "FFFFFF", "mcolor3" = "FFFFFF", "tail_lizard" = "Smooth", "tail_human" = "None", "snout" = "Round", "horns" = "None", "horns_color" = "85615a", "ears" = "None", "wings" = "None", "wings_color" = "FFF", "frills" = "None", "deco_wings" = "None", "spines" = "None", "legs" = "Plantigrade", "insect_wings" = "Plain", "insect_fluff" = "None", "insect_markings" = "None", "arachnid_legs" = "Plain", "arachnid_spinneret" = "Plain", "arachnid_mandibles" = "Plain", "mam_body_markings" = "Plain", "mam_ears" = "None", "mam_snouts" = "None", "mam_tail" = "None", "mam_tail_animated" = "None", "xenodorsal" = "Standard", "xenohead" = "Standard", "xenotail" = "Xenomorph Tail", "taur" = "None", "genitals_use_skintone" = FALSE, "has_cock" = FALSE, "cock_shape" = DEF_COCK_SHAPE, "cock_length" = COCK_SIZE_DEF, "cock_diameter_ratio" = COCK_DIAMETER_RATIO_DEF, "cock_color" = "ffffff", "cock_taur" = FALSE, "has_balls" = FALSE, "balls_color" = "ffffff", "balls_shape" = DEF_BALLS_SHAPE, "balls_size" = BALLS_SIZE_DEF, "balls_cum_rate" = CUM_RATE, "balls_cum_mult" = CUM_RATE_MULT, "balls_efficiency" = CUM_EFFICIENCY, "has_breasts" = FALSE, "breasts_color" = "ffffff", "breasts_size" = BREASTS_SIZE_DEF, "breasts_shape" = DEF_BREASTS_SHAPE, "breasts_producing" = FALSE, "has_vag" = FALSE, "vag_shape" = DEF_VAGINA_SHAPE, "vag_color" = "ffffff", "has_womb" = FALSE, "has_butt" = FALSE, "butt_color" = "ffffff", "butt_size" = BUTT_SIZE_DEF, "balls_visibility" = GEN_VISIBLE_NO_UNDIES, "breasts_visibility"= GEN_VISIBLE_NO_UNDIES, "cock_visibility" = GEN_VISIBLE_NO_UNDIES, "vag_visibility" = GEN_VISIBLE_NO_UNDIES, "butt_visibility" = GEN_VISIBLE_NO_UNDIES, "belly_visibility" = GEN_VISIBLE_NO_UNDIES, "belly_size" = BELLY_SIZE_DEF, "inflatable_belly" = FALSE, "ipc_screen" = "Sunburst", "ipc_antenna" = "None", "flavor_text" = "", "silicon_flavor_text" = "", "ooc_notes" = "", "meat_type" = "Mammalian", "body_model" = MALE, "body_size" = RESIZE_DEFAULT_SIZE, "color_scheme" = OLD_CHARACTER_COLORING)
S.cd = "/"
if(!slot)
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 6723d2d4a6..5a9d3971cb 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -66,6 +66,9 @@
var/blood_state = BLOOD_STATE_NOT_BLOODY
var/list/blood_smear = list(BLOOD_STATE_BLOOD = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
+ //GS13 Port - Arousal
+ var/cumdrip_rate = 0 //how long are we dripping jizz for?
+
var/name_override //For temporary visible name changes
var/genital_override = FALSE //Force genitals on things incase of chems
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 9b8daf0384..a209aca017 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -546,6 +546,13 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species)
+ //GS13 Port - Add back arousal
+ if(NOAROUSAL in species_traits)
+ C.canbearoused = FALSE
+ else
+ if(C.client)
+ C.canbearoused = C.client?.prefs?.arousable
+
/datum/species/proc/update_species_slowdown(mob/living/carbon/human/H)
H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/species, TRUE, multiplicative_slowdown = speedmod)
diff --git a/hyperstation/code/modules/arousal/arousalhud.dm b/hyperstation/code/modules/arousal/arousalhud.dm
index c881d29cba..f4df481b63 100644
--- a/hyperstation/code/modules/arousal/arousalhud.dm
+++ b/hyperstation/code/modules/arousal/arousalhud.dm
@@ -3,7 +3,7 @@
//if you wanna use this on your own server go ahead, but alittle credit would always be nice! -quotefox
//This still uses alot of cits arousal system!
-/obj/screen/arousal/ui_interact(mob/user)
+/atom/movable/screen/arousal/ui_interact(mob/user)
. = ..()
var/dat = {"Genitals