Implement wrapper functions for arousal

This looks at all accesses to ArousalSettings' properties, and add wrappers instead of direct access and safety checking.
This commit is contained in:
Jean-Baptiste Emmanuel Zorg 2022-01-30 21:59:56 +01:00
parent 70bf70b1e7
commit ca4f619072
14 changed files with 484 additions and 189 deletions
BondageClub
Screens
Character/Preference
Inventory
ItemBreast/FuturisticBra
ItemPelvis
FuturisticChastityBelt
FuturisticTrainingBelt
Online/ChatRoom
Room/Private
Scripts

View file

@ -63,6 +63,188 @@ var PreferenceGraphicsWebGLOptions = null;
var PreferenceGraphicsAnimationQualityList = [10000, 2000, 200, 100, 50, 0];
var PreferenceCalibrationStage = 0;
/** @type {Record<"Inactive"|"NoMeter"|"Manual"|"Hybrid"|"Automatic", ArousalMeterMode>} */
const ArousalMode = {
Inactive: "Inactive",
NoMeter: "NoMeter",
Manual: "Manual",
Hybrid: "Hybrid",
Automatic: "Automatic",
};
/** @type {Record<"All"|"Access"|"Self", ArousalAccessMode>} */
const ArousalAccess = {
All: "All",
Access: "Access",
Self: "Self",
};
/** @type {Record<"Inactive"|"Animated"|"AnimatedTemp", ArousalVFXMode>} */
const ArousalVFX = {
Inactive: "VFXInactive",
Animated: "VFXAnimated",
AnimatedTemp: "VFXAnimatedTemp",
};
/** @type {Record<"Inactive"|"Solid"|"Animated", ArousalVFXVibratorMode>} */
const ArousalVFXVibrator = {
Inactive: "VFXVibratorInactive",
Solid: "VFXVibratorSolid",
Animated: "VFXVibratorAnimated",
};
/** @type {Record<"Light"|"Medium"|"Heavy", ArousalVFXFilterMode>} */
const ArousalVFXFilter = {
Light: "VFXFilterLight",
Medium: "VFXFilterMedium",
Heavy: "VFXFilterHeavy",
};
/** @type {Record<"None"|"Arousal"|"Vibration"|"All", ArousalStutterMode>} */
const ArousalStutter = {
None: "None",
Arousal: "Arousal",
Vibration: "Vibration",
All: "All"
};
/**
* Check a character's arousal mode against a list.
* @param {Character|Player} character - The character or player to read arousal from
* @param {Array<ArousalMeterMode>} modes - The arousal modes
* @returns {boolean|null}
*/
function PreferenceArousalIsInMode(character, modes) {
if (character.ArousalSettings && character.ArousalSettings.Active)
return modes.includes(character.ArousalSettings.Active);
return false;
}
/**
* Check whether a character can be aroused.
* @param {Character|Player} character - The character or player to read arousal from
* @returns {boolean|null}
*/
function PreferenceArousalIsActive(character) {
return PreferenceArousalIsInMode(character, [ArousalMode.Manual, ArousalMode.Hybrid, ArousalMode.Automatic]);
}
/**
* Returns a character's arousal access mode.
* @param {Character|Player} character - The character or player to read arousal from
* @returns {ArousalAccessMode|null}
*/
function PreferenceArousalGetAccessMode(character) {
if (character.ArousalSettings && character.ArousalSettings.Visible)
return character.ArousalSettings.Visible;
return null;
}
/**
* Returns a character's arousal-stuttering setting.
* @param {Character|Player} character
* @returns {ArousalStutterMode}
*/
function PreferenceArousalGetStutterSetting(character) {
if (character.ArousalSettings && character.ArousalSettings.AffectStutter)
return character.ArousalSettings.AffectStutter;
return ArousalStutter.All;
}
/**
* Returns whether a character's arousal affects its expression.
* @param {Character|Player} character - The character or player to read arousal from
* @returns {boolean}
*/
function PreferenceArousalAffectsExpression(character) {
if (character.ArousalSettings && character.ArousalSettings.AffectExpression)
return !!character.ArousalSettings.AffectExpression;
return true;
}
/**
* Returns whether a character can see others' arousal meters.
* @param {Character|Player} character - The character or player to read arousal from
* @returns {boolean}
*/
function PreferenceArousalShowsMeter(character) {
if (character.ArousalSettings && character.ArousalSettings.ShowOtherMeter)
return !!character.ArousalSettings.ShowOtherMeter;
return false;
}
/**
* Returns whether a character can set its own arousal meter.
* @param {Character|Player} character - The character or player to read arousal from
* @returns {boolean}
*/
function PreferenceArousalCanChangeMeter(character) {
const access = PreferenceArousalGetAccessMode(character);
return (access === ArousalAccess.Access && character.AllowItem) || access === ArousalAccess.All;
}
/**
* Does the character allow advanced vibrating modes on itself?
* @param {Character|Player} character
*/
function PreferenceArousalHasAdvancedVibesDisabled(character) {
if (character.ArousalSettings && typeof character.ArousalSettings.DisableAdvancedVibes === "boolean")
return character.ArousalSettings.DisableAdvancedVibes;
return false;
}
/**
* Get the characters' arousal effect visual setting.
* @param {Character} character
* @return {ArousalVFXMode}
*/
function PreferenceArousalGetVFXSetting(character) {
if (character.ArousalSettings && character.ArousalSettings.VFX)
return character.ArousalSettings.VFX;
return ArousalVFX.Inactive;
}
/**
* Is the arousal effect enabled for this character?
* @param {Character} character
* @return {boolean}
*/
function PreferenceArousalVFXActive(character) {
return PreferenceArousalGetVFXSetting(character) !== ArousalVFX.Inactive;
}
/**
* Get the characters' arousal effect vibrator animation setting.
* @param {Character} character
* @return {ArousalVFXVibratorMode}
*/
function PreferenceArousalGetVFXVibratorSetting(character) {
if (character.ArousalSettings && character.ArousalSettings.VFXVibrator)
return character.ArousalSettings.VFXVibrator;
return ArousalVFXVibrator.Inactive;
}
/**
* Is vibrator effect animated for this character?
* @param {Character} character
* @return {boolean}
*/
function PreferenceArousalIsVibratorVFXAnimated(character) {
return PreferenceArousalGetVFXVibratorSetting(character) === ArousalVFXVibrator.Animated;
}
/**
* Get the characters' arousal effect visual filter.
* @param {Character} character
* @return {ArousalVFXFilterMode}
*/
function PreferenceArousalGetVFXFilterSetting(character) {
if (character.ArousalSettings && character.ArousalSettings.VFXFilter)
return character.ArousalSettings.VFXFilter;
return ArousalVFXFilter.Light;
}
/**
* Compares the arousal preference level and returns TRUE if that level is met, or an higher level is met
* @param {Character} C - The player who performs the sexual activity
@ -219,14 +401,6 @@ function PreferenceGetFactorColor(Factor) {
return "#80808044";
}
/**
* Checks, if the arousal activity controls must be activated
* @returns {boolean} - Returns true if we must activate the preference controls, false otherwise
*/
function PreferenceArousalIsActive() {
return (PreferenceArousalActiveList[PreferenceArousalActiveIndex] != "Inactive");
}
/**
* Loads the activity factor combo boxes based on the current activity selected
* @returns {void} - Nothing
@ -260,7 +434,7 @@ function PreferenceInit(C) {
if (typeof C.ArousalSettings.AffectExpression !== "boolean") C.ArousalSettings.AffectExpression = true;
if (typeof C.ArousalSettings.AffectStutter !== "string") C.ArousalSettings.AffectStutter = "All";
if (typeof C.ArousalSettings.VFX !== "string") C.ArousalSettings.VFX = "VFXAnimatedTemp";
if (typeof C.ArousalSettings.VFXVibrator !== "string") C.ArousalSettings.VFXVibrator = "VFXAnimated";
if (typeof C.ArousalSettings.VFXVibrator !== "string") C.ArousalSettings.VFXVibrator = "VFXVibratorAnimated";
if (typeof C.ArousalSettings.Progress !== "number" || isNaN(C.ArousalSettings.Progress)) C.ArousalSettings.Progress = 0;
if (typeof C.ArousalSettings.ProgressTimer !== "number" || isNaN(C.ArousalSettings.ProgressTimer)) C.ArousalSettings.ProgressTimer = 0;
if (typeof C.ArousalSettings.VibratorLevel !== "number" || isNaN(C.ArousalSettings.VibratorLevel)) C.ArousalSettings.VibratorLevel = 0;
@ -1259,7 +1433,7 @@ function PreferenceSubscreenArousalRun() {
// The other controls are only drawn if the arousal is active
if (PreferenceArousalIsActive()) {
if (PreferenceArousalIsActive(Player)) {
// Draws the labels and check boxes
DrawCheckbox(1250, 276, 64, 64, TextGet("ArousalAffectExpression"), Player.ArousalSettings.AffectExpression);
@ -1704,7 +1878,7 @@ function PreferenceSubscreenArousalClick() {
Player.ArousalSettings.DisableAdvancedVibes = !Player.ArousalSettings.DisableAdvancedVibes;
// If the arousal is active, we allow more controls
if (PreferenceArousalIsActive()) {
if (PreferenceArousalIsActive(Player)) {
// Meter affect your facial expressions check box
if (MouseIn(1250, 276, 64, 64))

View file

@ -49,23 +49,21 @@ function InventoryItemBreastFuturisticBraUpdate(C) {
current_bpm += C.MemberNumber % 20; // 'Pseudo random baseline'
}
if (C.ArousalSettings && C.ArousalSettings.Progress > 0) {
var Progress = C.ArousalSettings.Progress;
current_bpm += Math.floor(Progress*0.60);
current_temp += Math.floor(Progress*0.1)/10;
if ((C.ArousalSettings.OrgasmStage && C.ArousalSettings.OrgasmStage > 0) || (C.ArousalSettings.ProgressTimer && C.ArousalSettings.ProgressTimer > 1)) {
let progress = ActivityGetArousal(C);
if (progress > 0) {
current_bpm += Math.floor(progress * 0.6);
current_temp += Math.floor(progress * 0.1) / 10;
if (ActivityGetOrgasmStage(C) > 0 || ActivityGetArousalTimer(C) > 0) {
current_breathing = "Action";
current_bpm += 10;
current_temp += 0.5;
} else if (C.ArousalSettings.Progress > 10) {
if (C.ArousalSettings.Progress > 90) {
current_breathing = "High";
} else {
current_breathing = "Med";
}
} else if (progress > 90) {
current_breathing = "High";
} else if (progress > 10) {
current_breathing = "Med";
}
}
return { bpm: current_bpm, breathing: current_breathing, temp: current_temp };
}

View file

@ -28,7 +28,7 @@ function InventoryFuturisticChastityBeltCheckPunish(Item) {
}
// Punish the player if they orgasm
if (Item.Property.NextShockTime - CurrentTime <= 0 && (Item.Property.PunishOrgasm || (Item.Property.Type && Item.Property.Type.includes("o1"))) && Player.ArousalSettings && Player.ArousalSettings.OrgasmStage > 1) {
if (Item.Property.NextShockTime - CurrentTime <= 0 && (Item.Property.PunishOrgasm || (Item.Property.Type && Item.Property.Type.includes("o1"))) && ArousalGetOrgasmStage(Player) > 1) {
// Punish the player if they orgasm
return "Orgasm";
}

View file

@ -323,10 +323,11 @@ function InventoryItemPelvisFuturisticTrainingBeltNpcDialog(C, Option) {
}
function InventoryItemPelvisFuturisticTrainingBeltGetVibeMode(C, State, First) {
const ArousalActive = C.ArousalSettings && C.ArousalSettings.Progress && ["Manual", "Hybrid", "Automatic"].includes(C.ArousalSettings.Active);
let arousalActive = PreferenceArousalIsActive(C);
let progress = ActivityGetArousal(C);
if (State.includes("Edge")) {
if (First || (ArousalActive &&(C.ArousalSettings.Progress < 60 || C.ArousalSettings.Progress > 90)) || (CommonTime() % FuturisticTrainingBeltRandomEdgeCycle > FuturisticTrainingBeltRandomEdgeCycle / 5)) {
if ((ArousalActive && C.ArousalSettings.Progress > 90))
if (First || (arousalActive && !ActivityIsArousalBetween(C, 60, 90)) || (CommonTime() % FuturisticTrainingBeltRandomEdgeCycle > FuturisticTrainingBeltRandomEdgeCycle / 5)) {
if ((arousalActive && progress > 90))
return VibratorMode.MAXIMUM;
else return VibratorMode.HIGH;
} else
@ -334,9 +335,9 @@ function InventoryItemPelvisFuturisticTrainingBeltGetVibeMode(C, State, First) {
}
if (State.includes("Tease")) {
if (Math.random() < FuturisticTrainingBeltRandomTeaseMaxChance) return VibratorMode.MAXIMUM;
if (ArousalActive) {
if (C.ArousalSettings.Progress < 35) return VibratorMode.HIGH;
if (C.ArousalSettings.Progress < 70) return VibratorMode.MEDIUM;
if (arousalActive) {
if (progress < 35) return VibratorMode.HIGH;
if (progress < 70) return VibratorMode.MEDIUM;
}
return VibratorMode.LOW;
}
@ -449,7 +450,7 @@ function AssetsItemPelvisFuturisticTrainingBeltScriptUpdatePlayer(data, LastTime
let punishment = InventoryFuturisticChastityBeltCheckPunish(Item);
if (punishment != "") {
if (punishment == "Orgasm") {
if (Item.Property.PunishOrgasm && C.ArousalSettings && C.ArousalSettings.OrgasmStage > 1) {
if (Item.Property.PunishOrgasm && ActivityGetOrgasmStage(C) > 1) {
AssetsItemPelvisFuturisticChastityBeltScriptTrigger(C, Item, "Orgasm");
Item.Property.NextShockTime = CurrentTime + FuturisticChastityBeltShockCooldownOrgasm; // Difficult to have two orgasms in 10 seconds
}
@ -541,7 +542,7 @@ function AssetsItemPelvisFuturisticTrainingBeltScriptStateMachine(data) {
var C = data.C;
let PersistentData = data.PersistentData();
var ArousalActive = C.ArousalSettings && C.ArousalSettings.Progress && ["Manual", "Hybrid", "Automatic"].includes(C.ArousalSettings.Active);
var ArousalActive = PreferenceArousalIsActive(C);
var Property = Item ? Item.Property : null;
if (!Property) return;
@ -570,7 +571,7 @@ function AssetsItemPelvisFuturisticTrainingBeltScriptStateMachine(data) {
} else if (Mode == "EdgeAndDeny") {
if (State != "Cooldown")
DeviceSetToState = FuturisticTrainingBeltStates.indexOf("LowPriorityEdge");
if (ArousalActive && C.ArousalSettings.Progress > 90) {
if (ActivityGetArousal(C) > 90) {
if (Math.random() < FuturisticTrainingBeltRandomDenyChance) {
DeviceSetToState = FuturisticTrainingBeltStates.indexOf("Cooldown");
PersistentData.DeviceStateTimer = CommonTime() + FuturisticTrainingBeltRandomDenyDuration;
@ -689,12 +690,14 @@ function AssetsItemPelvisFuturisticTrainingBeltScriptStateMachine(data) {
if (ArousalActive) {
if (EdgeMode && C.ArousalSettings.Progress > 96 && !((ActivityOrgasmGameTimer != null) && (ActivityOrgasmGameTimer > 0) && (CurrentTime < C.ArousalSettings.OrgasmTimer))) { // Manually trigger orgasm at this stage
let timer = ActivityGetOrgasmTimer(C);
if (EdgeMode && ActivityGetArousal(C) > 96 && !(timer > 0 && (CurrentTime < timer))) {
// Manually trigger orgasm at this stage
DialogLeave();
ActivityOrgasmPrepare(C, true);
// Continuous edging~
if (Mode == "EdgeAndDeny")
C.ArousalSettings.Progress = 80;
ActivitySetArousal(C, 80);
}
}
}

View file

@ -772,7 +772,7 @@ function ChatRoomUpdateDisplay() {
function DrawStatus(C, X, Y, Zoom) {
if ((Player.OnlineSettings != null) && (Player.OnlineSettings.ShowStatus != null) && (Player.OnlineSettings.ShowStatus == false)) return;
if (ChatRoomHideIconState >= 2) return;
if ((C.ArousalSettings != null) && (C.ArousalSettings.OrgasmTimer != null) && (C.ArousalSettings.OrgasmTimer > 0)) {
if (ActivityGetOrgasmTimer(C) > 0) {
DrawImageResize("Icons/Status/Orgasm" + (Math.floor(CommonTime() / 1000) % 3).toString() + ".png", X + 225 * Zoom, Y + 920 * Zoom, 50 * Zoom, 30 * Zoom);
return;
}
@ -1042,14 +1042,10 @@ function ChatRoomClickCharacter(C, CharX, CharY, Zoom, ClickX, ClickY, Pos) {
}
// If the arousal meter is shown for that character, we can interact with it
if (PreferenceArousalAtLeast(C, "Manual")) {
if (PreferenceArousalIsActive(C)) {
let MeterShow = C.ID === 0;
if (C.ID !== 0 && Player.ArousalSettings.ShowOtherMeter && C.ArousalSettings) {
if (C.ArousalSettings.Visible === "Access") {
MeterShow = C.AllowItem;
} else if (C.ArousalSettings.Visible === "All") {
MeterShow = true;
}
if (C.ID !== 0 && PreferenceArousalShowsMeter(Player)) {
MeterShow = PreferenceArousalCanChangeMeter(C);
}
if (MeterShow) {
// The arousal meter can be maximized or minimized by clicking on it
@ -1058,11 +1054,11 @@ function ChatRoomClickCharacter(C, CharX, CharY, Zoom, ClickX, ClickY, Pos) {
// If the player can manually control her arousal, we set the progress manual and change the facial expression, it can trigger an orgasm at 100%
if (C.ID === 0 && MouseIn(CharX + 50 * Zoom, CharY + 200 * Zoom, 100 * Zoom, 500 * Zoom) && C.ArousalZoom) {
if (PreferenceArousalAtLeast(Player, "Manual") && !PreferenceArousalAtLeast(Player, "Automatic")) {
if (PreferenceArousalIsInMode(Player, ["Manual", "Hybrid"])) {
var Arousal = Math.round((CharY + 625 * Zoom - MouseY) / (4 * Zoom));
ActivitySetArousal(Player, Arousal);
if (Player.ArousalSettings.AffectExpression) ActivityExpression(Player, Player.ArousalSettings.Progress);
if (Player.ArousalSettings.Progress == 100) ActivityOrgasmPrepare(Player);
if (PreferenceArousalAffectsExpression(Player)) ActivityExpression(Player, ActivityGetArousal(Player));
if (ActivityGetArousal(Player) == 100) ActivityOrgasmPrepare(Player);
}
return;
}
@ -1287,8 +1283,9 @@ function ChatRoomStimulationMessage(Context, Color = "#FFB0B0", FlashIntensity =
else trigPlug = "Back";
}
}
if (trigMsgTemp != "" && Player.ArousalSettings && Player.ArousalSettings.Progress > 0) {
trigChance += modArousal * Player.ArousalSettings.Progress/100;
let progress = ActivityGetArousal(Player);
if (trigMsgTemp != "" && progress > 0) {
trigChance += modArousal * progress / 100;
}
if (trigMsgTemp != "") {
const Inflation = InventoryGetItemProperty(C.Appearance[A], "InflateLevel", true);
@ -1337,7 +1334,7 @@ function ChatRoomStimulationMessage(Context, Color = "#FFB0B0", FlashIntensity =
ChatRoomPinkFlashAlphaStrength = AlphaStrength;
} else {
// Increase player arousal to the zone
if (!Player.IsEdged() && Player.ArousalSettings && Player.ArousalSettings.Progress && Player.ArousalSettings.Progress < 70 - arousalAmount && trigMsgTemp != "Gag")
if (!Player.IsEdged() && ActivityIsArousalBetween(Player, null, 70 - arousalAmount) && trigMsgTemp != "Gag")
ActivityEffectFlat(Player, Player, arousalAmount, trigGroup, 1);
ChatRoomPinkFlashTime = CommonTime() + (Math.random() + arousalAmount/2.4) * 500;
ChatRoomPinkFlashColor = Color;
@ -1376,14 +1373,14 @@ function ChatRoomResize(load) {
* @returns {void} - Nothing.
*/
function ChatRoomDrawArousalScreenFilter(y1, h, Width, ArousalOverride, Color = '255, 100, 176', AlphaBonus = 0) {
let Progress = (ArousalOverride) ? ArousalOverride : Player.ArousalSettings.Progress;
let Progress = (ArousalOverride) ? ArousalOverride : ActivityGetArousal(Player);
let amplitude = 0.24 * Math.min(1, 2 - 1.5 * Progress/100); // Amplitude of the oscillation
let percent = Progress/100.0;
let level = Math.min(0.5, percent) + 0.5 * Math.pow(Math.max(0, percent*2 - 1), 4);
let oscillation = Math.sin(CommonTime() / 1000 % Math.PI);
let alpha = Math.min(1.0, AlphaBonus + 0.6 * level * (0.99 - amplitude + amplitude * oscillation));
if (Player.ArousalSettings.VFXFilter == "VFXFilterHeavy") {
if (PreferenceArousalGetVFXFilterSetting(Player) == ArousalVFXFilter.Heavy) {
const Grad = MainCanvas.createLinearGradient(0, y1, 0, h);
let alphamin = Math.max(0, alpha / 2 - 0.05);
Grad.addColorStop(0, `rgba(${Color}, ${alpha})`);
@ -1394,7 +1391,7 @@ function ChatRoomResize(load) {
MainCanvas.fillStyle = Grad;
MainCanvas.fillRect(0, y1, Width, h);
} else {
if (Player.ArousalSettings.VFXFilter != "VFXFilterMedium") {
if (PreferenceArousalGetVFXFilterSetting(Player) == ArousalVFXFilter.Light) {
alpha = (Progress >= 91) ? 0.25 : 0;
} else alpha /= 2;
if (alpha > 0)
@ -1444,13 +1441,13 @@ function ChatRoomDrawVibrationScreenFilter(y1, h, Width, VibratorLower, Vibrator
let percentSides = VibratorSides/100.0;
let level = Math.min(0.5, Math.max(percentLower, percentSides)) + 0.5 * Math.pow(Math.max(0, Math.max(percentLower, percentSides)*2 - 1), 4);
let oscillation = Math.sin(CommonTime() / 1000 % Math.PI);
if (Player.ArousalSettings.VFXVibrator != "VFXVibratorAnimated") oscillation = 0;
if (PreferenceArousalGetVFXVibratorSetting(Player) !== ArousalVFXVibrator.Animated) oscillation = 0;
let alpha = 0.6 * level * (0.99 - amplitude + amplitude * oscillation);
if (VibratorLower > 0) {
const Grad = MainCanvas.createRadialGradient(Width/2, y1, 0, Width/2, y1, h);
let alphamin = Math.max(0, alpha / 2 - 0.05);
let modifier = (Player.ArousalSettings.VFXVibrator == "VFXVibratorAnimated") ? Math.random() * 0.01: 0;
let modifier = (PreferenceArousalIsVibratorVFXAnimated(Player)) ? Math.random() * 0.01: 0;
Grad.addColorStop(VibratorLower / 100 * (0.7 + modifier), `rgba(255, 100, 176, 0)`);
Grad.addColorStop(VibratorLower / 100 * (0.85 - 0.1*percentLower * (0.5 * oscillation)), `rgba(255, 100, 176, ${alphamin})`);
Grad.addColorStop(1, `rgba(255, 100, 176, ${alpha})`);
@ -1460,7 +1457,7 @@ function ChatRoomDrawVibrationScreenFilter(y1, h, Width, VibratorLower, Vibrator
if (VibratorSides > 0) {
let Grad = MainCanvas.createRadialGradient(0, 0, 0, 0, 0, Math.sqrt(h*h + Width*Width));
let alphamin = Math.max(0, alpha / 2 - 0.05);
let modifier = (Player.ArousalSettings.VFXVibrator == "VFXVibratorAnimated") ? Math.random() * 0.01: 0;
let modifier = (PreferenceArousalIsVibratorVFXAnimated(Player)) ? Math.random() * 0.01: 0;
Grad.addColorStop(VibratorSides / 100 * (0.8 + modifier), `rgba(255, 100, 176, 0)`);
Grad.addColorStop(VibratorSides / 100 * (0.9 - 0.07*percentSides * (0.5 * oscillation)), `rgba(255, 100, 176, ${alphamin})`);
Grad.addColorStop(1, `rgba(255, 100, 176, ${alpha})`);
@ -1468,7 +1465,7 @@ function ChatRoomDrawVibrationScreenFilter(y1, h, Width, VibratorLower, Vibrator
MainCanvas.fillRect(0, y1, Width, h);
Grad = MainCanvas.createRadialGradient(Width, 0, 0, Width, 0, Math.sqrt(h*h + Width*Width));
modifier = (Player.ArousalSettings.VFXVibrator == "VFXVibratorAnimated") ? Math.random() * 0.01: 0;
modifier = (PreferenceArousalIsVibratorVFXAnimated(Player)) ? Math.random() * 0.01: 0;
Grad.addColorStop(VibratorSides / 100 * (0.8 + modifier), `rgba(255, 100, 176, 0)`);
Grad.addColorStop(VibratorSides / 100 * (0.9 - 0.07*percentSides * (0.5 * oscillation)), `rgba(255, 100, 176, ${alphamin})`);
Grad.addColorStop(1, `rgba(255, 100, 176, ${alpha})`);
@ -1679,21 +1676,21 @@ function ChatRoomRun() {
ChatRoomMenuDraw();
// In orgasm mode, we add a pink filter and different controls depending on the stage. The pink filter shows a little above 90
if ((Player.ArousalSettings != null) && (Player.ArousalSettings.Active != null) && (Player.ArousalSettings.Active != "Inactive") && (Player.ArousalSettings.Active != "NoMeter")) {
if ((Player.ArousalSettings.OrgasmTimer != null) && (typeof Player.ArousalSettings.OrgasmTimer === "number") && !isNaN(Player.ArousalSettings.OrgasmTimer) && (Player.ArousalSettings.OrgasmTimer > 0)) {
if (PreferenceArousalIsActive(Player)) {
if (ActivityGetOrgasmTimer(Player) > 0) {
let stage = ActivityGetOrgasmStage(Player);
DrawRect(0, 0, 1003, 1000, "#FFB0B0B0");
DrawRect(1003, 0, 993, 63, "#FFB0B0B0");
if (Player.ArousalSettings.OrgasmStage == null) Player.ArousalSettings.OrgasmStage = 0;
if (Player.ArousalSettings.OrgasmStage == 0) {
if (stage == 0) {
DrawText(TextGet("OrgasmComing"), 500, 410, "White", "Black");
DrawButton(200, 532, 250, 64, TextGet("OrgasmTryResist"), "White");
DrawButton(550, 532, 250, 64, TextGet("OrgasmSurrender"), "White");
}
if (Player.ArousalSettings.OrgasmStage == 1) DrawButton(ActivityOrgasmGameButtonX, ActivityOrgasmGameButtonY, 250, 64, ActivityOrgasmResistLabel, "White");
if (stage == 1) DrawButton(ActivityOrgasmGameButtonX, ActivityOrgasmGameButtonY, 250, 64, ActivityOrgasmResistLabel, "White");
if (ActivityOrgasmRuined) ActivityOrgasmControl();
if (Player.ArousalSettings.OrgasmStage == 2) DrawText(TextGet("OrgasmRecovering"), 500, 500, "White", "Black");
if (stage == 2) DrawText(TextGet("OrgasmRecovering"), 500, 500, "White", "Black");
ActivityOrgasmProgressBar(50, 970);
} else if ((Player.ArousalSettings.Progress != null) && (Player.ArousalSettings.Progress >= 1) && (Player.ArousalSettings.Progress <= 99) && !CommonPhotoMode) {
} else if (ActivityIsArousalBetween(Player, 0, 100) && !CommonPhotoMode) {
let y1 = 0;
let h = 1000;
@ -1701,11 +1698,11 @@ function ChatRoomRun() {
else if (ChatRoomCharacterCount == 4) {y1 = 150; h = 700;}
else if (ChatRoomCharacterCount == 5) {y1 = 250; h = 500;}
ChatRoomDrawArousalScreenFilter(y1, h, 1003, Player.ArousalSettings.Progress);
ChatRoomDrawArousalScreenFilter(y1, h, 1003, ActivityGetArousal(Player));
}
}
if (Player.ArousalSettings.VFXVibrator == "VFXVibratorSolid" || Player.ArousalSettings.VFXVibrator == "VFXVibratorAnimated") {
if (PreferenceArousalGetVFXVibratorSetting(Player) !== ArousalVFXVibrator.Inactive) {
let y1 = 0;
let h = 1000;
@ -1774,14 +1771,18 @@ function ChatRoomClick() {
// In orgasm mode, we do not allow any clicks expect the chat
if (MouseIn(1905, 910, 90, 90)) ChatRoomSendChat();
if ((Player.ArousalSettings != null) && (Player.ArousalSettings.OrgasmTimer != null) && (typeof Player.ArousalSettings.OrgasmTimer === "number") && !isNaN(Player.ArousalSettings.OrgasmTimer) && (Player.ArousalSettings.OrgasmTimer > 0)) {
if (ActivityGetOrgasmTimer(Player) > 0) {
let stage = ActivityGetOrgasmStage(Player);
// On stage 0, the player can choose to resist the orgasm or not. At 1, the player plays a mini-game to fight her orgasm
if (MouseIn(200, 532, 250, 68) && (Player.ArousalSettings.OrgasmStage == 0)) ActivityOrgasmGameGenerate(0);
if (MouseIn(550, 532, 250, 68) && (Player.ArousalSettings.OrgasmStage == 0)) ActivityOrgasmStart(Player);
if ((MouseX >= ActivityOrgasmGameButtonX) && (MouseX <= ActivityOrgasmGameButtonX + 250) && (MouseY >= ActivityOrgasmGameButtonY) && (MouseY <= ActivityOrgasmGameButtonY + 64) && (Player.ArousalSettings.OrgasmStage == 1)) ActivityOrgasmGameGenerate(ActivityOrgasmGameProgress + 1);
if (stage == 0) {
if (MouseIn(200, 532, 250, 68)) ActivityOrgasmGameGenerate(0);
if (MouseIn(550, 532, 250, 68)) ActivityOrgasmStart(Player);
} else if (stage == 1) {
if (MouseIn(ActivityOrgasmGameButtonX, ActivityOrgasmGameButtonY, 250, 64))
ActivityOrgasmGameGenerate(ActivityOrgasmGameProgress + 1);
}
return;
}
// When the user chats or clicks on a character
@ -2416,7 +2417,7 @@ function ChatRoomMessage(data) {
// If another player is using an item which applies an activity on the current player, apply the effect here
AsylumGGTSActivity(SenderCharacter, TargetCharacter, ActivityName, GroupName, ActivityCounter);
if ((ActivityName != null) && (TargetMemberNumber != null) && (TargetMemberNumber == Player.MemberNumber) && (SenderCharacter.MemberNumber != Player.MemberNumber))
if ((Player.ArousalSettings == null) || (Player.ArousalSettings.Active == null) || (Player.ArousalSettings.Active == "Hybrid") || (Player.ArousalSettings.Active == "Automatic"))
if (PreferenceArousalIsInMode(Player, ["Hybrid", "Automatic"]))
ActivityEffect(SenderCharacter, Player, ActivityName, GroupName, ActivityCounter);
// Launches the audio file if allowed
@ -2529,7 +2530,7 @@ function ChatRoomMessage(data) {
// If the player does the activity on herself or an NPC, we calculate the result right away
AsylumGGTSActivity(SenderCharacter, TargetCharacter, ActivityName, ActivityGroup, ActivityCounter);
if ((data.Type === "Action") || ((TargetMemberNumber == Player.MemberNumber) && (SenderCharacter.MemberNumber != Player.MemberNumber)))
if ((Player.ArousalSettings == null) || (Player.ArousalSettings.Active == null) || (Player.ArousalSettings.Active == "Hybrid") || (Player.ArousalSettings.Active == "Automatic"))
if (PreferenceArousalIsInMode(Player, ["Hybrid", "Automatic"]))
ActivityEffect(SenderCharacter, Player, ActivityName, ActivityGroup, ActivityCounter);
// When the player is in total sensory deprivation, hide messages if the player is not involved
@ -3024,7 +3025,8 @@ function ChatRoomSyncArousal(data) {
ChatRoomCharacter[C].ArousalSettings.OrgasmCount = data.OrgasmCount;
ChatRoomCharacter[C].ArousalSettings.Progress = data.Progress;
ChatRoomCharacter[C].ArousalSettings.ProgressTimer = data.ProgressTimer;
if ((ChatRoomCharacter[C].ArousalSettings.AffectExpression == null) || ChatRoomCharacter[C].ArousalSettings.AffectExpression) ActivityExpression(ChatRoomCharacter[C], ChatRoomCharacter[C].ArousalSettings.Progress);
if (PreferenceArousalAffectsExpression(ChatRoomCharacter[C]))
ActivityExpression(ChatRoomCharacter[C], ActivityGetArousal(ChatRoomCharacter[C]));
// Keeps a copy of the previous version
for (let C = 0; C < ChatRoomData.Character.length; C++)

View file

@ -457,23 +457,25 @@ function PrivateRun() {
}
// In orgasm mode, we add a pink filter and different controls depending on the stage
if ((Player.ArousalSettings != null) && (Player.ArousalSettings.Active != null) && (Player.ArousalSettings.Active != "Inactive") && (Player.ArousalSettings.Active != "NoMeter")) {
if ((Player.ArousalSettings.OrgasmTimer != null) && (typeof Player.ArousalSettings.OrgasmTimer === "number") && !isNaN(Player.ArousalSettings.OrgasmTimer) && (Player.ArousalSettings.OrgasmTimer > 0)) {
if (PreferenceArousalIsActive(Player)) {
if (ActivityGetOrgasmTimer(Player) > 0) {
DrawRect(0, 0, 2000, 1000, "#FFB0B0B0");
if (Player.ArousalSettings.OrgasmStage == null) Player.ArousalSettings.OrgasmStage = 0;
if (Player.ArousalSettings.OrgasmStage == 0) {
let stage = ActivityGetOrgasmStage(Player);
if (stage == 0) {
DrawText(TextGet("OrgasmComing"), 1000, 410, "White", "Black");
DrawButton(700, 532, 250, 64, TextGet("OrgasmTryResist"), "White");
DrawButton(1050, 532, 250, 64, TextGet("OrgasmSurrender"), "White");
}
if (Player.ArousalSettings.OrgasmStage == 1) DrawButton(ActivityOrgasmGameButtonX + 500, ActivityOrgasmGameButtonY, 250, 64, ActivityOrgasmResistLabel, "White");
if (stage == 1) DrawButton(ActivityOrgasmGameButtonX + 500, ActivityOrgasmGameButtonY, 250, 64, ActivityOrgasmResistLabel, "White");
if (ActivityOrgasmRuined) ActivityOrgasmControl();
if (Player.ArousalSettings.OrgasmStage == 2) DrawText(TextGet("OrgasmRecovering"), 1000, 500, "White", "Black");
if (stage == 2) DrawText(TextGet("OrgasmRecovering"), 1000, 500, "White", "Black");
ActivityOrgasmProgressBar(550, 970);
} else if ((Player.ArousalSettings.Progress != null) && (Player.ArousalSettings.Progress >= 1) && (Player.ArousalSettings.Progress <= 99)) ChatRoomDrawArousalScreenFilter(0, 1000, 2000, Player.ArousalSettings.Progress);
} else if (ActivityIsArousalBetween(Player, 0, 100)) {
ChatRoomDrawArousalScreenFilter(0, 1000, 2000, ActivityGetArousal(Player));
}
}
if (Player.ArousalSettings.VFXVibrator == "VFXVibratorSolid" || Player.ArousalSettings.VFXVibrator == "VFXVibratorAnimated") {
if (PreferenceArousalGetVFXVibratorSetting(Player) !== ArousalVFXVibrator.Inactive) {
ChatRoomVibrationScreenFilter(0, 1000, 2000, Player);
}
@ -550,25 +552,30 @@ function PrivateClickCharacter() {
}
// If the arousal meter is shown for that character, we can interact with it
if ((PrivateCharacter[C].ID == 0) || (Player.ArousalSettings.ShowOtherMeter == null) || Player.ArousalSettings.ShowOtherMeter)
if ((PrivateCharacter[C].ID == 0) || ((PrivateCharacter[C].ArousalSettings != null) && (PrivateCharacter[C].ArousalSettings.Visible != null) && (PrivateCharacter[C].ArousalSettings.Visible == "Access") && PrivateCharacter[C].AllowItem) || ((PrivateCharacter[C].ArousalSettings != null) && (PrivateCharacter[C].ArousalSettings.Visible != null) && (PrivateCharacter[C].ArousalSettings.Visible == "All")))
if ((PrivateCharacter[C].ArousalSettings != null) && (PrivateCharacter[C].ArousalSettings.Active != null) && ((PrivateCharacter[C].ArousalSettings.Active == "Manual") || (PrivateCharacter[C].ArousalSettings.Active == "Hybrid") || (PrivateCharacter[C].ArousalSettings.Active == "Automatic"))) {
if ((PrivateCharacter[C].ID == 0) || PreferenceArousalShowsMeter(Player))
if ((PrivateCharacter[C].ID == 0) || PreferenceArousalCanChangeMeter(PrivateCharacter[C]))
if (PreferenceArousalIsActive(PrivateCharacter[C])) {
// The arousal meter can be maximized or minimized by clicking on it
if ((MouseX >= X + (C - PrivateCharacterOffset) * 470 + 60) && (MouseX <= X + (C - PrivateCharacterOffset) * 470 + 140) && (MouseY >= 400) && (MouseY <= 500) && !PrivateCharacter[C].ArousalZoom) { PrivateCharacter[C].ArousalZoom = true; return; }
if ((MouseX >= X + (C - PrivateCharacterOffset) * 470 + 50) && (MouseX <= X + (C - PrivateCharacterOffset) * 470 + 150) && (MouseY >= 615) && (MouseY <= 715) && PrivateCharacter[C].ArousalZoom) { PrivateCharacter[C].ArousalZoom = false; return; }
if (!PrivateCharacter[C].ArousalZoom && MouseIn(X + (C - PrivateCharacterOffset) * 470 + 60, 400, 80, 100)) {
PrivateCharacter[C].ArousalZoom = true;
return;
}
if (PrivateCharacter[C].ArousalZoom && MouseIn(X + (C - PrivateCharacterOffset) * 470 + 50, 615, 100, 100)) {
PrivateCharacter[C].ArousalZoom = false;
return;
}
// If the player can manually control her arousal or wants to fight her desire
if ((PrivateCharacter[C].ID == 0) && (MouseX >= X + (C - PrivateCharacterOffset) * 470 + 50) && (MouseX <= X + (C - PrivateCharacterOffset) * 470 + 150) && (MouseY >= 200) && (MouseY <= 615) && PrivateCharacter[C].ArousalZoom)
if ((Player.ArousalSettings != null) && (Player.ArousalSettings.Active != null) && (Player.ArousalSettings.Progress != null)) {
if ((Player.ArousalSettings.Active == "Manual") || (Player.ArousalSettings.Active == "Hybrid")) {
var Arousal = Math.round((625 - MouseY) / 4, 0);
ActivitySetArousal(Player, Arousal);
if ((Player.ArousalSettings.AffectExpression == null) || Player.ArousalSettings.AffectExpression) ActivityExpression(Player, Player.ArousalSettings.Progress);
if (Player.ArousalSettings.Progress == 100) ActivityOrgasmPrepare(Player);
}
return;
if ((PrivateCharacter[C].ID == 0) && PrivateCharacter[C].ArousalZoom && MouseIn(X + (C - PrivateCharacterOffset) * 470 + 50, 200, 100, 415)) {
if (PreferenceArousalIsInMode(Player, ["Manual", "Hybrid"])) {
let arousal = Math.round((625 - MouseY) / 4);
ActivitySetArousal(Player, arousal);
if (PreferenceArousalAffectsExpression(Player)) ActivityExpression(Player, ActivityGetArousal(Player));
if (ActivityGetArousal(Player) == 100) ActivityOrgasmPrepare(Player);
}
return;
}
// Don't do anything if the thermometer is clicked without access to it
if ((MouseX >= X + (C - PrivateCharacterOffset) * 470 + 50) && (MouseX <= X + (C - PrivateCharacterOffset) * 470 + 150) && (MouseY >= 200) && (MouseY <= 615) && PrivateCharacter[C].ArousalZoom) return;
@ -576,7 +583,7 @@ function PrivateClickCharacter() {
}
// Cannot click on a character that's having an orgasm
if ((PrivateCharacter[C].ID != 0) && (PrivateCharacter[C].ArousalSettings != null) && (PrivateCharacter[C].ArousalSettings.OrgasmTimer != null) && (PrivateCharacter[C].ArousalSettings.OrgasmTimer > 0))
if ((PrivateCharacter[C].ID != 0) && ActivityGetOrgasmTimer(PrivateCharacter[C]) > 0)
return;
// Sets the new character (1000 if she's owner, 2000 if she's owned)
@ -622,14 +629,17 @@ function PrivateClickCharacter() {
function PrivateClick() {
// If the player is having an orgasm, only the orgasm controls are available
if ((Player.ArousalSettings != null) && (Player.ArousalSettings.OrgasmTimer != null) && (typeof Player.ArousalSettings.OrgasmTimer === "number") && !isNaN(Player.ArousalSettings.OrgasmTimer) && (Player.ArousalSettings.OrgasmTimer > 0)) {
if (ActivityGetOrgasmTimer(Player) > 0) {
let stage = ActivityGetOrgasmStage(Player);
// On stage 0, the player can choose to resist the orgasm or not. At 1, the player plays a mini-game to fight her orgasm
if ((MouseX >= 700) && (MouseX <= 950) && (MouseY >= 532) && (MouseY <= 600) && (Player.ArousalSettings.OrgasmStage == 0)) ActivityOrgasmGameGenerate(0);
if ((MouseX >= 1050) && (MouseX <= 1300) && (MouseY >= 532) && (MouseY <= 600) && (Player.ArousalSettings.OrgasmStage == 0)) ActivityOrgasmStart(Player);
if ((MouseX >= ActivityOrgasmGameButtonX + 500) && (MouseX <= ActivityOrgasmGameButtonX + 700) && (MouseY >= ActivityOrgasmGameButtonY) && (MouseY <= ActivityOrgasmGameButtonY + 64) && (Player.ArousalSettings.OrgasmStage == 1)) ActivityOrgasmGameGenerate(ActivityOrgasmGameProgress + 1);
if (stage == 0) {
if (MouseIn(700, 532, 250, 68)) ActivityOrgasmGameGenerate(0);
if (MouseIn(1050, 532, 250, 68)) ActivityOrgasmStart(Player);
} else if (stage == 1) {
if (MouseIn(ActivityOrgasmGameButtonX + 500, ActivityOrgasmGameButtonY, 200, 64))
ActivityOrgasmGameGenerate(ActivityOrgasmGameProgress + 1);
}
return;
}
// Main screens buttons

View file

@ -289,7 +289,7 @@ function ActivityAllowedForGroup(character, groupname) {
* Returns TRUE if an activity can be done
* @param {Character} C - The character to evaluate
* @param {string} Activity - The name of the activity
* @param {string} Group - The name of the group
* @param {string} Group - The name of the group
* @return {boolean} - TRUE if the activity can be done
*/
function ActivityCanBeDone(C, Activity, Group) {
@ -363,6 +363,34 @@ function ActivityChatRoomArousalSync(C) {
ServerSend("ChatRoomCharacterArousalUpdate", { OrgasmTimer: C.ArousalSettings.OrgasmTimer, Progress: C.ArousalSettings.Progress, ProgressTimer: C.ArousalSettings.ProgressTimer, OrgasmCount: C.ArousalSettings.OrgasmCount });
}
/**
* Returns a player or character's arousal progress value.
* @param {Character|Player} C - The character to read arousal from
* @returns {number|null}
*/
function ActivityGetArousal(C) {
if (!PreferenceArousalIsActive(C)) return 0;
if (C.ArousalSettings && typeof C.ArousalSettings.Progress === "number" && !isNaN(C.ArousalSettings.Progress))
return C.ArousalSettings.Progress;
return null;
}
/**
* Returns whether a player or character's arousal progress is between two values.
* @param {Character|Player} character - The character or player to read arousal from
* @param {number|null} lower - The lower bound, excluded
* @param {number|null} upper - The upper bound, excluded
* @returns {boolean}
*/
function ActivityIsArousalBetween(character, lower, upper) {
const arousal = ActivityGetArousal(character);
return (
arousal !== null &&
(lower === null || arousal > lower) &&
(upper === null || arousal < upper)
);
}
/**
* Sets the character arousal level and validates the value
* @param {Character} C - The character for which to set the arousal progress of
@ -381,6 +409,50 @@ function ActivitySetArousal(C, Progress) {
}
}
/**
* Returns a player or character's orgasm stage value.
* @param {Character|Player} C - The character or player to read arousal from
* @returns {number} - 0 is no orgasm, 1 means minigame is in progress, 2 means is orgasming
*/
function ActivityGetOrgasmStage(C) {
if (C.ArousalSettings && C.ArousalSettings.OrgasmStage)
return C.ArousalSettings.OrgasmStage;
return 0;
}
/**
* Returns a player or character's orgasm timer value.
* @param {Character|Player} C - The character or player to read arousal from
* @returns {number}
*/
function ActivityGetOrgasmTimer(C) {
if (C.ArousalSettings && C.ArousalSettings.OrgasmTimer)
return C.ArousalSettings.OrgasmTimer;
return 0;
}
/**
* Returns a player or character's orgasm count.
* @param {Character|Player} C
* @returns {number}
*/
function ActivityGetOrgasmCount(C) {
if (C.ArousalSettings && typeof C.ArousalSettings.OrgasmCount === "number")
return C.ArousalSettings.OrgasmCount;
return 0;
}
/**
* Returns the current timed progress for a character.
* @param {Character|Player} character - The character or player to read arousal from
* @returns {number}
*/
function ActivityGetArousalTimer(character) {
if (character.ArousalSettings)
return character.ArousalSettings.ProgressTimer;
return 0;
}
/**
* Sets an activity progress on a timer, activities are capped at MaxProgress
* @param {Character} C - The character for which to set the timer for
@ -390,10 +462,16 @@ function ActivitySetArousal(C, Progress) {
* @return {void} - Nothing
*/
function ActivitySetArousalTimer(C, Activity, Zone, Progress) {
// Make sure the property exist
if ((C.ArousalSettings.ProgressTimer == null)
|| (typeof C.ArousalSettings.ProgressTimer !== "number")
|| isNaN(C.ArousalSettings.ProgressTimer)) {
C.ArousalSettings.ProgressTimer = 0;
}
// If there's already a progress timer running, we add it's value but divide it by 2 to lessen the impact, the progress must be between -25 and 25
if ((C.ArousalSettings.ProgressTimer == null) || (typeof C.ArousalSettings.ProgressTimer !== "number") || isNaN(C.ArousalSettings.ProgressTimer)) C.ArousalSettings.ProgressTimer = 0;
Progress = Math.round((C.ArousalSettings.ProgressTimer / 2) + Progress);
let timer = ActivityGetArousalTimer(C);
Progress = Math.round((timer / 2) + Progress);
if (Progress < -25) Progress = -25;
if (Progress > 25) Progress = 25;
@ -401,16 +479,44 @@ function ActivitySetArousalTimer(C, Activity, Zone, Progress) {
var Max = ((Activity == null || Activity.MaxProgress == null) || (Activity.MaxProgress > 100)) ? 100 : Activity.MaxProgress;
if ((Max > 95) && !PreferenceGetZoneOrgasm(C, Zone)) Max = 95;
if ((Max > 67) && (Zone == "ActivityOnOther")) Max = 67;
if ((Progress > 0) && (C.ArousalSettings.Progress + Progress > Max)) Progress = (Max - C.ArousalSettings.Progress >= 0) ? Max - C.ArousalSettings.Progress : 0;
let arousal = ActivityGetArousal(C);
if ((Progress > 0) && (arousal + Progress > Max)) {
Progress = (Max - arousal >= 0) ? Max - arousal : 0;
}
// If we must apply a progress timer change, we publish it
if (C.ArousalSettings.ProgressTimer !== Progress) {
if (timer !== Progress) {
C.ArousalSettings.ProgressTimer = Progress;
ActivityChatRoomArousalSync(C);
}
}
/**
* Get the character current vibration level.
* @param {Character} C
* @return {number}
*/
function ActivityGetVibratorLevel(C) {
if (C.ArousalSettings && typeof C.ArousalSettings.VibratorLevel === "number" && !isNaN(C.ArousalSettings.VibratorLevel))
return C.ArousalSettings.VibratorLevel;
return 0;
}
/**
* Set the current vibrator level for drawing purposes
* @param {Character} C - Character for which the timer is progressing
* @param {number} Level - Level from 0 to 4 (higher = more vibration)
* @returns {void} - Nothing
*/
function ActivitySetVibratorLevel(C, Level) {
if (C.ArousalSettings != null) {
if (Level != ActivityGetVibratorLevel(C)) {
C.ArousalSettings.VibratorLevel = Level;
// reset the vibrator animation
C.ArousalSettings.ChangeTime = CommonTime();
}
}
}
/**
* Draws the arousal progress bar at the given coordinates for every orgasm timer.
@ -661,16 +767,13 @@ function ActivityExpression(C, Progress) {
*/
function ActivityTimerProgress(C, Progress) {
// Changes the current arousal progress value
C.ArousalSettings.Progress = C.ArousalSettings.Progress + Progress;
// Decrease the vibratorlevel to 0 if not being aroused, while also updating the change time to reset the vibrator animation
if (Progress < 0) {
if (C.ArousalSettings.VibratorLevel != 0) {
C.ArousalSettings.VibratorLevel = 0;
C.ArousalSettings.ChangeTime = CommonTime();
}
ActivitySetVibratorLevel(C, 0);
}
// Update the current arousal, make sure it stays between 0 & 100.
C.ArousalSettings.Progress = C.ArousalSettings.Progress + Progress;
if (C.ArousalSettings.Progress < 0) C.ArousalSettings.Progress = 0;
if (C.ArousalSettings.Progress > 100) C.ArousalSettings.Progress = 100;
@ -680,9 +783,11 @@ function ActivityTimerProgress(C, Progress) {
}
// Out of orgasm mode, it can affect facial expressions at every 10 steps
if ((C.ArousalSettings.OrgasmTimer == null) || (typeof C.ArousalSettings.OrgasmTimer !== "number") || isNaN(C.ArousalSettings.OrgasmTimer) || (C.ArousalSettings.OrgasmTimer < CurrentTime))
if (((C.ArousalSettings.AffectExpression == null) || C.ArousalSettings.AffectExpression) && ((C.ArousalSettings.Progress + ((Progress < 0) ? 1 : 0)) % 10 == 0))
if ((ActivityGetOrgasmTimer(C) < CurrentTime) && PreferenceArousalAffectsExpression(C)) {
if ((C.ArousalSettings.Progress + ((Progress < 0) ? 1 : 0)) % 10 == 0) {
ActivityExpression(C, C.ArousalSettings.Progress);
}
}
// Can trigger an orgasm
if (C.ArousalSettings.Progress == 100) ActivityOrgasmPrepare(C);
@ -704,7 +809,6 @@ function ActivityVibratorLevel(C, Level) {
}
}
/**
* Calculates the progress one character does on another right away
* @param {Character} Source - The character who performed the activity
@ -713,7 +817,7 @@ function ActivityVibratorLevel(C, Level) {
* @returns {void} - Nothing
*/
function ActivityRunSelf(Source, Target, Activity) {
if (((Player.ArousalSettings.Active == "Hybrid") || (Player.ArousalSettings.Active == "Automatic")) && (Source.ID == 0) && (Target.ID != 0)) {
if (PreferenceArousalIsInMode(Player, ["Hybrid", "Automatic"]) && (Source.ID == 0) && (Target.ID != 0)) {
var Factor = (PreferenceGetActivityFactor(Player, Activity.Name, false) * 5) - 10; // Check how much the player likes the activity, from -10 to +10
Factor = Factor + Math.floor((Math.random() * 8)); // Random 0 to 7 bonus
if (Target.IsLoverOfPlayer()) Factor = Factor + Math.floor((Math.random() * 8)); // Another random 0 to 7 bonus if the target is the player's lover
@ -741,7 +845,7 @@ function ActivityRun(C, Activity) {
let group = ActivityGetGroupOrMirror(C.AssetFamily, C.FocusGroup.Name);
// If the player does the activity on herself or an NPC, we calculate the result right away
if ((C.ArousalSettings.Active == "Hybrid") || (C.ArousalSettings.Active == "Automatic"))
if (PreferenceArousalIsInMode(C, ["Hybrid", "Automatic"]))
if ((C.ID == 0) || C.IsNpc())
ActivityEffect(Player, C, Activity, group.Name);
@ -789,7 +893,7 @@ function ActivityArousalItem(Source, Target, Asset) {
if (AssetActivity != null) {
var Activity = AssetGetActivity(Target.AssetFamily, AssetActivity);
if ((Source.ID == 0) && (Target.ID != 0)) ActivityRunSelf(Source, Target, Activity);
if (PreferenceArousalAtLeast(Target, "Hybrid") && ((Target.ID == 0) || (Target.IsNpc())))
if (PreferenceArousalIsInMode(Target, ["Hybrid", "Automatic"]) && (Target.IsPlayer() || Target.IsNpc()))
ActivityEffect(Source, Target, AssetActivity, Asset.Group.Name);
}
}

View file

@ -218,15 +218,14 @@ function DrawArousalThermometer(X, Y, Zoom, Progress, Automatic, Orgasm) {
* @returns {void} - Nothing
*/
function DrawArousalMeter(C, X, Y, Zoom) {
if (ActivityAllowed() && PreferenceArousalAtLeast(C, "Manual"))
if (C.ID == 0 || (C.ArousalSettings.Visible == "Access" && C.AllowItem) || C.ArousalSettings.Visible == "All")
if (C.ID == 0 || (Player.ArousalSettings.ShowOtherMeter == null) || Player.ArousalSettings.ShowOtherMeter) {
ActivitySetArousal(C, C.ArousalSettings.Progress);
if (Player.ArousalSettings.VFX != "VFXInactive" && C.ArousalSettings.Progress > 0 && PreferenceArousalAtLeast(C, "Hybrid")) {
let Progress = 0;
if (!(C.ArousalSettings.VibratorLevel == null || typeof C.ArousalSettings.VibratorLevel !== "number" || isNaN(C.ArousalSettings.VibratorLevel))) {
Progress = C.ArousalSettings.VibratorLevel;
}
if (ActivityAllowed() && PreferenceArousalIsActive(C))
if (C.ID == 0 || PreferenceArousalCanChangeMeter(C))
if (C.ID == 0 || PreferenceArousalShowsMeter(Player)) {
let arousal = ActivityGetArousal(C);
ActivitySetArousal(C, arousal);
if (PreferenceArousalVFXActive(Player) && arousal > 0 && PreferenceArousalIsInMode(C, ["Automatic", "Hybrid"])) {
let Progress = ActivityGetVibratorLevel(C);
if (Progress > 0) { // -1 is disabled
const animationTimeMax = 5000; // 5 seconds
@ -237,9 +236,9 @@ function DrawArousalMeter(C, X, Y, Zoom) {
Y + (C.ArousalZoom ? 200 : 400) * Zoom,
C.ArousalZoom ? Zoom : Zoom * 0.2,
Progress,
Player.ArousalSettings.VFX == "VFXAnimated" || (Player.ArousalSettings.VFX == "VFXAnimatedTemp" && C.ArousalSettings.ChangeTime != null && animationTimeLeft > 0),
PreferenceArousalVFXActive(Player) && C.ArousalSettings.ChangeTime != null && animationTimeLeft > 0,
Math.max(0, animationTimeLeft / animationTimeMax),
C.ArousalSettings.OrgasmTimer != null && typeof C.ArousalSettings.OrgasmTimer === "number" && !isNaN(C.ArousalSettings.OrgasmTimer) && C.ArousalSettings.OrgasmTimer > 0);
ActivityGetOrgasmTimer(C) > 0);
}
}
@ -247,13 +246,14 @@ function DrawArousalMeter(C, X, Y, Zoom) {
X + (C.ArousalZoom ? 50 : 90) * Zoom,
Y + (C.ArousalZoom ? 200 : 400) * Zoom,
C.ArousalZoom ? Zoom : Zoom * 0.2,
C.ArousalSettings.Progress,
PreferenceArousalAtLeast(C, "Automatic"),
C.ArousalSettings.OrgasmTimer != null && typeof C.ArousalSettings.OrgasmTimer === "number" && !isNaN(C.ArousalSettings.OrgasmTimer) && C.ArousalSettings.OrgasmTimer > 0);
arousal,
PreferenceArousalIsInMode(C, ["Automatic"]),
ActivityGetOrgasmTimer(C) > 0);
if (C.ArousalZoom && (typeof C.ArousalSettings.OrgasmCount === "number") && (C.ArousalSettings.OrgasmCount >= 0) && (C.ArousalSettings.OrgasmCount <= 9999)) {
let orgasmCount = ActivityGetOrgasmCount(C);
if (C.ArousalZoom && orgasmCount >= 0 && orgasmCount <= 9999) {
MainCanvas.font = CommonGetFont(Math.round(36 * Zoom).toString());
DrawText(((C.ArousalSettings.OrgasmCount != null) ? C.ArousalSettings.OrgasmCount : 0).toString(), X + 100 * Zoom, Y + 655 * Zoom, "Black", "Gray");
DrawText(orgasmCount.toString(), X + 100 * Zoom, Y + 655 * Zoom, "Black", "Gray");
MainCanvas.font = CommonGetFont(36);
}
}

View file

@ -457,16 +457,16 @@ function SpeechStutter(C, CD) {
if (CD == null) CD = "";
// Validates that the preferences allow stuttering
if ((C.ArousalSettings == null) || (C.ArousalSettings.AffectStutter !== "None")) {
let mode = PreferenceArousalGetStutterSetting(C);
if (mode !== ArousalStutter.None) {
// Gets the factor from current arousal
var Factor = 0;
if ((C.ArousalSettings == null) || (C.ArousalSettings.AffectStutter == null) || (C.ArousalSettings.AffectStutter == "Arousal") || (C.ArousalSettings.AffectStutter == "All"))
if ((C.ArousalSettings != null) && (C.ArousalSettings.Progress != null) && (typeof C.ArousalSettings.Progress === "number") && !isNaN(C.ArousalSettings.Progress))
Factor = Math.floor(C.ArousalSettings.Progress / 20);
let Factor = 0;
if (mode === ArousalStutter.Arousal || mode === ArousalStutter.All)
Factor = Math.floor(ActivityGetArousal(C) / 20);
// Checks all items that "eggs" with an intensity, and replaces the factor if it's higher
if (C.IsEgged() && ((C.ArousalSettings == null) || (C.ArousalSettings.AffectStutter == null) || (C.ArousalSettings.AffectStutter == "Vibration") || (C.ArousalSettings.AffectStutter == "All")))
if ((C.IsEgged() && mode === ArousalStutter.Vibration) || mode === ArousalStutter.All)
for (let A = 0; A < C.Appearance.length; A++) {
var Item = C.Appearance[A];
if (InventoryItemHasEffect(Item, "Egged", true) && Item.Property && Item.Property.Intensity && (typeof Item.Property.Intensity === "number") && !isNaN(Item.Property.Intensity) && (Item.Property.Intensity > Factor))

View file

@ -1026,9 +1026,10 @@ function StruggleDrawLockpickProgress(C) {
}
}
} else {
if ( Player.ArousalSettings && (Player.ArousalSettings.Active != "Inactive" && Player.ArousalSettings.Active != "NoMeter") && Player.ArousalSettings.Progress > 20 && StruggleLockPickProgressCurrentTries < StruggleLockPickProgressMaxTries && StruggleLockPickProgressCurrentTries > 0) {
let progress = ActivityGetArousal(Player);
if (PreferenceArousalIsActive(Player) && progress > 20 && StruggleLockPickProgressCurrentTries < StruggleLockPickProgressMaxTries && StruggleLockPickProgressCurrentTries > 0) {
if (CurrentTime > StruggleLockPickArousalTick) {
var arousalmaxtime = 2.6 - 2.0*Player.ArousalSettings.Progress/100;
var arousalmaxtime = 2.6 - 2.0 * progress / 100;
if (StruggleLockPickArousalTick - CurrentTime > CurrentTime + StruggleLockPickArousalTickTime*arousalmaxtime) {
StruggleLockPickArousalTick = CurrentTime + StruggleLockPickArousalTickTime*arousalmaxtime; // In case it gets set out way too far
}
@ -1258,7 +1259,3 @@ function StruggleLockPickProgressStart(C, Item) {
StruggleLockPickProgressMaxTries = Math.max(1, NumTries - NumPins);
}
}

View file

@ -151,26 +151,27 @@ function TimerProcess(Timestamp) {
TimerLastArousalProgress = CurrentTime;
TimerLastArousalProgressCount++;
for (let C = 0; C < Character.length; C++) {
let orgasmTimer = ActivityGetOrgasmTimer(Character[C]);
let arousalTimer = ActivityGetArousalTimer(Character[C]);
// If the character is having an orgasm and the timer ran out, we move to the next orgasm stage
if ((Character[C].ArousalSettings != null) && (Character[C].ArousalSettings.OrgasmTimer != null) && (Character[C].ArousalSettings.OrgasmTimer > 0)) {
if (Character[C].ArousalSettings.OrgasmTimer < CurrentTime) {
if ((Character[C].ArousalSettings.OrgasmStage == null) || (Character[C].ArousalSettings.OrgasmStage <= 1)) ActivityOrgasmStart(Character[C]);
else ActivityOrgasmStop(Character[C], 20);
}
if (orgasmTimer > 0 && orgasmTimer < CurrentTime) {
if (ActivityGetOrgasmStage(Character[C]) <= 1)
ActivityOrgasmStart(Character[C]);
else
ActivityOrgasmStop(Character[C], 20);
} else {
// Depending on the character settings, we progress the arousal meter
if (PreferenceArousalAtLeast(Character[C], "Hybrid")) {
// Activity impacts the progress slowly over time, if there's an activity running, vibrations are ignored
if ((Character[C].ArousalSettings.ProgressTimer != null) && (typeof Character[C].ArousalSettings.ProgressTimer === "number") && !isNaN(Character[C].ArousalSettings.ProgressTimer) && (Character[C].ArousalSettings.ProgressTimer != 0)) {
if (Character[C].ArousalSettings.ProgressTimer < 0) {
if (arousalTimer != 0) {
if (arousalTimer < 0) {
Character[C].ArousalSettings.ProgressTimer++;
ActivityTimerProgress(Character[C], -1);
ActivityVibratorLevel(Character[C], 0);
}
else {
} else {
Character[C].ArousalSettings.ProgressTimer--;
ActivityTimerProgress(Character[C], 1);
ActivityVibratorLevel(Character[C], 4);
@ -183,7 +184,7 @@ function TimerProcess(Timestamp) {
let Item = Character[C].Appearance[A];
let ZoneFactor = PreferenceGetZoneFactor(Character[C], Item.Asset.ArousalZone) - 2;
if (InventoryItemHasEffect(Item, "Egged", true) && (Item.Property != null) && (Item.Property.Intensity != null) && (typeof Item.Property.Intensity === "number") && !isNaN(Item.Property.Intensity) && (Item.Property.Intensity >= 0) && (ZoneFactor >= 0) && (Item.Property.Intensity + ZoneFactor > Factor)){
if ((Character[C].ArousalSettings.Progress < 95) || PreferenceGetZoneOrgasm(Character[C], Item.Asset.ArousalZone))
if (ActivityGetArousal(Character[C]) < 95 || PreferenceGetZoneOrgasm(Character[C], Item.Asset.ArousalZone))
Factor = Item.Property.Intensity + ZoneFactor;
}
}
@ -196,11 +197,11 @@ function TimerProcess(Timestamp) {
}
// Kicks the arousal timer faster from personal arousal
if ((Factor >= 4)) {ActivityVibratorLevel(Character[C], 4); if (TimerLastArousalProgressCount % 2 == 0)ActivityTimerProgress(Character[C], 1);}
if ((Factor >= 4)) {ActivityVibratorLevel(Character[C], 4); if (TimerLastArousalProgressCount % 2 == 0) ActivityTimerProgress(Character[C], 1);}
if ((Factor == 3)) {ActivityVibratorLevel(Character[C], 3); if (TimerLastArousalProgressCount % 3 == 0) ActivityTimerProgress(Character[C], 1);}
if ((Factor == 2)) {ActivityVibratorLevel(Character[C], 2); if (Character[C].ArousalSettings.Progress <= 95 && TimerLastArousalProgressCount % 4 == 0) ActivityTimerProgress(Character[C], 1);}
if ((Factor == 1)) {ActivityVibratorLevel(Character[C], 1); if (Character[C].ArousalSettings.Progress <= 65 && TimerLastArousalProgressCount % 6 == 0) ActivityTimerProgress(Character[C], 1);}
if ((Factor == 0)) {ActivityVibratorLevel(Character[C], 1); if (Character[C].ArousalSettings.Progress <= 35 && TimerLastArousalProgressCount % 8 == 0) ActivityTimerProgress(Character[C], 1);}
if ((Factor == 2)) {ActivityVibratorLevel(Character[C], 2); if (ActivityGetArousal(Character[C]) <= 95 && TimerLastArousalProgressCount % 4 == 0) ActivityTimerProgress(Character[C], 1);}
if ((Factor == 1)) {ActivityVibratorLevel(Character[C], 1); if (ActivityGetArousal(Character[C]) <= 65 && TimerLastArousalProgressCount % 6 == 0) ActivityTimerProgress(Character[C], 1);}
if ((Factor == 0)) {ActivityVibratorLevel(Character[C], 1); if (ActivityGetArousal(Character[C]) <= 35 && TimerLastArousalProgressCount % 8 == 0) ActivityTimerProgress(Character[C], 1);}
if ((Factor == -1)) {ActivityVibratorLevel(Character[C], 0);}
}
@ -215,24 +216,23 @@ function TimerProcess(Timestamp) {
if ((TimerLastArousalDecay + 12000 < CurrentTime) || (TimerLastArousalDecay - 12000 > CurrentTime)) {
TimerLastArousalDecay = CurrentTime;
for (let C = 0; C < Character.length; C++)
if (PreferenceArousalAtLeast(Character[C], "Hybrid"))
if ((Character[C].ArousalSettings.Progress != null) && (typeof Character[C].ArousalSettings.Progress === "number") && !isNaN(Character[C].ArousalSettings.Progress) && (Character[C].ArousalSettings.Progress > 0))
if ((Character[C].ArousalSettings.ProgressTimer == null) || (typeof Character[C].ArousalSettings.ProgressTimer !== "number") || isNaN(Character[C].ArousalSettings.ProgressTimer) || (Character[C].ArousalSettings.ProgressTimer == 0)) {
// If the character is egged, we find the highest intensity factor
let Factor = -1;
for (let A = 0; A < Character[C].Appearance.length; A++) {
let Item = Character[C].Appearance[A];
let ZoneFactor = PreferenceGetZoneFactor(Character[C], Item.Asset.ArousalZone) - 2;
if (InventoryItemHasEffect(Item, "Egged", true) && (Item.Property != null) && (Item.Property.Intensity != null) && (typeof Item.Property.Intensity === "number") && !isNaN(Item.Property.Intensity) && (Item.Property.Intensity >= 0) && (ZoneFactor >= 0) && (Item.Property.Intensity + ZoneFactor > Factor))
if ((Character[C].ArousalSettings.Progress < 95) || PreferenceGetZoneOrgasm(Character[C], Item.Asset.ArousalZone))
Factor = Item.Property.Intensity + ZoneFactor;
}
// No decay if there's a vibrating item running
if (Factor < 0) ActivityTimerProgress(Character[C], -1);
if (PreferenceArousalAtLeast(Character[C], "Hybrid") && ActivityGetArousal(Character[C]) > 0 && ActivityGetArousalTimer(Character[C]) == 0) {
// If the character is egged, we find the highest intensity factor
let Factor = -1;
for (let A = 0; A < Character[C].Appearance.length; A++) {
let Item = Character[C].Appearance[A];
let ZoneFactor = PreferenceGetZoneFactor(Character[C], Item.Asset.ArousalZone) - 2;
if (InventoryItemHasEffect(Item, "Egged", true) && (Item.Property != null) && (Item.Property.Intensity != null) && (typeof Item.Property.Intensity === "number") && !isNaN(Item.Property.Intensity) && (Item.Property.Intensity >= 0) && (ZoneFactor >= 0) && (Item.Property.Intensity + ZoneFactor > Factor)) {
if (ActivityGetArousal(Character[C]) < 95 || PreferenceGetZoneOrgasm(Character[C], Item.Asset.ArousalZone))
Factor = Item.Property.Intensity + ZoneFactor;
}
}
// No decay if there's a vibrating item running
if (Factor < 0) ActivityTimerProgress(Character[C], -1);
}
}
}

View file

@ -74,6 +74,13 @@ type VibratorModeState = "Default" | "Deny" | "Orgasm" | "Rest";
type VibratorRemoteAvailability = "Available" | "NoRemote" | "NoLoversRemote" | "RemotesBlocked" | "CannotInteract" | "NoAccess" | "InvalidItem";
type ArousalMeterMode = "Inactive" | "NoMeter" | "Manual" | "Hybrid" | "Automatic";
type ArousalAccessMode = "All" | "Access" | "Self";
type ArousalVFXMode = "VFXInactive" | "VFXAnimated" | "VFXAnimatedTemp";
type ArousalVFXVibratorMode = "VFXVibratorInactive" | "VFXVibratorSolid" | "VFXVibratorAnimated";
type ArousalVFXFilterMode = "VFXFilterLight" | "VFXFilterMedium" | "VFXFilterHeavy";
type ArousalStutterMode = "None" | "Arousal" | "Vibration" | "All";
//#endregion
//#region index.html
@ -678,14 +685,14 @@ interface Character {
HeightRatioProportion?: number;
// Properties created in other places
ArousalSettings?: {
Active: string;
Visible: string;
Active: ArousalMeterMode;
Visible: ArousalAccessMode;
ShowOtherMeter: boolean;
AffectExpression: boolean;
AffectStutter: string;
VFX: string;
VFXVibrator: string;
VFXFilter: string;
AffectStutter: ArousalStutterMode;
VFX: ArousalVFXMode;
VFXVibrator: ArousalVFXVibratorMode;
VFXFilter: ArousalVFXFilterMode;
Progress: number;
ProgressTimer: number;
VibratorLevel: number;

View file

@ -558,7 +558,7 @@ function ValidationSanitizeProperties(C, item) {
}
// Block advanced vibrator modes if disabled
if (typeof property.Mode === "string" && C.ArousalSettings && C.ArousalSettings.DisableAdvancedVibes && !VibratorModeOptions[VibratorModeSet.STANDARD].includes(VibratorModeGetOption(property.Mode))) {
if (typeof property.Mode === "string" && PreferenceArousalHasAdvancedVibesDisabled(C) && !VibratorModeOptions[VibratorModeSet.STANDARD].includes(VibratorModeGetOption(property.Mode))) {
console.warn(`Removing invalid mode "${property.Mode}" from ${asset.Name}`);
property.Mode = VibratorModeOptions[VibratorModeSet.STANDARD][0];
changed = true;

View file

@ -314,7 +314,7 @@ function VibratorModeDrawControls(Options, Y) {
OptionGroup.forEach((Option, I) => {
var X = 1175 + (I % 3) * 225;
if (I % 3 === 0) Y += 75;
var Color = Property.Mode === Option.Property.Mode ? "#888" : (!(OptionName == VibratorModeSet.ADVANCED && C.ArousalSettings && C.ArousalSettings.DisableAdvancedVibes) ? "White" : "Pink");
var Color = Property.Mode === Option.Property.Mode ? "#888" : (!(OptionName == VibratorModeSet.ADVANCED && PreferenceArousalHasAdvancedVibesDisabled(C)) ? "White" : "Pink");
DrawButton(X, Y, 200, 55, DialogFindPlayer(Option.Name), Color);
});
Y += 40;
@ -339,7 +339,7 @@ function VibratorModeClick(Options, Y) {
var X = 1175 + (I % 3) * 225;
if (I % 3 === 0) Y += 75;
if (MouseIn(X, Y, 200, 55)) {
if ((Option.Property != null) && (DialogFocusItem.Property != null) && (Option.Property.Mode !== DialogFocusItem.Property.Mode) && !(OptionName == VibratorModeSet.ADVANCED && C.ArousalSettings && C.ArousalSettings.DisableAdvancedVibes))
if ((Option.Property != null) && (DialogFocusItem.Property != null) && (Option.Property.Mode !== DialogFocusItem.Property.Mode) && !(OptionName == VibratorModeSet.ADVANCED && PreferenceArousalHasAdvancedVibesDisabled(C)))
VibratorModeSetMode(Option);
return true;
}
@ -542,7 +542,7 @@ function VibratorModeUpdateEdge(Item, C, PersistentData) {
* @returns {void} - Nothing
*/
function VibratorModeUpdateStateBased(Item, C, PersistentData, TransitionsFromDefault) {
var Arousal = C.ArousalSettings.Progress;
var Arousal = ActivityGetArousal(C);
var TimeSinceLastChange = CommonTime() - PersistentData.LastChange;
var OldState = Item.Property.State || VibratorModeState.DEFAULT;
var OldIntensity = Item.Property.Intensity;
@ -644,7 +644,7 @@ function VibratorModeStateUpdateOrgasm(C, Arousal, TimeSinceLastChange, OldInten
/** @type {VibratorModeState} */
var State = VibratorModeState.ORGASM;
var Intensity = OldIntensity;
if (C.ArousalSettings.OrgasmStage > 0) {
if (ActivityGetOrgasmStage(C) > 0) {
// If we're in orgasm mode and the player is either resisting or mid-orgasm, change back to either rest or default mode
State = Math.random() < 0.75 ? VibratorModeState.REST : VibratorModeState.DEFAULT;
} else if (TimeSinceLastChange > OneMinute && Math.random() < 0.1) {