mirror of
https://gitgud.io/BondageProjects/Bondage-College.git
synced 2025-04-25 17:59:34 +00:00
Merge branch 'side-pannel' into 'master'
ENH: Convert the dialog side pannel to DOM See merge request BondageProjects/Bondage-College!5516
This commit is contained in:
commit
08bb699b81
11 changed files with 1490 additions and 523 deletions
BondageClub
CSS
Icons
Screens
Scripts
|
@ -11,7 +11,8 @@
|
|||
"dialog-paginate dialog-grid" auto / var(--menu-button-size) auto;
|
||||
}
|
||||
|
||||
.dialog-root[data-unload] {
|
||||
.dialog-root[data-unload],
|
||||
.dialog-root [data-unload] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -141,10 +142,6 @@
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dialog-grid-button[data-unload] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@supports selector(:nth-child(1n of :not([data-unload]))) {
|
||||
.dialog-grid .dialog-grid-button:nth-child(4n - 3 of :not([data-unload])) > .button-tooltip {
|
||||
left: unset;
|
||||
|
@ -317,10 +314,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.dialog-dialog-button[data-unload] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dialog-dialog-button > .button-label {
|
||||
position: unset;
|
||||
padding-inline: 0.15em 0.15em;
|
||||
|
@ -351,6 +344,162 @@
|
|||
border-color: cyan;
|
||||
}
|
||||
|
||||
.dialog-self-menu-root {
|
||||
gap: 0 var(--gap);
|
||||
}
|
||||
|
||||
.dialog-self-menu-root > .dialog-grid {
|
||||
width: calc(100% - var(--scrollbar-gutter));
|
||||
padding-right: var(--scrollbar-gutter);
|
||||
}
|
||||
|
||||
.dialog-self-menu-root > .dialog-status {
|
||||
padding-bottom: var(--gap);
|
||||
}
|
||||
|
||||
.dialog-self-menu-menubar {
|
||||
min-width: calc(5 * var(--menu-button-size) + 2 * var(--gap));
|
||||
grid-area: dialog-menubar;
|
||||
display: grid;
|
||||
gap: calc(0.5 * var(--gap));
|
||||
direction: rtl;
|
||||
grid-auto-flow: column;
|
||||
grid-template-columns: repeat(3, var(--menu-button-size)) calc(3px + var(--menu-button-size) + 0.5 * var(--gap)) repeat(auto-fill, var(--menu-button-size));
|
||||
}
|
||||
|
||||
.dialog-expression-grid > .dialog-menubar-button,
|
||||
.dialog-pose-grid > .dialog-menubar-button,
|
||||
.dialog-expression-preset-slot > canvas,
|
||||
#dialog-owner-rules-grid > li {
|
||||
scroll-snap-align: start;
|
||||
}
|
||||
|
||||
#dialog-expression {
|
||||
grid-template:
|
||||
"dialog-menubar dialog-menubar" var(--menu-button-size)
|
||||
"dialog-status dialog-status" min-content
|
||||
"dialog-left-menu dialog-grid" auto / var(--menu-button-size) min-content;
|
||||
}
|
||||
|
||||
#dialog-expression-menu-left {
|
||||
grid-area: dialog-left-menu;
|
||||
display: grid;
|
||||
padding-top: 3px;
|
||||
gap: calc(0.5 * var(--gap));
|
||||
grid-template-rows: repeat(auto-fill, var(--menu-button-size));
|
||||
}
|
||||
|
||||
.dialog-expression-grid {
|
||||
display: grid;
|
||||
gap: calc(0.5 * var(--gap));
|
||||
grid-template-columns: repeat(3, var(--menu-button-size));
|
||||
grid-template-rows: repeat(auto-fill, var(--menu-button-size));
|
||||
height: calc(7 * var(--menu-button-size) + 3 * var(--gap));
|
||||
}
|
||||
|
||||
.dialog-expression-grid[data-unload] {
|
||||
display: grid;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#dialog-expression-button-grid {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#dialog-pose {
|
||||
width: fit-content;
|
||||
grid-template:
|
||||
"dialog-menubar dialog-menubar" var(--menu-button-size)
|
||||
"dialog-status dialog-status" min-content
|
||||
". dialog-grid" auto / var(--menu-button-size) min-content;
|
||||
}
|
||||
|
||||
#dialog-pose > .dialog-grid {
|
||||
gap: calc(0.5 * var(--gap));
|
||||
}
|
||||
|
||||
.dialog-pose-grid {
|
||||
display: grid;
|
||||
gap: calc(0.5 * var(--gap));
|
||||
}
|
||||
|
||||
#dialog-expression-preset {
|
||||
width: fit-content;
|
||||
grid-template:
|
||||
"dialog-menubar dialog-menubar" var(--menu-button-size)
|
||||
"dialog-status dialog-status" min-content
|
||||
"dialog-grid dialog-grid" auto / var(--menu-button-size) min-content;
|
||||
}
|
||||
|
||||
#dialog-expression-preset-button-grid {
|
||||
display: block;
|
||||
padding-block: 3px;
|
||||
margin: unset;
|
||||
}
|
||||
|
||||
.dialog-expression-preset-slot {
|
||||
--expression-preset-size: min(8dvh, 4dvw);
|
||||
display: grid;
|
||||
gap: calc(var(--gap) / 2);
|
||||
grid-template:
|
||||
"canvas save-button" 50%
|
||||
"canvas load-button" 50% / calc(2 * var(--menu-button-size) + var(--gap)) min-content;
|
||||
}
|
||||
|
||||
.dialog-expression-preset-slot button {
|
||||
height: var(--expression-preset-size);
|
||||
width: calc(2 * var(--menu-button-size) + 0.5 * var(--gap));
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.dialog-expression-preset-slot button[name="save"] {
|
||||
grid-area: save-button;
|
||||
margin-top: calc(var(--gap) / 4);
|
||||
align-self: self-end;
|
||||
}
|
||||
|
||||
.dialog-expression-preset-slot button[name="load"] {
|
||||
grid-area: load-button;
|
||||
margin-bottom: calc(var(--gap) / 4);
|
||||
align-self: self-start;
|
||||
}
|
||||
|
||||
.dialog-expression-preset-slot:first-child button {
|
||||
margin-top: unset;
|
||||
}
|
||||
|
||||
.dialog-expression-preset-slot:last-child button {
|
||||
margin-bottom: unset;
|
||||
}
|
||||
|
||||
.dialog-expression-preset-slot canvas {
|
||||
grid-area: canvas;
|
||||
max-height: calc(2 * var(--expression-preset-size) + var(--gap) / 2);
|
||||
max-width: calc(2 * var(--expression-preset-size) + var(--gap) / 2);
|
||||
min-height: min(20dvh, 10dvw, 200px) !important;
|
||||
min-width: min(20dvh, 10dvw, 200px) !important;
|
||||
position: static;
|
||||
}
|
||||
|
||||
#dialog-owner-rules {
|
||||
grid-template:
|
||||
"dialog-menubar dialog-menubar" var(--menu-button-size)
|
||||
"dialog-status dialog-status" min-content
|
||||
"dialog-grid dialog-grid" auto / var(--menu-button-size) min-content;
|
||||
}
|
||||
|
||||
#dialog-owner-rules-grid {
|
||||
width: 100% !important;
|
||||
display: block;
|
||||
color: white;
|
||||
margin-block: unset;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#dialog-owner-rules-grid > li {
|
||||
text-indent: 1em hanging each-line;
|
||||
}
|
||||
|
||||
@supports(height: 100dvh) {
|
||||
.dialog-root {
|
||||
--menu-button-size: min(9dvh, 4.5dvw);
|
||||
|
|
Before ![]() (image error) Size: 1,002 B After ![]() (image error) Size: 1,002 B ![]() ![]() |
Before ![]() (image error) Size: 919 B After ![]() (image error) Size: 919 B ![]() ![]() |
|
@ -364,7 +364,7 @@ RequireSelfBondage6,Requires self-bondage 6.
|
|||
RequireSelfBondage7,Requires self-bondage 7.
|
||||
RequireSelfBondage8,Requires self-bondage 8.
|
||||
RequireSelfBondage9,Requires self-bondage 9.
|
||||
RulesMenu,Active Rules
|
||||
RulesMenu,Active owner/lover rules
|
||||
RulesMenuBlockChange,Cannot change:
|
||||
RulesMenuBlockFamilyKey,Blocked: Family keys
|
||||
RulesMenuBlockKey,Blocked: Normal keys
|
||||
|
|
|
|
@ -848,6 +848,7 @@ const CommonCommands = [
|
|||
Action: () => {
|
||||
const expression = WardrobeGetExpression(Player).Emoticon != "Afk" ? "Afk" : null;
|
||||
CharacterSetFacialExpression(Player, "Emoticon", expression);
|
||||
Player.ActiveExpression.Emoticon = expression;
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -877,7 +878,7 @@ const CommonCommands = [
|
|||
}
|
||||
/** @type {(null | ExpressionNameMap["Blush"])[]} */
|
||||
let BlushLevels = [null, "Low", "Medium", "High", "VeryHigh", "Extreme"];
|
||||
/** @type {null | ExpressionName} */
|
||||
/** @type {null | ExpressionNameMap["Blush"]} */
|
||||
let NewExpression = null;
|
||||
let AcceptCmd = false;
|
||||
if (/^[0-5]$/.test(args)) {
|
||||
|
@ -912,11 +913,7 @@ const CommonCommands = [
|
|||
}
|
||||
if (AcceptCmd) {
|
||||
CharacterSetFacialExpression(Player, "Blush", NewExpression);
|
||||
// Also save in GUI
|
||||
if (DialogFacialExpressions.length == 0) {
|
||||
DialogFacialExpressionsBuild();
|
||||
}
|
||||
DialogFacialExpressions.find(FE => FE.Group == "Blush").CurrentExpression = NewExpression;
|
||||
Player.ActiveExpression.Blush = NewExpression;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -930,7 +927,7 @@ const CommonCommands = [
|
|||
return;
|
||||
}
|
||||
let AcceptCmd = false;
|
||||
/** @type {ExpressionName} */
|
||||
/** @type {ExpressionNameMap["Eyes"] | "Open"} */
|
||||
let NewExpression;
|
||||
let TargetLeft = false;
|
||||
let TargetRight = false;
|
||||
|
@ -979,15 +976,6 @@ const CommonCommands = [
|
|||
return;
|
||||
}
|
||||
if (NewExpression == "Open" || NewExpression == "Closed") {
|
||||
if (NewExpression == "Open") {
|
||||
// Restore opened eye expression set from GUI
|
||||
let DialogCurrentExpr = DialogFacialExpressions.find(FE => FE.Group == "Eyes");
|
||||
if (DialogCurrentExpr) {
|
||||
NewExpression = DialogCurrentExpr.CurrentExpression;
|
||||
} else {
|
||||
NewExpression = null;
|
||||
}
|
||||
}
|
||||
if (TargetLeft && TargetRight) {
|
||||
CharacterSetFacialExpression(Player, "Eyes", NewExpression);
|
||||
} else if (TargetLeft) {
|
||||
|
@ -996,20 +984,17 @@ const CommonCommands = [
|
|||
CharacterSetFacialExpression(Player, "Eyes2", NewExpression);
|
||||
}
|
||||
} else {
|
||||
// Change eye expression
|
||||
// Save new expression in GUI because close will erase it
|
||||
let DialogCurrentExpr = DialogFacialExpressions.find(FE => FE.Group == "Eyes");
|
||||
if (!DialogCurrentExpr) {
|
||||
DialogFacialExpressionsBuild();
|
||||
DialogCurrentExpr = DialogFacialExpressions.find(FE => FE.Group == "Eyes");
|
||||
}
|
||||
DialogCurrentExpr.CurrentExpression = NewExpression;
|
||||
|
||||
// Apply new expression only to eyes that are opened
|
||||
let LeftClosed = InventoryGetItemProperty(InventoryGet(Player, "Eyes"), "Expression") === "Closed";
|
||||
let RightClosed = InventoryGetItemProperty(InventoryGet(Player, "Eyes2"), "Expression") === "Closed";
|
||||
if (!LeftClosed) CharacterSetFacialExpression(Player, "Eyes1", NewExpression);
|
||||
if (!RightClosed) CharacterSetFacialExpression(Player, "Eyes2", NewExpression);
|
||||
if (!LeftClosed) {
|
||||
CharacterSetFacialExpression(Player, "Eyes1", NewExpression);
|
||||
Player.ActiveExpression.Eyes = NewExpression;
|
||||
}
|
||||
if (!RightClosed) {
|
||||
CharacterSetFacialExpression(Player, "Eyes2", NewExpression);
|
||||
Player.ActiveExpression.Eyes = NewExpression;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -863,7 +863,14 @@ var Shop2 = {
|
|||
return;
|
||||
}
|
||||
|
||||
DialogLoadPoseMenu(Shop2InitVars.Preview);
|
||||
/** @type {Partial<Record<AssetPoseCategory, Pose[]>>} */
|
||||
const poses = {};
|
||||
for (const pose of PoseFemale3DCG) {
|
||||
if (pose.AllowMenu || pose.AllowMenuTransient) {
|
||||
(poses[pose.Category] ??= []).push(pose);
|
||||
}
|
||||
}
|
||||
|
||||
ElementCreate({
|
||||
tag: "div",
|
||||
style: { display: "none" },
|
||||
|
@ -873,7 +880,7 @@ var Shop2 = {
|
|||
children: [{
|
||||
tag: "div",
|
||||
classList: ["shop2-pose-outer-grid"],
|
||||
children: DialogActivePoses.map(poseList => {
|
||||
children: Object.values(poses).map(poseList => {
|
||||
return {
|
||||
tag: "div",
|
||||
classList: ["shop2-pose-inner-grid"],
|
||||
|
|
|
@ -153,6 +153,7 @@ function CharacterCreate(CharacterAssetFamily, Type, CharacterID) {
|
|||
HeightRatio: 1,
|
||||
HasHiddenItems: false,
|
||||
SavedColors: GetDefaultSavedColors(),
|
||||
ActiveExpression: null,
|
||||
|
||||
PoseMapping: {},
|
||||
get Pose() {
|
||||
|
@ -752,6 +753,36 @@ function CharacterCreate(CharacterAssetFamily, Type, CharacterID) {
|
|||
}
|
||||
};
|
||||
|
||||
// Keep these two methods non-enumerable such that they do not interfere with the likes of `Object.keys`
|
||||
const activeExpression = Object.defineProperties(/** @type {Character["ActiveExpression"]} */({}), {
|
||||
setWithoutReload: {
|
||||
value: function (key, value) { this[key] = value; },
|
||||
enumerable: false,
|
||||
},
|
||||
deleteWithoutReload: {
|
||||
value: function (key) { delete this[key]; },
|
||||
enumerable: false,
|
||||
},
|
||||
});
|
||||
|
||||
/** @type {Mutable<Character>} */(NewCharacter).ActiveExpression = new Proxy(
|
||||
activeExpression,
|
||||
{
|
||||
set(...args) {
|
||||
if (DialogSelfMenuSelected === "Expression" && DialogSelfMenuMapping.Expression.C.ID === NewCharacter.ID) {
|
||||
DialogSelfMenuMapping.Expression.Reload();
|
||||
}
|
||||
return Reflect.set(...args);
|
||||
},
|
||||
deleteProperty(...args) {
|
||||
if (DialogSelfMenuSelected === "Expression" && DialogSelfMenuMapping.Expression.C.ID === NewCharacter.ID) {
|
||||
DialogSelfMenuMapping.Expression.Reload();
|
||||
}
|
||||
return Reflect.deleteProperty(...args);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Add the character to the cache
|
||||
Character.push(NewCharacter);
|
||||
|
||||
|
@ -1855,6 +1886,30 @@ function CharacterResetFacialExpression(C) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given expression is disallowed on a character
|
||||
* @param {Character} C
|
||||
* @param {Item} Item
|
||||
* @param {null | ExpressionName} Expression
|
||||
* @returns {null | string} - An exit status or `null` if the expression is otherwise allowed
|
||||
*/
|
||||
function CharacterIsExpressionDisallowed(C, Item, Expression) {
|
||||
if (!C || !Item) return "Internal error: missing character or item";
|
||||
|
||||
const allowedExpr = InventoryGetItemProperty(Item, "AllowExpression", true);
|
||||
const exprPres = InventoryGetItemProperty(Item, "ExpressionPrerequisite", true);
|
||||
const exprPre = exprPres[allowedExpr.indexOf(Expression)];
|
||||
const prereqMessage = !exprPre ? null : InventoryPrerequisiteMessage(C, exprPre, Item.Asset);
|
||||
|
||||
if (Expression != null && !allowedExpr.includes(Expression)) {
|
||||
return `Illegal expression "${Expression}"`;
|
||||
} else if (prereqMessage) {
|
||||
return prereqMessage;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given expression is allowed on a character
|
||||
* @param {Character} C
|
||||
|
@ -1862,13 +1917,7 @@ function CharacterResetFacialExpression(C) {
|
|||
* @param {ExpressionName} Expression
|
||||
*/
|
||||
function CharacterIsExpressionAllowed(C, Item, Expression) {
|
||||
if (!C || !Item) return false;
|
||||
|
||||
const allowedExpr = InventoryGetItemProperty(Item, "AllowExpression", true);
|
||||
const exprPres = InventoryGetItemProperty(Item, "ExpressionPrerequisite", true);
|
||||
const exprPre = exprPres[allowedExpr.indexOf(Expression)];
|
||||
|
||||
return ((Expression == null || allowedExpr.includes(Expression)) && (!exprPre || InventoryPrerequisiteMessage(C, exprPre, Item.Asset) === ""));
|
||||
return CharacterIsExpressionDisallowed(C, Item, Expression) == null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1002,6 +1002,68 @@ var ElementButton = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Click event listener for spin buttons.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/spinbutton_role
|
||||
* @private
|
||||
* @type {(this: HTMLButtonElement, ev: MouseEvent) => void}
|
||||
*/
|
||||
_ClickSpin: function _ClickSpin(ev) {
|
||||
const min = Number.parseInt(this.getAttribute("aria-valuemin"), 10);
|
||||
const max = Number.parseInt(this.getAttribute("aria-valuemax"), 10);
|
||||
const now = Number.parseInt(this.getAttribute("aria-valuenow"), 10);
|
||||
if (Number.isNaN(min) || Number.isNaN(max)) {
|
||||
ev.stopImmediatePropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Number.isNaN(now) || now < min || now === max) {
|
||||
this.setAttribute("aria-valuenow", min);
|
||||
} else if (now > max) {
|
||||
this.setAttribute("aria-valuenow", max);
|
||||
} else {
|
||||
this.setAttribute("aria-valuenow", now + 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Keydown event listener for spin buttons.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/spinbutton_role
|
||||
* @private
|
||||
* @type {(this: HTMLButtonElement, ev: KeyboardEvent) => void}
|
||||
*/
|
||||
_KeyDownSpin: function _KeyDownSpin(ev) {
|
||||
if (CommonKey.GetModifiers(ev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Decrement the current value such that the next click action will bring it to the correct, expected value
|
||||
let valuenow = null;
|
||||
switch (ev.key) {
|
||||
case "ArrowRight":
|
||||
case "ArrowUp":
|
||||
valuenow = this.getAttribute("aria-valuenow");
|
||||
break;
|
||||
case "ArrowLeft":
|
||||
case "ArrowDown":
|
||||
valuenow = Number.parseInt(this.getAttribute("aria-valuenow"), 10) - 2;
|
||||
break;
|
||||
case "Home":
|
||||
valuenow = Number.parseInt(this.getAttribute("aria-valuemax"), 10) - 1;
|
||||
break;
|
||||
case "End":
|
||||
valuenow = this.getAttribute("aria-valuemax");
|
||||
break;
|
||||
}
|
||||
|
||||
if (valuenow != null) {
|
||||
this.setAttribute("aria-valuenow", valuenow);
|
||||
this.click();
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HTMLElement}
|
||||
* @param {KeyboardEvent} ev
|
||||
|
@ -1313,6 +1375,19 @@ var ElementButton = {
|
|||
elem.setAttribute("aria-checked", "false");
|
||||
}
|
||||
break;
|
||||
case "spinbutton": {
|
||||
if (!elem.hasAttribute("aria-valuemin")) {
|
||||
elem.setAttribute("aria-valuemin", "0");
|
||||
}
|
||||
if (!elem.hasAttribute("aria-valuemax")) {
|
||||
elem.setAttribute("aria-valuemax", "100");
|
||||
}
|
||||
if (!elem.hasAttribute("aria-valuenow")) {
|
||||
elem.setAttribute("aria-valuenow", elem.getAttribute("aria-valuemin"));
|
||||
}
|
||||
elem.addEventListener("click", this._ClickSpin);
|
||||
elem.addEventListener("keydown", this._KeyDownSpin);
|
||||
}
|
||||
}
|
||||
|
||||
elem.addEventListener("click", onClick);
|
||||
|
|
|
@ -195,9 +195,10 @@ function PoseSetByItems(C, category, poseName) {
|
|||
* @param {Character} C - Character for which to set the pose
|
||||
* @param {null | AssetPoseName} poseName - Name of the pose to set as active or `null` to return to the default pose
|
||||
* @param {boolean} [ForceChange=false] - TRUE if the set pose(s) should overwrite current active pose(s)
|
||||
* @param {boolean} [RefreshDialog] - Refresh {@link DialogSelfMenuMapping.Pose} if so required
|
||||
* @returns {void} - Nothing
|
||||
*/
|
||||
function PoseSetActive(C, poseName, ForceChange = false) {
|
||||
function PoseSetActive(C, poseName, ForceChange=false, RefreshDialog=true) {
|
||||
const newPose = PoseRecord[poseName];
|
||||
if (
|
||||
poseName == null
|
||||
|
@ -206,6 +207,9 @@ function PoseSetActive(C, poseName, ForceChange = false) {
|
|||
) {
|
||||
C.ActivePoseMapping = (newPose == null) ? {} : { [newPose.Category]: newPose.Name };
|
||||
CharacterRefresh(C, false);
|
||||
if (RefreshDialog && DialogSelfMenuSelected === "Pose" && DialogSelfMenuMapping.Pose.C.ID === C.ID) {
|
||||
DialogSelfMenuMapping.Pose.Reload();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -229,7 +233,11 @@ function PoseSetActive(C, poseName, ForceChange = false) {
|
|||
C.PoseMapping.BodyUpper = C.PoseMapping.BodyUpper ?? "BaseUpper";
|
||||
C.PoseMapping.BodyLower = C.PoseMapping.BodyLower ?? "BaseLower";
|
||||
}
|
||||
|
||||
CharacterRefresh(C, false);
|
||||
if (RefreshDialog && DialogSelfMenuSelected === "Pose" && DialogSelfMenuMapping.Pose.C.ID === C.ID) {
|
||||
DialogSelfMenuMapping.Pose.Reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
67
BondageClub/Scripts/Typedef.d.ts
vendored
67
BondageClub/Scripts/Typedef.d.ts
vendored
|
@ -172,7 +172,7 @@ declare namespace ElementButton {
|
|||
*/
|
||||
icons?: readonly (null | undefined | InventoryIcon | CustomIcon)[];
|
||||
/** The role of the button. All accepted values are currently special-cased in order to set role-specific event listeners and/or attributes. */
|
||||
role?: "radio" | "checkbox" | "menuitemradio" | "menuitemcheckbox";
|
||||
role?: "radio" | "checkbox" | "menuitemradio" | "menuitemcheckbox" | "spinbutton";
|
||||
/** Whether to limit the default styling of the button's border and background */
|
||||
noStyling?: boolean;
|
||||
/** Whether the button should be disabled or not */
|
||||
|
@ -336,6 +336,46 @@ declare namespace DialogMenu {
|
|||
C: Character;
|
||||
focusGroup?: AssetGroup;
|
||||
}
|
||||
|
||||
/** An object representing the validation output of a menubar button, determining whether it should be clickable or not */
|
||||
interface MenuButtonValidateData {
|
||||
/**
|
||||
* The button's validation state:
|
||||
* * `null`: Validation successful
|
||||
* * `"hide"`: Validation failure; hide the button
|
||||
* * `"disabled"`: Validation failure; disable the button but do not hide it
|
||||
*/
|
||||
state: null | "hidden" | "disabled";
|
||||
/**
|
||||
* An optional status message to-be returned if the validation fails.
|
||||
*/
|
||||
status?: null | string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom validation function for determining whether the button should be enabled, disabled or hidden.
|
||||
* @param button The button in question
|
||||
* @param properties The {@link InitProperties} associated with the specific dialog menu
|
||||
* @param equippedItem The equipped item in question (if any)
|
||||
* @returns A nullish object if the validation passes or an object representing the validation state
|
||||
*/
|
||||
type MenuButtonValidator<T extends InitProperties> = (
|
||||
button: HTMLButtonElement,
|
||||
properties: T,
|
||||
equippedItem?: Item | null
|
||||
) => MenuButtonValidateData | null;
|
||||
|
||||
interface MenuButtonData<T extends InitProperties> {
|
||||
/**
|
||||
* @param button The button in question
|
||||
* @param ev The mouse event associated with the button click
|
||||
* @param properties The {@link InitProperties} associated with the specific dialog menu
|
||||
* @param equippedItem The equipped item in question (if any)
|
||||
*/
|
||||
click: (button: HTMLButtonElement, ev: MouseEvent, properties: T, equippedItem?: Item | null) => any;
|
||||
/** An object mapping labels to custom validation functions for button clicks. */
|
||||
validate?: Record<string, MenuButtonValidator<T>>;
|
||||
}
|
||||
}
|
||||
|
||||
type DialogSortOrder = | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
|
||||
|
@ -1124,6 +1164,11 @@ interface ExpressionItem {
|
|||
ExpressionList: ExpressionName[],
|
||||
}
|
||||
|
||||
interface ExpressionPair {
|
||||
Group: Exclude<ExpressionGroupName, "Eyes2">,
|
||||
Expression: null | ExpressionName,
|
||||
}
|
||||
|
||||
/**
|
||||
* The internal Asset definition of an asset.
|
||||
*
|
||||
|
@ -1630,6 +1675,18 @@ interface Character {
|
|||
Dialog: DialogLine[];
|
||||
Reputation: Reputation[];
|
||||
Skill: Skill[];
|
||||
/**
|
||||
* An object mapping expression group names to their respective currently explicitly, manually selected expression (which may thus diverge from the actual currently active expression).
|
||||
*
|
||||
* Setting or deleting an entry will potentially trigger a {@link DialogSelfMenuMapping.Expression} reload unless done so via `setWithoutReload()`/`deleteWithoutReload()` calls.
|
||||
*/
|
||||
readonly ActiveExpression: (
|
||||
Partial<Record<ExpressionGroupName, ExpressionName>>
|
||||
& {
|
||||
setWithoutReload(key: ExpressionGroupName, value: ExpressionName): void,
|
||||
deleteWithoutReload(key: ExpressionGroupName): void,
|
||||
}
|
||||
);
|
||||
/**
|
||||
* Get a copy or set the array of currently enabled poses.
|
||||
* @see {@link PoseMapping} - The underlying record of this property, usage of which is recommended
|
||||
|
@ -4300,13 +4357,7 @@ interface DialogInventoryItem extends Item {
|
|||
Vibrating: boolean;
|
||||
}
|
||||
|
||||
interface DialogSelfMenuOptionType {
|
||||
Name: string;
|
||||
IsAvailable: () => boolean;
|
||||
Load?: () => void;
|
||||
Draw: () => void;
|
||||
Click: () => void;
|
||||
}
|
||||
type DialogSelfMenuName = "Expression" | "Pose" | "SavedExpressions" | "OwnerRules";
|
||||
|
||||
// #end region
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue