mirror of
https://gitgud.io/BondageProjects/Bondage-College.git
synced 2025-04-25 17:59:34 +00:00
Fix/Improvement: Many small fixes, documentation and formatting (#2648)
* Formatting * index.html JSDoc * Documentation fixes Add LogRecord type * GLDraw docs * Lint: LoginQueue to string * Fix: GLDraw2DCanvasBlink alphaMasks * Fix: Possible LogQueryRemote crash * Lint: CommonDrawAppearanceBuild Color * Lint: DynamicDrawOptions
This commit is contained in:
parent
3800430e26
commit
35c5cdab0e
29 changed files with 357 additions and 157 deletions
BondageClub
|
@ -84,7 +84,7 @@ var AssetFemale3DCG = [
|
|||
]
|
||||
},
|
||||
{ Name: "TShirt1", HideItem: ["ItemNipplesPiercingsRoundPiercing", "ItemNipplesPiercingsWeightedPiercing", "ItemNipplesLactationPump", "BraRibbons", "ItemBreastRibbons"], Require: ["ClothLower"] },
|
||||
{
|
||||
{
|
||||
Name: "TShirt2", Value: 25, DefaultColor: ["#333", "Default"], Hide: ["Bra"], HideItem: ["ItemNipplesPiercingsRoundPiercing", "ItemNipplesPiercingsWeightedPiercing", "ItemNipplesLactationPump", "ItemBreastRibbons"], Require: ["ClothLower"], Extended: true,
|
||||
Layer: [
|
||||
{ Name: "Shirt", AllowColorize: true, HasType: false },
|
||||
|
@ -4165,7 +4165,8 @@ var AssetFemale3DCG = [
|
|||
{ Name: "Text" },
|
||||
]
|
||||
},
|
||||
{ Name: "ServingTray", Value: -1, Time: 5, Extended: true, Layer: [
|
||||
{ Name: "ServingTray", Value: -1, Time: 5, Extended: true,
|
||||
Layer: [
|
||||
{ Name: "Tray", AllowColorize: false },
|
||||
{ Name: "Objects", AllowTypes: ["Drinks", "Cake", "Cookies", "Toys"] },
|
||||
{ Name: "Details", AllowTypes: ["Drinks", "Cake", "Toys"] },
|
||||
|
@ -4302,7 +4303,7 @@ var AssetFemale3DCG = [
|
|||
{ Name: "SmallDisplayCase", Priority: 58, Fetish: ["Metal"], Value: 40, Difficulty: -2, SelfBondage: 1, Time: 15, RemoveTime: 10, AllowLock: true, Audio: "LockLarge", Prerequisite: ["NotSuspended", "NotHogtied", "NotMounted", "NotKneelingSpread", "NoFeetSpreader"], SetPose: ["Kneel"], Effect: ["ForceKneel", "Prone", "Enclose", "DeafLight", "GagLight", "Freeze"], HideItem: ["ShoesFlippers"], Alpha: [{ Masks: [[1, 1, 70, 999], [420, 1, 80, 999]] }], RemoveAtLogin: true },
|
||||
{ Name: "FuturisticCrate", Priority: 58, Category: ["SciFi"], Fetish: ["Metal"], Value: 70, Difficulty: -2, SelfBondage: 1, Time: 15, RemoveTime: 10, AllowLock: true, DrawLocks: false, Audio: "LockLarge", Prerequisite: ["NotSuspended"], LayerVisibility: true, Effect: [], AllowEffect: ["GagLight", "Freeze", "Prone", "BlindLight", "Enclose", "BlindNormal","BlindHeavy", "GagHeavy", "Freeze"], HideItem: ["ShoesFlippers"],
|
||||
Alpha: [{ Masks: [[1, 1, 70, 999], [420, 1, 80, 999]] }], RemoveAtLogin: true, SetPose: ["BaseLower"], Extended: true, AllowType: ["Window", "SmallWindow", "Closed"],
|
||||
DefaultColor: ["#93C48C", "#3B7F2C","Default", "Default", "#BBBBFF", "Default", ],
|
||||
DefaultColor: ["#93C48C", "#3B7F2C","Default", "Default", "#BBBBFF", "Default", ],
|
||||
Layer: [
|
||||
{Name:"Body", Priority:1, HasType: false},
|
||||
{Name:"Display", Priority:58, HasType: false},
|
||||
|
|
|
@ -10,6 +10,7 @@ var CharacterAppearanceAssets = [];
|
|||
var CharacterAppearanceColorPickerGroupName = "";
|
||||
var CharacterAppearanceColorPickerBackup = "";
|
||||
var CharacterAppearanceColorPickerRefreshTimer = null;
|
||||
/** @type {Character | null} */
|
||||
var CharacterAppearanceSelection = null;
|
||||
var CharacterAppearanceReturnRoom = "MainHall";
|
||||
var CharacterAppearanceReturnModule = "Room";
|
||||
|
|
|
@ -4,10 +4,10 @@ var LoginMessage = "";
|
|||
var LoginCredits = null;
|
||||
var LoginCreditsPosition = 0;
|
||||
var LoginThankYou = "";
|
||||
var LoginThankYouList = ["Anna", "Aylea", "BlueEyedCat", "BlueWinter", "Brian", "Bryce", "Christian", "DarkStar", "Dini", "ElCriminal",
|
||||
"Epona", "Escurse", "FanRunner", "Greendragon", "KamiKaze", "Kimuriel", "Longwave", "Michal", "Michel", "Mike",
|
||||
"Mindtie", "Misa", "MrUniver", "Mzklopyu", "Nick", "Nightcore", "Overlord", "Rashiash", "Ray", "Remydy",
|
||||
"Rika", "RobinHood", "Rutherford", "Ryner", "Samuel", "SeraDenoir", "Shadow", "SkyLord", "Stephanie", "Tam",
|
||||
var LoginThankYouList = ["Anna", "Aylea", "BlueEyedCat", "BlueWinter", "Brian", "Bryce", "Christian", "DarkStar", "Dini", "ElCriminal",
|
||||
"Epona", "Escurse", "FanRunner", "Greendragon", "KamiKaze", "Kimuriel", "Longwave", "Michal", "Michel", "Mike",
|
||||
"Mindtie", "Misa", "MrUniver", "Mzklopyu", "Nick", "Nightcore", "Overlord", "Rashiash", "Ray", "Remydy",
|
||||
"Rika", "RobinHood", "Rutherford", "Ryner", "Samuel", "SeraDenoir", "Shadow", "SkyLord", "Stephanie", "Tam",
|
||||
"TopHat", "Trent", "Troubadix", "William", "Xepherio", "Yuna", "Yurei", "Znarf"];
|
||||
var LoginThankYouNext = 0;
|
||||
var LoginSubmitted = false;
|
||||
|
@ -334,14 +334,14 @@ function LoginValidateArrays() {
|
|||
Player.HiddenItems = CleanHiddenItems;
|
||||
update = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var CleanFavoriteItems = AssetCleanArray(Player.FavoriteItems);
|
||||
if (CleanFavoriteItems.length != Player.FavoriteItems.length) {
|
||||
Player.FavoriteItems = CleanFavoriteItems;
|
||||
update = true;
|
||||
}
|
||||
|
||||
|
||||
if (update)
|
||||
ServerPlayerBlockItemsSync();
|
||||
}
|
||||
|
@ -384,7 +384,7 @@ function LoginExtremeItemSettings(applyDefaults) {
|
|||
function LoginQueue(Pos) {
|
||||
if (typeof Pos !== "number") return;
|
||||
|
||||
LoginMessage = TextGet("LoginQueueWait").replace("QUEUE_POS", Pos);
|
||||
LoginMessage = TextGet("LoginQueueWait").replace("QUEUE_POS", `${Pos}`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -592,7 +592,7 @@ function LoginResponse(C) {
|
|||
if (LogQuery("Locked", "Cell")) {
|
||||
CommonSetScreen("Room", "Cell");
|
||||
} else {
|
||||
|
||||
|
||||
// If the player must log back in Pandora's Box prison
|
||||
if ((Player.Infiltration != null) && (Player.Infiltration.Punishment != null) && (Player.Infiltration.Punishment.Timer != null) && (Player.Infiltration.Punishment.Timer > CurrentTime)) {
|
||||
PandoraWillpower = 0;
|
||||
|
@ -627,10 +627,10 @@ function LoginResponse(C) {
|
|||
}
|
||||
|
||||
} else {
|
||||
LoginStatusReset("ErrorLoadingCharacterData");
|
||||
}
|
||||
LoginStatusReset("ErrorLoadingCharacterData");
|
||||
}
|
||||
} else LoginStatusReset(C);
|
||||
LoginUpdateMessage();
|
||||
LoginUpdateMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -692,7 +692,7 @@ function LoginKeyDown() {
|
|||
*/
|
||||
function LoginDoLogin() {
|
||||
|
||||
// Ensure the login request is not sent twice
|
||||
// Ensure the login request is not sent twice
|
||||
if (!LoginSubmitted && ServerIsConnected) {
|
||||
var Name = ElementValue("InputName");
|
||||
var Password = ElementValue("InputPassword");
|
||||
|
@ -702,7 +702,7 @@ function LoginDoLogin() {
|
|||
ServerSend("AccountLogin", { AccountName: Name, Password: Password });
|
||||
} else LoginStatusReset("InvalidNamePassword");
|
||||
}
|
||||
LoginUpdateMessage();
|
||||
LoginUpdateMessage();
|
||||
|
||||
}
|
||||
|
||||
|
@ -711,20 +711,20 @@ function LoginDoLogin() {
|
|||
* @returns {void} Nothing
|
||||
*/
|
||||
function LoginSetSubmitted() {
|
||||
LoginSubmitted = true;
|
||||
if (ServerIsConnected) LoginErrorMessage = "";
|
||||
LoginSubmitted = true;
|
||||
if (ServerIsConnected) LoginErrorMessage = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the login submission state
|
||||
* @param {boolean} IsRelog - whether or not we're on the relog screen
|
||||
* @param {string} ErrorMessage - the login error message to set if the login is invalid - if not specified, will clear the login error message
|
||||
* @param {string} [ErrorMessage] - the login error message to set if the login is invalid - if not specified, will clear the login error message
|
||||
* @param {boolean} [IsRelog=false] - whether or not we're on the relog screen
|
||||
* @returns {void} Nothing
|
||||
*/
|
||||
function LoginStatusReset(ErrorMessage, IsRelog) {
|
||||
LoginSubmitted = false;
|
||||
LoginIsRelog = !!IsRelog;
|
||||
if (ErrorMessage) LoginErrorMessage = ErrorMessage;
|
||||
LoginSubmitted = false;
|
||||
LoginIsRelog = !!IsRelog;
|
||||
if (ErrorMessage) LoginErrorMessage = ErrorMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -740,8 +740,8 @@ function LoginUpdateMessage() {
|
|||
* @returns {string} The key of the message to display
|
||||
*/
|
||||
function LoginGetMessageKey() {
|
||||
if (LoginErrorMessage) return LoginErrorMessage;
|
||||
else if (!ServerIsConnected) return "ConnectingToServer";
|
||||
else if (LoginSubmitted) return "ValidatingNamePassword";
|
||||
else return LoginIsRelog ? "EnterPassword" : "EnterNamePassword";
|
||||
if (LoginErrorMessage) return LoginErrorMessage;
|
||||
else if (!ServerIsConnected) return "ConnectingToServer";
|
||||
else if (LoginSubmitted) return "ValidatingNamePassword";
|
||||
else return LoginIsRelog ? "EnterPassword" : "EnterNamePassword";
|
||||
}
|
||||
|
|
|
@ -2927,7 +2927,7 @@ function ChatRoomGetOwnerRule(RuleType) { return ChatRoomGetRule(RuleType, "Owne
|
|||
* Gets a rule from the current character
|
||||
* @param {string} RuleType - The name of the rule to retrieve.
|
||||
* @param {"Owner" | "Lover"} Sender - Type of the sender
|
||||
* @returns {Rule} - The owner or lover rule corresponding to the requested rule name
|
||||
* @returns {boolean} - The owner or lover rule corresponding to the requested rule name
|
||||
*/
|
||||
function ChatRoomGetRule(RuleType, Sender) {
|
||||
return LogQueryRemote(CurrentCharacter, RuleType, Sender + "Rule");
|
||||
|
@ -3190,7 +3190,7 @@ function ChatRoomGetLoadRules(C) {
|
|||
/**
|
||||
* Handles a response from another player containing the rules that the current player is allowed to read.
|
||||
* @param {Character} C - Character to set the rules on
|
||||
* @param {Rule[]} Rule - An array of rules that the current player can read.
|
||||
* @param {LogRecord[]} Rule - An array of rules that the current player can read.
|
||||
* @returns {void} - Nothing
|
||||
*/
|
||||
function ChatRoomSetLoadRules(C, Rule) {
|
||||
|
|
|
@ -730,7 +730,7 @@ function PrivateLoadCharacter(C) {
|
|||
* Triggered when a new character is added to the player's private room.
|
||||
* @param {object} Template - The base of the character, includes the name and appearance.
|
||||
* @param {string} Archetype - The type of character such as maid or mistress.
|
||||
* @param {boolean} CustomData - Whether or not the character has non-random traits.
|
||||
* @param {boolean} [CustomData=false] - Whether or not the character has non-random traits.
|
||||
* @returns {void} - Nothing.
|
||||
*/
|
||||
function PrivateAddCharacter(Template, Archetype, CustomData) {
|
||||
|
|
|
@ -338,14 +338,14 @@ function ActivityOrgasmStart(C) {
|
|||
if ((C.ID == 0) || C.IsNpc()) {
|
||||
if (C.ID == 0 && !ActivityOrgasmRuined) ActivityOrgasmGameResistCount = 0;
|
||||
ActivityOrgasmWillpowerProgress(C);
|
||||
|
||||
|
||||
if (!ActivityOrgasmRuined) {
|
||||
|
||||
|
||||
C.ArousalSettings.OrgasmTimer = CurrentTime + (Math.random() * 10000) + 5000;
|
||||
C.ArousalSettings.OrgasmStage = 2;
|
||||
C.ArousalSettings.OrgasmCount = (C.ArousalSettings.OrgasmCount == null) ? 1 : C.ArousalSettings.OrgasmCount + 1;
|
||||
ActivityOrgasmGameTimer = C.ArousalSettings.OrgasmTimer - CurrentTime;
|
||||
|
||||
|
||||
if ((C.ID == 0) && (CurrentScreen == "ChatRoom")) {
|
||||
let Dictionary = [];
|
||||
Dictionary.push({ Tag: "SourceCharacter", Text: Player.Name, MemberNumber: Player.MemberNumber });
|
||||
|
@ -354,7 +354,7 @@ function ActivityOrgasmStart(C) {
|
|||
}
|
||||
} else {
|
||||
ActivityOrgasmStop(Player, 65 + Math.ceil(Math.random()*20));
|
||||
|
||||
|
||||
if ((C.ID == 0) && (CurrentScreen == "ChatRoom")) {
|
||||
let Dictionary = [];
|
||||
let ChatModifier = C.ArousalSettings.OrgasmStage == 1 ? "Timeout" : "Surrender";
|
||||
|
@ -363,8 +363,8 @@ function ActivityOrgasmStart(C) {
|
|||
ActivityChatRoomArousalSync(C);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -421,12 +421,12 @@ function ActivityOrgasmGameGenerate(Progress) {
|
|||
/**
|
||||
* Triggers an orgasm for the player or an NPC which lasts from 5 to 15 seconds
|
||||
* @param {Character} C - Character for which an orgasm was triggered
|
||||
* @param {bool} Bypass - If true, this will do a ruined orgasm rather than a real one
|
||||
* @param {boolean} [Bypass=false] - If true, this will do a ruined orgasm rather than a real one
|
||||
* @returns {void} - Nothing
|
||||
*/
|
||||
function ActivityOrgasmPrepare(C, Bypass) {
|
||||
ActivityOrgasmRuined = false;
|
||||
|
||||
|
||||
if (C.Effect.includes("DenialMode")) {
|
||||
C.ArousalSettings.Progress = 99;
|
||||
if (C.ID == 0 && (Bypass || C.Effect.includes("RuinOrgasms"))) ActivityOrgasmRuined = true;
|
||||
|
@ -438,7 +438,7 @@ function ActivityOrgasmPrepare(C, Bypass) {
|
|||
if (C.ID == 0 && Bypass) ActivityOrgasmRuined = true;
|
||||
else return;
|
||||
}
|
||||
|
||||
|
||||
if (C.ID == 0 && ActivityOrgasmRuined) {
|
||||
ActivityOrgasmGameGenerate(0); // Resets the game
|
||||
}
|
||||
|
|
|
@ -397,7 +397,10 @@ function AssetBuildDescription(Family, CSV) {
|
|||
|
||||
}
|
||||
|
||||
// Loads the description of the assets in a specific language
|
||||
/**
|
||||
* Loads the description of the assets in a specific language
|
||||
* @param {string} Family The asset family to load the description for
|
||||
*/
|
||||
function AssetLoadDescription(Family) {
|
||||
|
||||
// Finds the full path of the CSV file to use cache
|
||||
|
|
|
@ -66,113 +66,185 @@ var AudioActions = [
|
|||
GetAudioInfo: AudioPlayAssetSound
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["pumps", "Suctightens", "InflatableBodyBagRestrain"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"pumps",
|
||||
"Suctightens",
|
||||
"InflatableBodyBagRestrain"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "Inflation"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["FuturisticTrainingBeltSetStateNoneOff"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticTrainingBeltSetStateNoneOff"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "FuturisticApply"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => [ "FuturisticTrainingBeltSetStateLowPriorityEdgeLow",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityEdgeLowSelf",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeLow",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeLowSelf"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticTrainingBeltSetStateLowPriorityEdgeLow",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityEdgeLowSelf",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeLow",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeLowSelf"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "VibrationEdgeLow"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => [ "FuturisticTrainingBeltSetStateLowPriorityTeaseLow",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityLowLow",].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticTrainingBeltSetStateLowPriorityTeaseLow",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityLowLow"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "VibrationTeaseLow"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => [ "FuturisticTrainingBeltSetStateCooldownOff"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticTrainingBeltSetStateCooldownOff"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "VibrationCooldown"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => [ "FuturisticTrainingBeltSetStateLowPriorityEdgeMedium",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityEdgeMediumSelf",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeMedium",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeMediumSelf"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticTrainingBeltSetStateLowPriorityEdgeMedium",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityEdgeMediumSelf",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeMedium",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeMediumSelf"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "VibrationEdgeMedium"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => [ "FuturisticTrainingBeltSetStateLowPriorityTeaseMedium",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityMedium"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticTrainingBeltSetStateLowPriorityTeaseMedium",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityMedium"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "VibrationTeaseMedium"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => [ "FuturisticTrainingBeltSetStateLowPriorityEdgeHigh",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityEdgeHighSelf",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeHigh",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeHighSelf"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticTrainingBeltSetStateLowPriorityEdgeHigh",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityEdgeHighSelf",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeHigh",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeHighSelf"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "VibrationEdgeHigh"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => [ "FuturisticTrainingBeltSetStateLowPriorityEdgeMaximum",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityEdgeMaximumSelf",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityTeaseMaximum",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityTeaseHigh",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityMax",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityMax",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeMaximum",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeMaximumSelf"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticTrainingBeltSetStateLowPriorityEdgeMaximum",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityEdgeMaximumSelf",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityTeaseMaximum",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityTeaseHigh",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityMax",
|
||||
"FuturisticTrainingBeltSetStateLowPriorityMax",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeMaximum",
|
||||
"FuturisticTrainingBeltSetStateHighPriorityEdgeMaximumSelf"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "VibrationMaximum"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["InteractiveVisorHeadSet"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"InteractiveVisorHeadSet"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "SciFiEffect"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["FuturisticPanelGagMouthSetLightBall", "FuturisticPanelGagMouthSetBall", "FuturisticPanelGagMouthSetPadded", "FuturisticPanelGagMouthSetPlug"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticPanelGagMouthSetLightBall",
|
||||
"FuturisticPanelGagMouthSetBall",
|
||||
"FuturisticPanelGagMouthSetPadded",
|
||||
"FuturisticPanelGagMouthSetPlug"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "SciFiPump"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["deflates", "Sucloosens"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"deflates",
|
||||
"Sucloosens"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "Deflation"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["ChainSet"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"ChainSet"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "ChainLong"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["RopeSet"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"RopeSet"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "RopeShort"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["ShacklesRestrain", "Ornate"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"ShacklesRestrain",
|
||||
"Ornate"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "CuffsMetal"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["FuturisticChastityBeltShock"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticChastityBeltShock"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "Shocks"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["FuturisticChastityBeltSetClosed", "FuturisticChastityBeltSetOpen", "InventoryItemBreastFuturisticBraSet", "FuturisticHeelsSet", "FuturisticArmbinderSet", "FuturisticCuffsRestrain", "FuturisticLegCuffsRestrain", "FuturisticAnkleCuffsRestrain", "SciFiPleasurePantiesAction"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticChastityBeltSetClosed",
|
||||
"FuturisticChastityBeltSetOpen",
|
||||
"InventoryItemBreastFuturisticBraSet",
|
||||
"FuturisticHeelsSet",
|
||||
"FuturisticArmbinderSet",
|
||||
"FuturisticCuffsRestrain",
|
||||
"FuturisticLegCuffsRestrain",
|
||||
"FuturisticAnkleCuffsRestrain",
|
||||
"SciFiPleasurePantiesAction"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "SciFiConfigure"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["FuturisticChastityBeltSetGeneric", "FuturisticChastityBeltSetPunish", "FuturisticChastityBeltSetGeneric", "FuturisticPanelGagMouthSetAutoPunish", "SciFiPleasurePantiesBeep"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticChastityBeltSetGeneric",
|
||||
"FuturisticChastityBeltSetPunish",
|
||||
"FuturisticChastityBeltSetGeneric",
|
||||
"FuturisticPanelGagMouthSetAutoPunish",
|
||||
"SciFiPleasurePantiesBeep"
|
||||
].find(A => data.Content.includes(A)),
|
||||
GetAudioInfo: AudioSciFiBeepSounds
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["FuturisticPanelGagMouthSetAutoInflate"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticPanelGagMouthSetAutoInflate"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "Inflation"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["FuturisticPanelGagMouthSetAutoDeflate"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticPanelGagMouthSetAutoDeflate"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "Deflation"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["FuturisticCrateSet"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"FuturisticCrateSet"
|
||||
].find(A => data.Content.includes(A)),
|
||||
Sound: "SciFiConfigure"
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["CollarShockUnitTrigger", "ShockCollarTrigger", "LoveChastityBeltShockTrigger", "SciFiPleasurePantiesShockTrigger", "TriggerShock", "CollarAutoShockUnitTrigger", "FuturisticVibratorShockTrigger"].find(A => data.Content.includes(A)),
|
||||
IsAction: (data) => [
|
||||
"CollarShockUnitTrigger",
|
||||
"ShockCollarTrigger",
|
||||
"LoveChastityBeltShockTrigger",
|
||||
"SciFiPleasurePantiesShockTrigger",
|
||||
"TriggerShock",
|
||||
"CollarAutoShockUnitTrigger",
|
||||
"FuturisticVibratorShockTrigger"
|
||||
].find(A => data.Content.includes(A)),
|
||||
GetAudioInfo: (data) => InventoryItemNeckAccessoriesCollarShockUnitDynamicAudio(data)
|
||||
},
|
||||
{
|
||||
IsAction: (data) => ["Decrease", "Increase"].find(A => data.Content.includes(A)) && !data.Content.endsWith("-1"),
|
||||
IsAction: (data) => [
|
||||
"Decrease",
|
||||
"Increase"
|
||||
].find(A => data.Content.includes(A)) && !data.Content.endsWith("-1"),
|
||||
GetAudioInfo: AudioVibratorSounds
|
||||
},
|
||||
];
|
||||
|
|
|
@ -708,7 +708,7 @@ function CharacterCanChangeToPose(C, poseName) {
|
|||
/**
|
||||
* Checks if a certain pose is whitelisted and available for the pose menu
|
||||
* @param {Character} C - Character to check for the pose
|
||||
* @param {string} Type - Pose type to check for within items
|
||||
* @param {string|undefined} Type - Pose type to check for within items
|
||||
* @param {string} Pose - Pose to check for whitelist
|
||||
* @returns {boolean} - TRUE if the character has the pose available
|
||||
*/
|
||||
|
@ -1295,7 +1295,7 @@ function CharacterResetFacialExpression(C) {
|
|||
|
||||
/**
|
||||
* Gets the currently selected character
|
||||
* @returns {Character} - Currently selected character
|
||||
* @returns {Character|null} - Currently selected character
|
||||
*/
|
||||
function CharacterGetCurrent() {
|
||||
return (Player.FocusGroup != null) ? Player : CurrentCharacter;
|
||||
|
@ -1490,7 +1490,7 @@ function CharacterCheckHooks(C, IgnoreHooks) {
|
|||
|
||||
})) refresh = true;
|
||||
} else if (C.UnregisterHook("BeforeSortLayers", "HideRestraints")) refresh = true;
|
||||
|
||||
|
||||
// Hook for layer visibility
|
||||
// Visibility is a string individual layers have. If an item has any layers with visibility, it should have the LayerVisibility: true property
|
||||
// We basically check the player's items and see if any are visible that have the LayerVisibility property.
|
||||
|
@ -1513,7 +1513,7 @@ function CharacterCheckHooks(C, IgnoreHooks) {
|
|||
(Layer.Visibility == "Owner" && C.IsOwnedByPlayer()) ||
|
||||
(Layer.Visibility == "Lovers" && C.IsLoverOfPlayer()) ||
|
||||
(Layer.Visibility == "Mistresses" && LogQuery("ClubMistress", "Management"))
|
||||
));
|
||||
));
|
||||
}))) refresh = true;
|
||||
// Use the regular hook when the character is not
|
||||
else if (!IgnoreHooks && (C.UnregisterHook("AfterLoadCanvas", "LayerVisibilityDialog") || C.RegisterHook("AfterLoadCanvas", "LayerVisibility", (C) => {
|
||||
|
@ -1526,9 +1526,9 @@ function CharacterCheckHooks(C, IgnoreHooks) {
|
|||
(Layer.Visibility == "Owner" && C.IsOwnedByPlayer()) ||
|
||||
(Layer.Visibility == "Lovers" && C.IsLoverOfPlayer()) ||
|
||||
(Layer.Visibility == "Mistresses" && LogQuery("ClubMistress", "Management"))
|
||||
));
|
||||
));
|
||||
}))) refresh = true;
|
||||
|
||||
|
||||
} else if (C.UnregisterHook("AfterLoadCanvas", "LayerVisibility")) refresh = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -319,7 +319,7 @@ function ColorPickerDraw(X, Y, Width, Height, Src, Callback) {
|
|||
ColorPickerLayout.SaveButtonX = X + (((ColorPickerWidth / 6) - 90) / 2);
|
||||
ColorPickerLayout.PrevButtonX = X + (ColorPickerWidth / 6) + (((ColorPickerWidth / 6) - 90) / 2);
|
||||
ColorPickerLayout.NextButtonX = X + (ColorPickerWidth / 3) + (((ColorPickerWidth / 6) - 90) / 2);
|
||||
|
||||
|
||||
var SVPanelOffset = ColorPickerLayout.SVPanelOffset;
|
||||
var SVPanelHeight = ColorPickerLayout.SVPanelHeight;
|
||||
var PaletteOffset = ColorPickerLayout.PaletteOffset;
|
||||
|
@ -327,9 +327,9 @@ function ColorPickerDraw(X, Y, Width, Height, Src, Callback) {
|
|||
var FavoritesPaletteOffset = ColorPickerLayout.FavoritesPaletteOffset;
|
||||
var FavoritesPaletteHeight = ColorPickerLayout.FavoritesPaletteHeight;
|
||||
var ButtonOffset = ColorPickerLayout.ButtonOffset;
|
||||
var NextButtonX = ColorPickerLayout.NextButtonX
|
||||
var SaveButtonX = ColorPickerLayout.SaveButtonX
|
||||
var PrevButtonX = ColorPickerLayout.PrevButtonX
|
||||
var NextButtonX = ColorPickerLayout.NextButtonX;
|
||||
var SaveButtonX = ColorPickerLayout.SaveButtonX;
|
||||
var PrevButtonX = ColorPickerLayout.PrevButtonX;
|
||||
|
||||
var HSV;
|
||||
if (ColorPickerInitialHSV == null) {
|
||||
|
|
|
@ -211,7 +211,7 @@ function CommonReadCSV(Array, Path, Screen, File) {
|
|||
/**
|
||||
* AJAX utility to get a file and return its content. By default will retry requests 10 times
|
||||
* @param {string} Path - Path of the resource to request
|
||||
* @param {function} Callback - Callback to execute once the resource is received
|
||||
* @param {(this: XMLHttpRequest, xhr: XMLHttpRequest) => void} Callback - Callback to execute once the resource is received
|
||||
* @param {number} [RetriesLeft] - How many more times to retry if the request fails - after this hits zero, an error will be logged
|
||||
* @returns {void} - Nothing
|
||||
*/
|
||||
|
@ -232,7 +232,7 @@ function CommonGet(Path, Callback, RetriesLeft) {
|
|||
* Retry handler for CommonGet requests. Exponentially backs off retry attempts up to a limit of 1 minute. By default,
|
||||
* retries up to a maximum of 10 times.
|
||||
* @param {string} Path - The path of the resource to request
|
||||
* @param {function} Callback - Callback to execute once the resource is received
|
||||
* @param {(this: XMLHttpRequest, xhr: XMLHttpRequest) => void} Callback - Callback to execute once the resource is received
|
||||
* @param {number} [RetriesLeft] - How many more times to retry - after this hits zero, an error will be logged
|
||||
* @returns {void} - Nothing
|
||||
*/
|
||||
|
@ -731,7 +731,7 @@ function CommonCompareVersion(Current, Other) {
|
|||
];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (CurrentVer[i] !== OtherVer[i]) {
|
||||
return Math.sign(OtherVer[i] - CurrentVer[i]);
|
||||
return /** @type {-1|0|1} */ (Math.sign(OtherVer[i] - CurrentVer[i]));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
/**
|
||||
* A callback function used to draw a canvas on a canvas
|
||||
* @callback drawCanvas
|
||||
* @param {string} Img - The canvas to draw
|
||||
* @param {HTMLImageElement | HTMLCanvasElement} Img - The canvas to draw
|
||||
* @param {number} x - The x coordinate to draw the canvas at
|
||||
* @param {number} y - The y coordinate to draw the canvas at
|
||||
*/
|
||||
|
@ -182,10 +182,7 @@ function CommonDrawAppearanceBuild(C, {
|
|||
return Acc;
|
||||
}, []);
|
||||
|
||||
let Color = CA.Color;
|
||||
if (Array.isArray(Color)) {
|
||||
Color = Color[Layer.ColorIndex] || AG.ColorSchema[0];
|
||||
}
|
||||
let Color = Array.isArray(CA.Color) ? (CA.Color[Layer.ColorIndex] || AG.ColorSchema[0]) : CA.Color;
|
||||
|
||||
// Fix to legacy appearance data when Hands could be different to BodyUpper
|
||||
if (GroupName === "Hands") Color = "Default";
|
||||
|
|
|
@ -113,7 +113,7 @@ function DialogSetReputation(RepType, Value) { ReputationChange(RepType, (parseI
|
|||
/**
|
||||
* Change the player's reputation progressively through dialog options (a reputation is easier to break than to build)
|
||||
* @param {string} RepType - The name of the reputation to change
|
||||
* @param {string} Value - The value, the player's reputation should be altered by
|
||||
* @param {number|string} Value - The value, the player's reputation should be altered by
|
||||
* @returns {void} - Nothing
|
||||
*/
|
||||
function DialogChangeReputation(RepType, Value) { ReputationProgress(RepType, Value); }
|
||||
|
@ -743,7 +743,7 @@ function DialogMenuButtonBuild(C) {
|
|||
if ((Item != null) && Item.Asset.Extended && ((Player.CanInteract()) || DialogAlwaysAllowRestraint() || Item.Asset.AlwaysInteract) && (!IsGroupBlocked || Item.Asset.AlwaysExtend) && (!Item.Asset.OwnerOnly || (C.IsOwnedByPlayer())) && (!Item.Asset.LoverOnly || (C.IsLoverOfPlayer()))) DialogMenuButton.push(ItemBlockedOrLimited ? "UseDisabled" : "Use");
|
||||
// Extended icon doesnt show up if remote works
|
||||
if (!DialogMenuButton.includes("Use") && DialogCanUseRemote(C, Item)) DialogMenuButton.push(ItemBlockedOrLimited ? "RemoteDisabled" : "Remote");
|
||||
|
||||
|
||||
if (DialogCanColor(C, Item)) DialogMenuButton.push(ItemBlockedOrLimited ? "ColorPickDisabled" : "ColorPick");
|
||||
|
||||
// Make sure the target player zone is allowed for an activity
|
||||
|
|
|
@ -855,7 +855,7 @@ function DrawTextFit(Text, X, Y, Width, Color, BackColor) {
|
|||
let Result = DrawingGetTextSize(Text, Width);
|
||||
Text = Result[0];
|
||||
MainCanvas.font = CommonGetFont(Result[1].toString());
|
||||
|
||||
|
||||
// Draw a back color relief text if needed
|
||||
if ((BackColor != null) && (BackColor != "")) {
|
||||
MainCanvas.fillStyle = BackColor;
|
||||
|
@ -870,7 +870,7 @@ function DrawTextFit(Text, X, Y, Width, Color, BackColor) {
|
|||
|
||||
/**
|
||||
* Gets the text size needed to fit inside a given width according to the current font.
|
||||
* This function is memoized because <code>MainCanvas.measureText(Text)</code> is a major resource hog.
|
||||
* This function is memoized because <code>MainCanvas.measureText(Text)</code> is a major resource hog.
|
||||
* @param {string} Text - Text to draw
|
||||
* @param {number} Width - Width in which the text has to fit
|
||||
* @returns {[string, number]} - Text to draw and its font size
|
||||
|
@ -884,7 +884,7 @@ const DrawingGetTextSize = CommonMemoize((Text, Width) => {
|
|||
if (metrics.width <= Width)
|
||||
return [Text, S];
|
||||
}
|
||||
|
||||
|
||||
// Cuts the text if it would go over the box
|
||||
while (Text.length > 0) {
|
||||
Text = Text.substr(1);
|
||||
|
@ -1304,7 +1304,7 @@ function DrawAssetPreview(X, Y, A, Options) {
|
|||
const Path = `${AssetGetPreviewPath(A)}/${A.Name}${DynamicPreviewIcon}.png`;
|
||||
if (Description == null) Description = C ? A.DynamicDescription(C) : A.Description;
|
||||
if (IsFavorite) Description = "★ " + Description;
|
||||
DrawPreviewBox(X, Y, Path, Description, { Background, Foreground, Vibrating, Border, Hover,
|
||||
DrawPreviewBox(X, Y, Path, Description, { Background, Foreground, Vibrating, Border, Hover,
|
||||
HoverBackground, Disabled });
|
||||
}
|
||||
|
||||
|
|
|
@ -355,10 +355,10 @@ function DynamicDrawTextArc(text, ctx, x, y, options) {
|
|||
* @returns {void} - Nothing
|
||||
*/
|
||||
function DynamicDrawTextAndEffects(text, ctx, x, y, options) {
|
||||
const { effect, width } = options;
|
||||
DynamicDrawApplyOptions(ctx, options);
|
||||
const effect = DynamicDrawTextEffects[options.effect] || {};
|
||||
if (typeof effect.before === "function") effect.before(text, ctx, x, y, options);
|
||||
ctx.fillText(text, x, y, width);
|
||||
ctx.fillText(text, x, y, options.width);
|
||||
if (typeof effect.after === "function") effect.after(text, ctx, x, y, options);
|
||||
}
|
||||
|
||||
|
@ -370,7 +370,6 @@ function DynamicDrawTextAndEffects(text, ctx, x, y, options) {
|
|||
function DynamicDrawParseOptions(options) {
|
||||
options = options || {};
|
||||
const parsedOptions = Object.assign({}, DynamicDrawTextDefaultOptions, options);
|
||||
parsedOptions.effect = DynamicDrawTextEffects[parsedOptions.effect] || {};
|
||||
return parsedOptions;
|
||||
}
|
||||
|
||||
|
|
|
@ -348,7 +348,7 @@ function ElementScrollToEnd(ID) {
|
|||
* @param {string} ID - The id of the element to find the scroll percentage of.
|
||||
* @returns {(number|null)} - A float representing the scroll percentage.
|
||||
*/
|
||||
function ElementGetScrollPercentage(ID) {
|
||||
function ElementGetScrollPercentage(ID) {
|
||||
var element = document.getElementById(ID);
|
||||
if (element != null) return (element.scrollTop + element.clientHeight) / element.scrollHeight;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ var GLDrawImageCache = new Map();
|
|||
var GLDrawCacheLoadedImages = 0;
|
||||
var GLDrawCacheTotalImages = 0;
|
||||
|
||||
/** @type {"webgl2"|"webgl"|"No WebGL"} */
|
||||
var GLVersion;
|
||||
|
||||
var GLDrawCanvas;
|
||||
|
@ -244,7 +245,7 @@ function GLDrawCreateProgram(gl, vertexShader, fragmentShader) {
|
|||
/**
|
||||
* Draws an image from a given url to a WebGLRenderingContext, used when the character is blinking
|
||||
* @param {string} url - URL of the image to render
|
||||
* @param {WebGLRenderingContext} gl - WebGL context
|
||||
* @param {WebGL2RenderingContext} gl - WebGL context
|
||||
* @param {number} dstX - Position of the image on the X axis
|
||||
* @param {number} dstY - Position of the image on the Y axis
|
||||
* @param {string} color - Color of the image to draw
|
||||
|
@ -257,7 +258,7 @@ function GLDrawImageBlink(url, gl, dstX, dstY, color, fullAlpha, alphaMasks, opa
|
|||
/**
|
||||
* Draws an image from a given url to a WebGLRenderingContext
|
||||
* @param {string} url - URL of the image to render
|
||||
* @param {WebGLRenderingContext} gl - WebGL context
|
||||
* @param {WebGL2RenderingContext} gl - WebGL context
|
||||
* @param {number} dstX - Position of the image on the X axis
|
||||
* @param {number} dstY - Position of the image on the Y axis
|
||||
* @param {number} offsetX - Additional offset to add to the X axis (for blinking)
|
||||
|
@ -311,17 +312,17 @@ function GLDrawImage(url, gl, dstX, dstY, offsetX, color, fullAlpha, alphaMasks,
|
|||
|
||||
/**
|
||||
* Draws a canvas on the WebGL canvas for the blinking effect
|
||||
* @param {WebGLRenderingContext} gl - WebGL context
|
||||
* @param {ImageData} Img - Canvas to get the data of
|
||||
* @param {WebGL2RenderingContext} gl - WebGL context
|
||||
* @param {HTMLImageElement | HTMLCanvasElement} Img - Canvas to get the data of
|
||||
* @param {number} X - Position of the image on the X axis
|
||||
* @param {number} Y - Position of the image on the Y axis
|
||||
* @param {number[][]} alphaMasks - A list of alpha masks to apply to the asset
|
||||
*/
|
||||
function GLDraw2DCanvasBlink(gl, Img, X, Y, alphaMasks) { GLDraw2DCanvas(gl, Img, X + 500, Y, 500, alphaMasks); }
|
||||
function GLDraw2DCanvasBlink(gl, Img, X, Y, alphaMasks) { GLDraw2DCanvas(gl, Img, X + 500, Y, alphaMasks); }
|
||||
/**
|
||||
* Draws a canvas on the WebGL canvas
|
||||
* @param {WebGLRenderingContext} gl - WebGL context
|
||||
* @param {ImageData} Img - Canvas to get the data of
|
||||
* @param {WebGL2RenderingContext} gl - WebGL context
|
||||
* @param {HTMLImageElement | HTMLCanvasElement} Img - Canvas to get the data of
|
||||
* @param {number} X - Position of the image on the X axis
|
||||
* @param {number} Y - Position of the image on the Y axis
|
||||
* @param {number[][]} alphaMasks - A list of alpha masks to apply to the asset
|
||||
|
@ -349,7 +350,7 @@ function GLDrawBingImageToTextureInfo(gl, Img, textureInfo) {
|
|||
|
||||
/**
|
||||
* Loads image texture data
|
||||
* @param {WebGLRenderingContext} gl - WebGL context
|
||||
* @param {WebGL2RenderingContext} gl - WebGL context
|
||||
* @param {string} url - URL of the image
|
||||
* @returns {{ width: number; height: number; texture: WebGLTexture; }} - The texture info of a given image
|
||||
*/
|
||||
|
@ -403,7 +404,7 @@ function GLDrawLoadImage(gl, url) {
|
|||
|
||||
/**
|
||||
* Loads alpha mask data
|
||||
* @param {WebGLRenderingContext} gl - The WebGL context
|
||||
* @param {WebGL2RenderingContext} gl - The WebGL context
|
||||
* @param {number} texWidth - The width of the texture to mask
|
||||
* @param {number} texHeight - The height of the texture to mask
|
||||
* @param {number} offsetX - The X offset at which the texture is to be drawn on the target canvas
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"use strict";
|
||||
/** @type {LogRecord[]} */
|
||||
var Log = [];
|
||||
|
||||
/**
|
||||
|
@ -6,7 +7,7 @@ var Log = [];
|
|||
* @param {string} NewLogName - The name of the log
|
||||
* @param {string} NewLogGroup - The name of the log's group
|
||||
* @param {number} [NewLogValue] - Value for the log as the time in ms. Is undefined if the value is permanent
|
||||
* @param {boolean} [Push] - TRUE if we must push the log to the server
|
||||
* @param {boolean} [Push=true] - TRUE if we must push the log to the server
|
||||
* @returns {void} - Nothing
|
||||
*/
|
||||
function LogAdd(NewLogName, NewLogGroup, NewLogValue, Push) {
|
||||
|
@ -43,7 +44,7 @@ function LogAdd(NewLogName, NewLogGroup, NewLogValue, Push) {
|
|||
* Deletes a log entry.
|
||||
* @param {string} DelLogName - The name of the log
|
||||
* @param {string} DelLogGroup - The name of the log's group
|
||||
* @param {boolean} [Push] - TRUE if we must push the log to the server
|
||||
* @param {boolean} [Push=true] - TRUE if we must push the log to the server
|
||||
* @returns {void} - Nothing
|
||||
*/
|
||||
function LogDelete(DelLogName, DelLogGroup, Push) {
|
||||
|
@ -82,7 +83,7 @@ function LogQuery(QueryLogName, QueryLogGroup) {
|
|||
* Returns the value associated to a log.
|
||||
* @param {string} QueryLogName - The name of the log to query the value
|
||||
* @param {string} QueryLogGroup - The name of the log's group
|
||||
* @returns {number | undefined} - Returns the value of the log which is a date represented in ms or undefined. Returns null if no matching log is found.
|
||||
* @returns {number | null} - Returns the value of the log which is a date represented in ms or undefined. Returns null if no matching log is found.
|
||||
*/
|
||||
function LogValue(QueryLogName, QueryLogGroup) {
|
||||
for (let L = 0; L < Log.length; L++)
|
||||
|
@ -93,7 +94,7 @@ function LogValue(QueryLogName, QueryLogGroup) {
|
|||
|
||||
/**
|
||||
* Loads the account log.
|
||||
* @param {Array.<{Name: string, Group: string, Value: number}>} NewLog - Existing logs received by the server
|
||||
* @param {LogRecord[]} NewLog - Existing logs received by the server
|
||||
* @returns {void} - Nothing
|
||||
*/
|
||||
function LogLoad(NewLog) {
|
||||
|
@ -121,14 +122,14 @@ function LogLoad(NewLog) {
|
|||
function LogQueryRemote(C, QueryLogName, QueryLogGroup) {
|
||||
if (C.ID == 0) return LogQuery(QueryLogName, QueryLogGroup);
|
||||
if (!C.Rule || !Array.isArray(C.Rule)) return false;
|
||||
var R = C.Rule.find(R => R.Name == QueryLogName && R.Group == QueryLogGroup);
|
||||
return (R != null) && ((R.Value == null) || (R.Value >= CurrenTime));
|
||||
var R = C.Rule.find(r => r.Name == QueryLogName && r.Group == QueryLogGroup);
|
||||
return (R != null) && ((R.Value == null) || (R.Value >= CurrentTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the Player's log and returns the rule entries that the player's owner is allowed to see.
|
||||
* @param {boolean} OwnerIsLover - Indicates that the requester is also the player's lover.
|
||||
* @returns {Rule[]} - A list of rules that the player's owner is permitted to see
|
||||
* @returns {LogRecord[]} - A list of rules that the player's owner is permitted to see
|
||||
*/
|
||||
function LogGetOwnerReadableRules(OwnerIsLover) {
|
||||
return Log.filter(L => L.Group == "OwnerRule" || (L.Group == "LoverRule" && (OwnerIsLover || L.Name.includes("Owner"))));
|
||||
|
@ -136,7 +137,7 @@ function LogGetOwnerReadableRules(OwnerIsLover) {
|
|||
|
||||
/**
|
||||
* Filters the Player's log and returns the rule entries that the player's lover is allowed to see.
|
||||
* @returns {Rule[]} - A list of rules that the player's lover is permitted to see
|
||||
* @returns {LogRecord[]} - A list of rules that the player's lover is permitted to see
|
||||
*/
|
||||
function LogGetLoverReadableRules() {
|
||||
return Log.filter(L => L.Group == "LoverRule");
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
class DirectedGraph {
|
||||
/**
|
||||
* @param {string[]} vertices
|
||||
* @param {[[string, string]]} edges
|
||||
* @param {[string, string][]} edges
|
||||
*/
|
||||
constructor(vertices, edges) {
|
||||
this.vertices = vertices;
|
||||
|
@ -21,6 +21,7 @@ class DirectedGraph {
|
|||
* @returns {Record<string, string[]>} - The adjacency list for the graph
|
||||
*/
|
||||
buildAdjacencyList() {
|
||||
/** @type {Record<string, string[]>} */
|
||||
const adjacencyList = {};
|
||||
for (const v of this.vertices) {
|
||||
adjacencyList[v] = [];
|
||||
|
@ -125,6 +126,7 @@ class DirectedGraph {
|
|||
const blocked = [];
|
||||
const blockMap = [];
|
||||
const cycles = [];
|
||||
/** @type {DirectedGraph} */
|
||||
let subgraph = this;
|
||||
let startVertex;
|
||||
let currentComponent;
|
||||
|
|
|
@ -32,7 +32,7 @@ function InventoryAdd(C, NewItemName, NewItemGroup, Push) {
|
|||
* Adds multiple new items by group & name to the character inventory
|
||||
* @param {Character} C - The character that gets the new items added to her inventory
|
||||
* @param {Array.<{ Name: string, Group: string }>} NewItems - The new items to add
|
||||
* @param {Boolean} Push - Set to TRUE to push to the server, pushed by default
|
||||
* @param {Boolean} [Push=true] - Set to TRUE to push to the server, pushed by default
|
||||
*/
|
||||
function InventoryAddMany(C, NewItems, Push) {
|
||||
|
||||
|
@ -198,7 +198,7 @@ function InventoryPrerequisiteMessage(C, Prerequisite) {
|
|||
|| !InventoryDoesItemExposeGroup(C, "Panties", "ItemVulva")
|
||||
|| InventoryDoesItemBlockGroup(C, "Socks", "ItemVulva")
|
||||
? "RemoveClothesForItem" : "";
|
||||
|
||||
|
||||
// Ensure crotch is empty
|
||||
case "VulvaEmpty": return ((InventoryGet(C, "ItemVulva") != null)) ? "MustFreeVulvaFirst" : "";
|
||||
case "ClitEmpty": return ((InventoryGet(C, "ItemVulvaPiercings") != null)) ? "MustFreeClitFirst" : "";
|
||||
|
@ -924,8 +924,8 @@ function InventoryTogglePermission(Item, Type) {
|
|||
Player.LimitedItems = Player.LimitedItems.filter(removeFromPermissions);
|
||||
} else if (InventoryIsFavorite(Player, Item.Asset.Name, Item.Asset.Group.Name, Type)) {
|
||||
if (Player.GetDifficulty() >= 3)
|
||||
Player.LimitedItems.push(permissionItem)
|
||||
else
|
||||
Player.LimitedItems.push(permissionItem);
|
||||
else
|
||||
Player.BlockItems.push(permissionItem);
|
||||
Player.FavoriteItems = Player.FavoriteItems.filter(removeFromPermissions);
|
||||
} else {
|
||||
|
|
|
@ -530,7 +530,7 @@ function ModularItemSetType(module, index, data) {
|
|||
* Publishes the chatroom message for a modular item when one of its modules has changed.
|
||||
* @param {ModularItemModule} module - The module that changed
|
||||
* @param {number} index - The index of the newly chosen option within the module
|
||||
* @param {ModularItemData} - The modular item's data
|
||||
* @param {ModularItemData} data - The modular item's data
|
||||
* @returns {void} - Nothing
|
||||
*/
|
||||
function ModularItemChatRoomMessage(module, index, { chatSetting, chatMessagePrefix }) {
|
||||
|
|
|
@ -91,7 +91,7 @@ function ReputationCharacterGet(C, RepType) {
|
|||
/**
|
||||
* Alter the reputation progress by a factor. The higher the rep, the slower it gets, a reputation is easier to break than to build. Takes the cheater version factor into account.
|
||||
* @param {string} RepType - Type/name of the reputation
|
||||
* @param {number} Value - Value of the reputation change before the factor is applied
|
||||
* @param {number|string} Value - Value of the reputation change before the factor is applied
|
||||
* @return {void} - Nothing
|
||||
*/
|
||||
function ReputationProgress(RepType, Value) {
|
||||
|
|
|
@ -52,7 +52,7 @@ function SkillChange(SkillType, SkillLevel, SkillProgress, Push) {
|
|||
|
||||
/**
|
||||
* Loads the skill data from the server on login
|
||||
* @param {Array.<{Type: string, Level: number, Progress: number}>} NewSkill - The player skills array sent by the server
|
||||
* @param {Skill[]} NewSkill - The player skills array sent by the server
|
||||
* @returns {void} - Nothing
|
||||
*/
|
||||
function SkillLoad(NewSkill) {
|
||||
|
|
|
@ -74,7 +74,7 @@ function SpeechGetGagLevel(C, AssetGroup) {
|
|||
*/
|
||||
function SpeechGetTotalGagLevel(C, NoDeaf=false) {
|
||||
let GagEffect = 0;
|
||||
|
||||
|
||||
GagEffect += SpeechGetGagLevel(C, "ItemMouth");
|
||||
GagEffect += SpeechGetGagLevel(C, "ItemMouth2");
|
||||
GagEffect += SpeechGetGagLevel(C, "ItemMouth3");
|
||||
|
@ -105,10 +105,10 @@ function SpeechGetTotalGagLevel(C, NoDeaf=false) {
|
|||
*/
|
||||
function SpeechGarble(C, CD, NoDeaf=false) {
|
||||
let NS = CD;
|
||||
|
||||
|
||||
let GagEffect = SpeechGetTotalGagLevel(C, NoDeaf);
|
||||
|
||||
|
||||
|
||||
|
||||
if (GagEffect > 0) NS = SpeechGarbleByGagLevel(GagEffect, CD);
|
||||
|
||||
// No gag effect, we return the regular text
|
||||
|
|
|
@ -299,8 +299,8 @@ function TypedItemGetOption(groupName, assetName, optionName) {
|
|||
* due to prerequisites or other requirements).
|
||||
* @param {Character} C - The character on whom the item is equipped
|
||||
* @param {Item} item - The item whose options are being validated
|
||||
* @param {ExtendedItemOption} option - The new option
|
||||
* @param {ExtendedItemOption} previousOption - The previously applied option
|
||||
* @param {ExtendedItemOption|ModularItemOption} option - The new option
|
||||
* @param {ExtendedItemOption|ModularItemOption} previousOption - The previously applied option
|
||||
* @returns {string|undefined} - undefined or an empty string if the validation passes. Otherwise, returns a string
|
||||
* message informing the player of the requirements that are not met.
|
||||
*/
|
||||
|
|
105
BondageClub/Scripts/Typedef.d.ts
vendored
105
BondageClub/Scripts/Typedef.d.ts
vendored
|
@ -11,8 +11,85 @@ type MemoizedFunction<T extends Function> = T & {
|
|||
clearCache(): void;
|
||||
};
|
||||
|
||||
// GL shim
|
||||
interface WebGL2RenderingContext {
|
||||
program?: WebGLProgram;
|
||||
programFull?: WebGLProgram;
|
||||
programHalf?: WebGLProgram;
|
||||
textureCache?: Map<string, any>;
|
||||
maskCache?: Map<string, any>;
|
||||
}
|
||||
|
||||
interface WebGLProgram {
|
||||
u_alpha?: WebGLUniformLocation;
|
||||
u_color?: WebGLUniformLocation;
|
||||
a_position?: number;
|
||||
a_texcoord?: number;
|
||||
u_matrix?: WebGLUniformLocation;
|
||||
u_texture?: WebGLUniformLocation;
|
||||
u_alpha_texture?: WebGLUniformLocation;
|
||||
position_buffer?: WebGLBuffer;
|
||||
texcoord_buffer?: WebGLBuffer;
|
||||
}
|
||||
|
||||
interface HTMLCanvasElement {
|
||||
GL?: WebGL2RenderingContext;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region index.html
|
||||
|
||||
/**
|
||||
* Main game running state, runs the drawing
|
||||
* @param {number} Timestamp
|
||||
*/
|
||||
declare function MainRun(Timestamp: number): void;
|
||||
|
||||
/**
|
||||
* When the user presses a key, we send the KeyDown event to the current screen if it can accept it
|
||||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
declare function KeyDown(event: KeyboardEvent): void;
|
||||
|
||||
/**
|
||||
* Handler for document-wide keydown event
|
||||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
declare function DocumentKeyDown(event: KeyboardEvent): void;
|
||||
|
||||
/**
|
||||
* When the user clicks, we fire the click event for other screens
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
declare function Click(event: MouseEvent): void;
|
||||
|
||||
/**
|
||||
* When the user touches the screen (mobile only), we fire the click event for other screens
|
||||
* @param {TouchEvent} event
|
||||
*/
|
||||
declare function TouchStart(event: TouchEvent): void;
|
||||
|
||||
/**
|
||||
* When touch moves, we keep it's position for other scripts
|
||||
* @param {Touch} touch
|
||||
*/
|
||||
declare function TouchMove(touch: Touch): void;
|
||||
|
||||
/**
|
||||
* When mouse move, we keep the mouse position for other scripts
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
declare function MouseMove(event: MouseEvent): void;
|
||||
|
||||
/**
|
||||
* When the mouse is away from the control, we stop keeping the coordinates,
|
||||
* we also check for false positives with "relatedTarget"
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
declare function LoseFocus(event: MouseEvent): void;
|
||||
|
||||
//#endregion
|
||||
|
||||
type IAssetFamily = "Female3DCG";
|
||||
|
||||
|
@ -255,6 +332,12 @@ interface Activity {
|
|||
MakeSound?: boolean;
|
||||
}
|
||||
|
||||
interface LogRecord {
|
||||
Name: string;
|
||||
Group: string;
|
||||
Value: number;
|
||||
}
|
||||
|
||||
/** An item is a pair of asset and its dynamic properties that define a worn asset. */
|
||||
interface Item {
|
||||
Asset: Asset;
|
||||
|
@ -267,6 +350,7 @@ interface Skill {
|
|||
Type: string;
|
||||
Level: number;
|
||||
Progress: number;
|
||||
Ratio?: number;
|
||||
}
|
||||
|
||||
interface Reputation {
|
||||
|
@ -307,9 +391,9 @@ interface Character {
|
|||
Skill: Skill[];
|
||||
Pose: string[];
|
||||
Effect: string[];
|
||||
FocusGroup: AssetGroup;
|
||||
Canvas: HTMLCanvasElement;
|
||||
CanvasBlink: HTMLCanvasElement;
|
||||
FocusGroup: AssetGroup | null;
|
||||
Canvas: HTMLCanvasElement | null;
|
||||
CanvasBlink: HTMLCanvasElement | null;
|
||||
MustDraw: boolean;
|
||||
BlinkFactor: number;
|
||||
AllowItem: boolean;
|
||||
|
@ -425,6 +509,7 @@ interface Character {
|
|||
};
|
||||
ArousalZoom?: boolean;
|
||||
FixedImage?: string;
|
||||
Rule?: LogRecord[];
|
||||
}
|
||||
|
||||
interface PlayerCharacter extends Character {
|
||||
|
@ -549,10 +634,12 @@ interface PlayerCharacter extends Character {
|
|||
Wardrobe?: any[][];
|
||||
WardrobeCharacterNames?: string[];
|
||||
SavedExpressions?: any[];
|
||||
SavedColors?: HSVColor[];
|
||||
SavedColors: HSVColor[];
|
||||
FriendList?: number[];
|
||||
FriendNames?: Map<number, string>;
|
||||
SubmissivesList?: Set<number>
|
||||
SubmissivesList?: Set<number>;
|
||||
KinkyDungeonKeybindings?: any;
|
||||
Infiltration?: any;
|
||||
}
|
||||
|
||||
//#region Extended items
|
||||
|
@ -662,6 +749,14 @@ interface ModularItemOption {
|
|||
BondageLevel?: number;
|
||||
/** The required self-bondage skill level for this option when using it on oneself */
|
||||
SelfBondageLevel?: number;
|
||||
/** The required prerequisites that must be met before this option can be selected */
|
||||
Prerequisite?: string|string[];
|
||||
/**
|
||||
* Whether or not prerequisites should be considered on the character's
|
||||
* appearance without the item equipped. Should be set to `true` if the item itself might interfere with prerequisites on
|
||||
* some of its options
|
||||
*/
|
||||
SelfBlockCheck?: boolean;
|
||||
/** A list of groups that this option blocks - defaults to [] */
|
||||
Block?: string[];
|
||||
/** A list of groups that this option hides - defaults to [] */
|
||||
|
|
|
@ -155,6 +155,7 @@ function ValidationResolveModifyDiff(previousItem, newItem, params) {
|
|||
const asset = previousItem.Asset;
|
||||
const group = asset.Group;
|
||||
const previousProperty = previousItem.Property || {};
|
||||
/** @type {ItemProperties} */
|
||||
const newProperty = newItem.Property = newItem.Property || {};
|
||||
const itemBlocked = ValidationIsItemBlockedOrLimited(C, sourceMemberNumber, group.Name, asset.Name) ||
|
||||
ValidationIsItemBlockedOrLimited(C, sourceMemberNumber, group.Name, asset.Name, newProperty.Type);
|
||||
|
@ -551,7 +552,7 @@ function ValidationSanitizeProperties(C, item) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Block advanced vibrator modes if disabled
|
||||
if (typeof property.Mode === "string" && C.ArousalSettings && C.ArousalSettings.DisableAdvancedVibes && !VibratorModeOptions[VibratorModeSet.STANDARD].includes(VibratorModeGetOption(property.Mode))) {
|
||||
console.warn(`Removing invalid mode "${property.Mode}" from ${asset.Name}`);
|
||||
|
@ -941,6 +942,7 @@ function ValidationResolveCyclicBlocks(appearance, diffMap) {
|
|||
*/
|
||||
function ValidationFindBlockCycles(appearance) {
|
||||
const groups = appearance.map((item) => item.Asset.Group.Name);
|
||||
/** @type {[string, string][]} */
|
||||
const edges = [];
|
||||
for (const item of appearance) {
|
||||
const blockedGroups = ValidationGetBlockedGroups(item, groups);
|
||||
|
|
|
@ -498,7 +498,7 @@ function VibratorModeStateUpdateDeny(C, Arousal, TimeSinceLastChange, OldIntensi
|
|||
// Here we give the fake orgasm, passing a special parameter that indicates we bypass the usual restriction on Edge
|
||||
ActivityOrgasmPrepare(C, true);
|
||||
}
|
||||
|
||||
|
||||
// Set the vibrator to rest
|
||||
State = VibratorModeState.REST;
|
||||
Intensity = -1;
|
||||
|
|
|
@ -354,19 +354,29 @@ window.onload = function() {
|
|||
MainRun(0);
|
||||
};
|
||||
|
||||
// Main game running state, runs the drawing
|
||||
/**
|
||||
* Main game running state, runs the drawing
|
||||
* @param {number} Timestamp
|
||||
*/
|
||||
function MainRun(Timestamp) {
|
||||
DrawProcess();
|
||||
TimerProcess(Timestamp);
|
||||
}
|
||||
|
||||
// When the user presses a key, we send the KeyDown event to the current screen if it can accept it
|
||||
/**
|
||||
* When the user presses a key, we send the KeyDown event to the current screen if it can accept it
|
||||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
function KeyDown(event) {
|
||||
if (event.repeat) return;
|
||||
KeyPress = event.keyCode || event.which;
|
||||
CommonKeyDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for document-wide keydown event
|
||||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
function DocumentKeyDown(event) {
|
||||
if (event.repeat) return;
|
||||
if (event.key == "Escape") {
|
||||
|
@ -383,7 +393,10 @@ function DocumentKeyDown(event) {
|
|||
}
|
||||
}
|
||||
|
||||
// When the user clicks, we fire the click event for other screens
|
||||
/**
|
||||
* When the user clicks, we fire the click event for other screens
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
function Click(event) {
|
||||
if (!CommonIsMobile) {
|
||||
MouseMove(event);
|
||||
|
@ -391,31 +404,44 @@ function Click(event) {
|
|||
}
|
||||
}
|
||||
|
||||
// When the user touches the screen (mobile only), we fire the click event for other screens
|
||||
function TouchStart(/** @type {TouchEvent} */ event) {
|
||||
/**
|
||||
* When the user touches the screen (mobile only), we fire the click event for other screens
|
||||
* @param {TouchEvent} event
|
||||
*/
|
||||
function TouchStart(event) {
|
||||
if (CommonIsMobile && MainCanvas) {
|
||||
TouchMove(event.touches[0]);
|
||||
CommonClick();
|
||||
}
|
||||
}
|
||||
|
||||
// When touch moves, we keep it's position for other scripts
|
||||
function TouchMove(/** @type {Touch} */ touch) {
|
||||
/**
|
||||
* When touch moves, we keep it's position for other scripts
|
||||
* @param {Touch} touch
|
||||
*/
|
||||
function TouchMove(touch) {
|
||||
if (MainCanvas) {
|
||||
MouseX = Math.round((touch.pageX - MainCanvas.canvas.offsetLeft) * 2000 / MainCanvas.canvas.clientWidth);
|
||||
MouseY = Math.round((touch.pageY - MainCanvas.canvas.offsetTop) * 1000 / MainCanvas.canvas.clientHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// When mouse move, we keep the mouse position for other scripts
|
||||
function MouseMove(/** @type {MouseEvent} */ event) {
|
||||
/**
|
||||
* When mouse move, we keep the mouse position for other scripts
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
function MouseMove(event) {
|
||||
if (MainCanvas) {
|
||||
MouseX = Math.round(event.offsetX * 2000 / MainCanvas.canvas.clientWidth);
|
||||
MouseY = Math.round(event.offsetY * 1000 / MainCanvas.canvas.clientHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// When the mouse is away from the control, we stop keeping the coordinates, we also check for false positives with "relatedTarget"
|
||||
/**
|
||||
* When the mouse is away from the control, we stop keeping the coordinates,
|
||||
* we also check for false positives with "relatedTarget"
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
function LoseFocus(event) {
|
||||
if (event.relatedTarget || event.toElement) {
|
||||
MouseX = -1;
|
||||
|
|
Loading…
Add table
Reference in a new issue