diff --git a/templates/roll/partials/roll-collab-action-gm.hbs b/templates/roll/partials/roll-collab-action-gm.hbs
index c6f24bb9..8c0b824d 100644
--- a/templates/roll/partials/roll-collab-action-gm.hbs
+++ b/templates/roll/partials/roll-collab-action-gm.hbs
@@ -241,7 +241,6 @@
{{#each csqData.partial as |cData cIndex|}}
- {{eLog "Partial cData" cData}}
{{#if cData.resistedTo}}
{{> "systems/eunos-blades/templates/components/consequence.hbs" cData
isResistanceVisible=true }}
diff --git a/ts/BladesChat.ts b/ts/BladesChat.ts
index 2b7388ac..64c0296a 100644
--- a/ts/BladesChat.ts
+++ b/ts/BladesChat.ts
@@ -11,7 +11,7 @@ import {ApplyTooltipListeners, ApplyConsequenceListeners} from "./core/gsap";
import BladesDialog from "./BladesDialog"; */
import U from "./core/utilities";
-import C, {BladesActorType, BladesItemType, RollType} from "./core/constants";
+import C, {BladesActorType, BladesItemType, RollType, RollResult} from "./core/constants";
import {BladesPC, BladesCrew} from "./documents/BladesActorProxy";
import {BladesItem} from "./documents/BladesItemProxy";
import {ApplyTooltipListeners, ApplyConsequenceListeners} from "./core/gsap";
@@ -66,7 +66,12 @@ class BladesChat extends ChatMessage {
const template = `systems/eunos-blades/templates/chat/roll-result-${U.lCase(rollInst.rollType)}-roll.hbs`;
- const templateData: BladesRoll & {rollResultDescription?: string} = rollInst;
+ const templateData: BladesRoll & {
+ rollResultDescription?: string, rollFlags?: BladesRoll.FlagData} = rollInst;
+ templateData.rollFlags = {...rollInst.flagData};
+
+ eLog.checkLog3("bladesRoll", "ConstructRollOutput Data", {rollInst, templateData});
+
if (rollInst.rollResult) {
templateData.rollResultDescription = C.RollResultDescriptions[rollInst.finalPosition][rollInst.rollResult];
}
diff --git a/ts/blades.ts b/ts/blades.ts
index 6e5a717f..21e2eea7 100644
--- a/ts/blades.ts
+++ b/ts/blades.ts
@@ -76,27 +76,6 @@ class GlobalGetter {
attribute: AttributeTrait.prowess,
attributeVal: 3,
name: "Broken Leg",
- resistOptions: {
- 0: {
- name: "Sprained Ankle",
- isSelected: true,
- type: ConsequenceType.ProwessHarm1,
- icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm1],
- typeDisplay: C.ConsequenceDisplay[ConsequenceType.ProwessHarm1]
- },
- 1: {
- name: "Bruised Leg",
- isSelected: false,
- type: ConsequenceType.ProwessHarm1,
- icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm1]
- },
- 2: {
- name: "Fractured Foot",
- isSelected: false,
- type: ConsequenceType.ProwessHarm1,
- icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm1]
- }
- },
resistedTo: {
name: "Sprained Ankle",
isSelected: true,
@@ -120,56 +99,24 @@ class GlobalGetter {
attribute: AttributeTrait.insight,
attributeVal: 4,
name: "You Lose Your Footing",
- /* "resistOptions": {
- "0": {
- "name": "Stumble",
- "isSelected": false
- },
- "1": {
- "name": "Trip",
- "isSelected": false
- },
- "2": {
- "name": "",
- "type": "None",
- "isSelected": true
- }
- }, */
resistedTo: {
name: "",
type: ConsequenceType.None,
isSelected: true
},
- icon: "main",
+ icon: C.ConsequenceIcons[ConsequenceType.ReducedEffect],
typeDisplay: "Reduced Effect"
},
2: {
type: ConsequenceType.ResolveHarm1,
attribute: AttributeTrait.resolve,
name: "Traumatic Flashbacks",
- resistOptions: {
- 0: {
- name: "",
- type: ConsequenceType.None,
- isSelected: true
- },
- 1: {
- name: "",
- type: ConsequenceType.None,
- isSelected: false
- },
- 2: {
- name: "",
- type: ConsequenceType.None,
- isSelected: false
- }
- },
resistedTo: {
name: "",
type: ConsequenceType.None,
isSelected: true
},
- icon: "spikes|eyeball|iris",
+ icon: C.ConsequenceIcons[ConsequenceType.ResolveHarm1],
typeDisplay: "Level 1 Harm (Lesser)",
attributeVal: 4
}
@@ -180,96 +127,39 @@ class GlobalGetter {
attribute: AttributeTrait.resolve,
attributeVal: 4,
name: "Time To Regroup",
- resistOptions: {
- 0: {
- name: "Time to Rest and Recuperate",
- isSelected: false
- },
- 1: {
- name: "Time to Reflect and Reevaluate",
- isSelected: false
- },
- 2: {
- name: "Time to Reorganize and Strategize",
- isSelected: false
- }
- },
resistedTo: {
name: "",
type: ConsequenceType.None,
isSelected: true
},
- icon: "horizon|boot|ice",
+ icon: C.ConsequenceIcons[ConsequenceType.WorsePosition],
typeDisplay: "Worse Position"
},
1: {
type: ConsequenceType.ComplicationMajor,
attribute: AttributeTrait.prowess,
name: "Your pick snaps off inside the lock.",
- resistOptions: {
- 0: {
- name: "Lock remains intact but jammed",
- isSelected: false,
- type: ConsequenceType.ComplicationMinor,
- icon: "main"
- },
- 1: {
- name: "Pick breaks, but lock is still pickable",
- isSelected: true,
- type: ConsequenceType.ComplicationMinor,
- icon: "main",
- typeDisplay: "Minor Complication"
- },
- 2: {
- name: "You manage to extract the broken pick from the lock.",
- isSelected: false,
- type: ConsequenceType.ComplicationMinor,
- icon: "main"
- }
- },
resistedTo: {
name: "Pick breaks, but lock is still pickable",
isSelected: true,
type: ConsequenceType.ComplicationMinor,
- icon: "main",
+ icon: C.ConsequenceIcons[ConsequenceType.ComplicationMinor],
typeDisplay: "Minor Complication"
},
attributeVal: 3,
- icon: "main",
+ icon: C.ConsequenceIcons[ConsequenceType.ComplicationMajor],
typeDisplay: "Major Complication"
},
2: {
type: ConsequenceType.InsightHarm2,
attribute: AttributeTrait.insight,
name: "Completely Misled",
- resistOptions: {
- 0: {
- name: "Partially Misinformed",
- isSelected: false,
- type: ConsequenceType.InsightHarm1,
- icon: "eye|iris",
- typeDisplay: "Level 1 Harm (Lesser)"
- },
- 1: {
- name: "Confused by Deception",
- isSelected: true,
- type: ConsequenceType.InsightHarm1,
- icon: "eye|iris",
- typeDisplay: "Level 1 Harm (Lesser)"
- },
- 2: {
- name: "Given Partially Incorrect Information",
- isSelected: false,
- type: ConsequenceType.InsightHarm1,
- icon: "eye|iris"
- }
- },
resistedTo: {
name: "Confused by Deception",
isSelected: true,
type: ConsequenceType.InsightHarm1,
typeDisplay: "Level 1 Harm (Lesser)",
- icon: "eye|iris"
+ icon: C.ConsequenceIcons[ConsequenceType.InsightHarm1]
},
specialArmorTo: {
name: "Sprained Ankle",
@@ -279,7 +169,7 @@ class GlobalGetter {
icon: C.ConsequenceIcons[ConsequenceType.InsightHarm1],
typeDisplay: "Level 1 Harm (Lesser)"
},
- icon: "eye|iris",
+ icon: C.ConsequenceIcons[ConsequenceType.InsightHarm2],
typeDisplay: "Level 2 Harm (Moderate)",
attributeVal: 4
}
@@ -403,6 +293,7 @@ Hooks.once("init", async () => {
BladesClockKeeperSheet.Initialize(),
BladesPushAlert.Initialize(),
BladesRoll.Initialize(),
+ BladesChat.Initialize(),
preloadHandlebarsTemplates()
]);
diff --git a/ts/core/constants.ts b/ts/core/constants.ts
index a97a52b5..95e13890 100644
--- a/ts/core/constants.ts
+++ b/ts/core/constants.ts
@@ -484,24 +484,24 @@ const C = {
[ConsequenceType.None]: "None"
},
ConsequenceIcons: {
- [ConsequenceType.ReducedEffect]: "main",
- [ConsequenceType.ComplicationMinor]: "main",
- [ConsequenceType.ComplicationMajor]: "main",
- [ConsequenceType.ComplicationSerious]: "main",
- [ConsequenceType.LostOpportunity]: "main",
- [ConsequenceType.WorsePosition]: "horizon|boot|ice",
- [ConsequenceType.InsightHarm1]: "eye|iris",
- [ConsequenceType.InsightHarm2]: "eye|iris",
- [ConsequenceType.InsightHarm3]: "eye|iris",
- [ConsequenceType.InsightHarm4]: "eye|iris",
- [ConsequenceType.ProwessHarm1]: "scar",
- [ConsequenceType.ProwessHarm2]: "scarTissue",
- [ConsequenceType.ProwessHarm3]: "scar|scarTissue",
- [ConsequenceType.ProwessHarm4]: "scar|scarTissue|welts",
- [ConsequenceType.ResolveHarm1]: "spikes|eyeball|iris",
- [ConsequenceType.ResolveHarm2]: "spikes|eyeball|iris",
- [ConsequenceType.ResolveHarm3]: "spikes|eyeball|iris",
- [ConsequenceType.ResolveHarm4]: "spikes|eyeball|iris",
+ [ConsequenceType.ReducedEffect]: "reduced-effect",
+ [ConsequenceType.ComplicationMinor]: "complication-minor",
+ [ConsequenceType.ComplicationMajor]: "complication-major",
+ [ConsequenceType.ComplicationSerious]: "complication-serious",
+ [ConsequenceType.LostOpportunity]: "lost-opportunity",
+ [ConsequenceType.WorsePosition]: "worse-position",
+ [ConsequenceType.InsightHarm1]: "harm-insight-1",
+ [ConsequenceType.InsightHarm2]: "harm-insight-2",
+ [ConsequenceType.InsightHarm3]: "harm-insight-3",
+ [ConsequenceType.InsightHarm4]: "harm-insight-4",
+ [ConsequenceType.ProwessHarm1]: "harm-prowess-1",
+ [ConsequenceType.ProwessHarm2]: "harm-prowess-2",
+ [ConsequenceType.ProwessHarm3]: "harm-prowess-3",
+ [ConsequenceType.ProwessHarm4]: "harm-prowess-4",
+ [ConsequenceType.ResolveHarm1]: "harm-resolve-1",
+ [ConsequenceType.ResolveHarm2]: "harm-resolve-2",
+ [ConsequenceType.ResolveHarm3]: "harm-resolve-3",
+ [ConsequenceType.ResolveHarm4]: "harm-resolve-4",
[ConsequenceType.None]: ""
},
RollResultDescriptions: {
diff --git a/ts/core/gsap.ts b/ts/core/gsap.ts
index b8e4fe76..307cf010 100644
--- a/ts/core/gsap.ts
+++ b/ts/core/gsap.ts
@@ -27,51 +27,59 @@ const gsapEffects: Record
= {
const csqBaseElems = csqRoot(".base-consequence:not(.consequence-icon-circle)");
const csqAcceptElems = csqRoot(".accept-consequence:not(.consequence-icon-circle):not(.consequence-button-container)");
- const tl = U.gsap.timeline({paused: true});
+ const tl = U.gsap.timeline({paused: true, defaults: { overwrite: "auto" }});
// Fade out base-consequence components
- tl.fromTo(csqBaseElems, {
- opacity: 1
- }, {
- opacity: 0,
- duration: config.duration / 3,
- ease: "none"
- }, 0);
+ if (csqBaseElems.length > 0) {
+ tl.fromTo(csqBaseElems, {
+ opacity: 1
+ }, {
+ opacity: 0,
+ duration: config.duration / 3,
+ ease: "none"
+ }, 0);
+ }
// Fade in accept-consequence components
- tl.fromTo(csqAcceptElems, {
- opacity: 0
- }, {
- opacity: 1,
- duration: config.duration / 3,
- ease: "none"
- }, 0);
+ if (csqAcceptElems.length > 0) {
+ tl.fromTo(csqAcceptElems, {
+ opacity: 0
+ }, {
+ opacity: 1,
+ duration: config.duration / 3,
+ ease: "none"
+ }, 0);
+ }
// Brighten the entire container slightly
- tl.fromTo(csqContainer, {
- filter: "brightness(1)"
- }, {
- filter: `brightness(${config.brightness})`,
- duration: config.duration / 3,
- ease: "none"
- }, 0);
+ if (csqContainer) {
+ tl.fromTo(csqContainer, {
+ filter: "brightness(1)"
+ }, {
+ filter: `brightness(${config.brightness})`,
+ duration: config.duration / 3,
+ ease: "none"
+ }, 0);
+ }
// Enlarge the icon circle, add stroke
- tl.fromTo(csqIconCircle, {
- xPercent: -50,
- yPercent: -50,
- scale: 0.75,
- outlineColor: C.Colors.dBLACK,
- outlineWidth: 0
- }, {
- xPercent: -50,
- yPercent: -50,
- scale: 0.85,
- outlineColor: C.Colors.GREY,
- outlineWidth: 1,
- duration: 0.55,
- ease: "sine.out"
- }, 0);
+ if (csqIconCircle.length > 0) {
+ tl.fromTo(csqIconCircle, {
+ // xPercent: -50,
+ // yPercent: -50,
+ scale: 0.75,
+ outlineColor: C.Colors.dBLACK,
+ outlineWidth: 0
+ }, {
+ // xPercent: -50,
+ // yPercent: -50,
+ scale: 0.85,
+ outlineColor: C.Colors.GREY,
+ outlineWidth: 1,
+ duration: 0.55,
+ ease: "sine.out"
+ }, 0);
+ }
return tl;
},
@@ -87,68 +95,90 @@ const gsapEffects: Record = {
csqClickIcon: {
effect: (csqIconContainer: HTMLElement, config) => {
const csqRoot = U.gsap.utils.selector(csqIconContainer);
+ const csqBackgroundImg = U.gsap.utils.selector($(csqIconContainer).parent())(".consequence-bg-image");
const csqInteractionPads = csqRoot(".consequence-interaction-pad");
const csqIconCircleBase = csqRoot(".consequence-icon-circle.base-consequence");
const csqIconCircleAccept = csqRoot(".consequence-icon-circle.accept-consequence");
const csqButtonContainers = csqRoot(".consequence-button-container");
- const tl = U.gsap.timeline({paused: true});
+ const tl = U.gsap.timeline({paused: true, defaults: { overwrite: "auto" }});
// Initialize interaction pads to display: none
if (csqInteractionPads.length) {
tl.set(csqInteractionPads, {display: "none"});
}
+ // Slide out the background
+ if (csqBackgroundImg.length) {
+ tl.fromTo(csqBackgroundImg, {
+ xPercent: -100,
+ yPercent: -50
+ }, {
+ xPercent: -60,
+ yPercent: -50,
+ duration: 0.5,
+ ease: "back"
+ }, 0);
+ }
+
// Fade out the base consequence icon circle
- tl.fromTo(csqIconCircleBase, {
- opacity: 1
- }, {
- opacity: 0,
- duration: 0.25,
- ease: "none"
- }, 0);
+ if (csqIconCircleBase.length > 0) {
+ tl.fromTo(csqIconCircleBase, {
+ opacity: 1
+ }, {
+ opacity: 0,
+ duration: 0.25,
+ ease: "none"
+ }, 0);
+ }
// Fade in the accept consequence icon circle, enlarging the stroke
- tl.fromTo(csqIconCircleAccept, {
- opacity: 0,
- xPercent: -50,
- yPercent: -50,
- scale: 0.85
- }, {
- opacity: 1,
- xPercent: -50,
- yPercent: -50,
- duration: 0.15,
- ease: "sine"
- }, 0);
-
- tl.fromTo(csqIconCircleAccept, {
- outlineWidth: 1,
- xPercent: -50,
- yPercent: -50,
- scale: 0.85
- }, {
- scale: 1,
- xPercent: -50,
- yPercent: -50,
- outlineWidth: 2,
- duration: 0.25,
- ease: "sine"
- }, 0.175);
+ if (csqIconCircleAccept.length > 0) {
+ tl.fromTo(csqIconCircleAccept, {
+ opacity: 0,
+ // xPercent: -50,
+ // yPercent: -50,
+ scale: 0.85
+ }, {
+ opacity: 1,
+ // xPercent: -50,
+ // yPercent: -50,
+ duration: 0.15,
+ ease: "sine"
+ }, 0);
+ }
+
+ if (csqIconCircleAccept.length > 0) {
+ tl.fromTo(csqIconCircleAccept, {
+ outlineWidth: 1,
+ // xPercent: -50,
+ // yPercent: -50,
+ scale: 0.85
+ }, {
+ scale: 1,
+ // xPercent: -50,
+ // yPercent: -50,
+ outlineWidth: 2,
+ duration: 0.25,
+ ease: "sine"
+ }, 0.175);
+ }
// Scale and fade in the button containers
- tl.fromTo(csqButtonContainers, {
- scale: config.scale,
- opacity: 0,
- filter: "blur(25px)"
- }, {
- scale: 1,
- opacity: 1,
- filter: "blur(0px)",
- stagger: config.stagger,
- duration: config.duration,
- ease: `${config.ease}.inOut(${config.easeStrength})`
- }, 0);
+ if (csqButtonContainers.length > 0) {
+ tl.fromTo(csqButtonContainers, {
+ scale: config.scale,
+ opacity: 0,
+ filter: "blur(25px)"
+ }, {
+ scale: 1,
+ opacity: 1,
+ filter: "blur(0px)",
+ stagger: config.stagger,
+ duration: config.duration,
+ ease: `${config.ease}.inOut(${config.easeStrength})`
+ }, 0);
+ }
// Finally, toggle on interaction pads
if (csqInteractionPads.length) {
@@ -176,75 +206,85 @@ const gsapEffects: Record = {
const buttonIcon = buttonRoot(".button-icon i");
const buttonLabel = buttonRoot(".consequence-button-label");
- const tl = U.gsap.timeline({paused: true});
+ const tl = U.gsap.timeline({paused: true, defaults: { overwrite: "auto" }});
// Turn type line white
- tl.fromTo(typeLine,
- {
- color: C.Colors.RED
- },
- {
- color: C.Colors.WHITE,
- duration: 0.5,
- ease: "sine.inOut"
- }, 0);
+ if (typeLine.length > 0) {
+ tl.fromTo(typeLine,
+ {
+ color: C.Colors.RED
+ },
+ {
+ color: C.Colors.WHITE,
+ duration: 0.5,
+ ease: "sine.inOut"
+ }, 0);
+ }
// Slide type line background out from under icon
- tl.fromTo(typeLineBg, {
- x: 5,
- scaleX: 0,
- color: C.Colors.RED,
- skewX: 0
- }, {
- scaleX: 1,
- skewX: -45,
- color: C.Colors.RED,
- duration: 0.5,
- ease: "back.out"
- }, 0);
+ if (typeLineBg.length > 0) {
+ tl.fromTo(typeLineBg, {
+ x: 5,
+ scaleX: 0,
+ color: C.Colors.RED,
+ skewX: 0
+ }, {
+ scaleX: 1,
+ skewX: -45,
+ color: C.Colors.RED,
+ duration: 0.5,
+ ease: "back.out"
+ }, 0);
+ }
// Slide accept button background out from under icon
- tl.fromTo(buttonBg, {
- scaleX: 0,
- color: C.Colors.RED,
- skewX: 0
- }, {
- x: 0,
- scaleX: 1,
- skewX: -45,
- color: C.Colors.RED,
- duration: 0.25,
- ease: "back.out"
- }, 0);
+ if (buttonBg.length > 0) {
+ tl.fromTo(buttonBg, {
+ scaleX: 0,
+ color: C.Colors.RED,
+ skewX: 0
+ }, {
+ x: 0,
+ scaleX: 1,
+ skewX: -45,
+ color: C.Colors.RED,
+ duration: 0.25,
+ ease: "back.out"
+ }, 0);
+ }
// Turn button icon black and scale
- tl.fromTo(buttonIcon,
- {
- color: C.Colors.GREY,
- opacity: 0.75,
- scale: 1
- },
- {
- color: C.Colors.dBLACK,
- scale: 1.25,
- opacity: 1,
- duration: 0.5,
- ease: "sine"
- }, 0);
+ if (buttonIcon.length > 0) {
+ tl.fromTo(buttonIcon,
+ {
+ color: C.Colors.GREY,
+ opacity: 0.75,
+ scale: 1
+ },
+ {
+ color: C.Colors.dBLACK,
+ scale: 1.25,
+ opacity: 1,
+ duration: 0.5,
+ ease: "sine"
+ }, 0);
+ }
// Turn button label black, add letter-spacing, bold
- tl.fromTo(buttonLabel,
- {
- color: C.Colors.GREY,
- fontWeight: 400,
- scale: 1
- },
- {
- color: C.Colors.dBLACK,
- fontWeight: 800,
- duration: 0.75,
- ease: "sine"
- }, 0);
+ if (buttonLabel.length > 0) {
+ tl.fromTo(buttonLabel,
+ {
+ color: C.Colors.GREY,
+ fontWeight: 400,
+ scale: 1
+ },
+ {
+ color: C.Colors.dBLACK,
+ fontWeight: 800,
+ duration: 0.75,
+ ease: "sine"
+ }, 0);
+ }
return tl;
},
@@ -270,17 +310,19 @@ const gsapEffects: Record = {
const buttonIcon = buttonRoot(".button-icon i");
const buttonLabel = buttonRoot(".consequence-button-label");
- const tl = U.gsap.timeline({paused: true});
+ const tl = U.gsap.timeline({paused: true, defaults: { overwrite: "auto" }});
// Fade out all accept elems and special armor elems
- tl.to([...acceptElems, ...specialArmorElems], {
- opacity: 0,
- duration: 0.25,
- ease: "sine.out"
- });
+ if ([...acceptElems, ...specialArmorElems].length > 0) {
+ tl.to([...acceptElems, ...specialArmorElems], {
+ opacity: 0,
+ duration: 0.25,
+ ease: "sine.out"
+ });
+ }
- if (typeLine.length) {
- // Slide out .consequence-type.resist-consequence from left
+ // Slide out .consequence-type.resist-consequence from left
+ if (typeLine.length > 0) {
tl.fromTo(typeLine, {
x: -15,
scaleX: 0,
@@ -297,90 +339,104 @@ const gsapEffects: Record = {
}
// Slide out .consequence-resist-button-bg from right
- tl.fromTo(buttonBg, {
- scaleX: 0,
- skewX: 0,
- opacity: 1
- }, {
- scaleX: 1,
- skewX: -45,
- opacity: 1,
- duration: 0.5,
- ease: "back.inOut"
- }, 0);
+ if (buttonBg.length > 0) {
+ tl.fromTo(buttonBg, {
+ scaleX: 0,
+ skewX: 0,
+ opacity: 1
+ }, {
+ scaleX: 1,
+ skewX: -45,
+ opacity: 1,
+ duration: 0.5,
+ ease: "back.inOut"
+ }, 0);
+ }
// Slide out .consequence-footer-bg.resist-consequence from left
- tl.fromTo(footerBg, {
- scaleX: 0,
- skewX: 0,
- opacity: 1
- }, {
- scaleX: 1,
- skewX: -45,
- opacity: 1,
- duration: 0.5,
- ease: "back.inOut"
- }, 0);
+ if (footerBg.length > 0) {
+ tl.fromTo(footerBg, {
+ scaleX: 0,
+ skewX: 0,
+ opacity: 1
+ }, {
+ scaleX: 1,
+ skewX: -45,
+ opacity: 1,
+ duration: 0.5,
+ ease: "back.inOut"
+ }, 0);
+ }
// Slide out .consequence-resist-attribute from left
- tl.fromTo(attrElem, {
- scaleX: 0,
- opacity: 1
- }, {
- scaleX: 1,
- opacity: 1,
- duration: 0.5,
- ease: "back.inOut"
- }, 0);
+ if (attrElem.length > 0) {
+ tl.fromTo(attrElem, {
+ scaleX: 0,
+ opacity: 1
+ }, {
+ scaleX: 1,
+ opacity: 1,
+ duration: 0.5,
+ ease: "back.inOut"
+ }, 0);
+ }
// Slide out .consequence-name.resist-consequence from left
- tl.fromTo(resistCsqName, {
- scaleX: 0,
- opacity: 1
- }, {
- scaleX: 1,
- opacity: 1,
- duration: 0.5,
- ease: "back.inOut"
- }, 0);
+ if (resistCsqName.length > 0) {
+ tl.fromTo(resistCsqName, {
+ scaleX: 0,
+ opacity: 1
+ }, {
+ scaleX: 1,
+ opacity: 1,
+ duration: 0.5,
+ ease: "back.inOut"
+ }, 0);
+ }
// Fade in .consequence-icon-circle.resist-consequence
- tl.fromTo(iconCircle, {
- opacity: 0
- }, {
- opacity: 1,
- duration: 0.5,
- ease: "back.out"
- }, 0);
-
- // Turn button icon black and scale
- tl.fromTo(buttonIcon,
- {
- color: C.Colors.GREY,
- opacity: 0.75,
- scale: 1
- },
- {
- color: C.Colors.dBLACK,
- scale: 1.25,
+ if (iconCircle.length > 0) {
+ tl.fromTo(iconCircle, {
+ opacity: 0
+ }, {
opacity: 1,
duration: 0.5,
- ease: "sine"
+ ease: "back.out"
}, 0);
+ }
+
+ // Turn button icon black and scale
+ if (buttonIcon.length > 0) {
+ tl.fromTo(buttonIcon,
+ {
+ color: C.Colors.GREY,
+ opacity: 0.75,
+ scale: 1
+ },
+ {
+ color: C.Colors.dBLACK,
+ scale: 1.25,
+ opacity: 1,
+ duration: 0.5,
+ ease: "sine"
+ }, 0);
+ }
// Turn button label black, bold
- tl.fromTo(buttonLabel,
- {
- color: C.Colors.GREY,
- fontWeight: 400,
- scale: 1
- },
- {
- color: C.Colors.dBLACK,
- fontWeight: 800,
- duration: 0.75,
- ease: "sine"
- }, 0);
+ if (buttonLabel.length > 0) {
+ tl.fromTo(buttonLabel,
+ {
+ color: C.Colors.GREY,
+ fontWeight: 400,
+ scale: 1
+ },
+ {
+ color: C.Colors.dBLACK,
+ fontWeight: 800,
+ duration: 0.75,
+ ease: "sine"
+ }, 0);
+ }
return tl;
},
@@ -400,23 +456,20 @@ const gsapEffects: Record = {
const specialArmorCsqName = csqRoot(".consequence-name.special-armor-consequence");
const iconCircle = csqRoot(".consequence-icon-circle.special-armor-consequence");
- const buttonRoot = U.gsap.utils.selector(csqRoot(".consequence-button-container.consequence-special-armor-button-container"));
-
- const buttonBg = buttonRoot(".consequence-button-bg");
- const buttonIcon = buttonRoot(".button-icon i");
- const buttonLabel = buttonRoot(".consequence-button-label");
- const tl = U.gsap.timeline({paused: true});
+ const tl = U.gsap.timeline({paused: true, defaults: { overwrite: "auto" }});
// Fade out all accept elems and resist elems
- tl.to([...acceptElems, ...resistElems], {
- opacity: 0,
- duration: 0.25,
- ease: "sine.out"
- });
+ if ([...acceptElems, ...resistElems].length > 0) {
+ tl.to([...acceptElems, ...resistElems], {
+ opacity: 0,
+ duration: 0.25,
+ ease: "sine.out"
+ });
+ }
- if (typeLine) {
- // Slide out .consequence-type.special-armor-consequence from left
+ // Slide out .consequence-type.special-armor-consequence from left
+ if (typeLine.length > 0) {
tl.fromTo(typeLine, {
x: -15,
scaleX: 0,
@@ -432,21 +485,8 @@ const gsapEffects: Record = {
}, 0);
}
- // Slide out .consequence-special-armor-button-bg from right
- tl.fromTo(buttonBg, {
- scaleX: 0,
- skewX: 0,
- opacity: 1
- }, {
- scaleX: 1,
- skewX: -45,
- opacity: 1,
- duration: 0.5,
- ease: "back.inOut"
- }, 0);
-
- if (footerBg) {
- // Slide out .consequence-footer-bg.special-armor-consequence from left
+ // Slide out .consequence-footer-bg.special-armor-consequence from left
+ if (footerBg.length > 0) {
tl.fromTo(footerBg, {
scaleX: 0,
skewX: 0,
@@ -461,7 +501,7 @@ const gsapEffects: Record = {
}
// Slide out .consequence-special-armor-message from left
- if (footerMsg) {
+ if (footerMsg.length > 0) {
tl.fromTo(footerMsg, {
scaleX: 0,
opacity: 1
@@ -474,7 +514,7 @@ const gsapEffects: Record = {
}
// Slide out .consequence-name.special-armor-consequence from left
- if (specialArmorCsqName) {
+ if (specialArmorCsqName.length > 0) {
tl.fromTo(specialArmorCsqName, {
scaleX: 0,
opacity: 1
@@ -487,42 +527,73 @@ const gsapEffects: Record = {
}
// Fade in .consequence-icon-circle.special-armor-consequence
- tl.fromTo(iconCircle, {
- opacity: 0
- }, {
- opacity: 1,
- duration: 0.5,
- ease: "back.out"
- }, 0);
-
- // Turn button icon black and scale
- tl.fromTo(buttonIcon,
- {
- color: C.Colors.GREY,
- opacity: 0.75,
- scale: 1
- },
- {
- color: C.Colors.dBLACK,
- scale: 1.25,
+ if (iconCircle.length > 0) {
+ tl.fromTo(iconCircle, {
+ opacity: 0
+ }, {
opacity: 1,
duration: 0.5,
- ease: "sine"
+ ease: "back.out"
}, 0);
+ }
- // Turn button label black, bold
- tl.fromTo(buttonLabel,
- {
- color: C.Colors.GREY,
- fontWeight: 400,
- scale: 1
- },
- {
- color: C.Colors.dBLACK,
- fontWeight: 800,
- duration: 0.75,
- ease: "sine"
- }, 0);
+
+ if (csqRoot(".consequence-button-container.consequence-special-armor-button-container").length > 0) {
+ const buttonRoot = U.gsap.utils.selector(csqRoot(".consequence-button-container.consequence-special-armor-button-container"));
+ const [buttonBg, buttonIcon, buttonLabel] = [
+ buttonRoot(".consequence-button-bg"),
+ buttonRoot(".button-icon i"),
+ buttonRoot(".consequence-button-label")
+ ];
+
+ // Slide out .consequence-special-armor-button-bg from right
+ if (buttonBg.length > 0) {
+ tl.fromTo(buttonBg, {
+ scaleX: 0,
+ skewX: 0,
+ opacity: 1
+ }, {
+ scaleX: 1,
+ skewX: -45,
+ opacity: 1,
+ duration: 0.5,
+ ease: "back.inOut"
+ }, 0);
+ }
+
+ // Turn button icon black and scale
+ if (buttonIcon.length > 0) {
+ tl.fromTo(buttonIcon,
+ {
+ color: C.Colors.GREY,
+ opacity: 0.75,
+ scale: 1
+ },
+ {
+ color: C.Colors.dBLACK,
+ scale: 1.25,
+ opacity: 1,
+ duration: 0.5,
+ ease: "sine"
+ }, 0);
+ }
+
+ // Turn button label black, bold
+ if (buttonLabel.length > 0) {
+ tl.fromTo(buttonLabel,
+ {
+ color: C.Colors.GREY,
+ fontWeight: 400,
+ scale: 1
+ },
+ {
+ color: C.Colors.dBLACK,
+ fontWeight: 800,
+ duration: 0.75,
+ ease: "sine"
+ }, 0);
+ }
+ }
return tl;
},
@@ -655,7 +726,7 @@ const gsapEffects: Record = {
},
hoverTooltip: {
effect: (tooltip, config) => {
- const tl = U.gsap.timeline({paused: true});
+ const tl = U.gsap.timeline({paused: true, defaults: { overwrite: "auto" }});
if (!tooltip) { return tl; }
// Tooltip = $(tooltip);
diff --git a/ts/core/helpers.ts b/ts/core/helpers.ts
index e6a05b8b..0bfcbbbc 100644
--- a/ts/core/helpers.ts
+++ b/ts/core/helpers.ts
@@ -1,7 +1,5 @@
// #region ▮▮▮▮▮▮▮ IMPORTS ▮▮▮▮▮▮▮ ~
import U from "./utilities";
-
-import {HbsSvgData, SVGDATA} from "./constants";
// #endregion ▮▮▮▮[IMPORTS]▮▮▮▮
// #region ░░░░░░░[Templates]░░░░ Preload Partials, Components & Overlay Templates ░░░░░░░ ~
@@ -164,17 +162,7 @@ const handlebarHelpers: Record = {
},
compileSvg(...args): string {
const [svgDotKey, svgPaths]: [string, string] = args as [string, string];
- const svgData = getProperty(SVGDATA, svgDotKey) as HbsSvgData|undefined;
- eLog.checkLog3("compileSvg", {svgDotKey, svgPaths, svgData});
- if (!svgData) { return ""; }
- const {viewBox, paths, classes} = svgData;
- return [
- `"
- ].join("\n");
+ return U.getSvgCode(svgDotKey, svgPaths);
},
eLog(...args) {
args.pop();
diff --git a/ts/core/utilities.ts b/ts/core/utilities.ts
index f5ec97a5..4e955626 100644
--- a/ts/core/utilities.ts
+++ b/ts/core/utilities.ts
@@ -1,5 +1,5 @@
// #region ▮▮▮▮▮▮▮ IMPORTS ▮▮▮▮▮▮▮ ~
-import C from "./constants";
+import C, {SVGDATA, HbsSvgData} from "./constants";
// eslint-disable-next-line import/no-unresolved
import {gsap} from "gsap/all";
// #endregion ▮▮▮▮ IMPORTS ▮▮▮▮
@@ -925,58 +925,43 @@ const objClean = (data: T, remVals: UncleanValues[] = [undefined, null, "", {
return data;
};
-
-/**
- *
- * @param items
- * @param key
- */
-export function toDict<
- T extends List,
- K extends string & KeyOf,
- V extends ValOf
->(items: T[], key: K): V extends key ? Record : never {
- const dict = {} as Record;
- const mappedItems = items
- .map((data) => {
- let {iData} = data;
- if (!iData) {iData = data;}
- const prefix = iData.linkName || iData.sourceItem?.name ? `>${iData.type.charAt(0)}>` : "";
- const newKey = `${prefix}${iData[key]}`;
- return [newKey, iData];
- })
- .sort(([a], [b]) => a.localeCompare(b)) as Array<[string, T]>;
- mappedItems.forEach(([newKey, iData]: [string, T]) => {
- if (newKey in dict) {
- newKey = indexString(newKey) as V;
- }
- dict[newKey as KeyOf] = iData;
- });
- // @ts-expect-error Oh it definitely does.
- return dict;
-
- /**
- * Given a string that could have an index suffix, returns the string with
- * the suffix incremented by one, or set to one if no suffix exists.
- * @param {string} str
- */
- function indexString(str: string) {
- if (/_\d+$/.test(str)) {
- const [curIndex, ...subStr] = [...str.split(/_/)].reverse();
- return [
- ...[...subStr].reverse(),
- parseInt(curIndex, 10) + 1
- ].join("_");
- }
- return `${str}_1`;
- }
-}
// Given an object and a predicate function, returns array of two objects:
// one with entries that pass, one with entries that fail.
const partition = (obj: Type[], predicate: testFunc = () => true): [Type[], Type[]] => [
objFilter(obj, predicate),
objFilter(obj, (v: unknown, k: string | number | undefined) => !predicate(v, k))
];
+
+/**
+ * Zips two arrays into an object.
+ *
+ * @template T - The type of the keys.
+ * @template U - The type of the values.
+ * @param {T[]} keys - The array of keys.
+ * @param {U[]} values - The array of values.
+ * @returns {Record} - The resulting object.
+ * @throws {Error} - Throws an error if the arrays are not of equal length, if the keys are not unique, or if the keys are not of a type that can be used as object keys.
+ */
+const zip = (keys: T[], values: U[]): Record => {
+ // Check that the arrays are of equal length
+ if (keys.length !== values.length) {
+ throw new Error("The arrays must be of equal length.");
+ }
+
+ // Check that the keys are unique
+ if (new Set(keys).size !== keys.length) {
+ throw new Error("The keys must be unique.");
+ }
+
+ // Zip the arrays into an object
+ const result = {} as Record;
+ keys.forEach((key, i) => {
+ result[key] = values[i];
+ });
+
+ return result;
+};
+
/**
* An object-equivalent Array.map() function, which accepts mapping functions to transform both keys and values.
* If only one function is provided, it's assumed to be mapping the values and will receive (v, k) args.
@@ -1350,6 +1335,44 @@ const withLog = (fn: (...args: unknown[]) => unknown) => {
// #region ████████ HTML: Parsing HTML Code, Manipulating DOM Objects ████████ ~
+const getSvgCode = (svgDotKey: string, svgPathKeys?: string|string[]) => {
+ const svgData = getProperty(SVGDATA, svgDotKey) as HbsSvgData|undefined;
+ // eLog.checkLog3("compileSvg", {svgDotKey, svgPaths, svgData});
+ if (!svgData) { return ""; }
+ const {viewBox, paths, classes} = svgData;
+ svgPathKeys ??= Object.keys(paths).join("|");
+ if (typeof svgPathKeys === "string") {
+ svgPathKeys = svgPathKeys.split("|");
+ }
+ return [
+ `"
+ ].join("\n");
+};
+
+const getSvgPaths = (svgDotKey: string, svgPathKeys?: string|string[]): Record => {
+ const svgData = getProperty(SVGDATA, svgDotKey) as HbsSvgData|undefined;
+ if (!svgData) { return {}; }
+ const {paths, classes} = svgData;
+ svgPathKeys ??= Object.keys(paths);
+ if (typeof svgPathKeys === "string") {
+ svgPathKeys = svgPathKeys.split("|");
+ }
+
+ const returnData: Record = {};
+
+ for (const pathKey of svgPathKeys) {
+ returnData[pathKey] = {
+ class: classes?.[pathKey] ?? "",
+ d: paths[pathKey] ?? ""
+ };
+ }
+
+ return returnData;
+};
+
// #region ░░░░░░░[GreenSock]░░░░ Wrappers for GreenSock Functions ░░░░░░░ ~
const set = (targets: gsap.TweenTarget, vars: gsap.TweenVars): gsap.core.Tween => gsap.set(targets, vars);
function get(target: gsap.TweenTarget, property: keyof gsap.CSSProperties & string, unit: string): number;
@@ -1372,6 +1395,20 @@ function get(target: gsap.TweenTarget, property: keyof gsap.CSSProperties & stri
}
const getGSAngleDelta = (startAngle: number, endAngle: number) => signNum(roundNum(getAngleDelta(startAngle, endAngle), 2)).replace(/^(.)/, "$1=");
+
+// const Animate = {
+// Timeline: {
+// to: (tl: gsap.core.Timeline, targets: gsap.TweenTarget[], vars: gsap.TweenVars, position: any) => {
+// if (targets.length === 0) {
+
+// }
+// }
+// } (tl: gsap.core.Timeline, )
+// }
+
+// const to = (targets: gsap.TweenTarget[], vars: gsap.TweenVars): gsap.core.Tween => {
+// gsap.
+// }
// #endregion ░░░░[GreenSock]░░░░
// #region ░░░░░░░[SVG]░░░░ SVG Generation & Manipulation ░░░░░░░ ~
@@ -1442,10 +1479,7 @@ const getRGBString = (red: string | number, green?: number, blue?: number, alpha
return null;
};
const getHEXString = (red: string | number, green?: number, blue?: number): HEXColor | null => {
- /**
- *
- * @param c
- */
+
function componentToHex(c: string | number): string {
const hex = c.toString(16);
return hex.length === 1 ? `0${hex}` : hex;
@@ -1683,7 +1717,7 @@ export default {
subGroup, shuffle,
// ████████ OBJECTS: Manipulation of Simple Key/Val Objects ████████
- remove, replace, partition,
+ remove, replace, partition, zip,
objClean, objSize, objMap, objFindKey, objFilter, objForEach, objCompact,
objClone, objMerge, objDiff, objExpand, objFlatten, objNullify,
objFreezeProps,
@@ -1692,8 +1726,10 @@ export default {
getDynamicFunc, withLog,
// ████████ HTML: Parsing HTML Code, Manipulating DOM Objects ████████
+ getSvgCode, getSvgPaths,
+
// ░░░░░░░ GreenSock ░░░░░░░
- gsap, get, set, getGSAngleDelta,
+ gsap, get, set, getGSAngleDelta, /* to, from, fromTo, */
getRawCirclePath, drawCirclePath,
@@ -1708,6 +1744,7 @@ export default {
// EVENT HANDLERS
EventHandlers,
+
// ░░░░░░░ SYSTEM: System-Specific Functions (Requires Configuration of System ID in constants.js) ░░░░░░░
isDocID, loc, getSetting, getTemplatePath, displayImageSelector