Merge branch 'ts/types' into 'master'

More TS strict typing

See merge request 
This commit is contained in:
zorgjeanbe 2025-04-08 21:52:42 +00:00
commit b3907669d9
53 changed files with 740 additions and 396 deletions

View file

@ -125,7 +125,7 @@ function CharacterAppearanceValidate(C) {
if (!AssetGroup[A].AllowNone && (CharacterAppearanceGetCurrentValue(C, AssetGroup[A].Name, "Name") == "None"))
for (let B = 0; B < Asset.length; B++)
if (Asset[B].Group.Name == AssetGroup[A].Name) {
CharacterAppearanceSetItem(C, Asset[B].Group.Name, Asset[B], Asset[B].Group.DefaultColor, null, null, false);
CharacterAppearanceSetItem(C, Asset[B].Group.Name, Asset[B], Asset[B].Group.DefaultColor);
Refresh = true;
break;
}
@ -166,11 +166,7 @@ function CharacterAppearanceSetDefault(C) {
CharacterAppearanceSetItem(
C,
CharacterAppearanceAssets[I].Group.Name,
CharacterAppearanceAssets[I],
undefined,
undefined,
undefined,
false,
CharacterAppearanceAssets[I]
);
}
}
@ -1016,11 +1012,10 @@ function AppearanceRun() {
}
}
/** @type {ScreenFunctions["Resize"]} */
/** @type {ScreenResizeHandler} */
function AppearanceResize(load) {
if (Layering.IsActive()) {
Layering.Resize(load);
return;
}
}
@ -1099,7 +1094,7 @@ function AppearancePreviewBuild(C, buildCanvases) {
let PreviewChar = CharacterLoadSimple("AppearancePreview-" + item.Asset.Name);
if (buildCanvases) {
PreviewChar.Appearance = Array.from(baseAppearance);
CharacterAppearanceSetItem(PreviewChar, item.Asset.Group.Name, item.Asset, null, null, null, false);
CharacterAppearanceSetItem(PreviewChar, item.Asset.Group.Name, item.Asset);
CharacterRefresh(PreviewChar, false);
}
AppearancePreviews.push(PreviewChar);
@ -1132,15 +1127,14 @@ function AppearancePreviewUseCharacter(assetGroup) {
* Sets an item in the character appearance
* @param {Character} C - The character whose appearance should be changed
* @param {AssetGroupName} Group - The name of the corresponding groupr for the item
* @param {Asset|null} ItemAsset - The asset collection of the item to be changed
* @param {Asset|undefined} ItemAsset - The asset collection of the item to be changed
* @param {string|string[]} [NewColor] - The new color (as "#xxyyzz" hex value) for that item
* @param {number} [DifficultyFactor=0] - The difficulty, on top of the base asset difficulty, that should be assigned
* to the item
* @param {number} [ItemMemberNumber=-1] - The member number of the player adding the item - defaults to -1
* @param {boolean} [Refresh=true] - Determines, wether the character should be redrawn after the item change
* @returns {null | Item} - Thew newly created item or `null` if the asset does not exist
* @returns {Item | null} - Thew newly created item or `undefined` if the asset does not exist
*/
function CharacterAppearanceSetItem(C, Group, ItemAsset, NewColor, DifficultyFactor, ItemMemberNumber, Refresh) {
function CharacterAppearanceSetItem(C, Group, ItemAsset, NewColor, DifficultyFactor, ItemMemberNumber) {
// Sets the difficulty factor
if (DifficultyFactor == null) DifficultyFactor = 0;
@ -1156,7 +1150,7 @@ function CharacterAppearanceSetItem(C, Group, ItemAsset, NewColor, DifficultyFac
} else if (ItemAsset != null) ItemColor = ItemAsset.DefaultColor ? ItemAsset.DefaultColor : ItemAsset.Group.DefaultColor;
// Add the new item to the character appearance
/** @type {null | Item} */
/** @type {Item | null} */
let NA = null;
if (ItemAsset != null) {
/** @type {Item} */
@ -1169,8 +1163,6 @@ function CharacterAppearanceSetItem(C, Group, ItemAsset, NewColor, DifficultyFac
C.Appearance.push(NA);
}
// Draw the character canvas and calculate the effects on the character
if (Refresh == null || Refresh) CharacterRefresh(C, false);
return NA;
}
@ -1386,16 +1378,19 @@ function AppearanceClick() {
if (!Group.AllowNone && !AppearancePreviewUseCharacter(Group)) {
const asset = CharacterAppearanceNextItem(C, Group.Name, MouseX > 1410);
CharacterAppearanceSetItem(C, Group.Name, asset);
CharacterRefresh(C);
return;
}
else if (MouseXIn(1210, 65)) {
const asset = CharacterAppearanceNextItem(C, Group.Name, false);
CharacterAppearanceSetItem(C, Group.Name, asset);
CharacterRefresh(C);
return;
}
else if (MouseXIn(1545, 65)) {
const asset = CharacterAppearanceNextItem(C, Group.Name, true);
CharacterAppearanceSetItem(C, Group.Name, asset);
CharacterRefresh(C);
return;
}
else {
@ -1515,6 +1510,7 @@ function AppearanceClick() {
DialogExtendItem(CurrentItem);
} else {
CharacterAppearanceSetItem(C, C.FocusGroup.Name, DialogInventory[I].Asset);
CharacterRefresh(C);
DialogInventoryBuild(C);
AppearancePreviewBuild(C, true);
AppearanceMenuBuild(C);
@ -1619,7 +1615,10 @@ function AppearanceMenuClick(C) {
}
// Strips the current item
if (Button === "Naked") CharacterAppearanceSetItem(C, C.FocusGroup.Name, null);
if (Button === "Naked") {
CharacterAppearanceSetItem(C, C.FocusGroup.Name, null);
CharacterRefresh(C);
}
// Jumps to the cloth page
if (Button === "Next" || Button === "Prev") {
@ -1683,7 +1682,7 @@ function AppearanceMenuClick(C) {
/**
* Handle the exiting of the appearance screen. The function name is created dynamically.
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function AppearanceExit() {
// We quit the extended item menu instead, if applicable.

View file

@ -205,7 +205,7 @@ function BackgroundSelectionKeyDown(event) {
/**
* Handles the exit of the selection screen. Sets the new background, if necessary, and
* calls the previously defined callback function. Then exits the screen to the screen, the player was before
* @satisfies {ScreenFunctions["Exit"]}
* @satisfies {ScreenExitHandler}
* @param {boolean} SetBackground - Defines, wether the background must be changed (true) or not (false)
*/
function BackgroundSelectionExit(SetBackground=false) {

View file

@ -114,7 +114,7 @@ function CheatClick() {
/**
* Handles exiting the cheat screen by saving the cheats and going back to the login screen.
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function CheatExit() {
CheatExport();

View file

@ -10,11 +10,15 @@ var CreationMessage = "";
function CreationLoad() {
// Gets the info to import Bondage College data
var DefaultName = "";
let DefaultName = "";
if ((localStorage.getItem("BondageClubImportSource") != null) && (localStorage.getItem("BondageClubImportSource") == "BondageCollege")) {
ImportBondageCollegeData = true;
if (localStorage.getItem("BondageCollegeExportName") != null) DefaultName = localStorage.getItem("BondageCollegeExportName");
} else ImportBondageCollegeData = null;
if (localStorage.getItem("BondageCollegeExportName") != null) {
DefaultName = localStorage.getItem("BondageCollegeExportName") ?? "";
}
} else {
ImportBondageCollegeData = false;
}
// Creates the text fields element
const form = ElementCreateForm("Creation");
@ -172,7 +176,7 @@ function CreationClick() {
// when the user exit this screen
/**
* Does the cleanup, if the user exits the screen
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function CreationExit() {
ElementRemove("InputCharacter");

View file

@ -49,7 +49,7 @@ const FriendListIDs = Object.freeze({
//#endregion
//#region SCREEN FUNCTIONS
/** @type {ScreenFunctions['Load']} */
/** @type {ScreenLoadHandler} */
function FriendListLoad() {
const mode = FriendListMode[FriendListModeIndex];
@ -306,7 +306,7 @@ function FriendListLoad() {
FriendListSortingDirection = 'Asc';
}
/** @type {ScreenFunctions['Resize']} */
/** @type {ScreenResizeHandler} */
function FriendListResize() {
ElementPositionFix(FriendListIDs.root, 36, 0, 0, 2000, 1000);
if (FriendListBeepTarget !== -1) {
@ -314,11 +314,11 @@ function FriendListResize() {
}
}
/** @type {ScreenFunctions['Run']} */
/** @type {ScreenRunHandler} */
function FriendListRun() {
}
/** @type {ScreenFunctions['Draw']} */
/** @type {ScreenDrawHandler} */
function FriendListDraw() {
if (Player.OnlineSettings.FriendListAutoRefresh && CommonTime() >= FriendListAutoRefresh.nextRefresh && ServerIsConnected) {
FriendListAutoRefresh.nextRefresh = CommonTime() + FriendListAutoRefresh.interval;
@ -326,11 +326,11 @@ function FriendListDraw() {
}
}
/** @type {ScreenFunctions['Click']} */
/** @type {MouseEventListener} */
function FriendListClick() {
}
/** @type {ScreenFunctions['KeyDown']} */
/** @type {KeyboardEventListener} */
function FriendListKeyDown(event) {
const beepTextArea = /** @type {HTMLTextAreaElement} */(document.getElementById(FriendListIDs.beepTextArea));
const beepTextAreaHasFocus = beepTextArea && document.activeElement === beepTextArea;
@ -351,11 +351,11 @@ function FriendListKeyDown(event) {
return false;
}
/** @type {ScreenFunctions['Unload']} */
/** @type {ScreenUnloadHandler} */
function FriendListUnload() {
}
/** @type {ScreenFunctions['Exit']} */
/** @type {ScreenExitHandler} */
function FriendListExit() {
const beepMenu = document.getElementById(FriendListIDs.beepList);
if (beepMenu) {

View file

@ -261,7 +261,7 @@ function InformationSheetClick() {
/**
* Cleanup all elements, if the user exits the screen
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function InformationSheetExit() {
InformationSheetSecondScreen = false;

View file

@ -1,7 +1,11 @@
"use strict";
var LoginBackground = "Dressing";
/** @type {null | string[][]} */
var LoginCredits = null;
/**
* Contents of the GameCredits.csv file
* Initialized once on screen load
* @type {string[][]}
*/
var LoginCredits;
var LoginCreditsPosition = 0;
var LoginThankYou = "";
/* eslint-disable */
@ -19,8 +23,12 @@ var LoginSubmitted = false;
var LoginQueuePosition = -1;
/** The server login status */
var LoginErrorMessage = "";
/** @type {NPCCharacter} */
var LoginCharacter = null;
/**
* The dummy on the login screen.
*
* Lifetime bound to the screen.
* @type {NPCCharacter} */
var LoginCharacter;
/* DEBUG: To measure FPS - uncomment this and change the + 4000 to + 40
var LoginLastCT = 0;
@ -134,7 +142,7 @@ function LoginLoad() {
LoginCharacter = CharacterLoadNPC("NPC_Login");
LoginDoNextThankYou();
LoginStatusReset();
if (LoginCredits == null) CommonReadCSV("LoginCredits", CurrentModule, CurrentScreen, "GameCredits");
if (!LoginCredits) CommonReadCSV("LoginCredits", CurrentModule, CurrentScreen, "GameCredits");
ActivityDictionaryLoad();
OnlneGameDictionaryLoad();
const form = ElementCreateForm("Login");
@ -180,7 +188,7 @@ function LoginLoad() {
function LoginRun() {
// Draw the credits
if (LoginCredits != null) LoginDrawCredits();
if (LoginCredits) LoginDrawCredits();
const CanLogin = ServerIsConnected && !LoginSubmitted;
@ -325,7 +333,7 @@ function LoginPerformInventoryFixups(Inventory) {
* @return {boolean}
*/
function LoginPerformAppearanceFixups(Appearance) {
if (!Appearance) return;
if (!Appearance) return true;
let fixedUp = false;
for (const fixup of LoginInventoryFixups) {
@ -348,15 +356,15 @@ function LoginPerformAppearanceFixups(Appearance) {
const asset = AssetGet("Female3DCG", worn.Group, worn.Name);
let opt = null;
if (asset.Archetype) {
if (asset?.Archetype) {
switch (asset.Archetype) {
case ExtendedArchetype.TYPED:
{
const opts = TypedItemGetOptions(worn.Group, worn.Name);
if (typeof fixup.New.Option === "undefined")
opt = opts[0];
opt = opts?.[0];
else
opt = opts.find(o => o.Name === fixup.New.Option);
opt = opts?.find(o => o.Name === fixup.New.Option);
if (!opt) {
console.error(`Unknown option ${fixup.New.Option}`);
@ -369,7 +377,7 @@ function LoginPerformAppearanceFixups(Appearance) {
// Replace old previous properties with the wanted ones
if (opt && opt.Property)
worn.Property = Object.assign(opt.Property);
} else if (asset.Extended) {
} else if (asset?.Extended) {
// Old-style extended item
} else {
@ -395,7 +403,7 @@ function LoginPerformCraftingFixups(Crafting) {
// Move crafts over to the new name
for (const craft of Crafting) {
if (!craft || craft.Item !== fixup.Old.Name) continue;
craft.Item = fixup.New.Name;
craft.Item = /** @type {string} */(fixup.New.Name);
}
}
}
@ -902,7 +910,7 @@ function LoginSetupPlayer(C) {
if (InventoryBeforeFixes != InventoryStringify(Player)) ServerPlayerInventorySync();
CharacterAppearanceValidate(Player);
ChatRoomCustomized = ((Player.OnlineSettings != null) && (Player.OnlineSettings.ShowRoomCustomization != null) && (Player.OnlineSettings.ShowRoomCustomization >= 2));
ChatRoomCustomized = Player.OnlineSettings.ShowRoomCustomization >= 2;
if (Player.Crafting.length > 80) Player.Crafting = Player.Crafting.slice(0, 80);
}
@ -962,7 +970,7 @@ function LoginResponse(C) {
if (LogQuery("Locked", "Cell")) {
// The player has been locked up, they must log back in the cell
CommonSetScreen("Room", "Cell");
} else if ((Player.Infiltration?.Punishment?.Timer ?? 0) > CurrentTime) {
} else if (Player.Infiltration?.Punishment && (Player.Infiltration.Punishment.Timer ?? 0) > CurrentTime) {
// The player must log back in Pandora's Box prison
PandoraWillpower = 0;
InfiltrationDifficulty = Player.Infiltration.Punishment.Difficulty;
@ -975,7 +983,7 @@ function LoginResponse(C) {
AsylumGGTSDroneDress(Player);
}
CommonSetScreen("Room", "AsylumBedroom");
} else if (LogValue("ForceGGTS", "Asylum") > 0) {
} else if (LogValue("ForceGGTS", "Asylum") ?? 0 > 0) {
// The owner is forcing the player to do GGTS
CommonSetScreen("Room", "AsylumEntrance");
} else if (LogQuery("SleepCage", "Rule") && Player.IsOwned() === "npc" && PrivateOwnerInRoom()) {
@ -1111,7 +1119,7 @@ function LoginGetStatus() {
/**
* Exit function - called when leaving the login page
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function LoginExit() {

View file

@ -113,7 +113,7 @@ function PasswordResetClick() {
/**
* Sends the player back to the login screen
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function PasswordResetExit() {
ElementRemove("InputEmail");

View file

@ -217,12 +217,12 @@ function PreferenceSubscreenArousalClick() {
if ((Player.FocusGroup != null) && MouseIn(550, 853, 600, 64)) {
if (MouseX <= 850) PreferenceArousalZoneFactor = PreferenceDecrementArousalFactor(PreferenceArousalZoneFactor);
else PreferenceArousalZoneFactor = PreferenceIncrementArousalFactor(PreferenceArousalZoneFactor);
PreferenceSetZoneFactor(Player, Player.FocusGroup.Name, PreferenceArousalZoneFactor);
PreferenceSetArousalZone(Player, Player.FocusGroup.Name, PreferenceArousalZoneFactor);
}
// Arousal zone orgasm check box
if ((Player.FocusGroup != null) && MouseIn(1230, 853, 64, 64))
PreferenceSetZoneOrgasm(Player, Player.FocusGroup.Name, !PreferenceGetZoneOrgasm(Player, Player.FocusGroup.Name));
PreferenceSetArousalZone(Player, Player.FocusGroup.Name, null, !PreferenceGetZoneOrgasm(Player, Player.FocusGroup.Name));
// In arousal mode, the player can click on her zones
for (const Group of AssetGroup) {

View file

@ -6,6 +6,13 @@ var PreferenceSettingsDeadZoneList = [0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.0
var PreferenceSettingsDeadZoneIndex = 1;
var PreferenceCalibrationStage = 0;
function PreferenceSubscreenControllerLoad() {
ControllerSensitivity = Player.ControllerSettings.ControllerSensitivity;
ControllerDeadZone = Player.ControllerSettings.ControllerDeadZone;
PreferenceSettingsSensitivityIndex = PreferenceSettingsSensitivityList.indexOf(Player.ControllerSettings.ControllerSensitivity);
PreferenceSettingsDeadZoneIndex = PreferenceSettingsDeadZoneList.indexOf(Player.ControllerSettings.ControllerDeadZone);
}
/**
* Sets the controller preferences for the player. Redirected to from the main Run function.
* @returns {void} - Nothing

View file

@ -120,6 +120,7 @@ const PreferenceSubscreens = [
},
{
name: "Controller",
load: () => PreferenceSubscreenControllerLoad(),
run: () => PreferenceSubscreenControllerRun(),
click: () => PreferenceSubscreenControllerClick(),
exit: () => PreferenceSubscreenControllerExit(),
@ -174,7 +175,7 @@ let PreferenceDidAddOldStyleScreens = false;
* @returns {void} - Nothing
*/
function PreferenceLoad() {
PreferenceSubscreen = PreferenceSubscreens.find(s => s.name === "Main");
PreferenceSubscreen = PreferenceSubscreens.find(s => s.name === "Main") ?? null;
// Backward-compatibility: just throw the old-style screens into the mix
if (!PreferenceDidAddOldStyleScreens) {
@ -224,7 +225,7 @@ function PreferenceClick() {
/**
* Is called when the player exits the preference screen. All settings of the preference screen are sent to the server.
* If the player is in a subscreen, they exit to the main preferences menu instead.
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function PreferenceExit() {
if (PreferenceSubscreen.name !== "Main") {

View file

@ -104,7 +104,7 @@ function RelogKeyDown(event) {
/**
* Sends the player back to the main login screen
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function RelogExit() {
window.location.reload();

View file

@ -243,7 +243,7 @@ function TitleClick() {
// when the user exit this screen
/**
* Exits the title selection screen and brings the player back to the InformationSheet
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function TitleExit() {
let Nick = ElementValue("InputNickname");

View file

@ -276,7 +276,7 @@ function WardrobeReorderModeSet (newmode=null)
/**
* Exits the wardorbe screen and sends the player back to her private room
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function WardrobeExit() {
WardrobeReorderModeSet ("None");
@ -338,7 +338,7 @@ function WardrobeFastLoad(C, W, Update) {
&& WardrobeGroupAccessible(C, A.Group, { ExcludeNonCloth: true })
&& InventoryAvailable(Player, A.Name, A.Group.Name)
) {
CharacterAppearanceSetItem(C, w.Group, A, w.Color, 0, null, false);
CharacterAppearanceSetItem(C, w.Group, A, w.Color, 0);
if (w.Property && InventoryGet(C, w.Group)) {
var item = InventoryGet(C, w.Group);
if (item.Property == null) item.Property = {};
@ -359,13 +359,12 @@ function WardrobeFastLoad(C, W, Update) {
CharacterAppearanceSetItem(
C, g.Name,
AssetGet(C.AssetFamily, g.Name, mirrorItem.Asset.Name),
mirrorItem.Color, null, null, false,
mirrorItem.Color
);
} else {
CharacterAppearanceSetItem(
C, g.Name,
AssetGroupGet(C.AssetFamily, g.Name).Asset[0],
null, null, null, false,
AssetGroupGet(C.AssetFamily, g.Name)?.Asset[0],
);
}
}

View file

@ -29,7 +29,7 @@ function AssetsItemNipplesLactationPumpScriptDraw(data) {
const persist = data.PersistentData();
// We do nothing if suction is disabled or if we're rendering someone else
if (!Item.Property.SuctionLevel || !C.IsPlayer()) return;
if (!Item.Property.SuctionLevel || !C.IsPlayer() || !Item.Asset.Group.IsItem()) return;
if (persist.LastSuction === undefined) {
persist.LastSuction = CurrentTime;

View file

@ -2650,7 +2650,7 @@ function KinkyDungeonClick() {
/**
* Handles exit during the kinky dungeon game
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function KinkyDungeonExit() {
KinkyDungeonGameKey.removeKeyListener();

View file

@ -3046,7 +3046,7 @@ function KinkyDungeonAddRestraint(restraint, Tightness, Bypass, Lock, Keep, Link
if (asset) {
placedOnPlayer = true;
const newColor = [...(color || asset.DefaultColor)];
CharacterAppearanceSetItem(Player, AssetGroup, asset, newColor, 0, null, false);
CharacterAppearanceSetItem(Player, AssetGroup, asset, newColor, 0);
KinkyDungeonPlayerNeedsRefresh = true;
}
}

View file

@ -86,7 +86,7 @@ function AdvancedRuleClick() {
/**
* Handles exiting from the screen, updates the sub rules
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function AdvancedRuleExit() {
CommonSetScreen("Online", "ChatRoom");

View file

@ -114,7 +114,7 @@ function ChatAdminCanEdit() {
/**
* Loads the given room data and sets up the UI for it
* @type {ScreenFunctions["Load"]}
* @type {ScreenLoadHandler}
*/
function ChatAdminLoad() {
if (!ChatAdminData.Background) {
@ -191,7 +191,7 @@ function ChatAdminLoad() {
/**
* Handles unloading the editor screen
* @type {ScreenFunctions["Unload"]}
* @type {ScreenUnloadHandler}
*/
function ChatAdminUnload() {
ElementRemove("InputName");
@ -422,7 +422,7 @@ function ChatAdminKeyDown(event) {
/**
* Handles exiting from the editor screen, removes the inputs and resets the state of the variables
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function ChatAdminExit() {
AsylumGGTSReset();

View file

@ -366,7 +366,7 @@ function ChatAdminRoomCustomizationClick() {
/**
* Handles exiting from the admin custom screen, removes the inputs
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function ChatAdminRoomCustomizationExit() {
ChatAdminRoomCustomizationPlayMusic("");

View file

@ -48,7 +48,7 @@ function ChatBlockItemClick() {
/**
* Handles exiting from the screen
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function ChatBlockItemExit() {
CommonSetScreen(...ChatBlockItemReturnScreen);

View file

@ -261,7 +261,7 @@ const ChatRoomArousalMsg_ChanceGagMod = {
var ChatRoomHideIconState = 0;
/**
* The list of buttons in the top-right
* @type {string[]}
* @type {ChatRoomMenuButton[]}
* */
var ChatRoomMenuButtons = [];
let ChatRoomFontSize = 30;
@ -1793,7 +1793,7 @@ function ChatRoomStimulationMessage(Action) {
// Increase player arousal to the zone
if (!Player.IsEdged() && arousal < 70 - event.arousal && event.event != "Talk")
ActivityEffectFlat(Player, Player, event.arousal, group.Name, 1);
ActivityEffectFlat(Player, Player, event.arousal, /** @type {AssetGroupItemName} */ (group.Name), 1);
const duration = (Math.random() + event.arousal / 2.4) * 500;
DrawFlashScreen("#FFB0B0", duration, 140);
if (Player.ArousalSettings?.AffectExpression) {
@ -1809,7 +1809,7 @@ function ChatRoomStimulationMessage(Action) {
}
/**
* @type {ScreenFunctions["Resize"]}
* @type {ScreenResizeHandler}
*/
function ChatRoomResize(load) {
const chatInput = document.getElementById("InputChat");
@ -1820,7 +1820,7 @@ function ChatRoomResize(load) {
}
/**
* @type {ScreenFunctions["Unload"]}
* @type {ScreenUnloadHandler}
*/
function ChatRoomUnload() {
ChatRoomHideElements();
@ -2180,7 +2180,7 @@ function ChatRoomCustomizationRun() {
/**
* Runs the chatroom screen.
* @type {ScreenFunctions["Run"]}
* @type {ScreenRunHandler}
*/
function ChatRoomRun(time) {
@ -2469,7 +2469,7 @@ function ChatRoomMouseMove(event) {
/**
* Redirects the Mouse Wheel event to the map if needed
* @type {ScreenFunctions["MouseWheel"]}
* @type {MouseWheelEventListener}
*/
function ChatRoomMouseWheel(event) {
if (ChatRoomActiveView.MouseWheel) return ChatRoomActiveView.MouseWheel(event);
@ -2477,7 +2477,7 @@ function ChatRoomMouseWheel(event) {
/**
* Handles clicks the chatroom screen.
* @type {ScreenFunctions["Click"]}
* @type {MouseEventListener}
*/
function ChatRoomClick(event) {
@ -2603,7 +2603,7 @@ function ChatRoomOpenAdminScreen() {
/**
* Process chat room menu button clicks
* @type {ScreenFunctions["Click"]}
* @type {MouseEventListener}
*/
function ChatRoomMenuClick(event) {
const Space = 992 / (ChatRoomMenuButtons.length);
@ -5095,7 +5095,7 @@ function ChatRoomSyncItem(data) {
item: if (item) {
// Puts the item on the character and apply the craft & property
const wornItem = CharacterAppearanceSetItem(ChatRoomCharacter[C], item.Asset.Group.Name, item.Asset, item.Color, item.Difficulty, null, false);
const wornItem = CharacterAppearanceSetItem(ChatRoomCharacter[C], item.Asset.Group.Name, item.Asset, item.Color, item.Difficulty);
if (wornItem == null) {
valid = false;
break item;

View file

@ -65,7 +65,7 @@ function ChatRoomCharacterViewClick(event) {
}
/** @type {ScreenFunctions["KeyDown"]} */
/** @type {KeyboardEventListener} */
function ChatRoomCharacterViewKeyDown(event) {
return false;
}

View file

@ -530,7 +530,7 @@ function ChatRoomMapViewIsActive() {
return ChatRoomIsViewActive(ChatRoomMapViewName);
}
/** @type {ScreenFunctions["Run"]} */
/** @type {ScreenRunHandler} */
function ChatRoomMapViewRun(time) {
// Syncs the room map data with the server if needed
@ -2046,7 +2046,7 @@ function ChatRoomMapViewMouseMove() {
/**
* Mouse up event is used to stop drawing
* @returns {void} - Nothing
* @type {MouseEventListener}
*/
function ChatRoomMapViewMouseUp() {
if ((CurrentScreen != "ChatRoom") || !ChatRoomMapViewIsActive()) return;
@ -2055,7 +2055,7 @@ function ChatRoomMapViewMouseUp() {
/**
* Mouse wheel event is used to zoom the map
* @type {ScreenFunctions["MouseWheel"]}
* @type {MouseWheelEventListener}
*/
function ChatRoomMapViewMouseWheel(Event) {
if ((CurrentScreen != "ChatRoom") || !ChatRoomMapViewIsActive()) return;

View file

@ -61,12 +61,13 @@ var ChatSearchLanguage = "";
/** @type {"" | ServerChatRoomLanguage} */
var ChatSearchLanguageTemp = "";
var ChatSearchFilterTermsTemp = "";
/** @type {ChatRoomSpaceLabel[]} */
var ChatSearchRoomSpaces = ["MIXED", "FEMALE_ONLY", "MALE_ONLY"];
var ChatSearchCurrentRoomSpaceIndex = 0;
/**
* Loads the chat search screen properties, creates the inputs and loads up the first 24 rooms.
* @type {ScreenFunctions["Load"]}
* @type {ScreenLoadHandler}
*/
function ChatSearchLoad() {
ChatRoomCustomizationClear();
@ -104,7 +105,7 @@ function ChatSearchLoad() {
TextPrefetch("Online", "ChatRoom");
}
/** @type {ScreenFunctions["Unload"]} */
/** @type {ScreenUnloadHandler} */
function ChatSearchUnload() {
ElementRemove("InputSearch");
ChatRoomStimulationMessage("Walk");
@ -112,7 +113,7 @@ function ChatSearchUnload() {
/**
* When the chat Search screen runs, draws the screen
* @type {ScreenFunctions["Run"]}
* @type {ScreenRunHandler}
*/
function ChatSearchRun() {
@ -181,7 +182,7 @@ function ChatSearchRun() {
/**
* Handles the click events on the chat search screen. Is called from CommonClick()
* @type {ScreenFunctions["Click"]}
* @type {MouseEventListener}
*/
function ChatSearchClick() {
if (ChatSearchFilterUnhideConfirm) {
@ -326,7 +327,7 @@ function ChatSearchRoomSpaceSelectClick() {
if (Genders.includes("M")) TempRoomSpaces = TempRoomSpaces.filter(space => space != "FEMALE_ONLY");
if (Genders.includes("F")) TempRoomSpaces = TempRoomSpaces.filter(space => space != "MALE_ONLY");
let CurrentRoomSpace = Object.keys(ChatRoomSpaceType).find(key => ChatRoomSpaceType[key] == ChatRoomSpace);
let CurrentRoomSpace = CommonKeys(ChatRoomSpaceType).find(key => ChatRoomSpaceType[key] == ChatRoomSpace);
ChatSearchCurrentRoomSpaceIndex = TempRoomSpaces.indexOf(CurrentRoomSpace);
@ -445,7 +446,7 @@ function ChatSearchKeyDown(event) {
/**
* Handles exiting from the chat search screen, removes the input.
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function ChatSearchExit() {
ChatSearchMode = "";

View file

@ -110,7 +110,7 @@ function ForbiddenWordsClick() {
/**
* Handles exiting from the screen, updates the sub rules
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function ForbiddenWordsExit() {
ElementRemove("InputWord");

View file

@ -172,7 +172,7 @@ function GameClubCardClick() {
/**
* Triggered when the player exits the Club Card config screen.
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function GameClubCardExit() {

View file

@ -386,7 +386,7 @@ function GameLARPClick() {
/**
* Triggered when the player exits the LARP info screen.
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function GameLARPExit() {

View file

@ -273,7 +273,7 @@ function GameMagicBattleClick() {
/**
* Triggered when the player exits the Magic Battle info screen.
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function GameMagicBattleExit() {

View file

@ -45,7 +45,7 @@ function NicknameManagementClick() {
/**
* Handles exiting from the screen, updates the sub rules
* @satisfies {ScreenFunctions["Exit"]}
* @satisfies {ScreenExitHandler}
* @param {boolean} Save - Received rule data object.
*/
function NicknameManagementExit(Save = false) {

View file

@ -411,7 +411,7 @@ function AsylumEntranceKidnapNurseTransferToRoom() {
/**
* When the player leaves the kidnap nurse
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function AsylumEntranceKidnapNurseExit() {
DialogLeave();

View file

@ -1426,7 +1426,7 @@ function CraftingRun() {
}
}
/** @type {ScreenFunctions["Resize"]} */
/** @type {ScreenResizeHandler} */
function CraftingResize(load) {
switch (CraftingMode) {
case "OverridePriority":
@ -1438,7 +1438,7 @@ function CraftingResize(load) {
}
}
/** @type {ScreenFunctions["Unload"]} */
/** @type {ScreenUnloadHandler} */
function CraftingUnload() {
switch (CraftingMode) {
case "OverridePriority":
@ -1754,7 +1754,7 @@ function CraftingReorderModeSet(newmode=null)
/**
* Handles clicks in the crafting room.
* @type {ScreenFunctions["Click"]}
* @type {MouseEventListener}
*/
function CraftingClick(event) {
// Can always exit or cancel
@ -2023,7 +2023,7 @@ function CraftingExitResetElements() {
/**
* When the player exits the crafting room
* @satisfies {ScreenFunctions["Exit"]}
* @satisfies {ScreenExitHandler}
* @param {boolean} allowPanelClose - Whether an exit call in the `Name` mode is allowed to close the side panels before performing a proper exit of the subscreen
*/
function CraftingExit(allowPanelClose=true) {

View file

@ -86,7 +86,7 @@ function InfiltrationPerksClick() {
/**
* Updates the infiltration data for the player
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function InfiltrationPerksExit() {
ServerAccountUpdate.QueueData({ Infiltration: Player.Infiltration });

View file

@ -1,13 +1,18 @@
"use strict";
var PlatformRunStandaloneMode = false;
/** @type {Platform.Character[]} */
var PlatformChar = [];
/** @type {Platform.DialogCharacter | null} */
var PlatformFocusCharacter = null;
var PlatformPlayer = null;
/** @type {number | null} */
var PlatformLastTime = null;
/** @type {Platform.KeyCode[]} */
var PlatformKeys = [];
var PlatformFloor = 1180;
var PlatformViewX = 0;
var PlatformViewY = 200;
/** @type {Platform.Room | null} */
var PlatformRoom = null;
var PlatformMusic = null;
var PlatformAllowAudio = true;
@ -18,6 +23,7 @@ var PlatformExperienceForLevel = [0, 10, 15, 25, 40, 60, 90, 135, 200, 300];
var PlatformShowHitBox = false;
var PlatformMessage = null;
var PlatformHeal = null;
/** @type {Platform.Event[]} */
var PlatformEvent = [];
var PlatformTempEvent = [];
var PlatformPlayerIdleTimer = null;
@ -33,13 +39,16 @@ var PlatformImmunityTime = 500;
var PlatformSaveMode = false;
var PlatformGiftMode = false;
var PlatformJumpPhase = "";
/** @type {Platform.PartyMember[]} */
var PlatformParty = [];
var PlatformRegen = 0;
/** @type {Platform.Cooldown[]} */
var PlatformCooldown = [];
var PlatformHeartEffect = false;
var PlatformTimedScreenFilter = { End: 0, Filter: "" };
var PlatformRightButtons = [];
var PlatformInventory = [];
/** @type {Platform.Item[]} */
var PlatformInventoryList = [
{
Name: "RedRose",
@ -80,6 +89,7 @@ var PlatformInventoryList = [
];
// Template for characters with their animations
/** @type {(Platform.CharacterTemplate | Platform.DummyTemplate)[]} */
var PlatformTemplate = [
{
Name: "Melody", // MMD Z: 41.50
@ -769,7 +779,10 @@ var PlatformTemplate = [
];
// All available rooms
/**
* All available rooms
* @type {Platform.Room[]}
*/
var PlatformRoomList = [
/*{
Name: "CollegeClass1",
@ -1962,26 +1975,25 @@ var PlatformRoomList = [
];
/**
* Creates a character in the current room and returns it
* @param {String} CharacterName - The character name to load
* @param {String} StatusName - The status of that character
* @param {Number} X - The X position of the character
* @param {Boolean} Fix - TRUE if the character won't move
* @param {Boolean} Combat - TRUE if the character will deal and receive combat damage
* @param {String} Dialog - The dialog name to open when talking to that character
* @param {Boolean} FaceLeft - TRUE if the character should be facing left
* @param {Number} ReplaceAtPos - The position in the index to replace the char, if NULL we add it
* @returns {Object} - Returns the platform character
* Loads a room and it's parameters
* @param {string} CharacterName - The character name to load
* @param {string} StatusName - The status of that character
* @param {number} X - The X position of the character
* @param {boolean} [Fix] - TRUE if the character won't move
* @param {boolean} [Combat] - TRUE if the character will deal and receive combat damage
* @param {string} [Dialog] - The dialog name to open when talking to that character
* @param {boolean} [FaceLeft] - TRUE if the character should be facing left
* @param {number} [ReplaceAtPos] - The position in the index to replace the char, if NULL we add it
* @returns {Platform.Character | undefined} - Returns the platform character
*/
function PlatformCreateCharacter(CharacterName, StatusName, X, Fix = null, Combat = null, Dialog = null, FaceLeft = null, ReplaceAtPos = null) {
let NewChar = null;
for (let CharTemplate of PlatformTemplate)
if ((CharTemplate.Name == CharacterName) && (CharTemplate.Status == StatusName)) {
NewChar = CommonCloneDeep(CharTemplate);
if (CharTemplate.OnBind != null) NewChar.OnBind = CharTemplate.OnBind;
break;
}
if (NewChar == null) return;
function PlatformCreateCharacter(CharacterName, StatusName, X, Fix = undefined, Combat = undefined, Dialog = undefined, FaceLeft = undefined, ReplaceAtPos = undefined) {
const template = PlatformTemplate.find(t => t.Name === CharacterName && t.Status === StatusName);
if (!template) return undefined;
const NewChar = /** @type {Platform.Character} */(Object.assign({}, template));
if (template.OnBind != null) NewChar.OnBind = template.OnBind;
NewChar.Camera = (PlatformChar.length == 0);
NewChar.ID = (ReplaceAtPos == null) ? PlatformChar.length : ReplaceAtPos;
NewChar.X = X;
@ -2026,8 +2038,8 @@ function PlatformCreateCharacter(CharacterName, StatusName, X, Fix = null, Comb
/**
* Returns TRUE if a specific event is already done
* @param {String} Event - The name of the event
* @returns {Boolean} - TRUE if done
* @param {string} Event - The name of the event
* @returns {boolean} - TRUE if done
*/
function PlatformEventDone(Event) {
return (PlatformEvent.indexOf(Event) >= 0);
@ -2035,7 +2047,7 @@ function PlatformEventDone(Event) {
/**
* Adds an event to the list of events done
* @param {String} Event - The name of the event
* @param {Platform.EventType} Event - The name of the event
* @returns {void} - Nothing
*/
function PlatformEventSet(Event) {
@ -2044,7 +2056,7 @@ function PlatformEventSet(Event) {
/**
* Sets the on screen message for 4 seconds
* @param {String} Text - The text to show
* @param {string} Text - The text to show
* @returns {void} - Nothing
*/
function PlatformMessageSet(Text) {
@ -2054,7 +2066,7 @@ function PlatformMessageSet(Text) {
/**
* Loads a room and it's parameters
* @param {Object} RoomName - The name of the room to load, can be null to reload the current room
* @param {string} [RoomName] - The name of the room to load, can be null to reload the current room
* @returns {void} - Nothing
*/
function PlatformLoadRoom(RoomName) {
@ -2081,7 +2093,7 @@ function PlatformLoadRoom(RoomName) {
/**
* Adds a character to the party
* @param {Object} C - The character to add to the roster
* @param {Platform.PartyMember} C - The character to add to the roster
* @returns {void} - Nothing
*/
function PlatformPartyAdd(C) {
@ -2154,7 +2166,7 @@ function PlatformPartyNext() {
/**
* Activates a specific character by name
* @param {String} CharacterName - The character name to activate
* @param {string} CharacterName - The character name to activate
* @returns {void} - Nothing
*/
function PlatformPartyActivate(CharacterName) {
@ -2228,10 +2240,10 @@ function PlatformLoad() {
/**
* Get the proper animation from the cycle to draw
* @param {Object} C - The character to evaluate
* @param {String} Pose - The pose we want
* @param {Boolean} Cycle - TRUE if we must use the animation cycle
* @returns {Object} - An object with the image, width & height to draw
* @param {Platform.Character} C - The character to evaluate
* @param {Platform.AnimationName} Pose - The pose we want
* @param {boolean} Cycle - TRUE if we must use the animation cycle
* @returns {Platform.AnimationSpec} - An object with the image, width & height to draw
*/
function PlatformGetAnim(C, Pose, Cycle = null) {
for (let A = 0; A < C.Animation.length; A++)
@ -2260,8 +2272,8 @@ function PlatformGetAnim(C, Pose, Cycle = null) {
/**
* Returns TRUE if the current action for a character is ActionName
* @param {Object} C - The character to validate
* @param {String} ActionName - The action to validate (all actions are valid if "Any"
* @param {Platform.Character} C - The character to validate
* @param {Platform.AnimationName | "Any"} ActionName - The action to validate (all actions are valid if "Any"
* @returns {boolean} - TRUE if the character action is that string
*/
function PlatformActionIs(C, ActionName) {
@ -2348,8 +2360,8 @@ function PlatformDrawBackground() {
/**
* Draw a specific character on the screen if needed
* @param {Object} C - The character to draw
* @param {Number} Time - The current time when the action is done
* @param {Platform.Character} C - The character to draw
* @param {number} Time - The current time when the action is done
* @returns {void} - Nothing
*/
function PlatformDrawCharacter(C, Time) {
@ -2377,7 +2389,7 @@ function PlatformDrawCharacter(C, Time) {
/**
* Sets the max health and current health for the character based on the level and skill
* @param {Object} C - The character to evaluate
* @param {Platform.Character} C - The character to evaluate
* @returns {void} - Nothing
*/
function PlatformSetHealth(C) {
@ -2406,8 +2418,8 @@ function PlatformSetHealth(C) {
/**
* Adds experience points to the player, can also gain a level which heals fully
* @param {Object} C - The character that will gain experience
* @param {Number} Value - The exp value to add
* @param {Platform.Character} C - The character that will gain experience
* @param {number} Value - The exp value to add
* @returns {void} - Nothing
*/
function PlatformAddExperience(C, Value) {
@ -2427,8 +2439,8 @@ function PlatformAddExperience(C, Value) {
/**
* Some perks allow the player to steal items from bound enemies
* @param {Object} C - The character that will gain experience
* @param {Number} Value - The experience value to factor the quantity
* @param {Platform.Character} C - The character that will gain experience
* @param {number} Value - The experience value to factor the quantity
* @returns {void} - Nothing
*/
function PlatformSteal(C, Value) {
@ -2446,7 +2458,7 @@ function PlatformSteal(C, Value) {
/**
* Gives a random inventory to the player
* @param {Object} Target - The target that gives the inventory
* @param {Platform.Character | Platform.DummyTemplate} Target - The target that gives the inventory
* @returns {void} - Nothing
*/
function PlatformAddRandomInventory(Target) {
@ -2458,8 +2470,8 @@ function PlatformAddRandomInventory(Target) {
/**
* Random odds of finding inventory on a defeated enemy
* @param {Object} Source - The victorious character
* @param {Object} Target - The defeated character
* @param {Platform.Character} Source - The victorious character
* @param {Platform.Character} Target - The defeated character
* @returns {void} - Nothing
*/
function PlatformFindInventory(Source, Target) {
@ -2482,12 +2494,12 @@ function PlatformCreateTreasure() {
/**
* Applies damage on a target, can become wounded at 0 health
* @param {Object} Source - The character doing the damage
* @param {Object} Target - The character getting the damage
* @param {Number} Damage - The number of damage to apply
* @param {Number} Time - The current time when the action is done
* @param {String} Type - The damage type (Collsion or Action)
* @param {String} AttackName - The name of the attack that was done
* @param {Platform.Character} Source - The character doing the damage
* @param {Platform.Character} Target - The character getting the damage
* @param {number} Damage - The number of damage to apply
* @param {number} Time - The current time when the action is done
* @param {string} Type - The damage type (Collsion or Action)
* @param {string} AttackName - The name of the attack that was done
* @returns {void} - Nothing
*/
function PlatformDamage(Source, Target, Damage, Time, Type, AttackName = "") {
@ -2537,9 +2549,9 @@ function PlatformDamage(Source, Target, Damage, Time, Type, AttackName = "") {
/**
* Checks if the hitbox of an attack clashes with a hitbox of the target
* @param {Object} Source - The character doing the damage
* @param {Object} Target - The character getting the damage
* @param {Array} HitBox - The hitbox of the attack
* @param {Platform.Character} Source - The character doing the damage
* @param {Platform.Character} Target - The character getting the damage
* @param {RectTuple} HitBox - The hitbox of the attack
* @returns {boolean} - TRUE if there's a clash
*/
function PlatformHitBoxClash(Source, Target, HitBox) {
@ -2589,9 +2601,9 @@ function PlatformHitBoxClash(Source, Target, HitBox) {
/**
* Plays a sound effect if needed
* @param {String} Category - The sound effect category
* @param {Object} Sound - The sound or array of sound to play
* @param {Number} Factor - The volume factor to apply
* @param {Platform.SoundCategory} Category - The sound effect category
* @param {Platform.SoundEffect | Platform.SoundEffect[]} Sound - The sound or array of sound to play
* @param {number} Factor - The volume factor to apply
* @returns {void} - Nothing
*/
function PlatformSoundEffect(Category, Sound, Factor = 0.3333) {
@ -2617,8 +2629,8 @@ function PlatformSoundEffect(Category, Sound, Factor = 0.3333) {
/**
* Checks if the character action can attack someone else
* @param {Object} Source - The character doing the action
* @param {Number} Time - The current time when the action is done
* @param {Platform.Character} Source - The character doing the action
* @param {number} Time - The current time when the action is done
* @returns {void} - Nothing
*/
function PlatformProcessAction(Source, Time) {
@ -2648,9 +2660,9 @@ function PlatformProcessAction(Source, Time) {
/**
* Calculates the X force to apply based on the time it took until the last frame and the speed of the object
* @param {Number} Speed - The speed of the object
* @param {Number} Frame - The number of milliseconds since the last frame
* @returns {Number} - The force to apply
* @param {number} Speed - The speed of the object
* @param {number} Frame - The number of milliseconds since the last frame
* @returns {number} - The force to apply
*/
function PlatformWalkFrame(Speed, Frame) {
return Frame * Speed / 50;
@ -2658,8 +2670,8 @@ function PlatformWalkFrame(Speed, Frame) {
/**
* Does collision damage for a character
* @param {Object} Target - The character that will be damaged
* @param {Number} Time - The current time when the action is done
* @param {Platform.Character} Target - The character that will be damaged
* @param {number} Time - The current time when the action is done
* @returns {void} - Nothing
*/
function PlatformCollisionDamage(Target, Time) {
@ -2672,8 +2684,8 @@ function PlatformCollisionDamage(Target, Time) {
/**
* Checks if an opponent can bind the player
* @param {Object} Source - The opponent that can bind
* @param {Number} Time - The current time when the action is done
* @param {Platform.Character} Source - The opponent that can bind
* @param {number} Time - The current time when the action is done
* @returns {void} - Nothing
*/
function PlatformBindPlayer(Source, Time) {
@ -2689,7 +2701,7 @@ function PlatformBindPlayer(Source, Time) {
/**
* Returns TRUE if the player input is valid for a move
* @param {Object} Move - The movement type (Crouch, jump, left, right, etc.)
* @param {Platform.AnimationName} Move - The movement type (Crouch, jump, left, right, etc.)
* @returns {boolean}
*/
function PlatformMoveActive(Move) {
@ -2735,8 +2747,8 @@ function PlatformMoveActive(Move) {
/**
* Returns TRUE if an animation is available for the character
* @param {Object} C - The character to evaluate
* @param {String} AnimationName - The animation name to search
* @param {Platform.Character} C - The character to evaluate
* @param {Platform.AnimationName} AnimationName - The animation name to search
* @returns {boolean} - TRUE if it's available
*/
function PlatformAnimAvailable(C, AnimationName) {
@ -2748,15 +2760,15 @@ function PlatformAnimAvailable(C, AnimationName) {
/**
* Creates a projectile that will disappear when it hits the floor or a wall
* @param {String} Name - The name of the projectile (Arrow, Bullet, etc.)
* @param {String} Type - The type of the projectile (Wood, Iron, etc.)
* @param {Boolean} FaceLeft - IF the projectile is facing the left direction
* @param {Number} X - The X position
* @param {Number} Y - The Y position
* @param {Number} Force - The speed of the projectile
* @param {Number} Gravity - The Y axis gravity pulling that projectile down (default to 0.25)
* @param {Number} Damage - The damage done by the projectile
* @param {any} HitAudio - The damage done by the projectile
* @param {Platform.ProjectileName} Name - The name of the projectile (Arrow, Bullet, etc.)
* @param {Platform.ProjectileType} Type - The type of the projectile (Wood, Iron, etc.)
* @param {boolean} FaceLeft - IF the projectile is facing the left direction
* @param {number} X - The X position
* @param {number} Y - The Y position
* @param {number} Force - The speed of the projectile
* @param {number} Gravity - The Y axis gravity pulling that projectile down (default to 0.25)
* @param {number} Damage - The damage done by the projectile
* @param {Platform.SoundEffect[]} [HitAudio] - The damage done by the projectile
* @returns {void} - Nothing
*/
function PlatformCreateProjectile(Name, Type, FaceLeft, X, Y, Force, Gravity = 0.25, Damage, HitAudio) {
@ -2771,7 +2783,7 @@ function PlatformCreateProjectile(Name, Type, FaceLeft, X, Y, Force, Gravity = 0
/**
* Calculates the projectiles
* @param {Number} Time - The current time stamp of the frame
* @param {number} Time - The current time stamp of the frame
* @returns {void} - Nothing
*/
function PlatformProcessProjectile(Time) {
@ -2813,8 +2825,8 @@ function PlatformProcessProjectile(Time) {
/**
* Consume a projectile from the character and creates it on screen
* @param {Object} C - The character that generates the projectile
* @param {Boolean} LongShot - TRUE if it's a long shot
* @param {Platform.Character} C - The character that generates the projectile
* @param {boolean} LongShot - TRUE if it's a long shot
* @returns {void} - Nothing
*/
function PlatformProjectile(C, LongShot) {
@ -2829,8 +2841,8 @@ function PlatformProjectile(C, LongShot) {
/**
* Binds a target character by the source character
* @param {Object} Source - The source character that's doing the bondage
* @param {Object} Target - The target character that's getting bound
* @param {Platform.Character} Source - The source character that's doing the bondage
* @param {Platform.Character} Target - The target character that's getting bound
* @returns {void} - Nothing
*/
function PlatformBindTarget(Source, Target) {
@ -2906,7 +2918,7 @@ function PlatformDrawGame() {
// If we must regenarate magic for the player
if (!MustHeal && (PlatformPlayer.MaxMagic != null) && (PlatformPlayer.Magic != null) && (PlatformRegen < PlatformTime)) {
if (PlatformPlayer.Magic < PlatformPlayer.MaxMagic) PlatformPlayer.Magic++;
PlatformRegen = PlatformTime + (PlatformHasPerk("Meditation") ? 6000 : 8000);
PlatformRegen = PlatformTime + (PlatformHasPerk(PlatformPlayer, "Meditation") ? 6000 : 8000);
}
// Draw each characters
@ -3246,8 +3258,8 @@ function PlatformRun() {
/**
* Starts an attack by the source
* @param {Object} Source - The character doing the action
* @param {String} Type - The action type (Punch, Kick, Sweep, etc.)
* @param {Platform.Character} Source - The character doing the action
* @param {Platform.AnimationName} Type - The action type (Punch, Kick, Sweep, etc.)
* @returns {void} - Nothing
*/
function PlatformAttack(Source, Type) {
@ -3286,7 +3298,7 @@ function PlatformAudioToggle() {
/**
* Gives an item to the currrent NPC
* @param {string} ItemName -
* @param {Platform.ItemName} ItemName -
* @returns {void} - Nothing
*/
function PlatformGiveItem(ItemName) {
@ -3357,7 +3369,7 @@ function PlatformLeave() {
/**
* Enters a new room if the entry conditions are met
* @param {String} FromType - The type of room enter (Up, Left, Right)
* @param {Platform.RoomType} FromType - The type of room enter (Up, Left, Right)
* @returns {void} - Nothing
*/
function PlatformEnterRoom(FromType) {
@ -3385,7 +3397,7 @@ function PlatformEnterRoom(FromType) {
/**
* Checks if there's a target character to bind and starts the binding process
* @param {Object} Source - The source character that does the binding
* @param {Platform.Character} Source - The source character that does the binding
* @returns {void} - Nothing
*/
function PlatformBindStart(Source) {
@ -3403,7 +3415,7 @@ function PlatformBindStart(Source) {
/**
* Saves the game on a specific slot
* @param {Number} Slot - The slot to use (from 0 to 9)
* @param {number} Slot - The slot to use (from 0 to 9)
* @returns {void} - Nothing
*/
function PlatformSaveGame(Slot) {
@ -3429,7 +3441,7 @@ function PlatformSaveGame(Slot) {
/**
* Adds an item to the player inventory
* @param {string} InventoryName - The item name to add
* @param {Platform.ItemName} InventoryName - The item name to add
* @param {number} QuantityToAdd - The quantity to add (1 if null)
* @returns {void} - Nothing
*/
@ -3445,7 +3457,7 @@ function PlatformInventoryAdd(InventoryName, QuantityToAdd = 1) {
/**
* Removes an item from the player inventory
* @param {string} InventoryName - The item name to add
* @param {Platform.ItemName} InventoryName - The item name to add
* @param {number} QuantityToRemove - The quantity to add (1 if null)
* @returns {void} - Nothing
*/
@ -3461,7 +3473,7 @@ function PlatformInventoryRemove(InventoryName, QuantityToRemove = 1) {
/**
* Loads the game on a specific slot
* @param {Number} Slot - The slot to use (from 0 to 9)
* @param {number} Slot - The slot to use (from 0 to 9)
* @returns {void} - Nothing
*/
function PlatformLoadGame(Slot) {
@ -3520,7 +3532,7 @@ function PlatformLoadGame(Slot) {
/**
* Teleports a character forward
* @param {Object} C - The character to teleport
* @param {Platform.Character} C - The character to teleport
* @returns {void} - Nothing
*/
function PlatformCastTeleport(C) {
@ -3537,7 +3549,7 @@ function PlatformCastTeleport(C) {
/**
* Heals the character for 20% of it's max HP
* @param {Object} C - The character to teleport
* @param {Platform.Character} C - The character to teleport
* @returns {void} - Nothing
*/
function PlatformCastHeal(C) {
@ -3557,7 +3569,7 @@ function PlatformCastHeal(C) {
/**
* Handles the keys pressed to move, attack or jump
* @param {string} Code - The key code pressed
* @param {Platform.KeyCode} Code - The key code pressed
* @returns {void} - Nothing
*/
function PlatformEventKeyDown(Code) {
@ -3587,7 +3599,7 @@ function PlatformEventKeyDown(Code) {
/**
* Handles keys pressed
* @param {Object} e - The key pressed
* @param {KeyboardEvent} e - The key pressed
* @returns {boolean} - TRUE if we handled the key pressed
*/
function PlatformKeyDown(e) {
@ -3600,7 +3612,7 @@ function PlatformKeyDown(e) {
/**
* Handles keys released
* @param {Object} e - The key released
* @param {KeyboardEvent} e - The key released
* @returns {void} - Nothing
*/
function PlatformKeyUp(e) {
@ -3684,8 +3696,8 @@ function PlatformTouch() {
/**
* Returns TRUE if a specific perk is allocated for that character
* @param {Object} C - The platform character to evaluate
* @param {Object} Perk - The perk name to validate
* @param {Platform.Character} C - The platform character to evaluate
* @param {Platform.PerkName} Perk - The perk name to validate
* @returns {boolean} - TRUE if the perk is paid
*/
function PlatformHasPerk(C, Perk) {
@ -3696,7 +3708,7 @@ function PlatformHasPerk(C, Perk) {
/**
* Returns TRUE if a specific cooldown is currently active
* @param {String} Name - The name of the cooldown to validate
* @param {Platform.EffectType | Platform.AnimationName} Name - The name of the cooldown to validate
* @returns {boolean} - TRUE if active
*/
function PlatformCooldownActive(Name) {

View file

@ -9,10 +9,12 @@ var PlatformDialogReply = null;
var PlatformDialogGoto = null;
var PlatformDialogCharacterDisplay = null;
var PlatformDialogPosition = 0;
/** @type {Platform.DialogCharacter[]} */
var PlatformDialogCharacter = null;
var PlatformDialogAudio = null;
var PlatformDialogControllerHandle = false;
var PlatformDialogAudioStyle = ["", "angry", "calm", "chat", "cheerful", "friendly", "sad", "serious", "shouting", "terrified", "unfriendly", "whispering"];
/** @type {Platform.DialogCharacter[]} */
var PlatformDialogCharacterTemplate = [
{
Name: "Melody",
@ -5187,7 +5189,7 @@ function PlatformDialogVoice(Source) {
/**
* Loads the dialog at a specific position
* @param {Number} Position - The position # to load
* @param {number} Position - The position # to load
* @returns {void} - Nothing
*/
function PlatformDialogLoadPosition(Position) {
@ -5220,7 +5222,7 @@ function PlatformDialogLoadPosition(Position) {
/**
* Starts a specific dialog
* @param {String} DialogName - The name of the dialog to start
* @param {string} DialogName - The name of the dialog to start
* @returns {void} - Nothing
*/
function PlatformDialogStart(DialogName) {
@ -5331,9 +5333,9 @@ function PlatformDialogRun() {
/**
* Change the love/domination value based on the option picked, influenced also by perks
* @param {Number} CurrentValue - The current value
* @param {Number} Change - The modifier to apply
* @param {Boolean} Bonus - If there's a bonus to apply or not
* @param {number} CurrentValue - The current value
* @param {number} Change - The modifier to apply
* @param {boolean} Bonus - If there's a bonus to apply or not
* @returns {Number} - The new stat after changes
*/
function PlatformDialogChangeValue(CurrentValue, Change, Bonus, Level) {
@ -5352,9 +5354,9 @@ function PlatformDialogChangeValue(CurrentValue, Change, Bonus, Level) {
/**
* Pick a specific idle pose if the character allows it
* @param {Object} Character - The character to evaluate
* @param {Number} Love - The love value that changed
* @param {Number} Domination - The domination value that changed
* @param {Platform.DialogCharacter} Character - The character to evaluate
* @param {number} Love - The love value that changed
* @param {number} Domination - The domination value that changed
* @returns {Object} - A unused object
*/
function PlatformDialogSetIdlePose(Character, Love, Domination) {
@ -5380,7 +5382,7 @@ function PlatformDialogSetIdlePose(Character, Love, Domination) {
/**
* Pick an answer in a specific dialog
* @param {Number} Position - The position of the answer picked
* @param {number} Position - The position of the answer picked
* @returns {void} - Nothing
*/
function PlatformDialogPickAnswer(Position) {
@ -5408,8 +5410,8 @@ function PlatformDialogPickAnswer(Position) {
/**
* Alters a property (love or domination) for a specific character
* @param {String} CharacterName - The name of the character to alter
* @param {String} Property - The name of the property to alter
* @param {string} CharacterName - The name of the character to alter
* @param {"Love" | "Domination"} Property - The name of the property to alter
* @param {Number} Value - The value to change
* @returns {void} - Nothing
*/
@ -5485,8 +5487,8 @@ function PlatformDialogClick() {
/**
* Returns a dialog character
* @param {String} Name - The name of a character
* @returns {Object} - The character object
* @param {string} Name - The name of a character
* @returns {Platform.DialogCharacter} - The character object
*/
function PlatformDialogGetCharacter(Name) {
if (PlatformDialogCharacter != null)
@ -5519,7 +5521,7 @@ function PlatformDialogController(buttons) {
/**
* Returns TRUE if the party leader (Melody) has a specific social perk
* @param {String} PerkName - The name of the perk
* @param {Platform.PerkName} PerkName - The name of the perk
* @returns {boolean} - TRUE if the perk is active
*/
function PlatformDialogLeaderHasPerk(PerkName) {
@ -5571,7 +5573,7 @@ function PlatformDialogEvent() {
/**
* Returns TRUE if the character is Melody's lover, make sure that character or Melody is currently active
* @param {String} Name - The name of a character
* @param {string} Name - The name of a character
* @returns {boolean} - TRUE if lover
*/
function PlatformDialogIsLover(Name) {
@ -5581,8 +5583,8 @@ function PlatformDialogIsLover(Name) {
/**
* Returns TRUE if two characters are lovers
* @param {String} Char1 - The name of the first character
* @param {String} Char2 - The name of the second character
* @param {string} Char1 - The name of the first character
* @param {string} Char2 - The name of the second character
* @returns {boolean} - TRUE if lover
*/
function PlatformDialogCharactersAreLovers(Char1, Char2) {
@ -5594,7 +5596,7 @@ function PlatformDialogCharactersAreLovers(Char1, Char2) {
/**
* Returns TRUE if the character is Melody's slave, make sure that character or Melody is currently active
* @param {String} Name - The name of a character
* @param {string} Name - The name of a character
* @returns {boolean} - TRUE if lover
*/
function PlatformDialogIsSlave(Name) {
@ -5604,8 +5606,8 @@ function PlatformDialogIsSlave(Name) {
/**
* Returns TRUE if the first character is the slave of the second character
* @param {String} Char1 - The name of the first character
* @param {String} Char2 - The name of the second character
* @param {string} Char1 - The name of the first character
* @param {string} Char2 - The name of the second character
* @returns {boolean} - TRUE if slave
*/
function PlatformDialogIsSlaveOfCharacter(Char1, Char2) {
@ -5617,7 +5619,7 @@ function PlatformDialogIsSlaveOfCharacter(Char1, Char2) {
/**
* Returns TRUE if the character is Melody's owner, make sure that character or Melody is currently active
* @param {String} Name - The name of a character
* @param {string} Name - The name of a character
* @returns {boolean} - TRUE if lover
*/
function PlatformDialogIsOwner(Name) {
@ -5627,7 +5629,7 @@ function PlatformDialogIsOwner(Name) {
/**
* Returns TRUE if the character doesn't have any lover
* @param {String} Name - The name of a character
* @param {string} Name - The name of a character
* @returns {boolean} - TRUE if no lover
*/
function PlatformDialogCharacterIsSingle(Name) {
@ -5638,8 +5640,8 @@ function PlatformDialogCharacterIsSingle(Name) {
/**
* Returns 0 if the source character and target character are not in a relationship, 1 if lover or slave, 2 if lover and slave
* @param {String} SourceName - The source character name to evaluate
* @param {String} TargetName - The target character name to evaluate
* @param {string} SourceName - The source character name to evaluate
* @param {string} TargetName - The target character name to evaluate
* @returns {number} - TRUE if lover
*/
function PlatformDialogLoverAndSlaveFactor(SourceName, TargetName) {

View file

@ -36,8 +36,8 @@ function PlatformProfileLoad() {
* @param {number} X - The X position on screen
* @param {number} Y - The Y position on screen
* @param {number} PerkNum - The perk number for the current character
* @param {Object} Prerequisite1 - If there's a first prerequisite to validate
* @param {Object} Prerequisite2 - If there's a second prerequisite to validate
* @param {Platform.PerkName} [Prerequisite1] - If there's a first prerequisite to validate
* @param {Platform.PerkName} [Prerequisite2] - If there's a second prerequisite to validate
* @returns {void} - Nothing
*/
function PlatformProfileDrawPerkButton(X, Y, PerkNum, Prerequisite1, Prerequisite2) {
@ -50,7 +50,7 @@ function PlatformProfileDrawPerkButton(X, Y, PerkNum, Prerequisite1, Prerequisit
/**
* Returns the text associated to the bonus given by the owner of the current player
* @param {Object} PlatformChar - The platform character to evaluate
* @param {Platform.Character} PlatformChar - The platform character to evaluate
* @returns {string} - The text string linked to the bonus
*/
function PlatformGetOwnerBonus(PlatformChar) {
@ -61,7 +61,7 @@ function PlatformGetOwnerBonus(PlatformChar) {
/**
* Returns the text associated to the bonus given by the lover of the current player
* @param {Object} PlatformChar - The platform character to evaluate
* @param {Platform.Character} PlatformChar - The platform character to evaluate
* @returns {string} - The text string linked to the bonus
*/
function PlatformGetLoverBonus(PlatformChar) {
@ -245,7 +245,7 @@ function PlatformProfileClick() {
/**
* When the screens exits, we unload the listeners
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function PlatformProfileExit() {
PlatformPartySave();

View file

@ -2050,7 +2050,7 @@ function PrivateGetBed(Type) {
/**
* When the player exits the private room
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function PrivateExit() {
if (!CurrentCharacter) {

View file

@ -195,7 +195,7 @@ function PrivateBedRun() {
* Starts an arousal action on a character.
* @param {Character} Source - The source character.
* @param {Character} Target - The target character.
* @param {AssetGroup} Group - The zone / group to target.
* @param {AssetItemGroup} Group - The zone / group to target.
* @param {ActivityName} Activity - The activity to do.
* @returns {boolean} - TRUE if the activity could start.
*/
@ -305,6 +305,7 @@ function PrivateBedNPCActivity(Source) {
let ActivityPenis = AssetGetActivity(Target.AssetFamily, Activity.Name + "Penis");
// Selects a random location on the body from available locations
/** @type {AssetGroup[]} */
let GroupList = [];
for (let G of AssetGroup) {
if (PrivateBedGroupActivityIsValid(Source, Target, G, Activity))
@ -316,6 +317,7 @@ function PrivateBedNPCActivity(Source) {
}
if (GroupList.length == 0) return;
let Group = CommonRandomItemFromList(null, GroupList);
if (!Group.IsItem()) return;
// Launches the activity
if (!PrivateBedActivityStart(Source, Target, Group, Activity.Name)) {
@ -381,7 +383,7 @@ function PrivateBedClick() {
/**
* When the player exits the private bedroom. The next sex scene will wait from 5 to 20 depending on luck and the frigid/honry trait.
* @type {ScreenFunctions["Exit"]}
* @type {ScreenExitHandler}
*/
function PrivateBedExit() {

View file

@ -335,7 +335,7 @@ var Shop2 = {
* @param {number} w
* @param {number} h
* @param {number} assetIndex - The assets index within {@link Shop2Vars.CurrentAssets}
* @satisfies {ShopScreenFunctions["Draw"]}
* @satisfies {ScreenDrawHandler}
* @private
*/
_AssetElementDraw: function _AssetElementDraw(x, y, w, h, assetIndex) {
@ -408,7 +408,7 @@ var Shop2 = {
* Click function for a single item in the shop
* @param {MouseEvent | TouchEvent} event
* @param {number} assetIndex - The assets index within {@link Shop2Vars.CurrentAssets}
* @satisfies {ShopScreenFunctions["Click"]}
* @satisfies {MouseEventListener}
* @private
*/
_AssetElementClick: function _AssetElementClick(event, assetIndex) {
@ -1066,10 +1066,7 @@ var Shop2 = {
Shop2InitVars.Preview,
Shop2Vars.EquippedItem.Asset.Group.Name,
Shop2Vars.EquippedItem.Asset,
Shop2Vars.EquippedItem.Color,
undefined,
undefined,
false,
Shop2Vars.EquippedItem.Color
);
if (item) {
item.Property = Shop2Vars.EquippedItem.Property;
@ -1189,7 +1186,7 @@ var Shop2 = {
}
};
/** @type {ScreenFunctions["Load"]} */
/** @type {ScreenLoadHandler} */
function Shop2Load() {
if (Shop2InitVars.Items.length === 0) {
Shop2InitVars.Items = Shop2.ParseAssets(
@ -1221,7 +1218,7 @@ function Shop2Load() {
});
}
/** @type {ScreenFunctions["Click"]} */
/** @type {MouseEventListener} */
function Shop2Click(event) {
Object.values(Shop2.Elements).some((e) => {
if (e.Click && e.Mode.has(Shop2Vars.Mode) && MouseIn(...e.Coords)) {
@ -1233,7 +1230,7 @@ function Shop2Click(event) {
});
}
/** @type {ScreenFunctions["Draw"]} */
/** @type {ScreenDrawHandler} */
function Shop2Draw() {
Object.values(Shop2.Elements).forEach((e) => {
if (e.Mode.has(Shop2Vars.Mode)) {
@ -1242,10 +1239,10 @@ function Shop2Draw() {
});
}
/** @type {ScreenFunctions["Run"]} */
/** @type {ScreenRunHandler} */
function Shop2Run() {}
/** @type {ScreenFunctions["Resize"]} */
/** @type {ScreenResizeHandler} */
function Shop2Resize(load) {
Object.values(Shop2.Elements).forEach((e) => {
if (e.Mode.has(Shop2Vars.Mode)) {
@ -1254,7 +1251,7 @@ function Shop2Resize(load) {
});
}
/** @type {ScreenFunctions["Unload"]} */
/** @type {ScreenUnloadHandler} */
function Shop2Unload() {
Object.values(Shop2.Elements).forEach((e) => {
if (e.Mode.has(Shop2Vars.Mode)) {
@ -1263,7 +1260,7 @@ function Shop2Unload() {
});
}
/** @type {ScreenFunctions["MouseWheel"]} */
/** @type {MouseWheelEventListener} */
function Shop2MouseWheel(event) {
Object.values(Shop2.Elements).forEach((e) => {
if (e.Mode.has(Shop2Vars.Mode) && MouseIn(...e.Coords)) {
@ -1272,7 +1269,7 @@ function Shop2MouseWheel(event) {
});
}
/** @type {ScreenFunctions["Exit"]} */
/** @type {ScreenExitHandler} */
function Shop2Exit() {
const oldMode = Shop2Vars.Mode;
Object.values(Shop2.Elements).forEach((e) => {

View file

@ -82,13 +82,13 @@ function ActivityDictionaryText(KeyWord) {
/**
* Resolve a group name to the correct group for activities
* @param {IAssetFamily} family - The asset family for the named group
* @param {AssetGroupName} groupname - The name of the group to resolve
* @returns {AssetGroup | null} - The resolved group
* @param {AssetGroupItemName} groupname - The name of the group to resolve
* @returns {AssetItemGroup | null} - The resolved group
*/
function ActivityGetGroupOrMirror(family, groupname) {
const group = AssetGroupGet(family, groupname);
const group = /** @type {AssetItemGroup} */ (AssetGroupGet(family, groupname));
if (group && group.MirrorActivitiesFrom != null) {
const mirror = AssetGroupGet(family, group.MirrorActivitiesFrom);
const mirror = /** @type {AssetItemGroup} */ (AssetGroupGet(family, group.MirrorActivitiesFrom));
if (mirror) {
return mirror;
}
@ -110,7 +110,7 @@ function ActivityGetAllMirrorGroups(family, groupName) {
/**
* Check if any activities are possible for a character's given group.
* @param {Character} C - The character on which the check is done
* @param {AssetGroupName} GroupName - The group to check access on
* @param {AssetGroupItemName} GroupName - The group to check access on
* @returns {boolean} Whether any activity is possible
*/
function ActivityPossibleOnGroup(C, GroupName) {
@ -301,7 +301,7 @@ function ActivityGenerateItemActivitiesFromNeed(allowed, acting, acted, needsIte
/**
* Builds the allowed activities on a group given the character's settings.
* @param {Character} character - The character for which to build the activity dialog options
* @param {AssetGroupName} groupname - The group to check
* @param {AssetGroupItemName} groupname - The group to check
* @return {ItemActivity[]} - The list of allowed activities
*/
function ActivityAllowedForGroup(character, groupname) {
@ -367,7 +367,7 @@ function ActivityAllowedForGroup(character, groupname) {
* Returns TRUE if an activity can be done
* @param {Character} C - The character to evaluate
* @param {ActivityName} Activity - The name of the activity
* @param {AssetGroupName} Group - The name of the group
* @param {AssetGroupItemName} Group - The name of the group
* @return {boolean} - TRUE if the activity can be done
*/
function ActivityCanBeDone(C, Activity, Group) {
@ -383,7 +383,7 @@ function ActivityCanBeDone(C, Activity, Group) {
* @param {Character} S - The character performing the activity
* @param {Character} C - The character on which the activity is performed
* @param {ActivityName | Activity} A - The activity performed
* @param {AssetGroupName} Z - The group/zone name where the activity was performed
* @param {AssetGroupItemName} Z - The group/zone name where the activity was performed
* @param {number} [Count=1] - If the activity is done repeatedly, this defines the number of times, the activity is done.
* If you don't want an activity to modify arousal, set this parameter to '0'
* @param {Asset} [Asset] - The asset used to perform the activity
@ -418,7 +418,7 @@ function ActivityEffect(S, C, A, Z, Count, Asset) {
* @param {Character} S - The character performing the activity
* @param {Character} C - The character on which the activity is performed
* @param {number} Amount - The base amount of arousal to add
* @param {AssetGroupName} Z - The group/zone name where the activity was performed
* @param {AssetGroupItemName} Z - The group/zone name where the activity was performed
* @param {number} [Count=1] - If the activity is done repeatedly, this defines the number of times, the activity is done.
* If you don't want an activity to modify arousal, set this parameter to '0'
* @param {Asset} [Asset] - The asset used to perform the activity
@ -471,7 +471,7 @@ function ActivitySetArousal(C, Progress) {
* Sets an activity progress on a timer, activities are capped at MaxProgress
* @param {Character} C - The character for which to set the timer for
* @param {null | Activity} Activity - The activity for which the timer is for
* @param {AssetGroupName | "ActivityOnOther"} Zone - The target zone of the activity
* @param {AssetGroupItemName | "ActivityOnOther"} Zone - The target zone of the activity
* @param {number} Progress - Progress to set
* @param {Asset} [Asset] - The asset used to perform the activity
* @return {void} - Nothing
@ -859,7 +859,7 @@ function ActivityBuildChatTag(character, group, activity, is_label = false) {
* Launches a sexual activity for a character and sends the chatroom message if applicable.
* @param {Character} actor - Character which is performing the activity
* @param {Character} acted - Character on which the activity was triggered
* @param {AssetGroup} targetGroup - The group targetted by the activity
* @param {AssetItemGroup} targetGroup - The group targetted by the activity
* @param {ItemActivity} ItemActivity - The activity performed, with its optional item used
* @param {boolean} sendMessage - Whether to send a message to the chat or not
*/
@ -921,7 +921,7 @@ function ActivityArousalItem(Source, Target, Asset) {
var Activity = AssetGetActivity(Target.AssetFamily, AssetActivity);
if (Source.IsPlayer() && !Target.IsPlayer()) ActivityRunSelf(Source, Target, Activity, Asset.Group);
if (PreferenceArousalAtLeast(Target, "Hybrid") && (Target.IsPlayer() || Target.IsNpc()))
ActivityEffect(Source, Target, AssetActivity, Asset.Group.Name);
ActivityEffect(Source, Target, AssetActivity, /** @type {AssetGroupItemName} */ (Asset.Group.Name));
}
}

View file

@ -985,10 +985,10 @@ function AssetAllActivities(family) {
* Gets an activity asset by family and name
* @param {IAssetFamily} family - The family to search in
* @param {string} name - Name of activity to search for
* @returns {Activity|undefined}
* @returns {Activity|null}
*/
function AssetGetActivity(family, name) {
return AssetAllActivities(family).find(a => (a.Name === name));
return AssetAllActivities(family).find(a => (a.Name === name)) ?? null;
}
/**
@ -1032,7 +1032,7 @@ function AssetActivitiesForGroup(family, groupname, onSelf = "other") {
* @returns {AssetGroup|null} - The asset group matching the provided family and group name
*/
function AssetGroupGet(Family, Group) {
return AssetGroupMap.get(Group) || null;
return AssetGroupMap.get(Group) ?? null;
}
/**

View file

@ -2037,12 +2037,14 @@ function CharacterIsEdged(C) {
// Get all zones that allow an orgasm
/** @type {AssetGroupItemName[]} */
let OrgasmZones = [];
for (let Group of AssetGroup)
for (let Group of AssetGroup) {
if (!Group.IsItem()) continue;
if (Group.ArousalZoneID != null) {
let Zone = PreferenceGetArousalZone(C, Group.Name);
if (Zone.Orgasm && (Zone.Factor > 0))
OrgasmZones.push(Zone.Name);
}
}
// Get every vibrating item acting on an orgasm zone
const VibratingItems = C.Appearance

View file

@ -257,7 +257,7 @@ function CommonGetRetry(Path, Callback, RetriesLeft) {
}
}
/** @type {ScreenFunctions["MouseDown"]} */
/** @type {MouseEventListener} */
function CommonMouseDown(event) {
if (CurrentCharacter == null) {
if (CurrentScreenFunctions.MouseDown) {
@ -268,7 +268,7 @@ function CommonMouseDown(event) {
}
}
/** @type {ScreenFunctions["MouseUp"]} */
/** @type {MouseEventListener} */
function CommonMouseUp(event) {
if (CurrentScreenFunctions.MouseUp)
{
@ -276,7 +276,7 @@ function CommonMouseUp(event) {
}
}
/** @type {ScreenFunctions["MouseMove"]} */
/** @type {MouseEventListener} */
function CommonMouseMove(event) {
if (CurrentScreenFunctions.MouseMove)
{
@ -284,7 +284,7 @@ function CommonMouseMove(event) {
}
}
/** @type {ScreenFunctions["MouseWheel"]} */
/** @type {MouseWheelEventListener} */
function CommonMouseWheel(event) {
if (CurrentScreenFunctions.MouseWheel) {
CurrentScreenFunctions.MouseWheel(event);
@ -293,7 +293,7 @@ function CommonMouseWheel(event) {
/**
* Catches the clicks on the main screen and forwards it to the current screen click function if it exists, otherwise it sends it to the dialog click function
* @type {ScreenFunctions["Click"]}
* @type {MouseEventListener}
*/
function CommonClick(event) {
ServerClickBeep();
@ -329,7 +329,7 @@ function CommonTouchActive(X, Y, W, H, TL) {
/**
* Catches key presses on the main screen and forwards it to the current screen key down function if it exists, otherwise it sends it to the dialog key down function
* @deprecated Use GameKeyDown instead
* @type {ScreenFunctions["KeyDown"]}
* @type {KeyboardEventListener}
*/
function CommonKeyDown(event) { return false; }

View file

@ -2209,7 +2209,7 @@ function DialogChangeFocusToGroup(C, Group) {
/**
* Handles the click in the dialog screen
* @type {ScreenFunctions["Click"]}
* @type {MouseEventListener}
*/
function DialogClick(event) {
// Check that there's actually a character selected
@ -2287,7 +2287,7 @@ function DialogClick(event) {
DialogMenuMapping[DialogMenuMode]?.Click(event);
}
/** @type {ScreenFunctions["Resize"]} */
/** @type {ScreenResizeHandler} */
function DialogResize(load) {
switch (DialogMenuMode) {
case "layering":
@ -2848,7 +2848,7 @@ class DialogMenu {
/**
* Initialize the {@link DialogMenu} subscreen.
*
* Serves as a {@link ScreenFunctions["Load"]} wrapper with added parameters.
* Serves as a {@link ScreenLoadHandler} wrapper with added parameters.
* @param {PropType} properties The to be initialized character and any other properties
* @param {null | { shape?: RectTuple }} style Misc styling for the subscreen
* @returns {null | HTMLDivElement} The div containing the dialog subscreen root element or `null` if the screen failed to initialize
@ -2861,7 +2861,7 @@ class DialogMenu {
return /** @type {null | HTMLDivElement} */(document.getElementById(this.ids.root));
}
/** @type {ScreenFunctions["Load"]} */
/** @type {ScreenLoadHandler} */
Load() {
if (this._initPropertyNames.some(p => this._initProperties?.[p] == null)) {
console.error(
@ -2909,23 +2909,23 @@ class DialogMenu {
throw new Error("Trying to call an abstract method");
}
/** @type {ScreenFunctions["Unload"]} */
/** @type {ScreenUnloadHandler} */
Unload() {
DialogStatusClear();
document.getElementById(this.ids.root)?.toggleAttribute("data-unload", true);
this._reloadQueue.clear();
}
/** @type {ScreenFunctions["Click"]} */
/** @type {MouseEventListener} */
Click(event) {}
/** @type {ScreenFunctions["Draw"]} */
/** @type {ScreenDrawHandler} */
Draw() {}
/** @type {ScreenFunctions["Run"]} */
/** @type {ScreenRunHandler} */
Run(time) {}
/** @type {ScreenFunctions["Resize"]} */
/** @type {ScreenResizeHandler} */
Resize(load) {
const shape = this.shape;
if (!shape) {
@ -2938,7 +2938,7 @@ class DialogMenu {
}
}
/** @type {ScreenFunctions["Exit"]} */
/** @type {ScreenExitHandler} */
Exit() {
ElementRemove(this.ids.root);
this._initProperties = null;
@ -2946,7 +2946,7 @@ class DialogMenu {
this._reloadQueue.clear();
}
/** @type {ScreenFunctions["KeyDown"]} */
/** @type {KeyboardEventListener} */
KeyDown(event) {
const grid = this.ids.grid ? document.getElementById(this.ids.grid) : null;
if (grid) {
@ -5459,7 +5459,7 @@ var DialogFocusGroup = {
/**
* Load function for starting the Dialog subscreen.
* @type {ScreenFunctions["Load"]}
* @type {ScreenLoadHandler}
*/
function DialogLoad() {
if (CurrentCharacter == null) {
@ -5482,7 +5482,7 @@ function DialogLoad() {
* This is the main handler for drawing the Dialog UI, which activates
* when the player clicks on herself or another player.
*
* @type {ScreenFunctions["Draw"]}
* @type {ScreenDrawHandler}
*/
function DialogDraw() {

View file

@ -257,7 +257,7 @@ function GameMouseUp(event) {
/**
* If the user rolls the mouse wheel, we fire the mousewheel event for other screens
* @type {ScreenFunctions["MouseWheel"]}
* @type {MouseWheelEventListener}
*/
function GameMouseWheel(event) {
if (CommonIsMobile) { return; }

View file

@ -61,14 +61,19 @@ function ImportBondageCollege(C) {
if ((localStorage.getItem("BondageCollegeExportLockedVibratingEgg") != null) && (localStorage.getItem("BondageCollegeExportLockedVibratingEgg") == "true")) DialogWearItem("VibratingEgg", "ItemVulva");
// Imports skills
if (localStorage.getItem("BondageCollegeExportArts") != null) SkillChange(C, "LockPicking", +(localStorage.getItem("BondageCollegeExportArts")), 0, true);
if (localStorage.getItem("BondageCollegeExportFighting") != null) SkillChange(C, "Willpower", +(localStorage.getItem("BondageCollegeExportFighting")), 0, true);
if (localStorage.getItem("BondageCollegeExportRopeMastery") != null) {
SkillChange(C, "Bondage", +(localStorage.getItem("BondageCollegeExportRopeMastery")), 0, true);
SkillChange(C, "SelfBondage", +(localStorage.getItem("BondageCollegeExportRopeMastery")), 0, true);
const arts = +(localStorage.getItem("BondageCollegeExportArts") ?? 0);
const fight = +(localStorage.getItem("BondageCollegeExportFighting") ?? 0);
const rope = +(localStorage.getItem("BondageCollegeExportRopeMastery") ?? 0);
const seduction = +(localStorage.getItem("BondageCollegeExportSeduction") ?? 0);
const sports = +(localStorage.getItem("BondageCollegeExportSports") ?? 0);
if (arts) SkillChange(C, "LockPicking", arts, 0, true);
if (fight) SkillChange(C, "Willpower", fight, 0, true);
if (rope) {
SkillChange(C, "Bondage", rope, 0, true);
SkillChange(C, "SelfBondage", rope, 0, true);
}
if (localStorage.getItem("BondageCollegeExportSeduction") != null) SkillChange(C, "Infiltration", +(localStorage.getItem("BondageCollegeExportSeduction")), 0, true);
if (localStorage.getItem("BondageCollegeExportSports") != null) SkillChange(C, "Evasion", +(localStorage.getItem("BondageCollegeExportSports")), 0, true);
if (seduction) SkillChange(C, "Infiltration", seduction, 0, true);
if (sports) SkillChange(C, "Evasion", sports, 0, true);
// Sync with the server
ServerPlayerSync();

View file

@ -812,7 +812,7 @@ function InventoryWear(C, AssetName, AssetGroup, ItemColor, Difficulty, MemberNu
const A = AssetGet(C.AssetFamily, AssetGroup, AssetName);
if (!A) return null;
const color = (ItemColor == null || ItemColor == "Default") ? [...A.DefaultColor] : ItemColor;
const item = CharacterAppearanceSetItem(C, AssetGroup, A, color, Difficulty, MemberNumber, false);
const item = CharacterAppearanceSetItem(C, AssetGroup, A, color, Difficulty, MemberNumber);
/**
* TODO: grant tighter control over setting expressions.
@ -911,7 +911,7 @@ function InventoryWearRandom(C, GroupName, Difficulty, Refresh = true, MustOwn =
// Pick a random color for clothes from their schema
const SelectedColor = IsClothes ? SelectedAsset.Group.ColorSchema[Math.floor(Math.random() * SelectedAsset.Group.ColorSchema.length)] : null;
let item = CharacterAppearanceSetItem(C, GroupName, SelectedAsset, SelectedColor, Difficulty, null, false);
let item = CharacterAppearanceSetItem(C, GroupName, SelectedAsset, SelectedColor, Difficulty);
if (Extend) {
item = InventoryRandomExtend(C, GroupName);
}

View file

@ -407,7 +407,7 @@ var Layering = {
return /** @type {HTMLDivElement} */(document.getElementById(this.ID.root));
},
/** @type {ScreenFunctions["Load"]} */
/** @type {ScreenLoadHandler} */
Load() {
let elem = document.getElementById(this.ID.root);
if (elem != null) {
@ -569,7 +569,7 @@ var Layering = {
/**
* Can be also be used, alternatively, as a {@link ScreenFunctions.Draw} function
* @type {ScreenFunctions["Resize"]}
* @type {ScreenResizeHandler}
*/
Resize(load) {
ElementPositionFix(this.ID.root, 0, this.Display.x, this.Display.y, this.Display.w, this.Display.h);
@ -577,7 +577,7 @@ var Layering = {
elem.style.display = "";
},
/** @type {ScreenFunctions["Unload"]} */
/** @type {ScreenUnloadHandler} */
Unload() {
// Need the null check here due to `CommonSetScreen` calling `Unload` after `Exit`
const elem = document.getElementById(this.ID.root);
@ -587,7 +587,7 @@ var Layering = {
},
/**
* @satisfies {ScreenFunctions["Exit"]}
* @satisfies {ScreenExitHandler}
* @param {boolean} reload - Whether the exit call is part of a reload (see {@link Layering.Init})
*/
Exit(reload=false) {

View file

@ -192,20 +192,18 @@ function NPCArousal(C) {
// Go through all zones
for (let Group of AssetGroup)
if (Group.ArousalZoneID != null) {
if (Group.IsItem() && Group.ArousalZoneID != null) {
// Gets the current factors for that charact
let Zone = PreferenceGetArousalZone(C, Group.Name);
// Orgasm zone is always maxed, if not it's randomized
if (Zone.Name == Orgasm) {
PreferenceSetZoneFactor(C, Zone.Name, 4);
PreferenceSetZoneOrgasm(C, Zone.Name, true);
PreferenceSetArousalZone(C, Zone.Name, 4, true);
} else {
let Factor = /** @type {ArousalFactor} */(Math.round((Math.random() * 4) + 0.5));
PreferenceSetZoneFactor(C, Zone.Name, Factor);
let CanOrgasm = ((Factor > 0) && (OrgasmZones.indexOf(Zone.Name) >= 0) && (Math.random() > 0.5));
PreferenceSetZoneOrgasm(C, Zone.Name, CanOrgasm);
PreferenceSetArousalZone(C, Zone.Name, Factor, CanOrgasm);
}
}

285
BondageClub/Scripts/Platform.d.ts vendored Normal file
View file

@ -0,0 +1,285 @@
namespace Platform {
type Perk = string;
type CharacterStatus = "Maid" | "Oracle" | "Archer" | "Thief"
type SoundCategory = string;
type SoundEffect = string;
type EffectType = "Teleport" | "Heal";
type AnimationName = "Petrified" | "Bound" | "HalfBoundWounded" | "Wounded" | "Aim" | "AimReady" | "AimFull" | "Bind" | "Jump" | "HalfBoundJump" | "Crawl" | "Stun" | "HalfBoundStun" | "Walk" | "HalfBoundWalk" | "WalkHit" | "Run" | "HalfBoundRun" | "Crouch" | "Block" | "Idle" | "HalfBoundIdle"
| "Uppercut" | "StandAttackFast" | "StandAttackSlow" | "CrouchAttackFast" | "CrouchAttackSlow" | "Scream" | "Backflip" | "FireProjectile" | "Left" | "Right";
type ItemName = "RedRose" | "PoisonApple" | "LeatherWhip" | "AnimalCollar";
interface AnimationSpec {
Name: AnimationName;
Cycle?: number[];
Speed?: number;
Audio?: SoundEffect[];
CycleLeft?: number[];
OffsetX?: number;
OffsetY?: number;
Width?: number;
Height?: number;
Image?: number;
Mirror?: boolean;
}
interface DummyTemplate {
Name: string;
Status: string;
Width: number;
Height: number;
Animation: AnimationSpec[];
OnBind?: () => void;
Loot?: { Name: ItemName, Expire: number; }[];
}
interface CharacterTemplate {
Name: string;
Status: string;
Width: number;
Height: number;
HitBox: RectTuple;
JumpHitBox?: RectTuple;
Health: number;
HealthPerLevel?: number;
ExperienceValue?: number;
Magic?: number;
MagicPerLevel?: number;
Projectile?: number;
ProjectileName?: ProjectileName;
ProjectileType?: ProjectileType;
ProjectileTime?: number;
ProjectileDamage?: number[];
ProjectileBothSides?: boolean;
Perk?: string;
PerkName?: PerkName[];
WalkSpeed: number;
RunSpeed?: number;
CrawlSpeed: number;
JumpForce?: number;
CollisionDamage?: number;
JumpOdds?: number;
LootOdds?: number;
RunOdds?: number;
ProjectileOdds?: number;
StandAttackSlowOdds?: number;
DamageBackOdds?: number;
DamageFaceOdds?: number;
DamageKnockForce?: number;
IdleAudio?: SoundEffect[];
DamageAudio?: SoundEffect[];
DownAudio?: SoundEffect[];
BindAudio?: SoundEffect[];
ProjectileHitAudio?: SoundEffect[];
RunHeight?: number;
FlyingHeight?: number;
IdleTurnToFace?: boolean;
PetrifyOnWound?: boolean;
Animation: AnimationSpec[];
Attack?: {
Name: string;
HitBox?: RectTuple;
HitAnimation?: number[];
Magic?: number;
Cooldown?: number;
StartAudio?: string[];
HitAudio?: string[];
Damage?: number[];
Speed: number;
JumpForce?: number;
}[];
OnBind?: () => void;
Loot?: { Name: ItemName, Expire: number; }[];
}
type ProjectileName = "Barrel" | "Arrow" | "Dagger";
type ProjectileType = "Wood" | "Iron";
interface Projectile {
IsProjectile: true;
Gravity: number;
ProjectileForce: number;
HitAudio: SoundEffect[];
}
interface Character extends CharacterTemplate, Projectile {
ID: number;
Camera: boolean;
X: number;
Y: number;
ForceX: number;
ForceY: number;
Experience: number;
Level: number;
BaseHealth: number;
BaseMagic?: number;
BaseProjectile?: number;
BaseProjectileTime?: number;
BaseWalkSpeed: number;
BaseRunSpeed: number;
MaxHealth: number;
MaxMagic?: number;
MaxProjectile?: number;
Fix: boolean;
Combat: boolean;
Dialog: string;
Run: boolean;
NextJump: number;
DamageBackOdds: number;
DamageFaceOdds: number;
FaceLeft: boolean;
HalfBound: boolean;
Bound: boolean;
Petrified: boolean;
OnBind?: () => void;
Immunity: number;
Damage: { Value: number, Expire: number }[];
RiseTime: number;
ProjectileAim: number;
Action: { Name: AnimationName, Start: number, Expire: number, Target?: number, Done?: boolean };
Effect?: { Name: EffectType, End: number };
Anim?: AnimationSpec;
}
interface PartyMember {
Character: string;
Status: Platform.CharacterStatus;
Level: number;
Experience: number;
Perk: string;
}
interface DialogCharacter {
Name: string;
Color: HexColor;
NickName?: string;
Love?: number;
LoverName?: string;
LoverLevel?: number;
OwnerName?: string;
OwnerLevel?: number;
Domination?: number;
IdlePose?: CharacterStatus[];
}
type RoomType = "Up" | "Down" | "Left" | "Right";
interface Room {
Name: string;
Text?: string;
Background: string;
AlternateBackground?: string;
BackgroundFilter?: string;
Music: string;
Width?: number;
Height?: number;
LimitLeft?: number | null;
LimitRight?: number | null;
Heal?: number;
Door?: {
Name: string;
FromX: number;
FromY: number;
FromW: number;
FromH: number;
FromType: RoomType;
ToX: number;
ToFaceLeft?: boolean;
}[];
Character?: {
Name: string;
Status: string;
X: number;
Fix?: boolean;
Combat?: boolean;
Battle?: boolean;
Dialog?: string;
}[];
Entry?: () => void;
}
interface Item {
Name: ItemName;
DisplayName: string;
Description: string;
OnGive: (Char: Platform.DialogCharacter) => void;
}
type EventType =
| "ThiefBossIntro" | "ThiefBossDefeat" | "ThiefBossSecondDefeat" | "ThiefBossRetreat"
| "EdlaranKey" | "EdlaranForestKey" | "EdlaranIntro" | "EdlaranCurseIntro" | "EdlaranUnlock" | "EdlaranBedroomIsabella"
| "EdlaranCountessBedroomOrgasmDom" | "EdlaranCountessBedroomOrgasmSub" | "EdlaranWineCellar" | "EdlaranJoin"
| "EdlaranForestIntro"
| "IntroForestBandit" | "ForestBanditCrate"
| "ForestCapture" | "ForestCaptureEnd" | "ForestCaptureRescueMelody"
| "BarnThiefRescueMelody"
| "CamilleEscape" | "CamilleDefeat"
| "JealousMaid" | "CursedMaid"
| "OliviaCollarKey" | "OliviaUnchain" | "OliviaBath" | "OliviaBathOrgasm"
| "OliviaCurseIntro" | "OliviaCurseRelease"
| "OliviaTerrace" | "OliviaTerraceKiss"
| "OliviaCabin" | "EdlaranCabin" | "LynCabin"
| "OliviaTent" | "EdlaranTent" | "LynTent"
| "IntroGuard" | "IntroGuardCurse"
| "DesertEntrance"
| "Curse"
| "LynJoin"
;
interface Event {
// Doesn't appear to be used for anything, just saved and loaded
}
type PerkName = "Healthy" | "Robust" | "Vigorous" | "Spring" | "Impact" | "Block" | "Deflect" | "Seduction" | "Persuasion" | "Manipulation" | "Apprentice" | "Witch" | "Meditation" | "Scholar" | "Heal" | "Cure" | "Howl" | "Roar" | "Teleport" | "Freedom" | "Fletcher" | "Burglar" | "Athletic" | "Sprint" | "Backflip" | "Acrobat" | "Archery" | "Celerity" | "Capacity" | "Sneak" | "Backstab" | "Kidnapper" | "Rigger" | "Thief" | "Bounce" | "Inventory" | "Duplicate";
type KeyCode = string;
type CooldownType = AnimationName | EffectType;
interface Cooldown {
Type: CooldownType;
Time: number;
Delay: number;
}
}

View file

@ -584,7 +584,7 @@ function PortalLinkProcessMessage(sender, data) {
return;
}
const group = AssetGroupGet("Female3DCG", /** @type {AssetGroupName} */ (attrTarget.substring("PortalLinkTarget".length)));
if (!group) return;
if (!group || !group.IsItem()) return;
ActivityRun(sender, Player, group, { Activity: act, Item: tablet, Group: tablet.Asset.Group.Name }, false);
builder.focusGroup(group.Name);

View file

@ -72,8 +72,8 @@ function PreferenceSetActivityFactor(C, Type, Self, Factor) {
// Make sure the Activity data is valid
if ((typeof Factor !== "number") || (Factor < 0) || (Factor > 4)) return;
if ((C == null) || (C.ArousalSettings == null)) return;
if ((C.ArousalSettings.Activity == null) || (typeof C.ArousalSettings.Activity !== "string")) C.ArousalSettings.Activity = PreferenceArousalActivityDefaultCompressedString;
if (!CommonIsObject(C.ArousalSettings)) return;
if (!C.ArousalSettings || typeof C.ArousalSettings?.Activity !== "string") C.ArousalSettings.Activity = PreferenceArousalActivityDefaultCompressedString;
while ((C.ArousalSettings.Activity.length < PreferenceArousalActivityDefaultCompressedString.length))
C.ArousalSettings.Activity = C.ArousalSettings.Activity + PreferenceArousalTwoFactorToChar();
@ -106,7 +106,7 @@ function PreferenceSetActivityFactor(C, Type, Self, Factor) {
function PreferenceGetFetishFactor(C, Type) {
// No valid data means no fetish
if ((C == null) || (C.ArousalSettings == null) || (C.ArousalSettings.Fetish == null) || (typeof C.ArousalSettings.Fetish !== "string")) return 2;
if (typeof C.ArousalSettings?.Fetish !== "string") return 2;
// Finds the ID of the fetish specified
let ID = -1;
@ -128,7 +128,7 @@ function PreferenceGetFetishFactor(C, Type) {
* Sets the arousal factor of a fetish for a character
* @param {Character} C - The character to set
* @param {FetishName} Type - The name of the fetish
* @param {ArousalFactor} Type - New arousal factor for that fetish (0 is horrible, 2 is normal, 4 is great)
* @param {ArousalFactor} Factor - New arousal factor for that fetish (0 is horrible, 2 is normal, 4 is great)
* @returns {void} - Nothing
*/
function PreferenceSetFetishFactor(C, Type, Factor) {
@ -181,82 +181,94 @@ function PreferenceArousalTwoFactorToChar(Factor1 = 2, Factor2 = 2) {
* Gets the corresponding arousal zone definition from a player's preferences (if the group's activities are mirrored,
* returns the arousal zone definition for the mirrored group).
* @param {Character} C - The character for whom to get the arousal zone
* @param {AssetGroupName} ZoneName - The name of the zone to get
* @param {AssetGroupItemName} ZoneName - The name of the zone to get
* @returns {null | ArousalZone} - Returns the arousal zone preference object,
* or null if a corresponding zone definition could not be found.
*/
function PreferenceGetArousalZone(C, ZoneName) {
// Make sure the data is valid
if ((C == null) || (C.ArousalSettings == null) || (C.ArousalSettings.Zone == null) || (typeof C.ArousalSettings.Zone !== "string")) return null;
if (typeof C.ArousalSettings?.Zone !== "string") return null;
// Finds the asset group and make sure the string contains it
let Group = AssetGroupGet(C.AssetFamily, ZoneName);
if ((Group.ArousalZoneID == null) || (C.ArousalSettings.Zone.length <= Group.ArousalZoneID)) return null;
if (!Group?.ArousalZoneID || (C.ArousalSettings.Zone.length <= Group.ArousalZoneID)) return null;
let Value = C.ArousalSettings.Zone.charCodeAt(Group.ArousalZoneID) - 100;
const Value = C.ArousalSettings.Zone.charCodeAt(Group.ArousalZoneID) - 100;
let Factor = /** @type {ArousalFactor} */ (CommonClamp(Value, 0, 4));
// If the value was clamped, default it
if (Factor !== Value) Factor = 2;
return {
// @ts-ignore
Name: ZoneName,
// @ts-ignore
Factor: Value % 10,
Factor: Factor,
Orgasm: (Value >= 10)
};
}
/**
* Gets the love factor of a zone for the character
* @param {Character} C - The character for whom the love factor of a particular zone should be gotten
* @param {AssetGroupName} ZoneName - The name of the zone to get the love factor for
* @param {AssetGroupItemName} ZoneName - The name of the zone to get the love factor for
* @returns {ArousalFactor} - Returns the love factor of a zone for the character (0 is horrible, 2 is normal, 4 is great)
*/
function PreferenceGetZoneFactor(C, ZoneName) {
const Zone = PreferenceGetArousalZone(C, ZoneName);
/** @type {ArousalFactor} */
let Factor = 2;
if (!Zone) return 2;
return Zone.Factor;
}
/**
* Sets the arousal zone data for a specific body zone on the player
* @param {Character} C - The character, for whom the love factor of a particular zone should be set
* @param {AssetGroupItemName} ZoneName - The name of the zone, the factor should be set for
* @param {ArousalFactor} Factor - The factor of the zone (0 is horrible, 2 is normal, 4 is great)
* @param {boolean} CanOrgasm - Sets, if the character can cum from the given zone (true) or not (false)
* @returns {void} - Nothing
*/
function PreferenceSetArousalZone(C, ZoneName, Factor = null, CanOrgasm = null) {
// Nothing to do if data is invalid
if (typeof C.ArousalSettings?.Zone !== "string") return;
// Finds the asset group and make sure the string contains it
let Group = AssetGroupGet(C.AssetFamily, ZoneName);
if (!Group || !CommonIsNonNegativeInteger(Group.ArousalZoneID) || (C.ArousalSettings.Zone.length <= Group.ArousalZoneID)) return;
// Gets the zone object
let Zone = PreferenceGetArousalZone(C, ZoneName);
if (Zone) Factor = Zone.Factor;
if ((Factor == null) || (typeof Factor !== "number") || (Factor < 0) || (Factor > 4)) Factor = 2;
return Factor;
Zone ??= {
Name: ZoneName,
Factor: 2,
Orgasm: false
};
if (typeof Factor === "number") {
Zone.Factor = Factor;
}
if (typeof CanOrgasm === "boolean") {
Zone.Orgasm = CanOrgasm;
}
// Creates the new char and slides it in the compressed string
C.ArousalSettings.Zone = C.ArousalSettings.Zone.substring(0, Group.ArousalZoneID) + PreferenceArousalFactorToChar(Zone.Factor, Zone.Orgasm) + C.ArousalSettings.Zone.substring(Group.ArousalZoneID + 1);
}
/**
* Sets the love factor for a specific body zone on the player
* @param {Character} C - The character, for whom the love factor of a particular zone should be set
* @param {AssetGroupName} ZoneName - The name of the zone, the factor should be set for
* @param {AssetGroupItemName} ZoneName - The name of the zone, the factor should be set for
* @param {ArousalFactor} Factor - The factor of the zone (0 is horrible, 2 is normal, 4 is great)
* @deprecated Use {@link PreferenceSetArousalZone}
* @returns {void} - Nothing
*/
function PreferenceSetZoneFactor(C, ZoneName, Factor) {
// Nothing to do if data is invalid
if ((C == null) || (C.ArousalSettings == null) || (C.ArousalSettings.Zone == null) || (typeof C.ArousalSettings.Zone !== "string")) return;
// Finds the asset group and make sure the string contains it
let Group = AssetGroupGet(C.AssetFamily, ZoneName);
if ((Group.ArousalZoneID == null) || (C.ArousalSettings.Zone.length <= Group.ArousalZoneID)) return;
// Gets the zone object
let Zone = PreferenceGetArousalZone(C, ZoneName);
if (Zone == null) {
Zone = {
// @ts-ignore
Name: ZoneName,
Factor: 2,
Orgasm: false
};
}
Zone.Factor = Factor;
// Creates the new char and slides it in the compressed string
C.ArousalSettings.Zone = C.ArousalSettings.Zone.substring(0, Group.ArousalZoneID) + PreferenceArousalFactorToChar(Zone.Factor, Zone.Orgasm) + C.ArousalSettings.Zone.substring(Group.ArousalZoneID + 1);
PreferenceSetArousalZone(C, ZoneName, Factor);
}
/**
* Determines, if a player can reach on orgasm from a particular zone
* @param {Character} C - The character whose ability to orgasm we check
* @param {AssetGroupName} ZoneName - The name of the zone to check
* @param {AssetGroupItemName} ZoneName - The name of the zone to check
* @returns {boolean} - Returns true if the zone allows orgasms for a character, false otherwise
*/
function PreferenceGetZoneOrgasm(C, ZoneName) {
@ -269,32 +281,11 @@ function PreferenceGetZoneOrgasm(C, ZoneName) {
* @param {Character} C - The characterfor whom we set the ability to órgasm from a given zone
* @param {AssetGroupItemName} ZoneName - The name of the zone to set the ability to orgasm for
* @param {boolean} CanOrgasm - Sets, if the character can cum from the given zone (true) or not (false)
* @deprecated Use {@link PreferenceSetArousalZone}
* @returns {void} - Nothing
*/
function PreferenceSetZoneOrgasm(C, ZoneName, CanOrgasm) {
// Nothing to do if data is invalid
if ((C == null) || (C.ArousalSettings == null) || (C.ArousalSettings.Zone == null) || (typeof C.ArousalSettings.Zone !== "string")) return;
// Finds the asset group and make sure the string contains it
let Group = AssetGroupGet(C.AssetFamily, ZoneName);
if ((Group.ArousalZoneID == null) || (C.ArousalSettings.Zone.length <= Group.ArousalZoneID)) return;
// Gets the zone object
let Zone = PreferenceGetArousalZone(C, ZoneName);
if (Zone == null) {
Zone = {
// @ts-ignore
Name: ZoneName,
Factor: 2,
Orgasm: false
};
}
Zone.Orgasm = CanOrgasm;
// Creates the new char and slides it in the compressed string
C.ArousalSettings.Zone = C.ArousalSettings.Zone.substring(0, Group.ArousalZoneID) + PreferenceArousalFactorToChar(Zone.Factor, Zone.Orgasm) + C.ArousalSettings.Zone.substring(Group.ArousalZoneID + 1);
PreferenceSetArousalZone(C, ZoneName, null, CanOrgasm);
}
/**

View file

@ -262,6 +262,26 @@ type ChatRoomSpaceLabel = "MIXED" | "FEMALE_ONLY" | "MALE_ONLY" | "ASYLUM";
type ChatRoomVisibilityModeLabel = "PUBLIC" | "ADMIN_WHITELIST" | "ADMIN" | "UNLISTED";
type ChatRoomAccessModeLabel = "PUBLIC" | "ADMIN_WHITELIST" | "ADMIN";
type ChatRoomMenuButton =
| "Camera"
| "Cancel"
| "CharacterView"
| "Cut"
| "CustomizationOn"
| "CustomizationOff"
| "Dress"
| "Exit"
| "GameOption"
| "Icons"
| "Kneel"
| "MapView"
| "NextCharacters"
| "PreviousCharacters"
| "Profile"
| "RoomAdmin"
| "ClearFocus"
;
type DialogMenuMode = (
"activities"
| "colorDefault"
@ -585,24 +605,6 @@ type ArousalActiveName = "Inactive" | "NoMeter" | "Manual" | "Hybrid" | "Automat
type ArousalVisibleName = "All" | "Access" | "Self";
type ArousalAffectStutterName = "None" | "Arousal" | "Vibration" | "All";
/**
* A listener for KeyboardEvents
*
* Cheat-sheet about how to do key checks with it:
* - {@link KeyboardEvent.code} is the layout-insensitive code
* for the key (so KeyA, Space, etc.) Use this if you want to
* keep the QWERTY location, like movement keys.
*
* {@link KeyboardEvent.key} is the layout-dependent string
* for the key (so "a" (or "q" on AZERTY), "A" (or "Q"), " ", etc.)
* Use this if you want the key to correspond to what's actually on
* the user's keyboard.
*
* @see {@link CommonKeyMove}
*/
type KeyboardEventListener = ScreenFunctions["KeyDown"];
type MouseEventListener = ScreenFunctions["MouseDown"];
type SettingsSensDepName = "SensDepLight" | "Normal" | "SensDepNames" | "SensDepTotal" | "SensDepExtreme";
type SettingsVFXName = "VFXInactive" | "VFXSolid" | "VFXAnimatedTemp" | "VFXAnimated";
type SettingsVFXVibratorName = "VFXVibratorInactive" | "VFXVibratorSolid" | "VFXVibratorAnimated";
@ -1491,63 +1493,92 @@ interface Lovership {
BeginWeddingOfferedByMemberNumber?: never;
}
/**
* A listener for KeyboardEvents
*
* Cheat-sheet about how to do key checks with it:
* - {@link KeyboardEvent.code} is the layout-insensitive code
* for the key (so KeyA, Space, etc.) Use this if you want to
* keep the QWERTY location, like movement keys.
*
* {@link KeyboardEvent.key} is the layout-dependent string
* for the key (so "a" (or "q" on AZERTY), "A" (or "Q"), " ", etc.)
* Use this if you want the key to correspond to what's actually on
* the user's keyboard.
*
* @see {@link CommonKeyMove}
*/
type KeyboardEventListener = (event: KeyboardEvent) => boolean;
type MouseEventListener = (event: MouseEvent | TouchEvent) => void;
type MouseWheelEventListener = (event: WheelEvent) => void;
type VoidHandler = () => void;
type ScreenLoadHandler = VoidHandler;
type ScreenUnloadHandler = VoidHandler;
type ScreenDrawHandler = VoidHandler;
type ScreenRunHandler = (time: number) => void;
type ScreenResizeHandler = (load: boolean) => void;
type ScreenExitHandler = VoidHandler;
interface ScreenFunctions {
// Required
/**
* Called each frame
* @param {number} time - The current time for frame
*/
Run(time: number): void;
Run: ScreenRunHandler;
/**
* Called if the user presses the mouse button or touches the touchscreen on the canvas
* @param {MouseEvent | TouchEvent} event - The event that triggered this
*/
MouseDown?(event: MouseEvent | TouchEvent): void;
MouseDown?: MouseEventListener;
/**
* Called if the user releases the mouse button or the touchscreen on the canvas
* @param {MouseEvent | TouchEvent} event - The event that triggered this
*/
MouseUp?(event: MouseEvent | TouchEvent): void;
MouseUp?: MouseEventListener;
/**
* Called if the user moves the mouse cursor or the touch on the touchscreen over the canvas
* @param {MouseEvent | TouchEvent} event - The event that triggered this
*/
MouseMove?(event: MouseEvent | TouchEvent): void;
MouseMove?: MouseEventListener;
/**
* Called if the user moves the mouse wheel on the canvas
* @param {MouseEvent | TouchEvent} event - The event that triggered this
*/
MouseWheel?(event: WheelEvent): void;
MouseWheel?: MouseWheelEventListener;
/**
* Called if the user clicks on the canvas
* @param {MouseEvent | TouchEvent} event - The event that triggered this
*/
Click(event: MouseEvent | TouchEvent): void;
Click: MouseEventListener;
// Optional
/** Called when screen is loaded using `CommonSetScreen` */
Load?(): void;
Load?: ScreenLoadHandler
/** Called when this screen is being replaced */
Unload?(): void;
Unload?: ScreenUnloadHandler;
/** Called each frame when the screen needs to be drawn. */
Draw?() : void;
Draw?: ScreenDrawHandler;
/**
* Called when screen size or position changes or after screen load
* @param {boolean} load - If the reason for call was load (`true`) or window resize (`false`)
*/
Resize?(load: boolean): void;
Resize?: ScreenResizeHandler;
/**
* Called if the the user presses any key
* @param {KeyboardEvent} event - The event that triggered this
*/
KeyDown?(event: KeyboardEvent): boolean;
KeyDown?: KeyboardEventListener;
/**
* Called if the user releases a pressed key
* @param {KeyboardEvent} event - The event that triggered this
*/
KeyUp?(event: KeyboardEvent): void;
KeyUp?: KeyboardEventListener;
/** Called when user presses Esc */
Exit?(): void;
Exit?: ScreenExitHandler;
}
//#region Characters