diff --git a/assets/animations/chat/roll-position-risky.webp b/assets/animations/chat/roll-position-risky.webp new file mode 100644 index 00000000..d2b417e7 Binary files /dev/null and b/assets/animations/chat/roll-position-risky.webp differ diff --git a/css/style.min.css b/css/style.min.css index af7d41b4..3966dd68 100644 --- a/css/style.min.css +++ b/css/style.min.css @@ -743,22 +743,63 @@ template { --blades-grey-dark-nums: 68, 68, 68; --blades-black-nums: 32, 32, 32; --blades-black-dark-nums: 0, 0, 0; - --blades-gold-bright-nums: 255, 231, 92; - --blades-gold-nums: 255, 215, 0; - --blades-gold-dark-nums: 184, 156, 0; - --blades-gold-darkest-nums: 55, 53, 0; - --blades-red-bright-nums: 220, 20, 60; - --blades-red-nums: 204, 0, 0; - --blades-red-dark-nums: 122, 0, 0; + --blades-gold-bright-nums: 206,180, 71; + --blades-gold-nums: 143,118, 11; + --blades-gold-dark-nums: 105, 86, 0; + --blades-gold-darkest-nums: 64, 52, 0; + --blades-red-bright-nums: 255, 0, 0; + --blades-red-nums: 200, 0, 0; + --blades-red-dark-nums: 150, 0, 0; --blades-red-darkest-nums: 50, 0, 0; --blades-green-bright-nums: 20, 220, 60; --blades-green-nums: 0, 204, 0; --blades-green-dark-nums: 0, 122, 0; --blades-green-darkest-nums: 0, 60, 0; - --blades-cyan-bright-nums: 198, 255, 255; - --blades-cyan-nums: 150, 255, 255; - --blades-cyan-dark-nums: 40, 120, 120; - --blades-cyan-darkest-nums: 25, 49, 49; + --blades-blue-bright-nums: 198, 255, 255; + --blades-blue-nums: 150, 255, 255; + --blades-blue-dark-nums: 40, 120, 120; + --blades-blue-darkest-nums: 25, 49, 49; + /* + NEW COLOR PALETTE OVERRIDE + + == GOLD == + http://paletton.com/#uid=11n0u0kNTr2qtG1K2DKRbkEVqcT + + shade 0 = #D7AF00 = rgb(215,175, 0) = rgba(215,175, 0,1) = rgb0(0.843,0.686,0) + shade 1 = #FFD82C = rgb(255,216, 44) = rgba(255,216, 44,1) = rgb0(1,0.847,0.173) + shade 2 = #FFCF00 = rgb(255,207, 0) = rgba(255,207, 0,1) = rgb0(1,0.812,0) + shade 3 = #A58600 = rgb(165,134, 0) = rgba(165,134, 0,1) = rgb0(0.647,0.525,0) + shade 4 = #675300 = rgb(103, 83, 0) = rgba(103, 83, 0,1) = rgb0(0.404,0.325,0)' + + == RED == + http://paletton.com/#uid=1000u0kTixTijNOwGQpTXmEXg9Y + shade 0 = #FF0000 = rgb(255, 0, 0) = rgba(255, 0, 0,1) = rgb0(1,0,0) + shade 1 = #FF6D6D = rgb(255,109,109) = rgba(255,109,109,1) = rgb0(1,0.427,0.427) + shade 2 = #FF0000 = rgb(255, 0, 0) = rgba(255, 0, 0,1) = rgb0(1,0,0) + shade 3 = #B40000 = rgb(180, 0, 0) = rgba(180, 0, 0,1) = rgb0(0.706,0,0) + shade 4 = #4F0000 = rgb( 79, 0, 0) = rgba( 79, 0, 0,1) = rgb0(0.31,0,0) + + == BLUE == + http://paletton.com/#uid=13i0u0kTixTodNREARdTRoAV1g4 + shade 0 = #009F9F = rgb( 0,159,159) = rgba( 0,159,159,1) = rgb0(0,0.624,0.624) + shade 1 = #34D5D5 = rgb( 52,213,213) = rgba( 52,213,213,1) = rgb0(0.204,0.835,0.835) + shade 2 = #00E0E0 = rgb( 0,224,224) = rgba( 0,224,224,1) = rgb0(0,0.878,0.878) + shade 3 = #007676 = rgb( 0,118,118) = rgba( 0,118,118,1) = rgb0(0,0.463,0.463) + shade 4 = #004D4D = rgb( 0, 77, 77) = rgba( 0, 77, 77,1) = rgb0(0,0.302,0.302) + */ + --blades-gold-bright-nums: 255,216, 44; + --blades-gold-nums: 215,175, 0; + --blades-gold-dark-nums: 165,134, 0; + --blades-gold-darkest-nums: 103, 83, 0; + /* --blades-red-bright-nums: 255,109,109; + --blades-red-nums: 255, 0, 0; + --blades-red-dark-nums: 180, 0, 0; + --blades-red-darkest-nums: 79, 0, 0; */ + --blades-blue-bright-nums: 0,224,224; + --blades-blue-nums: 52,213,213; + --blades-blue-dark-nums: 0,118,118; + --blades-blue-darkest-nums: 0, 77, 77; + /* END OVERRIDE */ --blades-white-bright: rgba(var(--blades-white-bright-nums), 1); --blades-white: rgba(var(--blades-white-nums), 1); --blades-grey-bright: rgba(var(--blades-grey-bright-nums), 1); @@ -766,6 +807,7 @@ template { --blades-grey-dark: rgba(var(--blades-grey-dark-nums), 1); --blades-black: rgba(var(--blades-black-nums), 1); --blades-black-dark: rgba(var(--blades-black-dark-nums), 1); + --blades-gold-brightest: rgba(var(--blades-gold-brightest-nums), 1); --blades-gold-bright: rgba(var(--blades-gold-bright-nums), 1); --blades-gold: rgba(var(--blades-gold-nums), 1); --blades-gold-dark: rgba(var(--blades-gold-dark-nums), 1); @@ -778,10 +820,10 @@ template { --blades-green: rgba(var(--blades-green-nums), 1); --blades-green-dark: rgba(var(--blades-green-dark-nums), 1); --blades-green-darkest: rgba(var(--blades-green-darkest-nums), 1); - --blades-cyan-bright: rgba(var(--blades-cyan-bright-nums), 1); - --blades-cyan: rgba(var(--blades-cyan-nums), 1); - --blades-cyan-dark: rgba(var(--blades-cyan-dark-nums), 1); - --blades-cyan-darkest: rgba(var(--blades-cyan-darkest-nums), 1); + --blades-blue-bright: rgba(var(--blades-blue-bright-nums), 1); + --blades-blue: rgba(var(--blades-blue-nums), 1); + --blades-blue-dark: rgba(var(--blades-blue-dark-nums), 1); + --blades-blue-darkest: rgba(var(--blades-blue-darkest-nums), 1); --blades-white-fade: rgba(var(--blades-white-nums), 0.5); --blades-white-fade-strong: rgba(var(--blades-white-nums), 0.25); --blades-white-bright-fade: rgba(var(--blades-white-bright-nums), 0.5); @@ -792,10 +834,10 @@ template { --blades-black-dark-fade-strong: rgba(var(--blades-black-dark-nums), 0.25); --blades-red-dark-fade: rgba(var(--blades-red-dark-nums), 0.5); --blades-green-dark-fade: rgba(var(--blades-green-dark-nums), 0.5); - --blades-cyan-dark-fade: rgba(var(--blades-cyan-dark-nums), 0.5); + --blades-blue-dark-fade: rgba(var(--blades-blue-dark-nums), 0.5); --blades-red-dark-fade-strong: rgba(var(--blades-red-dark-nums), 0.25); --blades-green-dark-fade-strong: rgba(var(--blades-green-dark-nums), 0.25); - --blades-cyan-dark-fade-strong: rgba(var(--blades-cyan-dark-nums), 0.25); + --blades-blue-dark-fade-strong: rgba(var(--blades-blue-dark-nums), 0.25); --color-primary: var(--blades-white-nums); --color-background: var(--blades-black-nums); --color-background-lightest: var(--blades-grey-nums); @@ -1819,13 +1861,9 @@ template { :root .comp.consequence-display-container, :root * .comp.consequence-display-container { --container-height: 40px; - --container-left-shift: 50px; - --csq-icon-dark: var(--blades-red-dark); - --csq-icon-med: var(--blades-red); - --csq-icon-bright: var(--blades-red-bright); - --csq-type-bg: var(--csq-icon-dark); - --csq-type-color: var(--blades-black-dark); + --container-left-shift: 70px; --csq-icon-bg-color: var(--blades-black-dark); + --csq-type-bg: var(--csq-icon-dark); --csq-button-size-mult: 0.33; position: relative; display: block; @@ -1833,34 +1871,128 @@ template { max-height: var(--container-height); min-height: var(--container-height); } +@keyframes anim-glow { + 0% { + box-shadow: 0 0 0px var(--blades-red-bright); + background-color: var(--blades-red-darkest); + } + 10% { + background-color: var(--blades-red-bright); + } + 100% { + box-shadow: 0 0 10px 10px transparent; + background-color: var(--blades-red-darkest); + } +} +@keyframes icon-glow { + 0% { + filter: brightness(1); + } + 10% { + filter: brightness(1); + } + 100% { + filter: brightness(1); + } +} +@keyframes icon-red-pulse { + 0% { + scale: 1; + fill: var(--blades-grey); + } + 10% { + scale: 1.1; + fill: var(--blades-red-bright); + } + 100% { + scale: 1; + fill: var(--blades-grey); + } +} +:root .comp.consequence-display-container .base-consequence, +:root * .comp.consequence-display-container .base-consequence { + --csq-icon-dark: var(--blades-black); + --csq-icon-med: var(--blades-grey); + --csq-icon-bright: var(--blades-white); + --csq-type-color: var(--blades-grey); + --csq-name-color: var(--blades-white); +} +:root .comp.consequence-display-container .accept-consequence, +:root * .comp.consequence-display-container .accept-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-red-dark); + --csq-icon-med: var(--blades-red); + --csq-icon-bright: var(--blades-red-bright); + --csq-type-color: var(--blades-black-dark); + --csq-name-color: var(--blades-red); +} :root .comp.consequence-display-container .resist-consequence, :root * .comp.consequence-display-container .resist-consequence { + opacity: 0; --csq-icon-dark: var(--blades-gold-dark); --csq-icon-med: var(--blades-gold); --csq-icon-bright: var(--blades-gold-bright); --csq-type-color: var(--blades-gold-dark); --csq-name-color: var(--blades-gold-bright); } +:root .comp.consequence-display-container .special-armor-consequence, +:root * .comp.consequence-display-container .special-armor-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-blue-dark); + --csq-icon-med: var(--blades-blue); + --csq-icon-bright: var(--blades-blue-bright); + --csq-type-color: var(--blades-blue-dark); + --csq-name-color: var(--blades-blue-bright); +} +:root .comp.consequence-display-container .consequence-interaction-pad, +:root * .comp.consequence-display-container .consequence-interaction-pad { + display: none; + display: block; + position: absolute; + z-index: 2; + pointer-events: auto; + height: 100%; + top: 0; +} +:root .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad, +:root * .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad { + --pad-left-shift: calc(var(--container-left-shift) + (var(--container-height))); + left: var(--pad-left-shift); + width: calc(100% - var(--pad-left-shift)); +} +:root .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, :root .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root * .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, +:root * .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + left: 0; + width: calc(var(--container-left-shift) - var(--container-height) * 0); +} +:root .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root * .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + height: 40%; + z-index: 3; +} :root .comp.consequence-display-container .consequence-icon-container, :root * .comp.consequence-display-container .consequence-icon-container { position: relative; - height: var(--container-height); - width: var(--container-height); - min-width: var(--container-height); - max-width: var(--container-height); - min-height: var(--container-height); - max-height: var(--container-height); + height: calc(var(--container-height) * 0.75); + max-width: calc(var(--container-height) * 0.75); background: transparent; left: var(--container-left-shift); z-index: 1; + pointer-events: auto; + transition: 0.2s; +} +:root .comp.consequence-display-container .consequence-icon-container:hover, +:root * .comp.consequence-display-container .consequence-icon-container:hover { + filter: brightness(2); } :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle, :root * .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle { position: absolute; translate: -50% -50%; - transform-origin: 50% 50%; - top: 50%; - left: 50%; + transform-origin: 100% 0%; + top: calc(var(--container-height) * 0.5); + left: calc(var(--container-height) * 0.5); border-radius: 50%; height: var(--container-height); width: var(--container-height); @@ -1897,6 +2029,30 @@ template { :root * .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg .fill-linear { fill: var(--csq-icon-med); } +:root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path, +:root * .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path { + transform-origin: 50% 50%; +} +:root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence, +:root * .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence { + scale: 0.75; + animation: icon-glow 2s ease infinite; + pointer-events: auto; +} +:root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path, +:root * .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path { + animation: icon-red-pulse 2s ease infinite; +} +:root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover, +:root * .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover { + filter: brightness(2) !important; + animation: none; +} +:root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence, +:root * .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, +:root * .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence { + outline-width: 2px; +} :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, :root * .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon { height: 100%; @@ -1908,7 +2064,7 @@ template { display: flex; flex-direction: row; flex-wrap: nowrap; - bottom: 0px; + bottom: -10px; } :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg, :root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg { @@ -1916,10 +2072,16 @@ template { z-index: -1; height: 100%; width: calc(100% + 24px); + transform-origin: 0% 50%; top: 0px; background: var(--csq-icon-bright); display: block; } +:root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg, +:root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, +:root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg { + transform-origin: 100% 50%; +} :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label, :root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label { position: relative; @@ -1928,6 +2090,9 @@ template { font-size: 10px; line-height: 14px; color: var(--blades-grey); + font-weight: 800; + text-shadow: 0px 0px 1px var(--blades-black-dark); + letter-spacing: 1; text-transform: uppercase; white-space: nowrap; } @@ -1947,7 +2112,7 @@ template { } :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container, :root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container { - right: calc(100% - 3px); + right: 100%; } :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg, :root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg { @@ -1956,7 +2121,7 @@ template { } :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container, :root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container { - left: 100%; + left: 140%; } :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg, :root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg { @@ -1974,7 +2139,7 @@ template { height: calc(var(--container-height) * 0.33); transform-origin: 0% 50%; left: calc(var(--container-height) + var(--container-left-shift) - 10px); - top: 0px; + top: -2px; padding: 0 5px 0 15px; } :root .comp.consequence-display-container .consequence-type-container .consequence-type-bg, @@ -1984,21 +2149,22 @@ template { left: -20px; height: 100%; width: 170px; + transform-origin: 0% 50%; transform: skewX(-45deg); - background: var(--csq-type-bg); + background: var(--csq-icon-dark); } :root .comp.consequence-display-container .consequence-type-container .consequence-type, :root * .comp.consequence-display-container .consequence-type-container .consequence-type { position: absolute; top: 0; - left: 10px; + transform-origin: 0% 50%; white-space: nowrap; font-family: Oswald, sans-serif; text-transform: uppercase; text-align: right; font-size: 10px; color: var(--csq-type-color); - font-weight: bold; + font-weight: normal; } :root .comp.consequence-display-container .consequence-name-container, :root * .comp.consequence-display-container .consequence-name-container { @@ -2016,9 +2182,10 @@ template { z-index: 1; padding: 0 5px 0 35px; font-size: 14px; - line-height: calc(var(--container-height) * 0.55); + line-height: 17px; font-family: Kirsty, serif; font-variant: small-caps; + transform-origin: 0% 50%; color: var(--csq-icon-bright); font-style: italic; } @@ -2030,7 +2197,7 @@ template { :root * .comp.consequence-display-container .consequence-footer-container { position: absolute; height: calc(var(--container-height) * var(--csq-button-size-mult)); - width: 120px; + width: auto; bottom: 0; top: unset; left: calc(var(--container-height) + var(--container-left-shift) - 20px); @@ -2045,9 +2212,20 @@ template { background: var(--csq-icon-bright); display: block; transform: skewX(45deg); + transform-origin: 0% 50%; +} +:root .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence, +:root * .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence { + width: 120px; } -:root .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute, -:root * .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute { +:root .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence, +:root * .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence { + width: 250px; +} +:root .comp.consequence-display-container .consequence-footer-container .consequence-footer-message, +:root * .comp.consequence-display-container .consequence-footer-container .consequence-footer-message { + position: absolute; + white-space: nowrap; font-family: Oswald, sans-serif; font-weight: bold; color: var(--blades-black-dark); @@ -2055,6 +2233,7 @@ template { line-height: 14px; padding-left: 25px; justify-content: flex-start; + transform-origin: 0% 50%; gap: 5px; } :root .comp.consequence-display-container .consequence-footer-container .dotline, @@ -3407,13 +3586,9 @@ template { } :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container { --container-height: 40px; - --container-left-shift: 50px; - --csq-icon-dark: var(--blades-red-dark); - --csq-icon-med: var(--blades-red); - --csq-icon-bright: var(--blades-red-bright); - --csq-type-bg: var(--csq-icon-dark); - --csq-type-color: var(--blades-black-dark); + --container-left-shift: 70px; --csq-icon-bg-color: var(--blades-black-dark); + --csq-type-bg: var(--csq-icon-dark); --csq-button-size-mult: 0.33; position: relative; display: block; @@ -3421,31 +3596,116 @@ template { max-height: var(--container-height); min-height: var(--container-height); } +@keyframes anim-glow { + 0% { + box-shadow: 0 0 0px var(--blades-red-bright); + background-color: var(--blades-red-darkest); + } + 10% { + background-color: var(--blades-red-bright); + } + 100% { + box-shadow: 0 0 10px 10px transparent; + background-color: var(--blades-red-darkest); + } +} +@keyframes icon-glow { + 0% { + filter: brightness(1); + } + 10% { + filter: brightness(1); + } + 100% { + filter: brightness(1); + } +} +@keyframes icon-red-pulse { + 0% { + scale: 1; + fill: var(--blades-grey); + } + 10% { + scale: 1.1; + fill: var(--blades-red-bright); + } + 100% { + scale: 1; + fill: var(--blades-grey); + } +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .base-consequence { + --csq-icon-dark: var(--blades-black); + --csq-icon-med: var(--blades-grey); + --csq-icon-bright: var(--blades-white); + --csq-type-color: var(--blades-grey); + --csq-name-color: var(--blades-white); +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .accept-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-red-dark); + --csq-icon-med: var(--blades-red); + --csq-icon-bright: var(--blades-red-bright); + --csq-type-color: var(--blades-black-dark); + --csq-name-color: var(--blades-red); +} :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .resist-consequence { + opacity: 0; --csq-icon-dark: var(--blades-gold-dark); --csq-icon-med: var(--blades-gold); --csq-icon-bright: var(--blades-gold-bright); --csq-type-color: var(--blades-gold-dark); --csq-name-color: var(--blades-gold-bright); } +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .special-armor-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-blue-dark); + --csq-icon-med: var(--blades-blue); + --csq-icon-bright: var(--blades-blue-bright); + --csq-type-color: var(--blades-blue-dark); + --csq-name-color: var(--blades-blue-bright); +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-interaction-pad { + display: none; + display: block; + position: absolute; + z-index: 2; + pointer-events: auto; + height: 100%; + top: 0; +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad { + --pad-left-shift: calc(var(--container-left-shift) + (var(--container-height))); + left: var(--pad-left-shift); + width: calc(100% - var(--pad-left-shift)); +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + left: 0; + width: calc(var(--container-left-shift) - var(--container-height) * 0); +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + height: 40%; + z-index: 3; +} :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container { position: relative; - height: var(--container-height); - width: var(--container-height); - min-width: var(--container-height); - max-width: var(--container-height); - min-height: var(--container-height); - max-height: var(--container-height); + height: calc(var(--container-height) * 0.75); + max-width: calc(var(--container-height) * 0.75); background: transparent; left: var(--container-left-shift); z-index: 1; + pointer-events: auto; + transition: 0.2s; +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container:hover { + filter: brightness(2); } :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle { position: absolute; translate: -50% -50%; - transform-origin: 50% 50%; - top: 50%; - left: 50%; + transform-origin: 100% 0%; + top: calc(var(--container-height) * 0.5); + left: calc(var(--container-height) * 0.5); border-radius: 50%; height: var(--container-height); width: var(--container-height); @@ -3476,6 +3736,24 @@ template { :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg .fill-linear { fill: var(--csq-icon-med); } +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path { + transform-origin: 50% 50%; +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence { + scale: 0.75; + animation: icon-glow 2s ease infinite; + pointer-events: auto; +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path { + animation: icon-red-pulse 2s ease infinite; +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover { + filter: brightness(2) !important; + animation: none; +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence { + outline-width: 2px; +} :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon { height: 100%; } @@ -3485,17 +3763,21 @@ template { display: flex; flex-direction: row; flex-wrap: nowrap; - bottom: 0px; + bottom: -10px; } :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg { position: absolute; z-index: -1; height: 100%; width: calc(100% + 24px); + transform-origin: 0% 50%; top: 0px; background: var(--csq-icon-bright); display: block; } +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg { + transform-origin: 100% 50%; +} :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label { position: relative; z-index: 1; @@ -3503,6 +3785,9 @@ template { font-size: 10px; line-height: 14px; color: var(--blades-grey); + font-weight: 800; + text-shadow: 0px 0px 1px var(--blades-black-dark); + letter-spacing: 1; text-transform: uppercase; white-space: nowrap; } @@ -3519,14 +3804,14 @@ template { margin: 0; } :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container { - right: calc(100% - 3px); + right: 100%; } :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg { left: -7px; transform: skewX(45deg); } :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container { - left: 100%; + left: 140%; } :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg { right: -7px; @@ -3541,7 +3826,7 @@ template { height: calc(var(--container-height) * 0.33); transform-origin: 0% 50%; left: calc(var(--container-height) + var(--container-left-shift) - 10px); - top: 0px; + top: -2px; padding: 0 5px 0 15px; } :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-type-container .consequence-type-bg { @@ -3550,20 +3835,21 @@ template { left: -20px; height: 100%; width: 170px; + transform-origin: 0% 50%; transform: skewX(-45deg); - background: var(--csq-type-bg); + background: var(--csq-icon-dark); } :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-type-container .consequence-type { position: absolute; top: 0; - left: 10px; + transform-origin: 0% 50%; white-space: nowrap; font-family: Oswald, sans-serif; text-transform: uppercase; text-align: right; font-size: 10px; color: var(--csq-type-color); - font-weight: bold; + font-weight: normal; } :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-name-container { position: absolute; @@ -3579,9 +3865,10 @@ template { z-index: 1; padding: 0 5px 0 35px; font-size: 14px; - line-height: calc(var(--container-height) * 0.55); + line-height: 17px; font-family: Kirsty, serif; font-variant: small-caps; + transform-origin: 0% 50%; color: var(--csq-icon-bright); font-style: italic; } @@ -3591,7 +3878,7 @@ template { :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-footer-container { position: absolute; height: calc(var(--container-height) * var(--csq-button-size-mult)); - width: 120px; + width: auto; bottom: 0; top: unset; left: calc(var(--container-height) + var(--container-left-shift) - 20px); @@ -3605,8 +3892,17 @@ template { background: var(--csq-icon-bright); display: block; transform: skewX(45deg); + transform-origin: 0% 50%; +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence { + width: 120px; } -:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute { +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence { + width: 250px; +} +:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-footer-container .consequence-footer-message { + position: absolute; + white-space: nowrap; font-family: Oswald, sans-serif; font-weight: bold; color: var(--blades-black-dark); @@ -3614,6 +3910,7 @@ template { line-height: 14px; padding-left: 25px; justify-content: flex-start; + transform-origin: 0% 50%; gap: 5px; } :root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-footer-container .dotline { @@ -6491,13 +6788,9 @@ template { :root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container, :root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container { --container-height: 40px; - --container-left-shift: 50px; - --csq-icon-dark: var(--blades-red-dark); - --csq-icon-med: var(--blades-red); - --csq-icon-bright: var(--blades-red-bright); - --csq-type-bg: var(--csq-icon-dark); - --csq-type-color: var(--blades-black-dark); + --container-left-shift: 70px; --csq-icon-bg-color: var(--blades-black-dark); + --csq-type-bg: var(--csq-icon-dark); --csq-button-size-mult: 0.33; position: relative; display: block; @@ -6505,32 +6798,153 @@ template { max-height: var(--container-height); min-height: var(--container-height); } +@keyframes anim-glow { + 0% { + box-shadow: 0 0 0px var(--blades-red-bright); + background-color: var(--blades-red-darkest); + } + 10% { + background-color: var(--blades-red-bright); + } + 100% { + box-shadow: 0 0 10px 10px transparent; + background-color: var(--blades-red-darkest); + } +} +@keyframes icon-glow { + 0% { + filter: brightness(1); + } + 10% { + filter: brightness(1); + } + 100% { + filter: brightness(1); + } +} +@keyframes icon-red-pulse { + 0% { + scale: 1; + fill: var(--blades-grey); + } + 10% { + scale: 1.1; + fill: var(--blades-red-bright); + } + 100% { + scale: 1; + fill: var(--blades-grey); + } +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .base-consequence, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .base-consequence, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .base-consequence, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .base-consequence, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .base-consequence { + --csq-icon-dark: var(--blades-black); + --csq-icon-med: var(--blades-grey); + --csq-icon-bright: var(--blades-white); + --csq-type-color: var(--blades-grey); + --csq-name-color: var(--blades-white); +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .accept-consequence, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .accept-consequence, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .accept-consequence, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .accept-consequence, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .accept-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-red-dark); + --csq-icon-med: var(--blades-red); + --csq-icon-bright: var(--blades-red-bright); + --csq-type-color: var(--blades-black-dark); + --csq-name-color: var(--blades-red); +} :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .resist-consequence, :root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .resist-consequence, :root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .resist-consequence, :root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .resist-consequence, :root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .resist-consequence { + opacity: 0; --csq-icon-dark: var(--blades-gold-dark); --csq-icon-med: var(--blades-gold); --csq-icon-bright: var(--blades-gold-bright); --csq-type-color: var(--blades-gold-dark); --csq-name-color: var(--blades-gold-bright); } +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .special-armor-consequence, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .special-armor-consequence, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .special-armor-consequence, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .special-armor-consequence, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .special-armor-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-blue-dark); + --csq-icon-med: var(--blades-blue); + --csq-icon-bright: var(--blades-blue-bright); + --csq-type-color: var(--blades-blue-dark); + --csq-name-color: var(--blades-blue-bright); +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-interaction-pad, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-interaction-pad, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-interaction-pad, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-interaction-pad, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-interaction-pad { + display: none; + display: block; + position: absolute; + z-index: 2; + pointer-events: auto; + height: 100%; + top: 0; +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad { + --pad-left-shift: calc(var(--container-left-shift) + (var(--container-height))); + left: var(--pad-left-shift); + width: calc(100% - var(--pad-left-shift)); +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + left: 0; + width: calc(var(--container-left-shift) - var(--container-height) * 0); +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + height: 40%; + z-index: 3; +} :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container, :root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container, :root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container, :root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container, :root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container { position: relative; - height: var(--container-height); - width: var(--container-height); - min-width: var(--container-height); - max-width: var(--container-height); - min-height: var(--container-height); - max-height: var(--container-height); + height: calc(var(--container-height) * 0.75); + max-width: calc(var(--container-height) * 0.75); background: transparent; left: var(--container-left-shift); z-index: 1; + pointer-events: auto; + transition: 0.2s; +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container:hover, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container:hover, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container:hover, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container:hover, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container:hover { + filter: brightness(2); } :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle, :root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle, @@ -6539,9 +6953,9 @@ template { :root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle { position: absolute; translate: -50% -50%; - transform-origin: 50% 50%; - top: 50%; - left: 50%; + transform-origin: 100% 0%; + top: calc(var(--container-height) * 0.5); + left: calc(var(--container-height) * 0.5); border-radius: 50%; height: var(--container-height); width: var(--container-height); @@ -6596,9 +7010,51 @@ template { :root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg .fill-linear { fill: var(--csq-icon-med); } -:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, -:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, -:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path { + transform-origin: 50% 50%; +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence { + scale: 0.75; + animation: icon-glow 2s ease infinite; + pointer-events: auto; +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path { + animation: icon-red-pulse 2s ease infinite; +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover { + filter: brightness(2) !important; + animation: none; +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence { + outline-width: 2px; +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, :root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, :root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon { height: 100%; @@ -6613,7 +7069,7 @@ template { display: flex; flex-direction: row; flex-wrap: nowrap; - bottom: 0px; + bottom: -10px; } :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg, :root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg, @@ -6624,10 +7080,22 @@ template { z-index: -1; height: 100%; width: calc(100% + 24px); + transform-origin: 0% 50%; top: 0px; background: var(--csq-icon-bright); display: block; } +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg { + transform-origin: 100% 50%; +} :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label, :root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label, :root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label, @@ -6639,6 +7107,9 @@ template { font-size: 10px; line-height: 14px; color: var(--blades-grey); + font-weight: 800; + text-shadow: 0px 0px 1px var(--blades-black-dark); + letter-spacing: 1; text-transform: uppercase; white-space: nowrap; } @@ -6667,7 +7138,7 @@ template { :root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container, :root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container, :root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container { - right: calc(100% - 3px); + right: 100%; } :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg, :root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg, @@ -6682,7 +7153,7 @@ template { :root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container, :root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container, :root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container { - left: 100%; + left: 140%; } :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg, :root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg, @@ -6709,7 +7180,7 @@ template { height: calc(var(--container-height) * 0.33); transform-origin: 0% 50%; left: calc(var(--container-height) + var(--container-left-shift) - 10px); - top: 0px; + top: -2px; padding: 0 5px 0 15px; } :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-type-container .consequence-type-bg, @@ -6722,8 +7193,9 @@ template { left: -20px; height: 100%; width: 170px; + transform-origin: 0% 50%; transform: skewX(-45deg); - background: var(--csq-type-bg); + background: var(--csq-icon-dark); } :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-type-container .consequence-type, :root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-type-container .consequence-type, @@ -6732,14 +7204,14 @@ template { :root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-type-container .consequence-type { position: absolute; top: 0; - left: 10px; + transform-origin: 0% 50%; white-space: nowrap; font-family: Oswald, sans-serif; text-transform: uppercase; text-align: right; font-size: 10px; color: var(--csq-type-color); - font-weight: bold; + font-weight: normal; } :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-name-container, :root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-name-container, @@ -6763,9 +7235,10 @@ template { z-index: 1; padding: 0 5px 0 35px; font-size: 14px; - line-height: calc(var(--container-height) * 0.55); + line-height: 17px; font-family: Kirsty, serif; font-variant: small-caps; + transform-origin: 0% 50%; color: var(--csq-icon-bright); font-style: italic; } @@ -6783,7 +7256,7 @@ template { :root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-footer-container { position: absolute; height: calc(var(--container-height) * var(--csq-button-size-mult)); - width: 120px; + width: auto; bottom: 0; top: unset; left: calc(var(--container-height) + var(--container-left-shift) - 20px); @@ -6801,12 +7274,29 @@ template { background: var(--csq-icon-bright); display: block; transform: skewX(45deg); + transform-origin: 0% 50%; +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence { + width: 120px; } -:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute, -:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute, -:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute, -:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute, -:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute { +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence { + width: 250px; +} +:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-footer-container .consequence-footer-message, +:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-footer-container .consequence-footer-message, +:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-footer-container .consequence-footer-message, +:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-footer-container .consequence-footer-message, +:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-footer-container .consequence-footer-message { + position: absolute; + white-space: nowrap; font-family: Oswald, sans-serif; font-weight: bold; color: var(--blades-black-dark); @@ -6814,6 +7304,7 @@ template { line-height: 14px; padding-left: 25px; justify-content: flex-start; + transform-origin: 0% 50%; gap: 5px; } :root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-footer-container .dotline, @@ -9229,13 +9720,9 @@ template { :root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container, :root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container { --container-height: 40px; - --container-left-shift: 50px; - --csq-icon-dark: var(--blades-red-dark); - --csq-icon-med: var(--blades-red); - --csq-icon-bright: var(--blades-red-bright); - --csq-type-bg: var(--csq-icon-dark); - --csq-type-color: var(--blades-black-dark); + --container-left-shift: 70px; --csq-icon-bg-color: var(--blades-black-dark); + --csq-type-bg: var(--csq-icon-dark); --csq-button-size-mult: 0.33; position: relative; display: block; @@ -9243,32 +9730,153 @@ template { max-height: var(--container-height); min-height: var(--container-height); } +@keyframes anim-glow { + 0% { + box-shadow: 0 0 0px var(--blades-red-bright); + background-color: var(--blades-red-darkest); + } + 10% { + background-color: var(--blades-red-bright); + } + 100% { + box-shadow: 0 0 10px 10px transparent; + background-color: var(--blades-red-darkest); + } +} +@keyframes icon-glow { + 0% { + filter: brightness(1); + } + 10% { + filter: brightness(1); + } + 100% { + filter: brightness(1); + } +} +@keyframes icon-red-pulse { + 0% { + scale: 1; + fill: var(--blades-grey); + } + 10% { + scale: 1.1; + fill: var(--blades-red-bright); + } + 100% { + scale: 1; + fill: var(--blades-grey); + } +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .base-consequence, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .base-consequence, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .base-consequence, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .base-consequence, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .base-consequence { + --csq-icon-dark: var(--blades-black); + --csq-icon-med: var(--blades-grey); + --csq-icon-bright: var(--blades-white); + --csq-type-color: var(--blades-grey); + --csq-name-color: var(--blades-white); +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .accept-consequence, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .accept-consequence, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .accept-consequence, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .accept-consequence, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .accept-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-red-dark); + --csq-icon-med: var(--blades-red); + --csq-icon-bright: var(--blades-red-bright); + --csq-type-color: var(--blades-black-dark); + --csq-name-color: var(--blades-red); +} :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .resist-consequence, :root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .resist-consequence, :root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .resist-consequence, :root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .resist-consequence, :root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .resist-consequence { + opacity: 0; --csq-icon-dark: var(--blades-gold-dark); --csq-icon-med: var(--blades-gold); --csq-icon-bright: var(--blades-gold-bright); --csq-type-color: var(--blades-gold-dark); --csq-name-color: var(--blades-gold-bright); } +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .special-armor-consequence, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .special-armor-consequence, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .special-armor-consequence, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .special-armor-consequence, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .special-armor-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-blue-dark); + --csq-icon-med: var(--blades-blue); + --csq-icon-bright: var(--blades-blue-bright); + --csq-type-color: var(--blades-blue-dark); + --csq-name-color: var(--blades-blue-bright); +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-interaction-pad, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-interaction-pad, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-interaction-pad, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-interaction-pad, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-interaction-pad { + display: none; + display: block; + position: absolute; + z-index: 2; + pointer-events: auto; + height: 100%; + top: 0; +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad { + --pad-left-shift: calc(var(--container-left-shift) + (var(--container-height))); + left: var(--pad-left-shift); + width: calc(100% - var(--pad-left-shift)); +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + left: 0; + width: calc(var(--container-left-shift) - var(--container-height) * 0); +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + height: 40%; + z-index: 3; +} :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container, :root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container, :root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container, :root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container, :root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container { position: relative; - height: var(--container-height); - width: var(--container-height); - min-width: var(--container-height); - max-width: var(--container-height); - min-height: var(--container-height); - max-height: var(--container-height); + height: calc(var(--container-height) * 0.75); + max-width: calc(var(--container-height) * 0.75); background: transparent; left: var(--container-left-shift); z-index: 1; + pointer-events: auto; + transition: 0.2s; +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container:hover, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container:hover, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container:hover, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container:hover, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container:hover { + filter: brightness(2); } :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle, :root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle, @@ -9277,9 +9885,9 @@ template { :root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle { position: absolute; translate: -50% -50%; - transform-origin: 50% 50%; - top: 50%; - left: 50%; + transform-origin: 100% 0%; + top: calc(var(--container-height) * 0.5); + left: calc(var(--container-height) * 0.5); border-radius: 50%; height: var(--container-height); width: var(--container-height); @@ -9334,6 +9942,48 @@ template { :root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg .fill-linear { fill: var(--csq-icon-med); } +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path { + transform-origin: 50% 50%; +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence { + scale: 0.75; + animation: icon-glow 2s ease infinite; + pointer-events: auto; +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path { + animation: icon-red-pulse 2s ease infinite; +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover { + filter: brightness(2) !important; + animation: none; +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence { + outline-width: 2px; +} :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, :root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, :root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, @@ -9351,7 +10001,7 @@ template { display: flex; flex-direction: row; flex-wrap: nowrap; - bottom: 0px; + bottom: -10px; } :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg, :root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg, @@ -9362,10 +10012,22 @@ template { z-index: -1; height: 100%; width: calc(100% + 24px); + transform-origin: 0% 50%; top: 0px; background: var(--csq-icon-bright); display: block; } +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg { + transform-origin: 100% 50%; +} :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label, :root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label, :root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label, @@ -9377,6 +10039,9 @@ template { font-size: 10px; line-height: 14px; color: var(--blades-grey); + font-weight: 800; + text-shadow: 0px 0px 1px var(--blades-black-dark); + letter-spacing: 1; text-transform: uppercase; white-space: nowrap; } @@ -9405,7 +10070,7 @@ template { :root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container, :root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container, :root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container { - right: calc(100% - 3px); + right: 100%; } :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg, :root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg, @@ -9420,7 +10085,7 @@ template { :root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container, :root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container, :root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container { - left: 100%; + left: 140%; } :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg, :root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg, @@ -9447,7 +10112,7 @@ template { height: calc(var(--container-height) * 0.33); transform-origin: 0% 50%; left: calc(var(--container-height) + var(--container-left-shift) - 10px); - top: 0px; + top: -2px; padding: 0 5px 0 15px; } :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-type-container .consequence-type-bg, @@ -9460,8 +10125,9 @@ template { left: -20px; height: 100%; width: 170px; + transform-origin: 0% 50%; transform: skewX(-45deg); - background: var(--csq-type-bg); + background: var(--csq-icon-dark); } :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-type-container .consequence-type, :root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-type-container .consequence-type, @@ -9470,14 +10136,14 @@ template { :root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-type-container .consequence-type { position: absolute; top: 0; - left: 10px; + transform-origin: 0% 50%; white-space: nowrap; font-family: Oswald, sans-serif; text-transform: uppercase; text-align: right; font-size: 10px; color: var(--csq-type-color); - font-weight: bold; + font-weight: normal; } :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-name-container, :root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-name-container, @@ -9501,9 +10167,10 @@ template { z-index: 1; padding: 0 5px 0 35px; font-size: 14px; - line-height: calc(var(--container-height) * 0.55); + line-height: 17px; font-family: Kirsty, serif; font-variant: small-caps; + transform-origin: 0% 50%; color: var(--csq-icon-bright); font-style: italic; } @@ -9521,7 +10188,7 @@ template { :root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-footer-container { position: absolute; height: calc(var(--container-height) * var(--csq-button-size-mult)); - width: 120px; + width: auto; bottom: 0; top: unset; left: calc(var(--container-height) + var(--container-left-shift) - 20px); @@ -9539,12 +10206,29 @@ template { background: var(--csq-icon-bright); display: block; transform: skewX(45deg); + transform-origin: 0% 50%; +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence { + width: 120px; } -:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute, -:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute, -:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute, -:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute, -:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute { +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence { + width: 250px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-message, +:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-message, +:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-message, +:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-message, +:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-footer-container .consequence-footer-message { + position: absolute; + white-space: nowrap; font-family: Oswald, sans-serif; font-weight: bold; color: var(--blades-black-dark); @@ -9552,6 +10236,7 @@ template { line-height: 14px; padding-left: 25px; justify-content: flex-start; + transform-origin: 0% 50%; gap: 5px; } :root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-footer-container .dotline, @@ -10157,143 +10842,1709 @@ template { bottom: 100%; } } -:root body.vtt.game.system-eunos-blades #interface #chat, -:root body.vtt.game.system-eunos-blades #interface #chat *, -:root body.vtt.game.system-eunos-blades #controls #chat, -:root body.vtt.game.system-eunos-blades #controls #chat *, -:root body.vtt.game.system-eunos-blades #navigation #chat, -:root body.vtt.game.system-eunos-blades #navigation #chat *, -:root body.vtt.game.system-eunos-blades #hotbar #chat, -:root body.vtt.game.system-eunos-blades #hotbar #chat *, -:root body.vtt.game.system-eunos-blades #players #chat, -:root body.vtt.game.system-eunos-blades #players #chat * { - --font-primary: "Minion Pro"; - --font-heading: "Kirsty"; - --font-weight-heading: normal; - --text-shadow-heading: none; - --line-height-heading: 1.2; +:root body.vtt.game.system-eunos-blades #interface #chat blockquote, +:root body.vtt.game.system-eunos-blades #controls #chat blockquote, +:root body.vtt.game.system-eunos-blades #navigation #chat blockquote, +:root body.vtt.game.system-eunos-blades #hotbar #chat blockquote, +:root body.vtt.game.system-eunos-blades #players #chat blockquote { + border-left: 2px solid var(--blades-grey-bright); + margin-left: 1.5rem; + padding-left: 1rem; } -:root body.vtt.game.system-eunos-blades #interface #chat .chat-message, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message { - background: var(--blades-black); +:root body.vtt.game.system-eunos-blades #interface #chat table, +:root body.vtt.game.system-eunos-blades #interface #chat table tr, +:root body.vtt.game.system-eunos-blades #interface #chat table tr th, +:root body.vtt.game.system-eunos-blades #interface #chat table tr td, +:root body.vtt.game.system-eunos-blades #interface #chat table tbody, +:root body.vtt.game.system-eunos-blades #interface #chat table tbody tr, +:root body.vtt.game.system-eunos-blades #interface #chat table tbody td, +:root body.vtt.game.system-eunos-blades #interface #chat table thead, +:root body.vtt.game.system-eunos-blades #interface #chat table thead tr, +:root body.vtt.game.system-eunos-blades #interface #chat table thead tr th, +:root body.vtt.game.system-eunos-blades #interface #chat table thead tr td, +:root body.vtt.game.system-eunos-blades #controls #chat table, +:root body.vtt.game.system-eunos-blades #controls #chat table tr, +:root body.vtt.game.system-eunos-blades #controls #chat table tr th, +:root body.vtt.game.system-eunos-blades #controls #chat table tr td, +:root body.vtt.game.system-eunos-blades #controls #chat table tbody, +:root body.vtt.game.system-eunos-blades #controls #chat table tbody tr, +:root body.vtt.game.system-eunos-blades #controls #chat table tbody td, +:root body.vtt.game.system-eunos-blades #controls #chat table thead, +:root body.vtt.game.system-eunos-blades #controls #chat table thead tr, +:root body.vtt.game.system-eunos-blades #controls #chat table thead tr th, +:root body.vtt.game.system-eunos-blades #controls #chat table thead tr td, +:root body.vtt.game.system-eunos-blades #navigation #chat table, +:root body.vtt.game.system-eunos-blades #navigation #chat table tr, +:root body.vtt.game.system-eunos-blades #navigation #chat table tr th, +:root body.vtt.game.system-eunos-blades #navigation #chat table tr td, +:root body.vtt.game.system-eunos-blades #navigation #chat table tbody, +:root body.vtt.game.system-eunos-blades #navigation #chat table tbody tr, +:root body.vtt.game.system-eunos-blades #navigation #chat table tbody td, +:root body.vtt.game.system-eunos-blades #navigation #chat table thead, +:root body.vtt.game.system-eunos-blades #navigation #chat table thead tr, +:root body.vtt.game.system-eunos-blades #navigation #chat table thead tr th, +:root body.vtt.game.system-eunos-blades #navigation #chat table thead tr td, +:root body.vtt.game.system-eunos-blades #hotbar #chat table, +:root body.vtt.game.system-eunos-blades #hotbar #chat table tr, +:root body.vtt.game.system-eunos-blades #hotbar #chat table tr th, +:root body.vtt.game.system-eunos-blades #hotbar #chat table tr td, +:root body.vtt.game.system-eunos-blades #hotbar #chat table tbody, +:root body.vtt.game.system-eunos-blades #hotbar #chat table tbody tr, +:root body.vtt.game.system-eunos-blades #hotbar #chat table tbody td, +:root body.vtt.game.system-eunos-blades #hotbar #chat table thead, +:root body.vtt.game.system-eunos-blades #hotbar #chat table thead tr, +:root body.vtt.game.system-eunos-blades #hotbar #chat table thead tr th, +:root body.vtt.game.system-eunos-blades #hotbar #chat table thead tr td, +:root body.vtt.game.system-eunos-blades #players #chat table, +:root body.vtt.game.system-eunos-blades #players #chat table tr, +:root body.vtt.game.system-eunos-blades #players #chat table tr th, +:root body.vtt.game.system-eunos-blades #players #chat table tr td, +:root body.vtt.game.system-eunos-blades #players #chat table tbody, +:root body.vtt.game.system-eunos-blades #players #chat table tbody tr, +:root body.vtt.game.system-eunos-blades #players #chat table tbody td, +:root body.vtt.game.system-eunos-blades #players #chat table thead, +:root body.vtt.game.system-eunos-blades #players #chat table thead tr, +:root body.vtt.game.system-eunos-blades #players #chat table thead tr th, +:root body.vtt.game.system-eunos-blades #players #chat table thead tr td { + margin: 0; + padding: 0; + background: none; + border: none; } -:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .dice-roll-strip, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .dice-roll-strip, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .dice-roll-strip, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .dice-roll-strip, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .dice-roll-strip { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - height: 30px; - width: 100%; - align-items: stretch; - justify-content: space-evenly; +:root body.vtt.game.system-eunos-blades #interface #chat table, +:root body.vtt.game.system-eunos-blades #controls #chat table, +:root body.vtt.game.system-eunos-blades #navigation #chat table, +:root body.vtt.game.system-eunos-blades #hotbar #chat table, +:root body.vtt.game.system-eunos-blades #players #chat table { + border-collapse: collapse; } -:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .dice-roll-strip .blades-die, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .dice-roll-strip .blades-die, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .dice-roll-strip .blades-die, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .dice-roll-strip .blades-die, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .dice-roll-strip .blades-die { - display: block; - height: 30px; +:root body.vtt.game.system-eunos-blades #interface #chat table thead tr th, +:root body.vtt.game.system-eunos-blades #controls #chat table thead tr th, +:root body.vtt.game.system-eunos-blades #navigation #chat table thead tr th, +:root body.vtt.game.system-eunos-blades #hotbar #chat table thead tr th, +:root body.vtt.game.system-eunos-blades #players #chat table thead tr th { + background-color: var(--blades-white); + text-shadow: none; + color: var(--blades-black); + font-family: var(--font-emphasis); + font-weight: normal; + font-size: 20px; + font-variant: small-caps; + padding: 2px; } -:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .dice-roll-strip .blades-die img, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .dice-roll-strip .blades-die img, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .dice-roll-strip .blades-die img, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .dice-roll-strip .blades-die img, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .dice-roll-strip .blades-die img { - height: 30px; - width: 30px; +:root body.vtt.game.system-eunos-blades #interface #chat figure, +:root body.vtt.game.system-eunos-blades #controls #chat figure, +:root body.vtt.game.system-eunos-blades #navigation #chat figure, +:root body.vtt.game.system-eunos-blades #hotbar #chat figure, +:root body.vtt.game.system-eunos-blades #players #chat figure { + display: table; + margin: 1rem auto; +} +:root body.vtt.game.system-eunos-blades #interface #chat figure figcaption, +:root body.vtt.game.system-eunos-blades #controls #chat figure figcaption, +:root body.vtt.game.system-eunos-blades #navigation #chat figure figcaption, +:root body.vtt.game.system-eunos-blades #hotbar #chat figure figcaption, +:root body.vtt.game.system-eunos-blades #players #chat figure figcaption { + font-family: var(--font-primary-small); + color: var(--blades-grey-bright); display: block; + margin-top: 0.25rem; + text-align: center; } -:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-critical, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-critical, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-critical, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-critical, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-critical { - outline: 2px solid var(--blades-gold-bright); +:root body.vtt.game.system-eunos-blades #interface #chat hr, +:root body.vtt.game.system-eunos-blades #controls #chat hr, +:root body.vtt.game.system-eunos-blades #navigation #chat hr, +:root body.vtt.game.system-eunos-blades #hotbar #chat hr, +:root body.vtt.game.system-eunos-blades #players #chat hr { + border-color: var(--blades-grey-bright); + border-style: solid; + border-width: 1px 0 0 0; } -:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-success, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-success, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-success, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-success, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-success { - outline: 2px solid var(--blades-white-bright); +:root body.vtt.game.system-eunos-blades #interface #chat code, +:root body.vtt.game.system-eunos-blades #controls #chat code, +:root body.vtt.game.system-eunos-blades #navigation #chat code, +:root body.vtt.game.system-eunos-blades #hotbar #chat code, +:root body.vtt.game.system-eunos-blades #players #chat code { + padding: 0.1rem 0.2rem; } -:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-ghost img, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-ghost img, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-ghost img, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-ghost img, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-ghost img { - opacity: 0.5; +:root body.vtt.game.system-eunos-blades #interface #chat .text-secret, +:root body.vtt.game.system-eunos-blades #controls #chat .text-secret, +:root body.vtt.game.system-eunos-blades #navigation #chat .text-secret, +:root body.vtt.game.system-eunos-blades #hotbar #chat .text-secret, +:root body.vtt.game.system-eunos-blades #players #chat .text-secret { + display: var(--secret-text-display, "none"); + background-color: var(--blades-white); + color: var(--blades-black); + font-weight: bold; + font-style: italic; + border-radius: 3px; + box-shadow: 0 0 3px var(--blades-white-bright), 0 0 3px var(--blades-white-bright), 0 0 3px var(--blades-white-bright); + padding: 0 0.3125rem; + margin: 0 0.3125rem; } -:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-resistance, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-resistance, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-resistance, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-resistance, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .dice-roll-strip .blades-die.blades-die-resistance { - outline: 2px solid var(--blades-cyan-bright); +:root body.vtt.game.system-eunos-blades #interface #chat .text-secret:first-child:last-child, +:root body.vtt.game.system-eunos-blades #controls #chat .text-secret:first-child:last-child, +:root body.vtt.game.system-eunos-blades #navigation #chat .text-secret:first-child:last-child, +:root body.vtt.game.system-eunos-blades #hotbar #chat .text-secret:first-child:last-child, +:root body.vtt.game.system-eunos-blades #players #chat .text-secret:first-child:last-child { + display: block; } -:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .chat-label, :root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .chat-trait-label, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .chat-label, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .chat-trait-label, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .chat-label, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .chat-trait-label, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .chat-label, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .chat-trait-label, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .chat-label, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .chat-trait-label { - background-color: var(--blades-grey-bright); - font-family: var(--font-emphasis); - color: var(--blades-white); - font-size: 21px; - text-align: center; - padding: 0px 5px; - height: 30px; - text-transform: capitalize; +:root body.vtt.game.system-eunos-blades #interface #chat label:not([class]), +:root body.vtt.game.system-eunos-blades #controls #chat label:not([class]), +:root body.vtt.game.system-eunos-blades #navigation #chat label:not([class]), +:root body.vtt.game.system-eunos-blades #hotbar #chat label:not([class]), +:root body.vtt.game.system-eunos-blades #players #chat label:not([class]) { + color: var(--blades-white-bright); + font-weight: bold; } -:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .chat-label-small, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .chat-label-small, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .chat-label-small, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .chat-label-small, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .chat-label-small { - background-color: var(--blades-grey); +:root body.vtt.game.system-eunos-blades #interface #chat .filled-label, +:root body.vtt.game.system-eunos-blades #controls #chat .filled-label, +:root body.vtt.game.system-eunos-blades #navigation #chat .filled-label, +:root body.vtt.game.system-eunos-blades #hotbar #chat .filled-label, +:root body.vtt.game.system-eunos-blades #players #chat .filled-label { + font-size: 1.25rem; + line-height: 1; + flex-grow: 0; + padding: 0.3125rem; + background: var(--blades-white); color: var(--blades-black); - font-size: small; - text-align: center; - padding: 3px 5px; - height: 20px; -} -:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .label-stripe-chat, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .label-stripe-chat, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .label-stripe-chat, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .label-stripe-chat, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .label-stripe-chat { + font-family: var(--font-emphasis); text-transform: uppercase; - background-color: var(--blades-black); - color: var(--blades-white); position: relative; - padding-top: 3px; - display: flex; - font-weight: bold; - margin: 0; + display: block; + width: min-content; + white-space: nowrap; } -:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .label-stripe-chat-small, -:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .label-stripe-chat-small, -:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .label-stripe-chat-small, -:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .label-stripe-chat-small, -:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .label-stripe-chat-small { - text-transform: capitalize; - background-color: var(--blades-grey); - color: var(--blades-black); - margin-bottom: 10px; - position: relative; - padding-top: 3px; - display: flex; +:root body.vtt.game.system-eunos-blades #interface #chat .filled-label.narrow-label, +:root body.vtt.game.system-eunos-blades #controls #chat .filled-label.narrow-label, +:root body.vtt.game.system-eunos-blades #navigation #chat .filled-label.narrow-label, +:root body.vtt.game.system-eunos-blades #hotbar #chat .filled-label.narrow-label, +:root body.vtt.game.system-eunos-blades #players #chat .filled-label.narrow-label { + transform-origin: 0 50%; + scale: 0.8 1; + min-width: 125%; + margin-left: 25%; +} +:root body.vtt.game.system-eunos-blades #interface #chat .number-circle, +:root body.vtt.game.system-eunos-blades #controls #chat .number-circle, +:root body.vtt.game.system-eunos-blades #navigation #chat .number-circle, +:root body.vtt.game.system-eunos-blades #hotbar #chat .number-circle, +:root body.vtt.game.system-eunos-blades #players #chat .number-circle { + --nc-size: var(--number-circle-size, 1.25rem); + --nc-border-width: calc(var(--nc-size) * (1/10)); + --nc-font-size: calc(var(--nc-size) * (7/10)); + --nc-border-radius: calc(var(--nc-size) * (5/10)); + --nc-line-height: calc(var(--nc-size) * (8/10)); + --nc-drop-shadow: calc(var(--nc-size) * (0.5/10)); + height: var(--nc-size); + width: var(--nc-size); + font-weight: bold; + font-family: "Minion Pro Caption Cond", serif; + font-size: var(--nc-font-size); + text-align: center; + border-radius: var(--nc-border-radius); + background: var(--number-circle-bg, var(--blades-grey-dark)); + color: var(--number-circle-color, var(--blades-white)); + line-height: var(--nc-line-height); + border: var(--nc-border-width) outset var(--number-circle-color, var(--blades-white)); + margin: -0.1875rem 0.25rem 0 0; + flex-shrink: 0; +} +:root body.vtt.game.system-eunos-blades #interface #chat .gold-bright, +:root body.vtt.game.system-eunos-blades #controls #chat .gold-bright, +:root body.vtt.game.system-eunos-blades #navigation #chat .gold-bright, +:root body.vtt.game.system-eunos-blades #hotbar #chat .gold-bright, +:root body.vtt.game.system-eunos-blades #players #chat .gold-bright { + color: var(--blades-gold-bright) !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .red-bright, +:root body.vtt.game.system-eunos-blades #controls #chat .red-bright, +:root body.vtt.game.system-eunos-blades #navigation #chat .red-bright, +:root body.vtt.game.system-eunos-blades #hotbar #chat .red-bright, +:root body.vtt.game.system-eunos-blades #players #chat .red-bright { + color: var(--blades-red-bright) !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .red-dark, +:root body.vtt.game.system-eunos-blades #controls #chat .red-dark, +:root body.vtt.game.system-eunos-blades #navigation #chat .red-dark, +:root body.vtt.game.system-eunos-blades #hotbar #chat .red-dark, +:root body.vtt.game.system-eunos-blades #players #chat .red-dark { + color: var(--blades-red-dark) !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .grey, +:root body.vtt.game.system-eunos-blades #controls #chat .grey, +:root body.vtt.game.system-eunos-blades #navigation #chat .grey, +:root body.vtt.game.system-eunos-blades #hotbar #chat .grey, +:root body.vtt.game.system-eunos-blades #players #chat .grey { + color: var(--blades-grey) !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .white, +:root body.vtt.game.system-eunos-blades #controls #chat .white, +:root body.vtt.game.system-eunos-blades #navigation #chat .white, +:root body.vtt.game.system-eunos-blades #hotbar #chat .white, +:root body.vtt.game.system-eunos-blades #players #chat .white { + color: var(--blades-white) !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .white-bright, +:root body.vtt.game.system-eunos-blades #controls #chat .white-bright, +:root body.vtt.game.system-eunos-blades #navigation #chat .white-bright, +:root body.vtt.game.system-eunos-blades #hotbar #chat .white-bright, +:root body.vtt.game.system-eunos-blades #players #chat .white-bright { + color: var(--blades-white-bright) !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .cyan-bright, +:root body.vtt.game.system-eunos-blades #controls #chat .cyan-bright, +:root body.vtt.game.system-eunos-blades #navigation #chat .cyan-bright, +:root body.vtt.game.system-eunos-blades #hotbar #chat .cyan-bright, +:root body.vtt.game.system-eunos-blades #players #chat .cyan-bright { + color: var(--blades-blue-bright) !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .uppercase, +:root body.vtt.game.system-eunos-blades #controls #chat .uppercase, +:root body.vtt.game.system-eunos-blades #navigation #chat .uppercase, +:root body.vtt.game.system-eunos-blades #hotbar #chat .uppercase, +:root body.vtt.game.system-eunos-blades #players #chat .uppercase { + text-transform: uppercase !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .inline-code, +:root body.vtt.game.system-eunos-blades #controls #chat .inline-code, +:root body.vtt.game.system-eunos-blades #navigation #chat .inline-code, +:root body.vtt.game.system-eunos-blades #hotbar #chat .inline-code, +:root body.vtt.game.system-eunos-blades #players #chat .inline-code { + font-family: var(--font-mono) !important; + font-size: smaller; + text-transform: uppercase; +} +:root body.vtt.game.system-eunos-blades #interface #chat .shadowed, +:root body.vtt.game.system-eunos-blades #controls #chat .shadowed, +:root body.vtt.game.system-eunos-blades #navigation #chat .shadowed, +:root body.vtt.game.system-eunos-blades #hotbar #chat .shadowed, +:root body.vtt.game.system-eunos-blades #players #chat .shadowed { + box-shadow: none; + background: transparent; + text-shadow: 0 0 4px var(--blades-black-dark), 0 0 4px var(--blades-black-dark), 0 0 4px var(--blades-black-dark), 0 0 4px var(--blades-black-dark); +} +:root body.vtt.game.system-eunos-blades #interface #chat .hidden, +:root body.vtt.game.system-eunos-blades #controls #chat .hidden, +:root body.vtt.game.system-eunos-blades #navigation #chat .hidden, +:root body.vtt.game.system-eunos-blades #hotbar #chat .hidden, +:root body.vtt.game.system-eunos-blades #players #chat .hidden { + display: none !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .start-invisible, +:root body.vtt.game.system-eunos-blades #controls #chat .start-invisible, +:root body.vtt.game.system-eunos-blades #navigation #chat .start-invisible, +:root body.vtt.game.system-eunos-blades #hotbar #chat .start-invisible, +:root body.vtt.game.system-eunos-blades #players #chat .start-invisible { + opacity: 0; +} +:root body.vtt.game.system-eunos-blades #interface #chat .text-checkbox, +:root body.vtt.game.system-eunos-blades #controls #chat .text-checkbox, +:root body.vtt.game.system-eunos-blades #navigation #chat .text-checkbox, +:root body.vtt.game.system-eunos-blades #hotbar #chat .text-checkbox, +:root body.vtt.game.system-eunos-blades #players #chat .text-checkbox { + position: relative; + display: inline-block; +} +:root body.vtt.game.system-eunos-blades #interface #chat .text-checkbox input[type=checkbox], +:root body.vtt.game.system-eunos-blades #controls #chat .text-checkbox input[type=checkbox], +:root body.vtt.game.system-eunos-blades #navigation #chat .text-checkbox input[type=checkbox], +:root body.vtt.game.system-eunos-blades #hotbar #chat .text-checkbox input[type=checkbox], +:root body.vtt.game.system-eunos-blades #players #chat .text-checkbox input[type=checkbox] { + opacity: 0; + height: 100%; + width: 100%; + margin-right: -100%; + display: inline-block; +} +:root body.vtt.game.system-eunos-blades #interface #chat .text-checkbox span, +:root body.vtt.game.system-eunos-blades #controls #chat .text-checkbox span, +:root body.vtt.game.system-eunos-blades #navigation #chat .text-checkbox span, +:root body.vtt.game.system-eunos-blades #hotbar #chat .text-checkbox span, +:root body.vtt.game.system-eunos-blades #players #chat .text-checkbox span { + display: inline-block; +} +:root body.vtt.game.system-eunos-blades #interface #chat .text-checkbox input[type=checkbox]:checked + span, +:root body.vtt.game.system-eunos-blades #controls #chat .text-checkbox input[type=checkbox]:checked + span, +:root body.vtt.game.system-eunos-blades #navigation #chat .text-checkbox input[type=checkbox]:checked + span, +:root body.vtt.game.system-eunos-blades #hotbar #chat .text-checkbox input[type=checkbox]:checked + span, +:root body.vtt.game.system-eunos-blades #players #chat .text-checkbox input[type=checkbox]:checked + span { + color: var(--active-text-color, var(--blades-gold-bright)); +} +:root body.vtt.game.system-eunos-blades #interface #chat .no-img img, +:root body.vtt.game.system-eunos-blades #controls #chat .no-img img, +:root body.vtt.game.system-eunos-blades #navigation #chat .no-img img, +:root body.vtt.game.system-eunos-blades #hotbar #chat .no-img img, +:root body.vtt.game.system-eunos-blades #players #chat .no-img img { + display: none; +} +:root body.vtt.game.system-eunos-blades #interface #chat .flex-horizontal, +:root body.vtt.game.system-eunos-blades #controls #chat .flex-horizontal, +:root body.vtt.game.system-eunos-blades #navigation #chat .flex-horizontal, +:root body.vtt.game.system-eunos-blades #hotbar #chat .flex-horizontal, +:root body.vtt.game.system-eunos-blades #players #chat .flex-horizontal { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: space-between; + align-items: center; +} +:root body.vtt.game.system-eunos-blades #interface #chat .flex-horizontal.flex-wrap, +:root body.vtt.game.system-eunos-blades #controls #chat .flex-horizontal.flex-wrap, +:root body.vtt.game.system-eunos-blades #navigation #chat .flex-horizontal.flex-wrap, +:root body.vtt.game.system-eunos-blades #hotbar #chat .flex-horizontal.flex-wrap, +:root body.vtt.game.system-eunos-blades #players #chat .flex-horizontal.flex-wrap { + flex-wrap: wrap; + align-content: flex-start; +} +:root body.vtt.game.system-eunos-blades #interface #chat .flex-horizontal.full-width, +:root body.vtt.game.system-eunos-blades #controls #chat .flex-horizontal.full-width, +:root body.vtt.game.system-eunos-blades #navigation #chat .flex-horizontal.full-width, +:root body.vtt.game.system-eunos-blades #hotbar #chat .flex-horizontal.full-width, +:root body.vtt.game.system-eunos-blades #players #chat .flex-horizontal.full-width { + width: 100%; + justify-content: space-evenly; +} +:root body.vtt.game.system-eunos-blades #interface #chat .flex-vertical, +:root body.vtt.game.system-eunos-blades #controls #chat .flex-vertical, +:root body.vtt.game.system-eunos-blades #navigation #chat .flex-vertical, +:root body.vtt.game.system-eunos-blades #hotbar #chat .flex-vertical, +:root body.vtt.game.system-eunos-blades #players #chat .flex-vertical { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} +:root body.vtt.game.system-eunos-blades #interface #chat .full-width, +:root body.vtt.game.system-eunos-blades #controls #chat .full-width, +:root body.vtt.game.system-eunos-blades #navigation #chat .full-width, +:root body.vtt.game.system-eunos-blades #hotbar #chat .full-width, +:root body.vtt.game.system-eunos-blades #players #chat .full-width { + width: 100%; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip-trigger, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip-trigger, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip-trigger, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip-trigger, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip-trigger { + pointer-events: auto !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip { + opacity: 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: stretch; + color: var(--blades-white); + background: var(--blades-black-dark); + width: auto; + padding: 0 0.3125rem; + border: 0.0625rem solid var(--blades-grey); + border-radius: 0.1875rem; + text-align: center; + position: absolute; + top: unset; + left: -200px; + bottom: 50px; + z-index: 12; + pointer-events: none; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip > h1, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip > h1, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip > h1, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip > h1, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip > h1 { + text-align: center; + background: transparent; + font-family: var(--font-emphasis); + text-shadow: 0px 0px 4px var(--blades-black-dark), 0px 0px 4px var(--blades-black-dark), 0px 0px 4px var(--blades-black-dark), 0px 0px 4px var(--blades-black-dark); + width: 90%; + margin: 0 auto; + border-bottom: 2px solid var(--blades-white); + white-space: nowrap; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip > h1:last-of-type, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip > h1:last-of-type, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip > h1:last-of-type, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip > h1:last-of-type, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip > h1:last-of-type { + margin-bottom: 5px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip > p, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip > p, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip > p, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip > p, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip > p { + margin: 5px 0; + text-wrap: balance; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip > ul, :root body.vtt.game.system-eunos-blades #interface #chat .tooltip ol, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip > ul, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip ol, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip > ul, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip ol, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip > ul, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip ol, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip > ul, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip ol { + margin: 5px 0; + text-align: left; + text-wrap: normal; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip > ul li, :root body.vtt.game.system-eunos-blades #interface #chat .tooltip ol li, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip > ul li, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip ol li, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip > ul li, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip ol li, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip > ul li, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip ol li, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip > ul li, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip ol li { + text-wrap: normal; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip h2, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip h2, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip h2, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip h2, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip h2 { + color: var(--blades-white-bright); +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-left, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-left, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-left, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-left, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-left { + left: 200px; + max-width: unset; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-portrait, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-portrait, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-portrait, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-portrait, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-portrait { + bottom: -45px; + left: -200px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-playbook, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-playbook, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-playbook, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-playbook, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-playbook { + bottom: unset; + top: 0px; + left: -220px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-trauma, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-trauma, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-trauma, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-trauma, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-trauma { + left: -300px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-dialog-selection, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-dialog-selection, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-dialog-selection, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-dialog-selection, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-dialog-selection { + left: -300px; + bottom: 75px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-attribute, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-attribute, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-attribute, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-attribute, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-attribute { + left: -110px; + bottom: 10px; + translate: 0% 50% !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-attribute > p, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-attribute > p, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-attribute > p, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-attribute > p, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-attribute > p { + font-size: 16px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-action, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-action, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-action, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-action, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-action { + left: unset; + right: -100px; + bottom: 0px; + max-width: 525px; + translate: 0% 50% !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-action > p, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-action > p, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-action > p, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-action > p, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-action > p { + font-size: 16px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-roll-mod, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-roll-mod, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-roll-mod, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-roll-mod, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-roll-mod { + left: calc(50% + 350px); + bottom: 20px; + max-width: 350px; + width: 350px; + min-width: 350px; + translate: -50% 0% !important; + text-align: center; + hyphens: none; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-roll-mod > p:not(:last-of-type), +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-roll-mod > p:not(:last-of-type), +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-roll-mod > p:not(:last-of-type), +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-roll-mod > p:not(:last-of-type), +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-roll-mod > p:not(:last-of-type) { + margin-bottom: 10px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-pos-effect-trade, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-pos-effect-trade, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-pos-effect-trade, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-pos-effect-trade, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-pos-effect-trade { + width: 330px; + max-width: 330px; + min-width: 330px; + bottom: 5px; + left: 0px; + font-family: var(--font-default); + font-size: 0.875rem; + text-transform: none; + text-align: center; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-roll-trait-pc, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-roll-trait-pc, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-roll-trait-pc, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-roll-trait-pc, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-roll-trait-pc { + bottom: 100px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-roll-trait-pc table tbody tr:nth-child(4n), +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-roll-trait-pc table tbody tr:nth-child(4n), +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-roll-trait-pc table tbody tr:nth-child(4n), +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-roll-trait-pc table tbody tr:nth-child(4n), +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-roll-trait-pc table tbody tr:nth-child(4n) { + border-bottom: 2px solid var(--blades-white); +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(1), +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(1), +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(1), +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(1), +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(1) { + text-align: right; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(2), +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(2), +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(2), +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(2), +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(2) { + text-align: left; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(3), +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(3), +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(3), +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(3), +:root body.vtt.game.system-eunos-blades #players #chat .tooltip.tooltip-roll-trait-pc table tbody tr td:nth-child(3) { + text-align: left; +} +:root body.vtt.game.system-eunos-blades #interface #chat .tooltip-scaling-elem, +:root body.vtt.game.system-eunos-blades #controls #chat .tooltip-scaling-elem, +:root body.vtt.game.system-eunos-blades #navigation #chat .tooltip-scaling-elem, +:root body.vtt.game.system-eunos-blades #hotbar #chat .tooltip-scaling-elem, +:root body.vtt.game.system-eunos-blades #players #chat .tooltip-scaling-elem { + display: inline-block; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel { + display: flex; + flex-direction: column; + height: min-content; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel > *, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel > *, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel > *, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel > *, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel > * { + flex-grow: 1; + flex-shrink: 1; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary { + width: 100%; + overflow: visible; + padding: 5px; + position: relative; + z-index: 4; + pointer-events: auto; + display: grid; + grid-template-areas: "header header header notes" "actions assets harm notes"; + grid-template-rows: 20px 1fr; + grid-template-columns: 16% 16% 16% 1fr; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary:hover, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary:hover, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary:hover, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary:hover, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary:hover { + z-index: 5; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary .pc-summary-img, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary .pc-summary-img, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary .pc-summary-img, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary .pc-summary-img, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary .pc-summary-img { + position: absolute; + top: 20px; + width: 16%; + pointer-events: none; + z-index: -1; + opacity: 0.5; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-header, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-header, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-header, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-header, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-header { + grid-area: header; + display: grid; + grid-template-areas: "name name heritage background-img vice-img playbook-img"; + grid-template-rows: 20px; + grid-template-columns: 1fr 1fr 40px 20px 20px 20px; + position: relative; + width: 100%; + grid-gap: 5px; + height: min-content; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img { + filter: drop-shadow(1px 1px 0px var(--blades-black-dark)); +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-background-img, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-background-img, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-background-img, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-background-img, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-background-img { + grid-area: background-img; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-vice-img, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-vice-img, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-vice-img, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-vice-img, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-vice-img { + grid-area: vice-img; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-playbook-img, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-playbook-img, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-playbook-img, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-playbook-img, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-header > img.pc-summary-playbook-img { + grid-area: playbook-img; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-header .pc-summary-name, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-header .pc-summary-name, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-header .pc-summary-name, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-header .pc-summary-name, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-header .pc-summary-name { + grid-area: name; + font-family: var(--font-emphasis); + font-size: 1.25rem; + line-height: 1.25rem; + color: var(--blades-white-bright); + font-variant: small-caps; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-header .pc-summary-heritage, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-header .pc-summary-heritage, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-header .pc-summary-heritage, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-header .pc-summary-heritage, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-header .pc-summary-heritage { + grid-area: heritage; + font-size: 10px; + font-family: Oswald, sans-serif; + text-transform: uppercase; + line-height: 20px; + text-align: right; + color: var(--blades-white-bright); + padding-right: 5px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-actions, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-actions, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-actions, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-actions, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-actions { + grid-area: actions; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: flex-start; + align-content: flex-start; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute { + text-align: center; + flex-basis: 100%; + flex-grow: 1; + flex-shrink: 0; + line-height: 0.875rem; + height: 0.875rem; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute:not(:first-child), +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute:not(:first-child), +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute:not(:first-child), +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute:not(:first-child), +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute:not(:first-child) { + margin-top: 5px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute label, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute label, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute label, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute label, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute label { + font-family: Oswald, sans-serif; + font-size: 0.75rem; + line-height: 0.75rem; + color: var(--blades-white); +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute span, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute span, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute span, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute span, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-attribute span { + font-family: Oswald, sans-serif; + font-size: 0.75rem; + line-height: 0.75rem; + color: var(--blades-white-bright); +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action { + flex-basis: 50%; + flex-grow: 0; + flex-shrink: 0; + line-height: 0.75rem; + height: 0.75rem; + display: flex; + flex-direction: row; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action label, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action label, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action label, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action label, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action label { + font-family: Oswald, sans-serif; + font-size: 0.625rem; + line-height: 0.625rem; + color: var(--blades-grey-bright); + flex-grow: 1; + text-align: right; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action span, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action span, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action span, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action span, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-actions .pc-summary-action span { + font-family: Oswald, sans-serif; + font-size: 0.625rem; + line-height: 0.625rem; + color: var(--blades-white-bright); + width: 20px; + text-align: center; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-assets, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-assets, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-assets, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-assets, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-assets { + grid-area: assets; + padding-right: 3px; + position: relative; + z-index: 5; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-assets:hover, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-assets:hover, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-assets:hover, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-assets:hover, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-assets:hover { + z-index: 10; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container:not(:first-child), +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container:not(:first-child), +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container:not(:first-child), +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container:not(:first-child), +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container:not(:first-child) { + margin-top: 5px; + border-top: 1px dotted var(--blades-white-bright); +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: stretch; + gap: 3px; + height: 14px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container .pc-summary-gear-loadout, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container .pc-summary-gear-loadout, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container .pc-summary-gear-loadout, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container .pc-summary-gear-loadout, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container .pc-summary-gear-loadout { + flex-grow: 1; + font-family: "Fjalla One", sans-serif; + font-size: 10px; + line-height: 12px; + text-transform: uppercase; + white-space: nowrap; + overflow: hidden; + text-align: left; + color: var(--blades-gold-bright); +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container i, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container i, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container i, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container i, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-gear-loadout-container i { + font-size: 10px; + line-height: 12px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset { + display: flex; + flex-direction: row; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-img, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-img, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-img, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-img, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-img { + height: 12px; + flex-grow: 0; + flex-shrink: 0; + flex-basis: 12px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-name, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-name, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-name, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-name, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-name { + flex-grow: 1; + font-family: "Fjalla One", sans-serif; + font-size: 10px; + line-height: 12px; + white-space: nowrap; + overflow: hidden; + text-transform: uppercase; + color: var(--blades-white-bright); +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-name:hover, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-name:hover, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-name:hover, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-name:hover, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .pc-summary-asset .pc-summary-asset-name:hover { + overflow: visible; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .tooltip.pc-summary-asset-tooltip, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .tooltip.pc-summary-asset-tooltip, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .tooltip.pc-summary-asset-tooltip, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .tooltip.pc-summary-asset-tooltip, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-assets .pc-summary-assets-container .tooltip.pc-summary-asset-tooltip { + font-size: 10px; + line-height: 10px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses { + grid-area: harm; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container { + width: 100%; + display: flex; + justify-content: flex-end; + height: 14px; + overflow: hidden; + flex-wrap: nowrap; + flex-direction: row; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container .pc-summary-stress-box, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container .pc-summary-stress-box, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container .pc-summary-stress-box, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container .pc-summary-stress-box, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container .pc-summary-stress-box { + display: block; + height: 100%; + width: 5px; + margin: 0px 1px; + border: 1px solid var(--blades-red-dark); + border-radius: 3px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container .pc-summary-stress-box.stress-box-full, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container .pc-summary-stress-box.stress-box-full, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container .pc-summary-stress-box.stress-box-full, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container .pc-summary-stress-box.stress-box-full, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-stress-container .pc-summary-stress-box.stress-box-full { + background: var(--blades-red-bright); + border-color: var(--blades-red); +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-trauma-container, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-trauma-container, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-trauma-container, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-trauma-container, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-trauma-container { + margin-top: 5px; + border-bottom: 1px dotted var(--blades-red); +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-trauma-container .pc-summary-trauma, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-trauma-container .pc-summary-trauma, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-trauma-container .pc-summary-trauma, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-trauma-container .pc-summary-trauma, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-trauma-container .pc-summary-trauma { + color: var(--blades-red-bright); + font-weight: bold; + font-family: Kirsty, serif; + font-size: 12px; + text-transform: uppercase; + text-align: center; + width: 100%; + display: block; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container { + margin-top: 5px; + display: flex; + justify-content: flex-start; + align-items: stretch; + flex-direction: column; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm { + font-size: 10px; + font-family: "Fjalla One", sans-serif; + overflow: hidden; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: stretch; + flex-grow: 1; + padding: 0 5px; + line-height: 15px; + pointer-events: auto; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm:hover, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm:hover, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm:hover, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm:hover, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm:hover { + overflow: visible; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm > label, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm > label, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm > label, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm > label, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm > label { + flex-grow: 1; + white-space: nowrap; + padding-left: 4px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm > i, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm > i, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm > i, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm > i, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm > i { + line-height: 15px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-heavy, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-heavy, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-heavy, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-heavy, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-heavy { + color: var(--blades-white-bright); + background: var(--blades-red-dark); + font-weight: bold; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-medium, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-medium, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-medium, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-medium, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-medium { + color: var(--blades-red-bright); + background: var(--blades-red-dark-fade); + font-weight: bold; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-light, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-light, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-light, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-light, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.pc-harm-light { + color: var(--blades-red-bright); + background: var(--blades-red-dark-fade-strong); +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.harm-inactive, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.harm-inactive, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.harm-inactive, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.harm-inactive, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-maluses .pc-summary-harm-container .pc-summary-harm.harm-inactive { + background: transparent; + color: var(--blades-grey); +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-notes, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-notes, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-notes, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-notes, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-notes { + grid-area: notes; +} +:root body.vtt.game.system-eunos-blades #interface #chat .player-character-summary-panel .pc-summary section.pc-summary-notes .pc-summary-notes-body, +:root body.vtt.game.system-eunos-blades #controls #chat .player-character-summary-panel .pc-summary section.pc-summary-notes .pc-summary-notes-body, +:root body.vtt.game.system-eunos-blades #navigation #chat .player-character-summary-panel .pc-summary section.pc-summary-notes .pc-summary-notes-body, +:root body.vtt.game.system-eunos-blades #hotbar #chat .player-character-summary-panel .pc-summary section.pc-summary-notes .pc-summary-notes-body, +:root body.vtt.game.system-eunos-blades #players #chat .player-character-summary-panel .pc-summary section.pc-summary-notes .pc-summary-notes-body { + height: 100%; + resize: none; +} +:root body.vtt.game.system-eunos-blades #interface #chat .selectable-image-panel, +:root body.vtt.game.system-eunos-blades #controls #chat .selectable-image-panel, +:root body.vtt.game.system-eunos-blades #navigation #chat .selectable-image-panel, +:root body.vtt.game.system-eunos-blades #hotbar #chat .selectable-image-panel, +:root body.vtt.game.system-eunos-blades #players #chat .selectable-image-panel { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + align-content: flex-start; + justify-content: flex-start; + border-left: 4px double var(--blades-white); + border-right: 4px double var(--blades-white); + gap: 5px; + height: min-content; + position: relative; + min-height: 100px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .selectable-image-panel .selectable-image, +:root body.vtt.game.system-eunos-blades #controls #chat .selectable-image-panel .selectable-image, +:root body.vtt.game.system-eunos-blades #navigation #chat .selectable-image-panel .selectable-image, +:root body.vtt.game.system-eunos-blades #hotbar #chat .selectable-image-panel .selectable-image, +:root body.vtt.game.system-eunos-blades #players #chat .selectable-image-panel .selectable-image { + pointer-events: auto !important; + max-height: 100px; + max-width: 100px; + filter: brightness(1.5); +} +:root body.vtt.game.system-eunos-blades #interface #chat .selectable-image-panel .selectable-image.image-selected, +:root body.vtt.game.system-eunos-blades #controls #chat .selectable-image-panel .selectable-image.image-selected, +:root body.vtt.game.system-eunos-blades #navigation #chat .selectable-image-panel .selectable-image.image-selected, +:root body.vtt.game.system-eunos-blades #hotbar #chat .selectable-image-panel .selectable-image.image-selected, +:root body.vtt.game.system-eunos-blades #players #chat .selectable-image-panel .selectable-image.image-selected { + outline: 2px solid var(--blades-green-bright); + position: relative; + z-index: 2; +} +:root body.vtt.game.system-eunos-blades #interface #chat .selectable-image-panel .add-image-control, +:root body.vtt.game.system-eunos-blades #controls #chat .selectable-image-panel .add-image-control, +:root body.vtt.game.system-eunos-blades #navigation #chat .selectable-image-panel .add-image-control, +:root body.vtt.game.system-eunos-blades #hotbar #chat .selectable-image-panel .add-image-control, +:root body.vtt.game.system-eunos-blades #players #chat .selectable-image-panel .add-image-control { + position: absolute; + z-index: 3; + top: 10px; + right: 10px; + pointer-events: auto !important; + height: 20px; + width: 20px; + rotate: 45deg; + opacity: 0.5; + transition: 0.25s; +} +:root body.vtt.game.system-eunos-blades #interface #chat .selectable-image-panel .add-image-control:hover, +:root body.vtt.game.system-eunos-blades #controls #chat .selectable-image-panel .add-image-control:hover, +:root body.vtt.game.system-eunos-blades #navigation #chat .selectable-image-panel .add-image-control:hover, +:root body.vtt.game.system-eunos-blades #hotbar #chat .selectable-image-panel .add-image-control:hover, +:root body.vtt.game.system-eunos-blades #players #chat .selectable-image-panel .add-image-control:hover { + opacity: 1; +} +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel { + display: flex; + flex-direction: row; + flex-wrap: wrap; + height: min-content; + flex-grow: 0; + gap: 5px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container { + flex-basis: 45%; + max-width: 50%; + flex-grow: 1; + flex-shrink: 1; + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: flex-start; +} +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container.selected-opposition, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container.selected-opposition, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container.selected-opposition, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container.selected-opposition, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container.selected-opposition { + box-shadow: inset 0 0 10px var(--blades-green-bright), inset 0 0 10px var(--blades-green-bright), inset 0 0 10px var(--blades-green-bright); +} +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container.opposition-blank, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container.opposition-blank, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container.opposition-blank, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container.opposition-blank, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container.opposition-blank { + opacity: 0.5; + filter: blur(2px); +} +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container { + flex-grow: 1; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: stretch; + align-items: flex-start; +} +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container .roll-opposition-img, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container .roll-opposition-img, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container .roll-opposition-img, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container .roll-opposition-img, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container .roll-opposition-img { + max-height: 58px; + max-width: 58px; + flex-grow: 0; + pointer-events: auto !important; +} +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container .roll-opposition-text-container, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container .roll-opposition-text-container, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container .roll-opposition-text-container, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container .roll-opposition-text-container, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-top-container .roll-opposition-text-container { + display: flex; + flex-grow: 1; + flex-direction: column; + align-items: stretch; + justify-content: space-between; +} +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container, +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container { + flex-grow: 1; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: space-evenly; + align-items: center; + gap: 5px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container .factor-label, +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container .factor-label, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container .factor-label, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container .factor-label, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container .factor-label, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container .factor-label, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container .factor-label, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container .factor-label, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container .factor-label, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container .factor-label { + flex-grow: 1; + text-align: center; + font-family: "Fjalla One", sans-serif; + font-size: 8px; + color: var(--blades-white-bright); + max-width: 50px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container .factor-selector, +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container .factor-selector, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container .factor-selector, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container .factor-selector, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container .factor-selector, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container .factor-selector, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container .factor-selector, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container .factor-selector, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factors-container .factor-selector, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container .roll-opposition-factor-label-container .factor-selector { + flex-grow: 0; + flex-basis: 25%; + flex-shrink: 1; + pointer-events: auto !important; + font-family: var(--font-emphasis); + color: var(--blades-white-bright); + text-align: center; + font-size: 14px; + height: 18px; + max-width: 50px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .opposition-creation-panel .roll-opposition-container input.shadowed, +:root body.vtt.game.system-eunos-blades #controls #chat .opposition-creation-panel .roll-opposition-container input.shadowed, +:root body.vtt.game.system-eunos-blades #navigation #chat .opposition-creation-panel .roll-opposition-container input.shadowed, +:root body.vtt.game.system-eunos-blades #hotbar #chat .opposition-creation-panel .roll-opposition-container input.shadowed, +:root body.vtt.game.system-eunos-blades #players #chat .opposition-creation-panel .roll-opposition-container input.shadowed { + pointer-events: auto !important; + font-family: "Fjalla One", sans-serif; + background: var(--blades-black-dark-fade-strong); + border-radius: 3px; + box-shadow: inset 0px 0px 3px var(--blades-black-dark), inset 0px 0px 3px var(--blades-black-dark), inset 0px 0px 3px var(--blades-black-dark), inset 0px 0px 3px var(--blades-black-dark); + font-size: 0.875rem; + color: var(--blades-white-bright); + height: 1.125rem; + transform-origin: 0% 50%; + flex-shrink: 1; + scale: 0.75 1; + width: 133.3333333333%; +} +:root body.vtt.game.system-eunos-blades #interface #chat .accordian-label, +:root body.vtt.game.system-eunos-blades #controls #chat .accordian-label, +:root body.vtt.game.system-eunos-blades #navigation #chat .accordian-label, +:root body.vtt.game.system-eunos-blades #hotbar #chat .accordian-label, +:root body.vtt.game.system-eunos-blades #players #chat .accordian-label { + position: relative; + filter: sepia(0.75); +} +:root body.vtt.game.system-eunos-blades #interface #chat .accordian-label .randomizer-trigger, +:root body.vtt.game.system-eunos-blades #controls #chat .accordian-label .randomizer-trigger, +:root body.vtt.game.system-eunos-blades #navigation #chat .accordian-label .randomizer-trigger, +:root body.vtt.game.system-eunos-blades #hotbar #chat .accordian-label .randomizer-trigger, +:root body.vtt.game.system-eunos-blades #players #chat .accordian-label .randomizer-trigger { + position: absolute; + text-indent: 0px; + left: 5px; + translate: 0 -50%; + top: 50%; + scale: 1; + color: var(--blades-black); + text-shadow: none; + transform-origin: 50% 50%; + transition: 0.25s; +} +:root body.vtt.game.system-eunos-blades #interface #chat .accordian-label .randomizer-trigger:hover, +:root body.vtt.game.system-eunos-blades #controls #chat .accordian-label .randomizer-trigger:hover, +:root body.vtt.game.system-eunos-blades #navigation #chat .accordian-label .randomizer-trigger:hover, +:root body.vtt.game.system-eunos-blades #hotbar #chat .accordian-label .randomizer-trigger:hover, +:root body.vtt.game.system-eunos-blades #players #chat .accordian-label .randomizer-trigger:hover { + scale: 1.2; + color: var(--blades-gold-bright); + text-shadow: 0px 0px 4px var(--blades-black-dark), 0px 0px 4px var(--blades-black-dark), 0px 0px 4px var(--blades-black-dark), 0px 0px 4px var(--blades-black-dark); +} +:root body.vtt.game.system-eunos-blades #interface #chat .accordian-label.accordian-label-small, +:root body.vtt.game.system-eunos-blades #controls #chat .accordian-label.accordian-label-small, +:root body.vtt.game.system-eunos-blades #navigation #chat .accordian-label.accordian-label-small, +:root body.vtt.game.system-eunos-blades #hotbar #chat .accordian-label.accordian-label-small, +:root body.vtt.game.system-eunos-blades #players #chat .accordian-label.accordian-label-small { + height: calc(0.75 * var(--header-height)); + line-height: calc(0.8 * var(--header-height)); + width: calc(100% - 10px); + margin-left: 10px; + filter: none; +} +:root body.vtt.game.system-eunos-blades #interface #chat .accordian-label.accordian-label-small .randomizer-trigger, +:root body.vtt.game.system-eunos-blades #controls #chat .accordian-label.accordian-label-small .randomizer-trigger, +:root body.vtt.game.system-eunos-blades #navigation #chat .accordian-label.accordian-label-small .randomizer-trigger, +:root body.vtt.game.system-eunos-blades #hotbar #chat .accordian-label.accordian-label-small .randomizer-trigger, +:root body.vtt.game.system-eunos-blades #players #chat .accordian-label.accordian-label-small .randomizer-trigger { + top: 60%; +} +:root body.vtt.game.system-eunos-blades #interface #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item, +:root body.vtt.game.system-eunos-blades #controls #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item, +:root body.vtt.game.system-eunos-blades #navigation #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item, +:root body.vtt.game.system-eunos-blades #hotbar #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item, +:root body.vtt.game.system-eunos-blades #players #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item { + position: relative; +} +:root body.vtt.game.system-eunos-blades #interface #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item:not(:last-child), +:root body.vtt.game.system-eunos-blades #controls #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item:not(:last-child), +:root body.vtt.game.system-eunos-blades #navigation #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item:not(:last-child), +:root body.vtt.game.system-eunos-blades #hotbar #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item:not(:last-child), +:root body.vtt.game.system-eunos-blades #players #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item:not(:last-child) { + border-bottom: 1px dotted var(--blades-white); +} +:root body.vtt.game.system-eunos-blades #interface #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item:not(.randomizer-item-locked) .randomizer-input, +:root body.vtt.game.system-eunos-blades #controls #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item:not(.randomizer-item-locked) .randomizer-input, +:root body.vtt.game.system-eunos-blades #navigation #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item:not(.randomizer-item-locked) .randomizer-input, +:root body.vtt.game.system-eunos-blades #hotbar #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item:not(.randomizer-item-locked) .randomizer-input, +:root body.vtt.game.system-eunos-blades #players #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item:not(.randomizer-item-locked) .randomizer-input { + background: transparent; + box-shadow: none; +} +:root body.vtt.game.system-eunos-blades #interface #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item *:not(i), +:root body.vtt.game.system-eunos-blades #controls #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item *:not(i), +:root body.vtt.game.system-eunos-blades #navigation #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item *:not(i), +:root body.vtt.game.system-eunos-blades #hotbar #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item *:not(i), +:root body.vtt.game.system-eunos-blades #players #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item *:not(i) { + font-family: "Minion Pro Cond", serif; +} +:root body.vtt.game.system-eunos-blades #interface #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item textarea, +:root body.vtt.game.system-eunos-blades #controls #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item textarea, +:root body.vtt.game.system-eunos-blades #navigation #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item textarea, +:root body.vtt.game.system-eunos-blades #hotbar #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item textarea, +:root body.vtt.game.system-eunos-blades #players #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item textarea { + resize: none; + min-height: 2.5rem; + padding: 3px; + text-indent: 0; + font-size: 10px; + overflow: hidden; +} +:root body.vtt.game.system-eunos-blades #interface #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item ul, +:root body.vtt.game.system-eunos-blades #controls #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item ul, +:root body.vtt.game.system-eunos-blades #navigation #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item ul, +:root body.vtt.game.system-eunos-blades #hotbar #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item ul, +:root body.vtt.game.system-eunos-blades #players #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item ul { + font-size: 10px; + margin: 0 0 0 10px; + padding: 0; + list-style: none; +} +:root body.vtt.game.system-eunos-blades #interface #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item ul li::before, +:root body.vtt.game.system-eunos-blades #controls #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item ul li::before, +:root body.vtt.game.system-eunos-blades #navigation #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item ul li::before, +:root body.vtt.game.system-eunos-blades #hotbar #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item ul li::before, +:root body.vtt.game.system-eunos-blades #players #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item ul li::before { + content: "● "; + margin-left: -9px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item .randomizer-input-title, +:root body.vtt.game.system-eunos-blades #controls #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item .randomizer-input-title, +:root body.vtt.game.system-eunos-blades #navigation #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item .randomizer-input-title, +:root body.vtt.game.system-eunos-blades #hotbar #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item .randomizer-input-title, +:root body.vtt.game.system-eunos-blades #players #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item .randomizer-input-title { + max-width: calc(100% - 25px); + margin-left: 25px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item .toggle-icon, +:root body.vtt.game.system-eunos-blades #controls #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item .toggle-icon, +:root body.vtt.game.system-eunos-blades #navigation #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item .toggle-icon, +:root body.vtt.game.system-eunos-blades #hotbar #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item .toggle-icon, +:root body.vtt.game.system-eunos-blades #players #chat .randomizer-panel .randomizer-container .randomizer-list .randomizer-item .toggle-icon { + position: absolute; + cursor: pointer; + left: 0; + bottom: unset; + right: unset; + top: 0; + pointer-events: auto; + z-index: 20; +} +:root body.vtt.game.system-eunos-blades #interface #chat, +:root body.vtt.game.system-eunos-blades #interface #chat *, +:root body.vtt.game.system-eunos-blades #controls #chat, +:root body.vtt.game.system-eunos-blades #controls #chat *, +:root body.vtt.game.system-eunos-blades #navigation #chat, +:root body.vtt.game.system-eunos-blades #navigation #chat *, +:root body.vtt.game.system-eunos-blades #hotbar #chat, +:root body.vtt.game.system-eunos-blades #hotbar #chat *, +:root body.vtt.game.system-eunos-blades #players #chat, +:root body.vtt.game.system-eunos-blades #players #chat * { + --font-primary: "Minion Pro"; + --font-heading: "Kirsty"; + --font-weight-heading: normal; + --text-shadow-heading: none; + --line-height-heading: 1.2; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message { + background: var(--blades-black); +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-header, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-header, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-header, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-header, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-header { + position: relative; + z-index: 1; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-header .message-sender, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-header .message-sender, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-header .message-sender, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-header .message-sender, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-header .message-sender { + color: var(--blades-grey); +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .chat-message-bg, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .chat-message-bg, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .chat-message-bg, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .chat-message-bg, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .chat-message-bg { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: 0; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .chat-message-bg.roll-position-risky, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .chat-message-bg.roll-position-risky, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .chat-message-bg.roll-position-risky, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .chat-message-bg.roll-position-risky, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .chat-message-bg.roll-position-risky { + background-image: url("../assets/animations/chat/roll-position-risky.webp"); + background-size: cover; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll { + position: relative; + z-index: 2; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll .chat-header, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll .chat-header, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll .chat-header, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll .chat-header, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll .chat-header { + margin: 0; + padding: 0; + background: transparent; + box-shadow: none; + color: var(--blades-grey); +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll h1.chat-header, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll h1.chat-header, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll h1.chat-header, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll h1.chat-header, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll h1.chat-header { + margin: 0; + padding: 0; + text-align: center; + background: transparent; + box-shadow: none; + font-size: 32px; + color: var(--blades-gold); +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll .roll-states, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll .roll-states, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll .roll-states, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll .roll-states, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll .roll-states { + justify-content: space-between; + padding: 0 20px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll .roll-states .roll-state-container, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll .roll-states .roll-state-container, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll .roll-states .roll-state-container, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll .roll-states .roll-state-container, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll .roll-states .roll-state-container { + flex-basis: 30%; + flex-grow: 0; + flex-shrink: 0; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll .roll-states h4.roll-state-label, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll .roll-states h4.roll-state-label, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll .roll-states h4.roll-state-label, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll .roll-states h4.roll-state-label, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll .roll-states h4.roll-state-label { + font-family: var(--font-primary); + font-size: 12px; + display: block; + width: 100%; + text-align: center; + white-space: nowrap; + color: var(--blades-grey); + margin-top: -4px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll .roll-states h3.roll-state, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll .roll-states h3.roll-state, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll .roll-states h3.roll-state, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll .roll-states h3.roll-state, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll .roll-states h3.roll-state { + white-space: nowrap; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll h2.chat-header.roll-position, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll h2.chat-header.roll-position, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll h2.chat-header.roll-position, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll h2.chat-header.roll-position, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll h2.chat-header.roll-position { + text-align: right; + font-family: var(--font-primary); +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll h2.chat-header.roll-position .position-text, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll h2.chat-header.roll-position .position-text, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll h2.chat-header.roll-position .position-text, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll h2.chat-header.roll-position .position-text, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll h2.chat-header.roll-position .position-text { + margin: 0 15px; + transform-origin: 50% 50%; + scale: 1.5; + display: inline-block; + font-family: var(--font-emphasis); +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll .dice-roll-strip, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll .dice-roll-strip, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll .dice-roll-strip, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll .dice-roll-strip, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll .dice-roll-strip { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + height: 30px; + width: 100%; + align-items: stretch; + justify-content: center; + gap: 10px; + margin: 10px 0; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die { + display: block; + height: 30px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die img, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die img, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die img, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die img, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die img { + height: 30px; + width: 30px; + display: block; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-critical, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-critical, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-critical, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-critical, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-critical { + outline: 2px solid var(--blades-gold-bright); +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-success, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-success, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-success, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-success, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-success { + outline: 2px solid var(--blades-white-bright); +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-ghost img, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-ghost img, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-ghost img, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-ghost img, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-ghost img { + opacity: 0.5; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-resistance, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-resistance, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-resistance, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-resistance, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .blades-roll .dice-roll-strip .blades-die.blades-die-resistance { + outline: 2px solid var(--blades-blue-bright); +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .chat-label, :root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .chat-trait-label, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .chat-label, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .chat-trait-label, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .chat-label, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .chat-trait-label, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .chat-label, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .chat-trait-label, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .chat-label, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .chat-trait-label { + background-color: var(--blades-grey-bright); + font-family: var(--font-emphasis); + color: var(--blades-white); + font-size: 21px; + text-align: center; + padding: 0px 5px; + height: 30px; + text-transform: capitalize; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .chat-label-small, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .chat-label-small, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .chat-label-small, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .chat-label-small, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .chat-label-small { + background-color: var(--blades-grey); + color: var(--blades-black); + font-size: small; + text-align: center; + padding: 3px 5px; + height: 20px; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .label-stripe-chat, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .label-stripe-chat, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .label-stripe-chat, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .label-stripe-chat, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .label-stripe-chat { + text-transform: uppercase; + background-color: var(--blades-black); + color: var(--blades-white); + position: relative; + padding-top: 3px; + display: flex; + font-weight: bold; + margin: 0; +} +:root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .label-stripe-chat-small, +:root body.vtt.game.system-eunos-blades #controls #chat .chat-message .message-content .label-stripe-chat-small, +:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message .message-content .label-stripe-chat-small, +:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message .message-content .label-stripe-chat-small, +:root body.vtt.game.system-eunos-blades #players #chat .chat-message .message-content .label-stripe-chat-small { + text-transform: capitalize; + background-color: var(--blades-grey); + color: var(--blades-black); + margin-bottom: 10px; + position: relative; + padding-top: 3px; + display: flex; font-weight: bold; } :root body.vtt.game.system-eunos-blades #interface #chat .chat-message .message-content .blades-die-tooltip .die, @@ -11260,13 +13511,9 @@ template { } :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container { --container-height: 40px; - --container-left-shift: 50px; - --csq-icon-dark: var(--blades-red-dark); - --csq-icon-med: var(--blades-red); - --csq-icon-bright: var(--blades-red-bright); - --csq-type-bg: var(--csq-icon-dark); - --csq-type-color: var(--blades-black-dark); + --container-left-shift: 70px; --csq-icon-bg-color: var(--blades-black-dark); + --csq-type-bg: var(--csq-icon-dark); --csq-button-size-mult: 0.33; position: relative; display: block; @@ -11274,31 +13521,116 @@ template { max-height: var(--container-height); min-height: var(--container-height); } +@keyframes anim-glow { + 0% { + box-shadow: 0 0 0px var(--blades-red-bright); + background-color: var(--blades-red-darkest); + } + 10% { + background-color: var(--blades-red-bright); + } + 100% { + box-shadow: 0 0 10px 10px transparent; + background-color: var(--blades-red-darkest); + } +} +@keyframes icon-glow { + 0% { + filter: brightness(1); + } + 10% { + filter: brightness(1); + } + 100% { + filter: brightness(1); + } +} +@keyframes icon-red-pulse { + 0% { + scale: 1; + fill: var(--blades-grey); + } + 10% { + scale: 1.1; + fill: var(--blades-red-bright); + } + 100% { + scale: 1; + fill: var(--blades-grey); + } +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .base-consequence { + --csq-icon-dark: var(--blades-black); + --csq-icon-med: var(--blades-grey); + --csq-icon-bright: var(--blades-white); + --csq-type-color: var(--blades-grey); + --csq-name-color: var(--blades-white); +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .accept-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-red-dark); + --csq-icon-med: var(--blades-red); + --csq-icon-bright: var(--blades-red-bright); + --csq-type-color: var(--blades-black-dark); + --csq-name-color: var(--blades-red); +} :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .resist-consequence { + opacity: 0; --csq-icon-dark: var(--blades-gold-dark); --csq-icon-med: var(--blades-gold); --csq-icon-bright: var(--blades-gold-bright); --csq-type-color: var(--blades-gold-dark); --csq-name-color: var(--blades-gold-bright); } +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .special-armor-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-blue-dark); + --csq-icon-med: var(--blades-blue); + --csq-icon-bright: var(--blades-blue-bright); + --csq-type-color: var(--blades-blue-dark); + --csq-name-color: var(--blades-blue-bright); +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-interaction-pad { + display: none; + display: block; + position: absolute; + z-index: 2; + pointer-events: auto; + height: 100%; + top: 0; +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad { + --pad-left-shift: calc(var(--container-left-shift) + (var(--container-height))); + left: var(--pad-left-shift); + width: calc(100% - var(--pad-left-shift)); +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + left: 0; + width: calc(var(--container-left-shift) - var(--container-height) * 0); +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + height: 40%; + z-index: 3; +} :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container { position: relative; - height: var(--container-height); - width: var(--container-height); - min-width: var(--container-height); - max-width: var(--container-height); - min-height: var(--container-height); - max-height: var(--container-height); + height: calc(var(--container-height) * 0.75); + max-width: calc(var(--container-height) * 0.75); background: transparent; left: var(--container-left-shift); z-index: 1; + pointer-events: auto; + transition: 0.2s; +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container:hover { + filter: brightness(2); } :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle { position: absolute; translate: -50% -50%; - transform-origin: 50% 50%; - top: 50%; - left: 50%; + transform-origin: 100% 0%; + top: calc(var(--container-height) * 0.5); + left: calc(var(--container-height) * 0.5); border-radius: 50%; height: var(--container-height); width: var(--container-height); @@ -11329,6 +13661,24 @@ template { :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg .fill-linear { fill: var(--csq-icon-med); } +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path { + transform-origin: 50% 50%; +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence { + scale: 0.75; + animation: icon-glow 2s ease infinite; + pointer-events: auto; +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path { + animation: icon-red-pulse 2s ease infinite; +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover { + filter: brightness(2) !important; + animation: none; +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence { + outline-width: 2px; +} :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon { height: 100%; } @@ -11338,17 +13688,21 @@ template { display: flex; flex-direction: row; flex-wrap: nowrap; - bottom: 0px; + bottom: -10px; } :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg { position: absolute; z-index: -1; height: 100%; width: calc(100% + 24px); + transform-origin: 0% 50%; top: 0px; background: var(--csq-icon-bright); display: block; } +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg { + transform-origin: 100% 50%; +} :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label { position: relative; z-index: 1; @@ -11356,6 +13710,9 @@ template { font-size: 10px; line-height: 14px; color: var(--blades-grey); + font-weight: 800; + text-shadow: 0px 0px 1px var(--blades-black-dark); + letter-spacing: 1; text-transform: uppercase; white-space: nowrap; } @@ -11372,14 +13729,14 @@ template { margin: 0; } :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container { - right: calc(100% - 3px); + right: 100%; } :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg { left: -7px; transform: skewX(45deg); } :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container { - left: 100%; + left: 140%; } :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg { right: -7px; @@ -11394,7 +13751,7 @@ template { height: calc(var(--container-height) * 0.33); transform-origin: 0% 50%; left: calc(var(--container-height) + var(--container-left-shift) - 10px); - top: 0px; + top: -2px; padding: 0 5px 0 15px; } :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-type-container .consequence-type-bg { @@ -11403,20 +13760,21 @@ template { left: -20px; height: 100%; width: 170px; + transform-origin: 0% 50%; transform: skewX(-45deg); - background: var(--csq-type-bg); + background: var(--csq-icon-dark); } :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-type-container .consequence-type { position: absolute; top: 0; - left: 10px; + transform-origin: 0% 50%; white-space: nowrap; font-family: Oswald, sans-serif; text-transform: uppercase; text-align: right; font-size: 10px; color: var(--csq-type-color); - font-weight: bold; + font-weight: normal; } :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-name-container { position: absolute; @@ -11432,9 +13790,10 @@ template { z-index: 1; padding: 0 5px 0 35px; font-size: 14px; - line-height: calc(var(--container-height) * 0.55); + line-height: 17px; font-family: Kirsty, serif; font-variant: small-caps; + transform-origin: 0% 50%; color: var(--csq-icon-bright); font-style: italic; } @@ -11444,7 +13803,7 @@ template { :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-footer-container { position: absolute; height: calc(var(--container-height) * var(--csq-button-size-mult)); - width: 120px; + width: auto; bottom: 0; top: unset; left: calc(var(--container-height) + var(--container-left-shift) - 20px); @@ -11458,8 +13817,17 @@ template { background: var(--csq-icon-bright); display: block; transform: skewX(45deg); + transform-origin: 0% 50%; +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence { + width: 120px; } -:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute { +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence { + width: 250px; +} +:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-footer-container .consequence-footer-message { + position: absolute; + white-space: nowrap; font-family: Oswald, sans-serif; font-weight: bold; color: var(--blades-black-dark); @@ -11467,6 +13835,7 @@ template { line-height: 14px; padding-left: 25px; justify-content: flex-start; + transform-origin: 0% 50%; gap: 5px; } :root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-footer-container .dotline { @@ -11959,7 +14328,7 @@ template { color: var(--blades-white-bright) !important; } :root body.vtt.game.system-eunos-blades .app.window-app .cyan-bright { - color: var(--blades-cyan-bright) !important; + color: var(--blades-blue-bright) !important; } :root body.vtt.game.system-eunos-blades .app.window-app .uppercase { text-transform: uppercase !important; @@ -13880,7 +16249,7 @@ template { color: var(--blades-white-bright) !important; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet .window-content form .cyan-bright { - color: var(--blades-cyan-bright) !important; + color: var(--blades-blue-bright) !important; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet .window-content form .uppercase { text-transform: uppercase !important; @@ -16919,7 +19288,7 @@ template { opacity: 1; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.dialog .window-content .dialog-content .nav-group .tab[data-tab] .comp.fine-quality .comp-body .comp-title { - color: var(--blades-cyan); + color: var(--blades-blue); } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.dialog .window-content .dialog-content .nav-group .tab[data-tab] .comp.unaffordable { order: 2; @@ -16990,7 +19359,7 @@ template { background: var(--section-bg-color); } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.dialog .window-content .consequence-section.consequence-section-controlled { - --section-bg-color: var(--blades-cyan-dark-fade) ; + --section-bg-color: var(--blades-blue-dark-fade) ; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.dialog .window-content .consequence-section.consequence-section-risky { --section-bg-color: transparent ; @@ -17011,7 +19380,7 @@ template { box-shadow: none; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.dialog .window-content .consequence-section h1.consequence-header-controlled { - --h1-color: var(--blades-cyan) ; + --h1-color: var(--blades-blue) ; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.dialog .window-content .consequence-section h1.consequence-header-risky { --h1-color: var(--blades-grey-bright) ; @@ -17552,9 +19921,9 @@ template { --final-block-border-color: var(--blades-red); } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .roll-sheet-float-block.final-block.position-final-block.position-controlled { - --final-block-text-color: var(--blades-cyan-bright); - --final-block-background-color: var(--blades-cyan-dark-fade); - --final-block-border-color: var(--blades-cyan); + --final-block-text-color: var(--blades-blue-bright); + --final-block-background-color: var(--blades-blue-dark-fade); + --final-block-border-color: var(--blades-blue); } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .roll-sheet-float-block.final-block.effect-final-block.effect-zero { --final-block-text-color: var(--blades-red-dark); @@ -17567,9 +19936,9 @@ template { --final-block-border-color: var(--blades-red); } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .roll-sheet-float-block.final-block.effect-final-block.effect-great { - --final-block-text-color: var(--blades-cyan-bright); - --final-block-background-color: var(--blades-cyan-dark-fade); - --final-block-border-color: var(--blades-cyan); + --final-block-text-color: var(--blades-blue-bright); + --final-block-background-color: var(--blades-blue-dark-fade); + --final-block-border-color: var(--blades-blue); } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .roll-sheet-float-block.final-block.effect-final-block.effect-extreme { --final-block-text-color: var(--blades-white-bright); @@ -17690,8 +20059,8 @@ template { :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab.gm-roll-collab .window-content form .sheet-root .roll-sheet-block .roll-sheet-sub-block .position-controls-container .roll-mod-gm-control.gm-control-effect-great, :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab.gm-roll-collab .window-content form .sheet-root .roll-sheet-block .roll-sheet-sub-block .position-controls-container .roll-mod-gm-control.gm-control-position-controlled, :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab.gm-roll-collab .window-content form .sheet-root .roll-sheet-block .roll-sheet-sub-block .effect-controls-container .roll-mod-gm-control.gm-control-effect-great, :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab.gm-roll-collab .window-content form .sheet-root .roll-sheet-block .roll-sheet-sub-block .effect-controls-container .roll-mod-gm-control.gm-control-position-controlled { - --gm-control-border: var(--blades-cyan-bright); - --gm-control-background: var(--blades-cyan-dark); + --gm-control-border: var(--blades-blue-bright); + --gm-control-background: var(--blades-blue-dark); } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab.gm-roll-collab .window-content form .sheet-root .roll-sheet-block .roll-sheet-sub-block .position-controls-container .roll-mod-gm-control.gm-control-effect-extreme, :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab.gm-roll-collab .window-content form .sheet-root .roll-sheet-block .roll-sheet-sub-block .effect-controls-container .roll-mod-gm-control.gm-control-effect-extreme { @@ -17880,10 +20249,10 @@ template { --roll-type-header-shadow-color: var(--blades-black-dark); } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root .sheet-topper.roll-type.roll-type-fortune { - --roll-type-header-color: var(--blades-cyan-bright); - --roll-type-header-bg-color: var(--blades-cyan-dark); - --roll-type-header-underline-color: var(--blades-cyan-bright); - --roll-type-header-shadow-color: var(--blades-cyan-dark); + --roll-type-header-color: var(--blades-blue-bright); + --roll-type-header-bg-color: var(--blades-blue-dark); + --roll-type-header-underline-color: var(--blades-blue-bright); + --roll-type-header-shadow-color: var(--blades-blue-dark); } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root .sheet-topper.roll-type .roll-type-header { font-family: var(--font-emphasis); @@ -18585,7 +20954,7 @@ template { color: var(--blades-red-bright) !important; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root section.sheet-footer .roll-sheet-float-block.roll-stress-block .cyan-bright, :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root section.sheet-footer .roll-sheet-float-block.roll-stress-block .cyan-bright * { - color: var(--blades-cyan-bright) !important; + color: var(--blades-blue-bright) !important; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root section.sheet-footer .roll-sheet-float-block.roll-stress-block .tooltip.tooltip-roll-stress { font-family: var(--font-default); diff --git a/css/tinymce/content.min.css b/css/tinymce/content.min.css index f213d60f..847f80bc 100644 --- a/css/tinymce/content.min.css +++ b/css/tinymce/content.min.css @@ -109,22 +109,63 @@ html, :root { --blades-grey-dark-nums: 68, 68, 68; --blades-black-nums: 32, 32, 32; --blades-black-dark-nums: 0, 0, 0; - --blades-gold-bright-nums: 255, 231, 92; - --blades-gold-nums: 255, 215, 0; - --blades-gold-dark-nums: 184, 156, 0; - --blades-gold-darkest-nums: 55, 53, 0; - --blades-red-bright-nums: 220, 20, 60; - --blades-red-nums: 204, 0, 0; - --blades-red-dark-nums: 122, 0, 0; + --blades-gold-bright-nums: 206,180, 71; + --blades-gold-nums: 143,118, 11; + --blades-gold-dark-nums: 105, 86, 0; + --blades-gold-darkest-nums: 64, 52, 0; + --blades-red-bright-nums: 255, 0, 0; + --blades-red-nums: 200, 0, 0; + --blades-red-dark-nums: 150, 0, 0; --blades-red-darkest-nums: 50, 0, 0; --blades-green-bright-nums: 20, 220, 60; --blades-green-nums: 0, 204, 0; --blades-green-dark-nums: 0, 122, 0; --blades-green-darkest-nums: 0, 60, 0; - --blades-cyan-bright-nums: 198, 255, 255; - --blades-cyan-nums: 150, 255, 255; - --blades-cyan-dark-nums: 40, 120, 120; - --blades-cyan-darkest-nums: 25, 49, 49; + --blades-blue-bright-nums: 198, 255, 255; + --blades-blue-nums: 150, 255, 255; + --blades-blue-dark-nums: 40, 120, 120; + --blades-blue-darkest-nums: 25, 49, 49; + /* + NEW COLOR PALETTE OVERRIDE + + == GOLD == + http://paletton.com/#uid=11n0u0kNTr2qtG1K2DKRbkEVqcT + + shade 0 = #D7AF00 = rgb(215,175, 0) = rgba(215,175, 0,1) = rgb0(0.843,0.686,0) + shade 1 = #FFD82C = rgb(255,216, 44) = rgba(255,216, 44,1) = rgb0(1,0.847,0.173) + shade 2 = #FFCF00 = rgb(255,207, 0) = rgba(255,207, 0,1) = rgb0(1,0.812,0) + shade 3 = #A58600 = rgb(165,134, 0) = rgba(165,134, 0,1) = rgb0(0.647,0.525,0) + shade 4 = #675300 = rgb(103, 83, 0) = rgba(103, 83, 0,1) = rgb0(0.404,0.325,0)' + + == RED == + http://paletton.com/#uid=1000u0kTixTijNOwGQpTXmEXg9Y + shade 0 = #FF0000 = rgb(255, 0, 0) = rgba(255, 0, 0,1) = rgb0(1,0,0) + shade 1 = #FF6D6D = rgb(255,109,109) = rgba(255,109,109,1) = rgb0(1,0.427,0.427) + shade 2 = #FF0000 = rgb(255, 0, 0) = rgba(255, 0, 0,1) = rgb0(1,0,0) + shade 3 = #B40000 = rgb(180, 0, 0) = rgba(180, 0, 0,1) = rgb0(0.706,0,0) + shade 4 = #4F0000 = rgb( 79, 0, 0) = rgba( 79, 0, 0,1) = rgb0(0.31,0,0) + + == BLUE == + http://paletton.com/#uid=13i0u0kTixTodNREARdTRoAV1g4 + shade 0 = #009F9F = rgb( 0,159,159) = rgba( 0,159,159,1) = rgb0(0,0.624,0.624) + shade 1 = #34D5D5 = rgb( 52,213,213) = rgba( 52,213,213,1) = rgb0(0.204,0.835,0.835) + shade 2 = #00E0E0 = rgb( 0,224,224) = rgba( 0,224,224,1) = rgb0(0,0.878,0.878) + shade 3 = #007676 = rgb( 0,118,118) = rgba( 0,118,118,1) = rgb0(0,0.463,0.463) + shade 4 = #004D4D = rgb( 0, 77, 77) = rgba( 0, 77, 77,1) = rgb0(0,0.302,0.302) + */ + --blades-gold-bright-nums: 255,216, 44; + --blades-gold-nums: 215,175, 0; + --blades-gold-dark-nums: 165,134, 0; + --blades-gold-darkest-nums: 103, 83, 0; + /* --blades-red-bright-nums: 255,109,109; + --blades-red-nums: 255, 0, 0; + --blades-red-dark-nums: 180, 0, 0; + --blades-red-darkest-nums: 79, 0, 0; */ + --blades-blue-bright-nums: 0,224,224; + --blades-blue-nums: 52,213,213; + --blades-blue-dark-nums: 0,118,118; + --blades-blue-darkest-nums: 0, 77, 77; + /* END OVERRIDE */ --blades-white-bright: rgba(var(--blades-white-bright-nums), 1); --blades-white: rgba(var(--blades-white-nums), 1); --blades-grey-bright: rgba(var(--blades-grey-bright-nums), 1); @@ -132,6 +173,7 @@ html, :root { --blades-grey-dark: rgba(var(--blades-grey-dark-nums), 1); --blades-black: rgba(var(--blades-black-nums), 1); --blades-black-dark: rgba(var(--blades-black-dark-nums), 1); + --blades-gold-brightest: rgba(var(--blades-gold-brightest-nums), 1); --blades-gold-bright: rgba(var(--blades-gold-bright-nums), 1); --blades-gold: rgba(var(--blades-gold-nums), 1); --blades-gold-dark: rgba(var(--blades-gold-dark-nums), 1); @@ -144,10 +186,10 @@ html, :root { --blades-green: rgba(var(--blades-green-nums), 1); --blades-green-dark: rgba(var(--blades-green-dark-nums), 1); --blades-green-darkest: rgba(var(--blades-green-darkest-nums), 1); - --blades-cyan-bright: rgba(var(--blades-cyan-bright-nums), 1); - --blades-cyan: rgba(var(--blades-cyan-nums), 1); - --blades-cyan-dark: rgba(var(--blades-cyan-dark-nums), 1); - --blades-cyan-darkest: rgba(var(--blades-cyan-darkest-nums), 1); + --blades-blue-bright: rgba(var(--blades-blue-bright-nums), 1); + --blades-blue: rgba(var(--blades-blue-nums), 1); + --blades-blue-dark: rgba(var(--blades-blue-dark-nums), 1); + --blades-blue-darkest: rgba(var(--blades-blue-darkest-nums), 1); --blades-white-fade: rgba(var(--blades-white-nums), 0.5); --blades-white-fade-strong: rgba(var(--blades-white-nums), 0.25); --blades-white-bright-fade: rgba(var(--blades-white-bright-nums), 0.5); @@ -158,10 +200,10 @@ html, :root { --blades-black-dark-fade-strong: rgba(var(--blades-black-dark-nums), 0.25); --blades-red-dark-fade: rgba(var(--blades-red-dark-nums), 0.5); --blades-green-dark-fade: rgba(var(--blades-green-dark-nums), 0.5); - --blades-cyan-dark-fade: rgba(var(--blades-cyan-dark-nums), 0.5); + --blades-blue-dark-fade: rgba(var(--blades-blue-dark-nums), 0.5); --blades-red-dark-fade-strong: rgba(var(--blades-red-dark-nums), 0.25); --blades-green-dark-fade-strong: rgba(var(--blades-green-dark-nums), 0.25); - --blades-cyan-dark-fade-strong: rgba(var(--blades-cyan-dark-nums), 0.25); + --blades-blue-dark-fade-strong: rgba(var(--blades-blue-dark-nums), 0.25); --color-primary: var(--blades-white-nums); --color-background: var(--blades-black-nums); --color-background-lightest: var(--blades-grey-nums); @@ -1022,13 +1064,9 @@ html .comp.controls-container .controls-panel.active .controls-list li:hover > a } html .comp.consequence-display-container, :root .comp.consequence-display-container { --container-height: 40px; - --container-left-shift: 50px; - --csq-icon-dark: var(--blades-red-dark); - --csq-icon-med: var(--blades-red); - --csq-icon-bright: var(--blades-red-bright); - --csq-type-bg: var(--csq-icon-dark); - --csq-type-color: var(--blades-black-dark); + --container-left-shift: 70px; --csq-icon-bg-color: var(--blades-black-dark); + --csq-type-bg: var(--csq-icon-dark); --csq-button-size-mult: 0.33; position: relative; display: block; @@ -1036,31 +1074,116 @@ html .comp.consequence-display-container, :root .comp.consequence-display-contai max-height: var(--container-height); min-height: var(--container-height); } +@keyframes anim-glow { + 0% { + box-shadow: 0 0 0px var(--blades-red-bright); + background-color: var(--blades-red-darkest); + } + 10% { + background-color: var(--blades-red-bright); + } + 100% { + box-shadow: 0 0 10px 10px transparent; + background-color: var(--blades-red-darkest); + } +} +@keyframes icon-glow { + 0% { + filter: brightness(1); + } + 10% { + filter: brightness(1); + } + 100% { + filter: brightness(1); + } +} +@keyframes icon-red-pulse { + 0% { + scale: 1; + fill: var(--blades-grey); + } + 10% { + scale: 1.1; + fill: var(--blades-red-bright); + } + 100% { + scale: 1; + fill: var(--blades-grey); + } +} +html .comp.consequence-display-container .base-consequence, :root .comp.consequence-display-container .base-consequence { + --csq-icon-dark: var(--blades-black); + --csq-icon-med: var(--blades-grey); + --csq-icon-bright: var(--blades-white); + --csq-type-color: var(--blades-grey); + --csq-name-color: var(--blades-white); +} +html .comp.consequence-display-container .accept-consequence, :root .comp.consequence-display-container .accept-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-red-dark); + --csq-icon-med: var(--blades-red); + --csq-icon-bright: var(--blades-red-bright); + --csq-type-color: var(--blades-black-dark); + --csq-name-color: var(--blades-red); +} html .comp.consequence-display-container .resist-consequence, :root .comp.consequence-display-container .resist-consequence { + opacity: 0; --csq-icon-dark: var(--blades-gold-dark); --csq-icon-med: var(--blades-gold); --csq-icon-bright: var(--blades-gold-bright); --csq-type-color: var(--blades-gold-dark); --csq-name-color: var(--blades-gold-bright); } +html .comp.consequence-display-container .special-armor-consequence, :root .comp.consequence-display-container .special-armor-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-blue-dark); + --csq-icon-med: var(--blades-blue); + --csq-icon-bright: var(--blades-blue-bright); + --csq-type-color: var(--blades-blue-dark); + --csq-name-color: var(--blades-blue-bright); +} +html .comp.consequence-display-container .consequence-interaction-pad, :root .comp.consequence-display-container .consequence-interaction-pad { + display: none; + display: block; + position: absolute; + z-index: 2; + pointer-events: auto; + height: 100%; + top: 0; +} +html .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad, :root .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad { + --pad-left-shift: calc(var(--container-left-shift) + (var(--container-height))); + left: var(--pad-left-shift); + width: calc(100% - var(--pad-left-shift)); +} +html .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, html .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, :root .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, :root .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + left: 0; + width: calc(var(--container-left-shift) - var(--container-height) * 0); +} +html .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, :root .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + height: 40%; + z-index: 3; +} html .comp.consequence-display-container .consequence-icon-container, :root .comp.consequence-display-container .consequence-icon-container { position: relative; - height: var(--container-height); - width: var(--container-height); - min-width: var(--container-height); - max-width: var(--container-height); - min-height: var(--container-height); - max-height: var(--container-height); + height: calc(var(--container-height) * 0.75); + max-width: calc(var(--container-height) * 0.75); background: transparent; left: var(--container-left-shift); z-index: 1; + pointer-events: auto; + transition: 0.2s; +} +html .comp.consequence-display-container .consequence-icon-container:hover, :root .comp.consequence-display-container .consequence-icon-container:hover { + filter: brightness(2); } html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle { position: absolute; translate: -50% -50%; - transform-origin: 50% 50%; - top: 50%; - left: 50%; + transform-origin: 100% 0%; + top: calc(var(--container-height) * 0.5); + left: calc(var(--container-height) * 0.5); border-radius: 50%; height: var(--container-height); width: var(--container-height); @@ -1091,6 +1214,24 @@ html .comp.consequence-display-container .consequence-icon-container .consequenc html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg .fill-linear, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg .fill-linear { fill: var(--csq-icon-med); } +html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path { + transform-origin: 50% 50%; +} +html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence { + scale: 0.75; + animation: icon-glow 2s ease infinite; + pointer-events: auto; +} +html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path { + animation: icon-red-pulse 2s ease infinite; +} +html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover { + filter: brightness(2) !important; + animation: none; +} +html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence { + outline-width: 2px; +} html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon { height: 100%; } @@ -1100,17 +1241,21 @@ html .comp.consequence-display-container .consequence-icon-container .consequenc display: flex; flex-direction: row; flex-wrap: nowrap; - bottom: 0px; + bottom: -10px; } html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg { position: absolute; z-index: -1; height: 100%; width: calc(100% + 24px); + transform-origin: 0% 50%; top: 0px; background: var(--csq-icon-bright); display: block; } +html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg { + transform-origin: 100% 50%; +} html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label { position: relative; z-index: 1; @@ -1118,6 +1263,9 @@ html .comp.consequence-display-container .consequence-icon-container .consequenc font-size: 10px; line-height: 14px; color: var(--blades-grey); + font-weight: 800; + text-shadow: 0px 0px 1px var(--blades-black-dark); + letter-spacing: 1; text-transform: uppercase; white-space: nowrap; } @@ -1134,14 +1282,14 @@ html .comp.consequence-display-container .consequence-icon-container .consequenc margin: 0; } html .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container { - right: calc(100% - 3px); + right: 100%; } html .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg { left: -7px; transform: skewX(45deg); } html .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container { - left: 100%; + left: 140%; } html .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg { right: -7px; @@ -1156,7 +1304,7 @@ html .comp.consequence-display-container .consequence-type-container, :root .com height: calc(var(--container-height) * 0.33); transform-origin: 0% 50%; left: calc(var(--container-height) + var(--container-left-shift) - 10px); - top: 0px; + top: -2px; padding: 0 5px 0 15px; } html .comp.consequence-display-container .consequence-type-container .consequence-type-bg, :root .comp.consequence-display-container .consequence-type-container .consequence-type-bg { @@ -1165,20 +1313,21 @@ html .comp.consequence-display-container .consequence-type-container .consequenc left: -20px; height: 100%; width: 170px; + transform-origin: 0% 50%; transform: skewX(-45deg); - background: var(--csq-type-bg); + background: var(--csq-icon-dark); } html .comp.consequence-display-container .consequence-type-container .consequence-type, :root .comp.consequence-display-container .consequence-type-container .consequence-type { position: absolute; top: 0; - left: 10px; + transform-origin: 0% 50%; white-space: nowrap; font-family: Oswald, sans-serif; text-transform: uppercase; text-align: right; font-size: 10px; color: var(--csq-type-color); - font-weight: bold; + font-weight: normal; } html .comp.consequence-display-container .consequence-name-container, :root .comp.consequence-display-container .consequence-name-container { position: absolute; @@ -1194,9 +1343,10 @@ html .comp.consequence-display-container .consequence-name-container .consequenc z-index: 1; padding: 0 5px 0 35px; font-size: 14px; - line-height: calc(var(--container-height) * 0.55); + line-height: 17px; font-family: Kirsty, serif; font-variant: small-caps; + transform-origin: 0% 50%; color: var(--csq-icon-bright); font-style: italic; } @@ -1206,7 +1356,7 @@ html .comp.consequence-display-container .consequence-name-container .consequenc html .comp.consequence-display-container .consequence-footer-container, :root .comp.consequence-display-container .consequence-footer-container { position: absolute; height: calc(var(--container-height) * var(--csq-button-size-mult)); - width: 120px; + width: auto; bottom: 0; top: unset; left: calc(var(--container-height) + var(--container-left-shift) - 20px); @@ -1220,8 +1370,17 @@ html .comp.consequence-display-container .consequence-footer-container .conseque background: var(--csq-icon-bright); display: block; transform: skewX(45deg); + transform-origin: 0% 50%; +} +html .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence, :root .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence { + width: 120px; } -html .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute, :root .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute { +html .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence, :root .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence { + width: 250px; +} +html .comp.consequence-display-container .consequence-footer-container .consequence-footer-message, :root .comp.consequence-display-container .consequence-footer-container .consequence-footer-message { + position: absolute; + white-space: nowrap; font-family: Oswald, sans-serif; font-weight: bold; color: var(--blades-black-dark); @@ -1229,6 +1388,7 @@ html .comp.consequence-display-container .consequence-footer-container .conseque line-height: 14px; padding-left: 25px; justify-content: flex-start; + transform-origin: 0% 50%; gap: 5px; } html .comp.consequence-display-container .consequence-footer-container .dotline, :root .comp.consequence-display-container .consequence-footer-container .dotline { @@ -2521,13 +2681,9 @@ html .comp.controls-container .controls-panel.active .controls-list li:hover > a } html .comp.consequence-display-container, :root .comp.consequence-display-container { --container-height: 40px; - --container-left-shift: 50px; - --csq-icon-dark: var(--blades-red-dark); - --csq-icon-med: var(--blades-red); - --csq-icon-bright: var(--blades-red-bright); - --csq-type-bg: var(--csq-icon-dark); - --csq-type-color: var(--blades-black-dark); + --container-left-shift: 70px; --csq-icon-bg-color: var(--blades-black-dark); + --csq-type-bg: var(--csq-icon-dark); --csq-button-size-mult: 0.33; position: relative; display: block; @@ -2535,31 +2691,116 @@ html .comp.consequence-display-container, :root .comp.consequence-display-contai max-height: var(--container-height); min-height: var(--container-height); } +@keyframes anim-glow { + 0% { + box-shadow: 0 0 0px var(--blades-red-bright); + background-color: var(--blades-red-darkest); + } + 10% { + background-color: var(--blades-red-bright); + } + 100% { + box-shadow: 0 0 10px 10px transparent; + background-color: var(--blades-red-darkest); + } +} +@keyframes icon-glow { + 0% { + filter: brightness(1); + } + 10% { + filter: brightness(1); + } + 100% { + filter: brightness(1); + } +} +@keyframes icon-red-pulse { + 0% { + scale: 1; + fill: var(--blades-grey); + } + 10% { + scale: 1.1; + fill: var(--blades-red-bright); + } + 100% { + scale: 1; + fill: var(--blades-grey); + } +} +html .comp.consequence-display-container .base-consequence, :root .comp.consequence-display-container .base-consequence { + --csq-icon-dark: var(--blades-black); + --csq-icon-med: var(--blades-grey); + --csq-icon-bright: var(--blades-white); + --csq-type-color: var(--blades-grey); + --csq-name-color: var(--blades-white); +} +html .comp.consequence-display-container .accept-consequence, :root .comp.consequence-display-container .accept-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-red-dark); + --csq-icon-med: var(--blades-red); + --csq-icon-bright: var(--blades-red-bright); + --csq-type-color: var(--blades-black-dark); + --csq-name-color: var(--blades-red); +} html .comp.consequence-display-container .resist-consequence, :root .comp.consequence-display-container .resist-consequence { + opacity: 0; --csq-icon-dark: var(--blades-gold-dark); --csq-icon-med: var(--blades-gold); --csq-icon-bright: var(--blades-gold-bright); --csq-type-color: var(--blades-gold-dark); --csq-name-color: var(--blades-gold-bright); } +html .comp.consequence-display-container .special-armor-consequence, :root .comp.consequence-display-container .special-armor-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-blue-dark); + --csq-icon-med: var(--blades-blue); + --csq-icon-bright: var(--blades-blue-bright); + --csq-type-color: var(--blades-blue-dark); + --csq-name-color: var(--blades-blue-bright); +} +html .comp.consequence-display-container .consequence-interaction-pad, :root .comp.consequence-display-container .consequence-interaction-pad { + display: none; + display: block; + position: absolute; + z-index: 2; + pointer-events: auto; + height: 100%; + top: 0; +} +html .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad, :root .comp.consequence-display-container .consequence-interaction-pad.accept-consequence-pad { + --pad-left-shift: calc(var(--container-left-shift) + (var(--container-height))); + left: var(--pad-left-shift); + width: calc(100% - var(--pad-left-shift)); +} +html .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, html .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, :root .comp.consequence-display-container .consequence-interaction-pad.resist-consequence-pad, :root .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + left: 0; + width: calc(var(--container-left-shift) - var(--container-height) * 0); +} +html .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad, :root .comp.consequence-display-container .consequence-interaction-pad.special-armor-consequence-pad { + height: 40%; + z-index: 3; +} html .comp.consequence-display-container .consequence-icon-container, :root .comp.consequence-display-container .consequence-icon-container { position: relative; - height: var(--container-height); - width: var(--container-height); - min-width: var(--container-height); - max-width: var(--container-height); - min-height: var(--container-height); - max-height: var(--container-height); + height: calc(var(--container-height) * 0.75); + max-width: calc(var(--container-height) * 0.75); background: transparent; left: var(--container-left-shift); z-index: 1; + pointer-events: auto; + transition: 0.2s; +} +html .comp.consequence-display-container .consequence-icon-container:hover, :root .comp.consequence-display-container .consequence-icon-container:hover { + filter: brightness(2); } html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle { position: absolute; translate: -50% -50%; - transform-origin: 50% 50%; - top: 50%; - left: 50%; + transform-origin: 100% 0%; + top: calc(var(--container-height) * 0.5); + left: calc(var(--container-height) * 0.5); border-radius: 50%; height: var(--container-height); width: var(--container-height); @@ -2590,6 +2831,24 @@ html .comp.consequence-display-container .consequence-icon-container .consequenc html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg .fill-linear, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg .fill-linear { fill: var(--csq-icon-med); } +html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle svg path { + transform-origin: 50% 50%; +} +html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence { + scale: 0.75; + animation: icon-glow 2s ease infinite; + pointer-events: auto; +} +html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence svg path { + animation: icon-red-pulse 2s ease infinite; +} +html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.base-consequence:hover { + filter: brightness(2) !important; + animation: none; +} +html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.resist-consequence, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle.special-armor-consequence { + outline-width: 2px; +} html .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon, :root .comp.consequence-display-container .consequence-icon-container .consequence-icon-circle .consequence-icon { height: 100%; } @@ -2599,17 +2858,21 @@ html .comp.consequence-display-container .consequence-icon-container .consequenc display: flex; flex-direction: row; flex-wrap: nowrap; - bottom: 0px; + bottom: -10px; } html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg { position: absolute; z-index: -1; height: 100%; width: calc(100% + 24px); + transform-origin: 0% 50%; top: 0px; background: var(--csq-icon-bright); display: block; } +html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-resist-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-armor-button-bg { + transform-origin: 100% 50%; +} html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label { position: relative; z-index: 1; @@ -2617,6 +2880,9 @@ html .comp.consequence-display-container .consequence-icon-container .consequenc font-size: 10px; line-height: 14px; color: var(--blades-grey); + font-weight: 800; + text-shadow: 0px 0px 1px var(--blades-black-dark); + letter-spacing: 1; text-transform: uppercase; white-space: nowrap; } @@ -2633,14 +2899,14 @@ html .comp.consequence-display-container .consequence-icon-container .consequenc margin: 0; } html .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container { - right: calc(100% - 3px); + right: 100%; } html .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg { left: -7px; transform: skewX(45deg); } html .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container { - left: 100%; + left: 140%; } html .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg { right: -7px; @@ -2655,7 +2921,7 @@ html .comp.consequence-display-container .consequence-type-container, :root .com height: calc(var(--container-height) * 0.33); transform-origin: 0% 50%; left: calc(var(--container-height) + var(--container-left-shift) - 10px); - top: 0px; + top: -2px; padding: 0 5px 0 15px; } html .comp.consequence-display-container .consequence-type-container .consequence-type-bg, :root .comp.consequence-display-container .consequence-type-container .consequence-type-bg { @@ -2664,20 +2930,21 @@ html .comp.consequence-display-container .consequence-type-container .consequenc left: -20px; height: 100%; width: 170px; + transform-origin: 0% 50%; transform: skewX(-45deg); - background: var(--csq-type-bg); + background: var(--csq-icon-dark); } html .comp.consequence-display-container .consequence-type-container .consequence-type, :root .comp.consequence-display-container .consequence-type-container .consequence-type { position: absolute; top: 0; - left: 10px; + transform-origin: 0% 50%; white-space: nowrap; font-family: Oswald, sans-serif; text-transform: uppercase; text-align: right; font-size: 10px; color: var(--csq-type-color); - font-weight: bold; + font-weight: normal; } html .comp.consequence-display-container .consequence-name-container, :root .comp.consequence-display-container .consequence-name-container { position: absolute; @@ -2693,9 +2960,10 @@ html .comp.consequence-display-container .consequence-name-container .consequenc z-index: 1; padding: 0 5px 0 35px; font-size: 14px; - line-height: calc(var(--container-height) * 0.55); + line-height: 17px; font-family: Kirsty, serif; font-variant: small-caps; + transform-origin: 0% 50%; color: var(--csq-icon-bright); font-style: italic; } @@ -2705,7 +2973,7 @@ html .comp.consequence-display-container .consequence-name-container .consequenc html .comp.consequence-display-container .consequence-footer-container, :root .comp.consequence-display-container .consequence-footer-container { position: absolute; height: calc(var(--container-height) * var(--csq-button-size-mult)); - width: 120px; + width: auto; bottom: 0; top: unset; left: calc(var(--container-height) + var(--container-left-shift) - 20px); @@ -2719,8 +2987,17 @@ html .comp.consequence-display-container .consequence-footer-container .conseque background: var(--csq-icon-bright); display: block; transform: skewX(45deg); + transform-origin: 0% 50%; +} +html .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence, :root .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.resist-consequence { + width: 120px; +} +html .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence, :root .comp.consequence-display-container .consequence-footer-container .consequence-footer-bg.special-armor-consequence { + width: 250px; } -html .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute, :root .comp.consequence-display-container .consequence-footer-container .consequence-resist-attribute { +html .comp.consequence-display-container .consequence-footer-container .consequence-footer-message, :root .comp.consequence-display-container .consequence-footer-container .consequence-footer-message { + position: absolute; + white-space: nowrap; font-family: Oswald, sans-serif; font-weight: bold; color: var(--blades-black-dark); @@ -2728,6 +3005,7 @@ html .comp.consequence-display-container .consequence-footer-container .conseque line-height: 14px; padding-left: 25px; justify-content: flex-start; + transform-origin: 0% 50%; gap: 5px; } html .comp.consequence-display-container .consequence-footer-container .dotline, :root .comp.consequence-display-container .consequence-footer-container .dotline { @@ -3231,7 +3509,7 @@ html .white-bright, :root .white-bright { color: var(--blades-white-bright) !important; } html .cyan-bright, :root .cyan-bright { - color: var(--blades-cyan-bright) !important; + color: var(--blades-blue-bright) !important; } html .uppercase, :root .uppercase { text-transform: uppercase !important; diff --git a/gulpfile.js b/gulpfile.js index 9cc7e426..492374cb 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,3 +1,4 @@ +/* eslint-disable func-names */ /* eslint-disable @typescript-eslint/no-var-requires, */ // #region ▮▮▮▮▮▮▮ IMPORTS ▮▮▮▮▮▮▮ ~ const argv = require("yargs").argv; @@ -80,15 +81,15 @@ const ANSICOLORS = { invert: "\u001b[7m" }; const STREAMSTYLES = { - gulp: ["grey", "█", "(gulp)"], - initWhiteSpace: ["bred", "█", "(staging)"], - tsInit: ["blue", "░", " TS "], - jsFull: ["bmagenta", "█", " JS "], - jsMin: ["magenta", "░", " js "], - cssFull: ["byellow", "█", " SCSS "], - cssMin: ["yellow", "░", " scss "], - hbs: ["bblue", "█", " HBS "], - toDest: ["bgreen", "█", " ASSETS "] + gulp: ["grey", "-", "(gulp)"], + initWhiteSpace: ["bred", "=", "(staging)"], + tsInit: ["blue", "T", " TS "], + jsFull: ["bmagenta", "J", " JS "], + jsMin: ["magenta", "J", " js "], + cssFull: ["byellow", "C", " SCSS "], + cssMin: ["yellow", "C", " scss "], + hbs: ["bblue", "<", " HBS "], + toDest: ["bgreen", "$", " ASSETS "] }; const ansi = (str, {fg, bg, style} = {}) => { fg = ANSICOLORS[fg ?? "white"]; @@ -99,7 +100,7 @@ const ansi = (str, {fg, bg, style} = {}) => { const toBright = (color) => (`b${color}` in ANSICOLORS ? `b${color}` : color); const toDim = (color) => (color.slice(1) in ANSICOLORS ? color.slice(1) : color); const logParts = { - tag: (tag = "gulp", color = "white", padChar = "█") => ansi(`▌${centerString(tag, 10, padChar)}▐`, {fg: color}), + tag: (tag = "gulp", color = "white", padChar = " ") => ansi(` ${centerString(tag, 10, padChar)} `, {fg: color}), error: (tag, message) => [ansi(`[ERROR: ${tag}]`, {fg: "white", bg: "red", style: "bold"}), ansi(message, {fg: "red"})].join(" "), finish: function(title, source, destination) { title ??= "gulp"; @@ -226,7 +227,22 @@ const BANNERTEMPLATE = { let BUILDFILES = {}; -if (ISCOMPILINGCODE) { +if (ISRAPIDGULPING) { + BUILDFILES = { + ts: { + "./module/": ["ts/**/*.*"] + }, + css: { + "./css/": ["scss/**/*.scss"] + }, + hbs: { + "./templates/": ["DISABLE"] + }, + quickAssets: { + "./css/": ["scss/**/*.css"] + } + }; +} else if (ISCOMPILINGCODE) { BUILDFILES = { ts: { "./module_staging_1/": ["ts/**/*.*"] @@ -301,12 +317,14 @@ const REGEXPPATTERNS = { init: new Map([ [/^\s+$/gm, "/*~ @@DOUBLE-BLANK@@ ~*/"] // Replace double-blank lines with token for later retrieval ]), - ts: new Map([[/from "gsap\/all"/gu, 'from "/scripts/greensock/esm/all.js"']]), + ts: new Map([ + [/from "gsap\/all"/gu, 'from "/scripts/greensock/esm/all.js"'], + [/from ['"](.+?)(\.ts|\.js)?['"]/g, "from \"$1.js\""] // Fix imports to include .js suffix + ]), js: new Map([ ISDEPLOYING || ISBUILDINGDIST ? [/(\r\n)?\s*?(\/\*DEVCODE\*\/.*?\/\*!DEVCODE\*\/)\s*?(\r\n)?/gms, ""] : [/`/, "`"], // Strip developer code - [/from ['"](.+?)(\.ts|\.js)?['"]/g, "from \"$1.js\""], // Fix imports to include .js suffix [ (/\/\*~\s*\*{4}▌.*?▐\*{4}\s*\*\//s, padHeaderLines) ], // Pad header lines to same length @@ -376,7 +394,7 @@ const PIPES = { // #endregion ___ PIPES ___ const PLUMBING = { - analyzeJS: async function(done) { + analyzeJS: async function analyzeJS(done) { if (!ISANALYZING) { return done(); } try { const analysisData = analyzeProject("./"); @@ -421,16 +439,19 @@ const PLUMBING = { } return done(); }, - initDest: async function(done, destGlobs = ["./dist/", "./module/", "./module_staging_1", "./module_staging_2", "./css/"]) { + initDest: async function initDest(done, destGlobs = ["./dist/", "./module/", "./module_staging_1", "./module_staging_2", "./css/"]) { destGlobs.forEach((d) => { try { fs.rmSync(d); } catch{ } }); return done(); }, - watch: function() { + watch: function watchFunc() { + console.log("\n\n==== BUILDFILES ====\n\n"); + console.log(JSON.stringify(BUILDFILES, null, 2)); + console.log("\n\n--------------------\n\n"); for (const [type, globs] of Object.entries(BUILDFILES)) { Object.values(globs ?? {}).forEach((glob) => watch(glob, BUILDFUNCS[type])); } }, - tsInit: (source, destination) => function() { + tsInit: (source, destination) => function tsInit() { const tsStream = src(source, {allowEmpty: true}) // .pipe(sourcemaps.init()) .pipe(PIPES.openPipe("tsInit")()) @@ -448,18 +469,18 @@ const PLUMBING = { } return tsStream .pipe(PIPES.replacer("ts")()) - .pipe(PIPES.replacer("js")()) + // .pipe(PIPES.replacer("js")()) // .pipe(sourcemaps.write(".")) .pipe(PIPES.closePipe("tsInit", source, destination)); }, - jsFull: (source, destination) => function() { + jsFull: (source, destination) => function jsFull() { return src(source, {allowEmpty: true}) .pipe(PIPES.openPipe("jsFull")()) .pipe(header(BANNERS.js.full, {package: packageJSON})) .pipe(PIPES.replacer("js")()) .pipe(PIPES.closePipe("jsFull", source, destination)); }, - jsMin: (source, destination) => function() { + jsMin: (source, destination) => function jsMin() { return src(source, {allowEmpty: true}) .pipe(PIPES.openPipe("jsMin")()) .pipe(header(BANNERS.js.min, {package: packageJSON})) @@ -468,7 +489,7 @@ const PLUMBING = { .pipe(PIPES.terser()()) .pipe(PIPES.closePipe("jsMin", source, destination)); }, - cssFull: (source, destination) => function() { + cssFull: (source, destination) => function cssFull() { if (ISRAPIDGULPING) { return src(source, {allowEmpty: true}) .pipe(PIPES.openPipe("cssFull")()) @@ -485,7 +506,7 @@ const PLUMBING = { .pipe(PIPES.closePipe("cssFull", source, destination)); } }, - cssMin: (source, destination) => function() { + cssMin: (source, destination) => function cssMin() { return src(source, {allowEmpty: true}) .pipe(PIPES.openPipe("cssMin")()) .pipe(sasser({outputStyle: "compressed"})) @@ -497,12 +518,12 @@ const PLUMBING = { .pipe(renamer({suffix: ".min"})) .pipe(PIPES.closePipe("cssMin", source, destination)); }, - hbs: (source, destination) => function() { + hbs: (source, destination) => function hbs() { return src(source, {allowEmpty: true}) .pipe(PIPES.openPipe("hbs")()) .pipe(PIPES.closePipe("hbs", source, destination)); }, - toDest: (source, destination) => function() { + toDest: (source, destination) => function toDest() { return src(source, {allowEmpty: true}) .pipe(PIPES.openPipe("toDest")()) .pipe(PIPES.closePipe("toDest", source, destination)); @@ -536,34 +557,36 @@ if (ISCOMPILINGCODE) { })(BUILDFILES.ts)); // ); - const jsBuildFuncs = [ - parallel(...((buildFiles) => { - const funcs = []; - for (const [destGlob, sourceGlobs] of Object.entries(buildFiles)) { - sourceGlobs.forEach((sourceGlob) => { - funcs.push(PLUMBING.jsFull(sourceGlob, destGlob)); - }); - } - return funcs; - })(BUILDFILES.js)) - ]; + if (!ISRAPIDGULPING) { + const jsBuildFuncs = [ + parallel(...((buildFiles) => { + const funcs = []; + for (const [destGlob, sourceGlobs] of Object.entries(buildFiles)) { + sourceGlobs.forEach((sourceGlob) => { + funcs.push(PLUMBING.jsFull(sourceGlob, destGlob)); + }); + } + return funcs; + })(BUILDFILES.js)) + ]; + + if (ISMINIFYINGJS) { + jsBuildFuncs.push(parallel(...((buildFiles) => { + const funcs = []; + for (const [destGlob, sourceGlobs] of Object.entries(buildFiles)) { + sourceGlobs.forEach((sourceGlob) => { + funcs.push(PLUMBING.jsMin(sourceGlob, destGlob)); + }); + } + return funcs; + })(BUILDFILES.js_2))); + } - if (ISMINIFYINGJS) { - jsBuildFuncs.push(parallel(...((buildFiles) => { - const funcs = []; - for (const [destGlob, sourceGlobs] of Object.entries(buildFiles)) { - sourceGlobs.forEach((sourceGlob) => { - funcs.push(PLUMBING.jsMin(sourceGlob, destGlob)); - }); - } - return funcs; - })(BUILDFILES.js_2))); + BUILDFUNCS.js = series( + ...jsBuildFuncs, + PLUMBING.analyzeJS + ); } - - BUILDFUNCS.js = series( - ...jsBuildFuncs, - PLUMBING.analyzeJS - ); } // #endregion ▄▄▄▄▄ JS ▄▄▄▄▄ @@ -617,8 +640,12 @@ if (ISCOMPILINGCODE) { return funcs; }; - BUILDFUNCS.quickAssets = parallel(...assetPipe(BUILDFILES.quickAssets)); - BUILDFUNCS.slowAssets = parallel(...assetPipe(BUILDFILES.slowAssets)); + if (BUILDFILES.quickAssets) { + BUILDFUNCS.quickAssets = parallel(...assetPipe(BUILDFILES.quickAssets)); + } + if (BUILDFILES.slowAssets) { + BUILDFUNCS.slowAssets = parallel(...assetPipe(BUILDFILES.slowAssets)); + } } // #endregion ▄▄▄▄▄ ASSETS ▄▄▄▄▄ @@ -634,7 +661,6 @@ const parallelFuncs = [ ].filter((pFunc) => pFunc !== undefined); // Const parallelFuncs = parallelBuildFuncs((pFunc) => pFunc !== undefined); - exports.default = series( PLUMBING.initDest, parallel(...parallelFuncs), diff --git a/module/BladesActiveEffect.js b/module/BladesActiveEffect.js index c81d85e1..7f307d33 100644 --- a/module/BladesActiveEffect.js +++ b/module/BladesActiveEffect.js @@ -1,14 +1,9 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import BladesActor from "./BladesActor.js"; import U from "./core/utilities.js"; import { Tag, BladesPhase, BladesActorType } from "./core/constants.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ const FUNCQUEUE = {}; +// {type: "ability", name: "rX:/^(?!Ghost)/"} const CUSTOMFUNCS = { addItem: async (actor, funcData, _, isReversing = false) => { eLog.checkLog("activeEffects", "addItem", { actor, funcData, isReversing }); @@ -47,6 +42,7 @@ const CUSTOMFUNCS = { APPLYTOMEMBERS: async () => new Promise(() => undefined), APPLYTOCOHORTS: async () => new Promise(() => undefined), remItem: async (actor, funcData, _, isReversing = false) => { + /*~ @@DOUBLE-BLANK@@ ~*/ function testString(targetString, testDef) { if (testDef.startsWith("rX")) { const pat = new RegExp(testDef.replace(/^rX:\/(.*?)\//, "$1")); @@ -54,6 +50,7 @@ const CUSTOMFUNCS = { } return targetString === testDef; } + /*~ @@DOUBLE-BLANK@@ ~*/ if (funcData.startsWith("{")) { if (isReversing) { console.error("Cannot reverse a 'remItem' custom effect that was defined with a JSON object."); @@ -95,6 +92,7 @@ const CUSTOMFUNCS = { return undefined; } }; +/*~ @@DOUBLE-BLANK@@ ~*/ var EffectMode; (function (EffectMode) { EffectMode[EffectMode["Custom"] = 0] = "Custom"; @@ -104,20 +102,29 @@ var EffectMode; EffectMode[EffectMode["Upgrade"] = 4] = "Upgrade"; EffectMode[EffectMode["Override"] = 5] = "Override"; })(EffectMode || (EffectMode = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesActiveEffect extends ActiveEffect { static Initialize() { CONFIG.ActiveEffect.documentClass = BladesActiveEffect; + /*~ @@DOUBLE-BLANK@@ ~*/ Hooks.on("preCreateActiveEffect", async (effect) => { + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog3("effect", "PRECREATE ActiveEffect", { effect, parent: effect.parent?.name }); + /*~ @@DOUBLE-BLANK@@ ~*/ if (!(effect.parent instanceof BladesActor)) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Does this effect have an "APPLYTOMEMBERS" or "APPLYTOCOHORTS" CUSTOM effect? if (effect.changes.some((change) => change.key === "APPLYTOMEMBERS")) { + /*~ @@DOUBLE-BLANK@@ ~*/ if (BladesActor.IsType(effect.parent, BladesActorType.pc) && BladesActor.IsType(effect.parent.crew, BladesActorType.crew)) { const otherMembers = effect.parent.crew.members.filter((member) => member.id !== effect.parent?.id); if (otherMembers.length > 0) { + // If PC & APPLYTOMEMBERS --> Create effect on members MINUS the 'APPLYTOMEMBERS' key, leave PC's effect unchanged. effect.changes = effect.changes.filter((change) => change.key !== "APPLYTOMEMBERS"); await Promise.all(otherMembers.map(async (member) => member.createEmbeddedDocuments("ActiveEffect", [effect.toJSON()]))); + // Set flag with effect's data on member, so future members can have effect applied to them. await effect.parent.setFlag("eunos-blades", `memberEffects.${effect.id}`, { appliedTo: otherMembers.map((member) => member.id), effect: effect.toJSON() @@ -130,28 +137,37 @@ class BladesActiveEffect extends ActiveEffect { return; } if (effect.parent.members.length > 0) { + // If Crew & APPLYTOMEMBERS --> Create effect on members MINUS the 'APPLYTOMEMBERS' key await Promise.all(effect.parent.members.map(async (member) => member.createEmbeddedDocuments("ActiveEffect", [effect.toJSON()]))); } + // Set flag with effect's data on crew, so future members can have effect applied to them. await effect.parent.setFlag("eunos-blades", `memberEffects.${effect.id}`, { appliedTo: effect.parent.members.map((member) => member.id), effect }); + // Update effect on crew-parent to only include 'APPLYTOMEMBERS' change await effect.updateSource({ changes: [changeKey] }); } } else if (effect.changes.some((change) => change.key === "APPLYTOCOHORTS") && (BladesActor.IsType(effect.parent, BladesActorType.pc) || BladesActor.IsType(effect.parent, BladesActorType.crew))) { if (effect.parent.cohorts.length > 0) { + // If APPLYTOCOHORTS --> Create effect on cohorts await Promise.all(effect.parent.cohorts.map(async (cohort) => cohort.createEmbeddedDocuments("ActiveEffect", [effect.toJSON()]))); } + // Set flag with effect's data on parent, so future cohorts can have effect applied to them. await effect.parent.setFlag("eunos-blades", `cohortEffects.${effect.id}`, { appliedTo: effect.parent.cohorts.map((cohort) => cohort.id), effect }); + // Update effect on parent to only include 'APPLYTOCOHORTS' change await effect.updateSource({ changes: effect.changes.filter((change) => change.key === "APPLYTOCOHORTS") }); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Partition effect.changes into permanent and non-permanent changes: const [permChanges, changes] = U.partition(effect.changes, (change) => change.key.startsWith("perm")); await effect.updateSource({ changes }); + /*~ @@DOUBLE-BLANK@@ ~*/ for (const permChange of permChanges) { const { key, value } = permChange; const permFuncName = key.replace(/^perm/, ""); @@ -170,10 +186,13 @@ class BladesActiveEffect extends ActiveEffect { } } }); + /*~ @@DOUBLE-BLANK@@ ~*/ Hooks.on("applyActiveEffect", (actor, changeData) => { + /*~ @@DOUBLE-BLANK@@ ~*/ if (!(actor instanceof BladesActor)) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ if (changeData.key in CUSTOMFUNCS) { const funcData = { funcName: changeData.key, @@ -184,6 +203,7 @@ class BladesActiveEffect extends ActiveEffect { BladesActiveEffect.ThrottleCustomFunc(actor, funcData); } }); + /*~ @@DOUBLE-BLANK@@ ~*/ Hooks.on("updateActiveEffect", (effect, { disabled }) => { if (!(effect.parent instanceof BladesActor)) { return; @@ -199,41 +219,51 @@ class BladesActiveEffect extends ActiveEffect { BladesActiveEffect.ThrottleCustomFunc(effect.parent, funcData); }); }); + /*~ @@DOUBLE-BLANK@@ ~*/ Hooks.on("deleteActiveEffect", async (effect) => { if (!(effect.parent instanceof BladesActor)) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Does this effect have an "APPLYTOMEMBERS" or "APPLYTOCOHORTS" CUSTOM effect? if (effect.changes.some((change) => change.key === "APPLYTOMEMBERS")) { if (BladesActor.IsType(effect.parent, BladesActorType.pc) && BladesActor.IsType(effect.parent.crew, BladesActorType.crew)) { const otherMembers = effect.parent.crew.members.filter((member) => member.id !== effect.parent?.id); if (otherMembers.length > 0) { + // If PC & APPLYTOMEMBERS --> Delete effect on all other members. await Promise.all(otherMembers .map(async (member) => Promise.all(member.effects .filter((e) => e.name === effect.name) .map(async (e) => e.delete())))); } + // Clear flag from parent await effect.parent.unsetFlag("eunos-blades", `memberEffects.${effect.id}`); } else if (BladesActor.IsType(effect.parent, BladesActorType.crew)) { if (effect.parent.members.length > 0) { + // If CREW & APPLYTOMEMBERS --> Delete effect on all other members. await Promise.all(effect.parent.members .map(async (member) => Promise.all(member.effects .filter((e) => e.name === effect.name) .map(async (e) => e.delete())))); } + // Clear flag from parent await effect.parent.unsetFlag("eunos-blades", `memberEffects.${effect.id}`); } } else if (effect.changes.some((change) => change.key === "APPLYTOCOHORTS") && (BladesActor.IsType(effect.parent, BladesActorType.pc, BladesActorType.crew))) { if (effect.parent.cohorts.length > 0) { + // If APPLYTOCOHORTS --> Delete effect on cohorts. await Promise.all(effect.parent.cohorts .map(async (cohort) => Promise.all(cohort.effects .filter((e) => e.name === effect.name) .map(async (e) => e.delete())))); } + // Clear flag from parent await effect.parent.unsetFlag("eunos-blades", `cohortEffects.${effect.id}`); } + /*~ @@DOUBLE-BLANK@@ ~*/ const customEffects = effect.changes.filter((changes) => changes.mode === 0); customEffects.forEach(({ key, value }) => { const funcData = { @@ -246,17 +276,21 @@ class BladesActiveEffect extends ActiveEffect { }); }); } + /*~ @@DOUBLE-BLANK@@ ~*/ static async AddActiveEffect(doc, name, eChanges, icon = "systems/eunos-blades/assets/icons/effect-icons/default.png") { const changes = [eChanges].flat(); await doc.createEmbeddedDocuments("ActiveEffect", [{ name, icon, changes }]); } + /*~ @@DOUBLE-BLANK@@ ~*/ static ThrottleCustomFunc(actor, data) { const { funcName, funcData, isReversing, effect } = data; if (!actor.id) { return; } eLog.display(`Throttling Func: ${funcName}(${funcData}, ${isReversing})`); + // Is there a currently-running function for this actor? if (actor.id && actor.id in FUNCQUEUE) { + // Is this a duplicate of a function already queued? const matchingQueue = FUNCQUEUE[actor.id].queue.find((fData) => JSON.stringify(fData) === JSON.stringify(data)); eLog.checkLog("activeEffects", "... Checking Queue", { data, FUNCQUEUE: FUNCQUEUE[actor.id], matchingQueue }); if (matchingQueue) { @@ -266,12 +300,14 @@ class BladesActiveEffect extends ActiveEffect { FUNCQUEUE[actor.id].queue.push(data); return; } + // If not, create FUNCQUEUE entry and run first function. eLog.display("... Creating New FUNCQUEUE, RUNNING:"); FUNCQUEUE[actor.id] = { curFunc: BladesActiveEffect.RunCustomFunc(actor, CUSTOMFUNCS[funcName](actor, funcData, effect, isReversing)), queue: [] }; } + /*~ @@DOUBLE-BLANK@@ ~*/ static async RunCustomFunc(actor, funcPromise) { if (!actor.id) { return; @@ -295,7 +331,12 @@ class BladesActiveEffect extends ActiveEffect { delete FUNCQUEUE[actor.id]; } } - static onManageActiveEffect(event, owner) { + /** + * Manage Active Effect instances through the Actor Sheet via effect control buttons. + * @param {MouseEvent} event The left-click event on the effect control + * @param {Actor|Item} owner The owning entity which manages this effect + */ + static onManageActiveEffect(event, owner) { event.preventDefault(); const a = event.currentTarget; if (a.dataset.action === "create") { @@ -324,6 +365,7 @@ class BladesActiveEffect extends ActiveEffect { default: return null; } } + /*~ @@DOUBLE-BLANK@@ ~*/ async _preCreate(data, options, user) { eLog.checkLog3("effect", "ActiveEffect._preCreate()", { data, options, user }); await super._preCreate(data, options, user); @@ -332,7 +374,9 @@ class BladesActiveEffect extends ActiveEffect { eLog.checkLog3("effect", "ActiveEffect._onDelete()", { options, userID }); super._onDelete(options, userID); } + /*~ @@DOUBLE-BLANK@@ ~*/ get isSuppressed() { + // Get source item from "origin.js" field -- of form 'Actor..Item.' if (!/Actor.*Item/.test(this.origin)) { return super.isSuppressed; } @@ -342,4 +386,5 @@ class BladesActiveEffect extends ActiveEffect { return super.isSuppressed || item?.hasTag(Tag.System.Archived); } } -export default BladesActiveEffect; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesActiveEffect; diff --git a/module/BladesActor.js b/module/BladesActor.js index eec973c4..76e8668b 100644 --- a/module/BladesActor.js +++ b/module/BladesActor.js @@ -1,21 +1,23 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - +// #region Imports ~ import U from "./core/utilities.js"; import C, { BladesActorType, Tag, Playbook, BladesItemType, ActionTrait, PrereqType, AdvancementPoint, Randomizers, Factor } from "./core/constants.js"; import { BladesItem } from "./documents/BladesItemProxy.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ import { BladesRollMod } from "./BladesRoll.js"; import BladesPushAlert from "./BladesPushAlert.js"; import { SelectionCategory } from "./BladesDialog.js"; +// #endregion +/*~ @@DOUBLE-BLANK@@ ~*/ +// Blades Theme Song: "Bangkok" from The Gray Man soundtrack: https://www.youtube.com/watch?v=cjjImvMqYlo&list=OLAK5uy_k9cZDd1Fbpd25jfDtte5A6HyauD2-cwgk&index=2 +// Also check out Discord thread: https://discord.com/channels/325094888133885952/1152316839163068527 +/*~ @@DOUBLE-BLANK@@ ~*/ +// eslint-disable-next-line no-shadow var BladesActorUniqueTags; (function (BladesActorUniqueTags) { BladesActorUniqueTags["CharacterCrew"] = "CharacterCrew"; BladesActorUniqueTags["VicePurveyor"] = "VicePurveyor"; })(BladesActorUniqueTags || (BladesActorUniqueTags = {})); +// eslint-disable-next-line no-shadow var BladesItemUniqueTypes; (function (BladesItemUniqueTypes) { BladesItemUniqueTypes["background"] = "background"; @@ -27,15 +29,22 @@ var BladesItemUniqueTypes; BladesItemUniqueTypes["preferred_op"] = "preferred_op"; })(BladesItemUniqueTypes || (BladesItemUniqueTypes = {})); class BladesActor extends Actor { - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Static Overrides: Create ~ static async create(data, options = {}) { data.token = data.token || {}; data.system = data.system ?? {}; + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Create world_name data.system.world_name = data.system.world_name ?? data.name.replace(/[^A-Za-z_0-9 ]/g, "").trim().replace(/ /g, "_"); + /*~ @@DOUBLE-BLANK@@ ~*/ return super.create(data, options); } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesDocument Implementation ~ static get All() { return game.actors; } + /*~ @@DOUBLE-BLANK@@ ~*/ static Get(actorRef) { if (actorRef instanceof BladesActor) { return actorRef; @@ -46,18 +55,23 @@ class BladesActor extends Actor { return BladesActor.All.find((a) => a.system.world_name === actorRef) || BladesActor.All.find((a) => a.name === actorRef); } + /*~ @@DOUBLE-BLANK@@ ~*/ static GetTypeWithTags(docType, ...tags) { return BladesActor.All.filter((actor) => actor.type === docType) .filter((actor) => actor.hasTag(...tags)); } + /*~ @@DOUBLE-BLANK@@ ~*/ static IsType(doc, ...types) { const typeSet = new Set(types); return doc instanceof BladesActor && typeSet.has(doc.type); } + /*~ @@DOUBLE-BLANK@@ ~*/ get tags() { return this.system.tags ?? []; } + /*~ @@DOUBLE-BLANK@@ ~*/ hasTag(...tags) { return tags.every((tag) => this.tags.includes(tag)); } + /*~ @@DOUBLE-BLANK@@ ~*/ async addTag(...tags) { const curTags = this.tags; tags.forEach((tag) => { @@ -69,18 +83,22 @@ class BladesActor extends Actor { eLog.checkLog2("actor", "BladesActor.addTag(...tags)", { tags, curTags }); await this.update({ "system.tags": curTags }); } + /*~ @@DOUBLE-BLANK@@ ~*/ async remTag(...tags) { const curTags = this.tags.filter((tag) => !tags.includes(tag)); eLog.checkLog2("actor", "BladesActor.remTag(...tags)", { tags, curTags }); await this.update({ "system.tags": curTags }); } + /*~ @@DOUBLE-BLANK@@ ~*/ get tooltip() { const tooltipText = [this.system.concept, this.system.subtitle] .filter(Boolean) .join("

"); return tooltipText ? (new Handlebars.SafeString(tooltipText)).toString() : undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ get dialogCSSClasses() { return ""; } + /*~ @@DOUBLE-BLANK@@ ~*/ getFactorTotal(factor) { switch (factor) { case Factor.tier: { @@ -105,26 +123,41 @@ class BladesActor extends Actor { default: return 0; } } + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region SubActorControl Implementation ~ + /*~ @@DOUBLE-BLANK@@ ~*/ get subActors() { return Object.keys(this.system.subactors) .map((id) => this.getSubActor(id)) .filter((subActor) => Boolean(subActor)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get activeSubActors() { return this.subActors.filter((subActor) => !subActor.hasTag(Tag.System.Archived)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get archivedSubActors() { return this.subActors.filter((subActor) => subActor.hasTag(Tag.System.Archived)); } + /*~ @@DOUBLE-BLANK@@ ~*/ checkActorPrereqs(actor) { - + /*~ @@DOUBLE-BLANK@@ ~*/ + /* Implement any prerequisite checks for embedding actors */ + /*~ @@DOUBLE-BLANK@@ ~*/ return Boolean(actor); } + /*~ @@DOUBLE-BLANK@@ ~*/ processEmbeddedActorMatches(globalActors) { + /*~ @@DOUBLE-BLANK@@ ~*/ return globalActors + // Step 1: Filter out globals that fail prereqs. .filter(this.checkActorPrereqs) + // Step 2: Filter out actors that are already active subActors .filter((gActor) => !this.activeSubActors.some((aActor) => aActor.id === gActor.id)) + // Step 3: Merge subactor data onto matching global actors .map((gActor) => this.getSubActor(gActor) || gActor) + // Step 4: Sort by name .sort((a, b) => { if (a.name === b.name) { return 0; @@ -144,9 +177,13 @@ class BladesActor extends Actor { return 0; }); } + /*~ @@DOUBLE-BLANK@@ ~*/ getDialogActors(category) { - + /*~ @@DOUBLE-BLANK@@ ~*/ + /* **** NEED TO FILTER OUT ACTORS PLAYER DOESN'T HAVE PERMISSION TO SEE **** */ + /*~ @@DOUBLE-BLANK@@ ~*/ const dialogData = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ switch (category) { case SelectionCategory.Contact: case SelectionCategory.Rival: @@ -173,19 +210,26 @@ class BladesActor extends Actor { default: return false; } } + /*~ @@DOUBLE-BLANK@@ ~*/ async addSubActor(actorRef, tags) { + /*~ @@DOUBLE-BLANK@@ ~*/ let focusSubActor; + // Does an embedded subActor of this Actor already exist on the character? if (this.hasSubActorOf(actorRef)) { const subActor = this.getSubActor(actorRef); if (!subActor) { return; } + // Is it an archived Item? if (subActor.hasTag(Tag.System.Archived)) { + // Unarchive it await subActor.remTag(Tag.System.Archived); } + // Make it the focus item. focusSubActor = subActor; } else { + // Is it not embedded at all? Create new entry in system.subactors from global actor const actor = BladesActor.Get(actorRef); if (!actor) { return; @@ -197,14 +241,19 @@ class BladesActor extends Actor { ...tags ]); } + // Await the update, then make the retrieved subactor the focus await this.update({ [`system.subactors.${actor.id}`]: subActorData }); focusSubActor = this.getSubActor(actor.id); } + /*~ @@DOUBLE-BLANK@@ ~*/ if (!focusSubActor) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Does this Actor contain any tags limiting it to one per actor? const uniqueTags = focusSubActor.tags.filter((tag) => tag in BladesActorUniqueTags); if (uniqueTags.length > 0) { + // ... then archive all other versions. uniqueTags.forEach((uTag) => this.activeSubActors .filter((subActor) => Boolean(focusSubActor?.id && subActor.id !== focusSubActor.id @@ -212,6 +261,7 @@ class BladesActor extends Actor { .map((subActor) => this.remSubActor(subActor.id))); } } + /*~ @@DOUBLE-BLANK@@ ~*/ getSubActor(actorRef) { const actor = BladesActor.Get(actorRef); if (!actor?.id) { @@ -225,6 +275,7 @@ class BladesActor extends Actor { actor.parentActor = this; return actor; } + /*~ @@DOUBLE-BLANK@@ ~*/ hasSubActorOf(actorRef) { const actor = BladesActor.Get(actorRef); if (!actor) { @@ -232,6 +283,7 @@ class BladesActor extends Actor { } return actor?.id ? actor.id in this.system.subactors : false; } + /*~ @@DOUBLE-BLANK@@ ~*/ async updateSubActor(actorRef, upData) { const updateData = U.objExpand(upData); if (!updateData.system) { @@ -241,15 +293,23 @@ class BladesActor extends Actor { if (!actor) { return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // DiffObject new update data against actor data. const diffUpdateSystem = U.objDiff(actor.system, updateData.system); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Merge new update data onto current subactor data. const mergedSubActorSystem = U.objMerge(this.system.subactors[actor.id] ?? {}, diffUpdateSystem, { isReplacingArrays: true, isConcatenatingArrays: false }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Confirm this update changes data: if (JSON.stringify(this.system.subactors[actor.id]) === JSON.stringify(mergedSubActorSystem)) { return undefined; } + // Update actor with new subactor data. return this.update({ [`system.subactors.${actor.id}`]: null }, undefined, true) .then(() => this.update({ [`system.subactors.${actor.id}`]: mergedSubActorSystem }, undefined, true)) .then(() => actor.sheet?.render()); } + /*~ @@DOUBLE-BLANK@@ ~*/ async remSubActor(actorRef) { const subActor = this.getSubActor(actorRef); if (!subActor) { @@ -257,6 +317,7 @@ class BladesActor extends Actor { } await this.update({ "system.subactors": mergeObject(this.system.subactors, { [`-=${subActor.id}`]: null }) }, undefined, true); } + /*~ @@DOUBLE-BLANK@@ ~*/ async clearSubActors(isReRendering = true) { this.subActors.forEach((subActor) => { if (subActor.parentActor?.id === this.id) { @@ -265,6 +326,7 @@ class BladesActor extends Actor { }); await this.sheet?.render(); } + /*~ @@DOUBLE-BLANK@@ ~*/ async clearParentActor(isReRendering = true) { const { parentActor } = this; if (!parentActor) { @@ -273,15 +335,21 @@ class BladesActor extends Actor { this.parentActor = undefined; this.system = this._source.system; this.ownership = this._source.ownership; + /*~ @@DOUBLE-BLANK@@ ~*/ this.prepareData(); if (isReRendering) { await this.sheet?.render(); } } - + // #endregion + // #region SubItemControl Implementation ~ + /*~ @@DOUBLE-BLANK@@ ~*/ get subItems() { return Array.from(this.items); } + /*~ @@DOUBLE-BLANK@@ ~*/ get activeSubItems() { return this.items.filter((item) => !item.hasTag(Tag.System.Archived)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get archivedSubItems() { return this.items.filter((item) => item.hasTag(Tag.System.Archived)); } + /*~ @@DOUBLE-BLANK@@ ~*/ _checkItemPrereqs(item) { if (!item.system.prereqs) { return true; @@ -295,6 +363,7 @@ class BladesActor extends Actor { } return true; } + /*~ @@DOUBLE-BLANK@@ ~*/ _processPrereqArray(pReqArray, pType, hitRecord) { while (pReqArray.length) { const pString = pReqArray.pop(); @@ -305,6 +374,7 @@ class BladesActor extends Actor { } return true; } + /*~ @@DOUBLE-BLANK@@ ~*/ _processPrereqType(pType, pString, hitRecord) { switch (pType) { case PrereqType.HasActiveItem: { @@ -319,6 +389,7 @@ class BladesActor extends Actor { default: return true; } } + /*~ @@DOUBLE-BLANK@@ ~*/ _processActiveItemPrereq(pString, hitRecord, pType) { const thisItem = this.activeSubItems .filter((i) => !hitRecord[pType]?.includes(i.id)) @@ -331,6 +402,7 @@ class BladesActor extends Actor { return false; } } + /*~ @@DOUBLE-BLANK@@ ~*/ _processActiveItemsByTagPrereq(pString, hitRecord, pType) { const thisItem = this.activeSubItems .filter((i) => !hitRecord[pType]?.includes(i.id)) @@ -343,6 +415,7 @@ class BladesActor extends Actor { return false; } } + /*~ @@DOUBLE-BLANK@@ ~*/ _processAdvancedPlaybookPrereq() { if (!BladesActor.IsType(this, BladesActorType.pc)) { return false; @@ -352,12 +425,20 @@ class BladesActor extends Actor { } return true; } + /*~ @@DOUBLE-BLANK@@ ~*/ _processEmbeddedItemMatches(globalItems) { + /*~ @@DOUBLE-BLANK@@ ~*/ return globalItems + /*~ @@DOUBLE-BLANK@@ ~*/ + // Step 1: Filter out globals that fail prereqs. .filter((item) => this._checkItemPrereqs(item)) + /*~ @@DOUBLE-BLANK@@ ~*/ + // Step 2: Filter out already-active items based on max_per_score (unless MultiplesOk) .filter((gItem) => gItem.hasTag(Tag.System.MultiplesOK) || (gItem.system.max_per_score ?? 1) > this.activeSubItems.filter((sItem) => sItem.system.world_name === gItem.system.world_name).length) + /*~ @@DOUBLE-BLANK@@ ~*/ + // Step 3: Replace with matching Archived, Embedded subItems .map((gItem) => { const matchingSubItems = this.archivedSubItems.filter((sItem) => sItem.system.world_name === gItem.system.world_name); if (matchingSubItems.length > 0) { @@ -368,6 +449,8 @@ class BladesActor extends Actor { } }) .flat() + /*~ @@DOUBLE-BLANK@@ ~*/ + // Step 4: Apply CSS classes .map((sItem) => { sItem.dialogCSSClasses = ""; const cssClasses = []; @@ -402,11 +485,15 @@ class BladesActor extends Actor { cssClasses.push("expensive"); } } + /*~ @@DOUBLE-BLANK@@ ~*/ if (cssClasses.length > 0) { sItem.dialogCSSClasses = cssClasses.join(" "); } + /*~ @@DOUBLE-BLANK@@ ~*/ return sItem; }) + /*~ @@DOUBLE-BLANK@@ ~*/ + // Step 5: Sort by featured, then by fine, then by world_name, then embedded first sorted by name .sort((a, b) => { if (a.hasTag(Tag.System.Featured) && !b.hasTag(Tag.System.Featured)) { return -1; @@ -450,6 +537,7 @@ class BladesActor extends Actor { return 0; }); } + /*~ @@DOUBLE-BLANK@@ ~*/ getDialogItems(category) { const dialogData = {}; const isPC = BladesActor.IsType(this, BladesActorType.pc); @@ -459,6 +547,7 @@ class BladesActor extends Actor { return false; } const { playbookName } = this; + /*~ @@DOUBLE-BLANK@@ ~*/ if (category === SelectionCategory.Heritage && isPC) { dialogData.Main = this._processEmbeddedItemMatches(BladesItem.GetTypeWithTags(BladesItemType.heritage)); } @@ -494,15 +583,19 @@ class BladesActor extends Actor { ...BladesItem.GetTypeWithTags(BladesItemType.gear, Tag.Gear.General) ]) .filter((item) => self.remainingLoad >= item.system.load); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Two tabs, one for playbook and the other for general items dialogData[playbookName] = gearItems.filter((item) => item.hasTag(playbookName)); dialogData.General = gearItems .filter((item) => item.hasTag(Tag.Gear.General)) + // Remove featured class from General items .map((item) => { if (item.dialogCSSClasses) { item.dialogCSSClasses = item.dialogCSSClasses.replace(/featured-item\s?/g, ""); } return item; }) + // Re-sort by world_name .sort((a, b) => { if (a.system.world_name > b.system.world_name) { return 1; @@ -521,12 +614,14 @@ class BladesActor extends Actor { dialogData[playbookName] = this._processEmbeddedItemMatches(BladesItem.GetTypeWithTags(BladesItemType.ability, playbookName)); dialogData.Veteran = this._processEmbeddedItemMatches(BladesItem.GetTypeWithTags(BladesItemType.ability)) .filter((item) => !item.hasTag(playbookName)) + // Remove featured class from Veteran items .map((item) => { if (item.dialogCSSClasses) { item.dialogCSSClasses = item.dialogCSSClasses.replace(/featured-item\s?/g, ""); } return item; }) + // Re-sort by world_name .sort((a, b) => { if (a.system.world_name > b.system.world_name) { return 1; @@ -545,8 +640,10 @@ class BladesActor extends Actor { dialogData[playbookName] = this._processEmbeddedItemMatches(BladesItem.GetTypeWithTags(BladesItemType.crew_upgrade, playbookName)); dialogData.General = this._processEmbeddedItemMatches(BladesItem.GetTypeWithTags(BladesItemType.crew_upgrade, Tag.Gear.General)); } + /*~ @@DOUBLE-BLANK@@ ~*/ return dialogData; } + /*~ @@DOUBLE-BLANK@@ ~*/ getSubItem(itemRef, activeOnly = false) { const activeCheck = (i) => !activeOnly || !i.hasTag(Tag.System.Archived); if (typeof itemRef === "string" && this.items.get(itemRef)) { @@ -567,6 +664,7 @@ class BladesActor extends Actor { ?? this.items.find((item) => item.system.world_name === globalItem.system.world_name && activeCheck(item)); } } + /*~ @@DOUBLE-BLANK@@ ~*/ hasSubItemOf(itemRef) { const item = BladesItem.Get(itemRef); if (!item) { @@ -574,6 +672,7 @@ class BladesActor extends Actor { } return Boolean(this.items.find((i) => i.system.world_name === item.system.world_name)); } + /*~ @@DOUBLE-BLANK@@ ~*/ hasActiveSubItemOf(itemRef) { const item = BladesItem.Get(itemRef); if (!item) { @@ -582,34 +681,57 @@ class BladesActor extends Actor { return Boolean(this.items.find((i) => !i.hasTag(Tag.System.Archived) && i.system.world_name === item.system.world_name)); } + /*~ @@DOUBLE-BLANK@@ ~*/ async addSubItem(itemRef) { - function isBladesItemUniqueTypes(type) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Determines whether a submitted BladesItemType is a type that should appear only once + * on any given Actor. + * @param {BladesItemType} type + * @returns {boolean} True if the type is a BladesItemUniqueTypes + **/ + function isBladesItemUniqueTypes(type) { return Object.values(BladesItemUniqueTypes).includes(type); } + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog3("subitems", "[addSubItem] itemRef", itemRef); + /*~ @@DOUBLE-BLANK@@ ~*/ let focusItem; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Does an embedded copy of this item already exist on the character? const embeddedItem = this.getSubItem(itemRef); + /*~ @@DOUBLE-BLANK@@ ~*/ if (embeddedItem) { + /*~ @@DOUBLE-BLANK@@ ~*/ + // Is it an archived Item? if (embeddedItem.hasTag(Tag.System.Archived)) { + // Unarchive it & make it the focus item. await embeddedItem.remTag(Tag.System.Archived); focusItem = embeddedItem; eLog.checkLog3("subitems", `[addSubItem] IS ARCHIVED EMBEDDED > Removing 'Archived' Tag, '${focusItem.id}':`, focusItem); } - else { + else { // Otherwise... + // Duplicate the item, and make the newly-created item the focus. focusItem = await BladesItem.create([embeddedItem], { parent: this }); eLog.checkLog3("subitems", `[addSubItem] IS ACTIVE EMBEDDED > Duplicating, focusItem '${focusItem.id}':`, focusItem); } } else { + // Is it not embedded at all? Embed from global instance. const globalItem = BladesItem.Get(itemRef); + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog3("subitems", `[addSubItem] IS NOT EMBEDDED > Fetching Global, globalItem '${globalItem?.id}':`, globalItem); + /*~ @@DOUBLE-BLANK@@ ~*/ if (!globalItem) { return; } focusItem = await BladesItem.create([globalItem], { parent: this }); focusItem = this.items.getName(globalItem.name); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Is this item type limited to one per actor? if (focusItem && isBladesItemUniqueTypes(focusItem.type)) { + // ... then archive all other versions. await Promise.all(this.activeSubItems .filter((subItem) => subItem.type === focusItem?.type && subItem.system.world_name !== focusItem?.system.world_name @@ -617,6 +739,7 @@ class BladesActor extends Actor { .map(this.remSubItem.bind(this))); } } + /*~ @@DOUBLE-BLANK@@ ~*/ async remSubItem(itemRef) { const subItem = this.getSubItem(itemRef); if (!subItem) { @@ -632,6 +755,7 @@ class BladesActor extends Actor { } await subItem.addTag(Tag.System.Archived); } + /*~ @@DOUBLE-BLANK@@ ~*/ async purgeSubItem(itemRef) { const subItem = this.getSubItem(itemRef); if (!subItem || subItem.hasTag(Tag.System.Archived)) { @@ -639,13 +763,40 @@ class BladesActor extends Actor { } await subItem.delete(); } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + // #region Advancement Implementation ~ + // get totalAbilityPoints(): number { + // if (!BladesActor.IsType(this, BladesActorType.pc, BladesActorType.crew)) { return 0 } + // if (!this.playbook) { return 0 } + // switch (this.type) { + // case BladesActorType.pc: return this.system.advancement.ability ?? 0; + // case BladesActorType.crew: return Math.floor(0.5 * (this.system.advancement.general ?? 0)) + // + (this.system.advancement.ability ?? 0); + // default: return 0; + // } + // } + // get spentAbilityPoints(): number { + // if (!BladesActor.IsType(this, BladesActorType.pc, BladesActorType.crew)) { return 0 } + // if (!this.playbook) { return 0 } + // return this.abilities.reduce((total, ability) => total + (ability.system.price ?? 1), 0); + // } + // get getAvailableAdvancements("Ability")(): number { + // if (!BladesActor.IsType(this, BladesActorType.pc, BladesActorType.crew)) { return 0 } + // if (!this.playbook) { return 0 } + // return this.totalAbilityPoints - this.spentAbilityPoints; + // } + /*~ @@DOUBLE-BLANK@@ ~*/ + /* Need simple getters for total ability & upgrade points that check for PRICES of items + (upgrade.system.price ?? 1) */ + /*~ @@DOUBLE-BLANK@@ ~*/ async grantAdvancementPoints(allowedTypes, amount = 1) { const aPtKey = Array.isArray(allowedTypes) ? [...allowedTypes].sort((a, b) => a.localeCompare(b)).join("_") : allowedTypes; await this.update({ [`system.advancement_points.${aPtKey}`]: (this.system.advancement_points?.[aPtKey] ?? 0) + amount }); } + /*~ @@DOUBLE-BLANK@@ ~*/ async removeAdvancementPoints(allowedTypes, amount = 1) { const aPtKey = Array.isArray(allowedTypes) ? [...allowedTypes].sort((a, b) => a.localeCompare(b)).join("_") @@ -658,6 +809,7 @@ class BladesActor extends Actor { await this.update({ [`system.advancement_points.${aPtKey}`]: newCount }); } } + /*~ @@DOUBLE-BLANK@@ ~*/ getAvailableAdvancements(trait) { if (!BladesActor.IsType(this, BladesActorType.pc, BladesActorType.crew)) { return 0; @@ -670,10 +822,12 @@ class BladesActor extends Actor { const spentCohort = this.cohorts.length; return Math.max(0, pointsCohort - spentCohort); } + /*~ @@DOUBLE-BLANK@@ ~*/ const pointsAbility = this.system.advancement_points?.[AdvancementPoint.Ability] ?? 0; const pointsCohortType = this.system.advancement_points?.[AdvancementPoint.CohortType] ?? 0; const pointsUpgrade = this.system.advancement_points?.[AdvancementPoint.Upgrade] ?? 0; const pointsUpgradeOrAbility = this.system.advancement_points?.[AdvancementPoint.UpgradeOrAbility] ?? 0; + /*~ @@DOUBLE-BLANK@@ ~*/ const spentAbility = U.sum(this.items .filter((item) => BladesItem.IsType(item, BladesItemType.ability, BladesItemType.crew_ability)) .map((abil) => abil.system.price ?? 1)); @@ -682,13 +836,16 @@ class BladesActor extends Actor { const spentUpgrade = U.sum(this.items .filter((item) => BladesItem.IsType(item, BladesItemType.crew_upgrade)) .map((upgrade) => upgrade.system.price ?? 1)); + /*~ @@DOUBLE-BLANK@@ ~*/ const excessUpgrade = Math.max(0, spentUpgrade - pointsUpgrade); const excessCohortType = Math.max(0, spentCohortType - pointsCohortType); const excessAbility = Math.max(0, spentAbility - pointsAbility); + /*~ @@DOUBLE-BLANK@@ ~*/ const remainingAbility = Math.max(0, pointsAbility - spentAbility); const remainingCohortType = Math.max(0, pointsCohortType - spentCohortType); const remainingUpgrade = Math.max(0, pointsUpgrade - spentUpgrade); const remainingUpgradeOrAbility = Math.max(0, pointsUpgradeOrAbility - excessUpgrade - (2 * excessAbility) - (2 * excessCohortType)); + /*~ @@DOUBLE-BLANK@@ ~*/ if (trait === "Ability") { return remainingAbility + Math.floor(0.5 * remainingUpgradeOrAbility); } @@ -698,16 +855,26 @@ class BladesActor extends Actor { if (trait === "CohortType") { return remainingCohortType + remainingUpgradeOrAbility; } + /*~ @@DOUBLE-BLANK@@ ~*/ return 0; } + /*~ @@DOUBLE-BLANK@@ ~*/ get availableAbilityPoints() { return this.getAvailableAdvancements("Ability"); } + /*~ @@DOUBLE-BLANK@@ ~*/ get availableUpgradePoints() { return this.getAvailableAdvancements("Upgrade"); } + /*~ @@DOUBLE-BLANK@@ ~*/ get availableCohortPoints() { return this.getAvailableAdvancements("Cohort"); } + /*~ @@DOUBLE-BLANK@@ ~*/ get availableCohortTypePoints() { return this.getAvailableAdvancements("CohortType"); } + /*~ @@DOUBLE-BLANK@@ ~*/ get canPurchaseAbility() { return this.availableAbilityPoints > 0; } + /*~ @@DOUBLE-BLANK@@ ~*/ get canPurchaseUpgrade() { return this.availableUpgradePoints > 0; } + /*~ @@DOUBLE-BLANK@@ ~*/ get canPurchaseCohort() { return this.availableCohortPoints > 0; } + /*~ @@DOUBLE-BLANK@@ ~*/ get canPurchaseCohortType() { return this.availableCohortTypePoints > 0; } + /*~ @@DOUBLE-BLANK@@ ~*/ async advancePlaybook() { if (!BladesActor.IsType(this, BladesActorType.pc, BladesActorType.crew) || !this.playbook) { return; @@ -728,17 +895,27 @@ class BladesActor extends Actor { this.grantAdvancementPoints(AdvancementPoint.UpgradeOrAbility, 2); } } + /*~ @@DOUBLE-BLANK@@ ~*/ async advanceAttribute(attribute) { await this.update({ [`system.experience.${attribute}.value`]: 0 }); const actions = C.Action[attribute].map((action) => `${U.tCase(action)}`); BladesPushAlert.Get().pushToAll("GM", `${this.name} Advances their ${U.uCase(attribute)}!`, `${this.name}, add a dot to one of ${U.oxfordize(actions, true, "or")}.`); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + // #region BladesSubActor Implementation ~ + /*~ @@DOUBLE-BLANK@@ ~*/ parentActor; + /*~ @@DOUBLE-BLANK@@ ~*/ get isSubActor() { return this.parentActor !== undefined; } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll Implementation get rollModsData() { return BladesRollMod.ParseDocRollMods(this); } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollFactors() { const factorData = { [Factor.tier]: { @@ -764,13 +941,24 @@ class BladesActor extends Actor { highFavorsPC: true } }; + /*~ @@DOUBLE-BLANK@@ ~*/ return factorData; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll.PrimaryDoc Implementation + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimaryID() { return this.id; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimaryDoc() { return this; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimaryName() { return this.name; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimaryType() { return this.type; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimaryImg() { return this.img; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesCrew Implementation ~ + /*~ @@DOUBLE-BLANK@@ ~*/ get members() { if (!BladesActor.IsType(this, BladesActorType.crew)) { return []; @@ -778,6 +966,7 @@ class BladesActor extends Actor { const self = this; return BladesActor.GetTypeWithTags(BladesActorType.pc).filter((actor) => actor.isMember(self)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get contacts() { if (!BladesActor.IsType(this, BladesActorType.crew) || !this.playbook) { return []; @@ -785,12 +974,14 @@ class BladesActor extends Actor { const self = this; return this.activeSubActors.filter((actor) => actor.hasTag(self.playbookName)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get claims() { if (!BladesActor.IsType(this, BladesActorType.crew) || !this.playbook) { return {}; } return this.playbook.system.turfs; } + /*~ @@DOUBLE-BLANK@@ ~*/ get turfCount() { if (!BladesActor.IsType(this, BladesActorType.crew) || !this.playbook) { return 0; @@ -798,6 +989,7 @@ class BladesActor extends Actor { return Object.values(this.playbook.system.turfs) .filter((claim) => claim.isTurf && claim.value).length; } + /*~ @@DOUBLE-BLANK@@ ~*/ get upgrades() { if (!BladesActor.IsType(this, BladesActorType.crew) || !this.playbook) { return []; @@ -805,14 +997,19 @@ class BladesActor extends Actor { return this.activeSubItems .filter((item) => item.type === BladesItemType.crew_upgrade); } + /*~ @@DOUBLE-BLANK@@ ~*/ get cohorts() { return this.activeSubItems .filter((item) => [BladesItemType.cohort_gang, BladesItemType.cohort_expert].includes(item.type)); } + /*~ @@DOUBLE-BLANK@@ ~*/ getTaggedItemBonuses(tags) { - return tags.length; + // Check ACTIVE EFFECTS supplied by upgrade/ability against submitted tags? + return tags.length; // Placeholder to avoid linter error } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region PREPARING DERIVED DATA prepareDerivedData() { if (BladesActor.IsType(this, BladesActorType.pc)) { this._preparePCData(this.system); @@ -821,10 +1018,13 @@ class BladesActor extends Actor { this._prepareCrewData(this.system); } } + /*~ @@DOUBLE-BLANK@@ ~*/ _preparePCData(system) { if (!BladesActor.IsType(this, BladesActorType.pc)) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Extract experience clues from playbook item, if any if (this.playbook) { system.experience.clues = [ ...system.experience.clues, @@ -832,6 +1032,7 @@ class BladesActor extends Actor { .filter((clue) => Boolean(clue.trim())) ]; } + // Extract gather information questions from playbook item, if any if (this.playbook) { system.gather_info = [ ...system.gather_info, @@ -840,10 +1041,13 @@ class BladesActor extends Actor { ]; } } + /*~ @@DOUBLE-BLANK@@ ~*/ _prepareCrewData(system) { if (!BladesActor.IsType(this, BladesActorType.crew)) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Extract experience clues and turfs from playbook item, if any if (this.playbook) { system.experience.clues = [ ...system.experience.clues, @@ -853,7 +1057,11 @@ class BladesActor extends Actor { system.turfs = this.playbook.system.turfs; } } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region OVERRIDES: _onCreateDescendantDocuments, update ~ + // @ts-expect-error New method not defined in @league VTT types. async _onCreateDescendantDocuments(parent, collection, docs, data, options, userId) { await Promise.all(docs.map(async (doc) => { if (BladesItem.IsType(doc, BladesItemType.playbook, BladesItemType.crew_playbook)) { @@ -862,8 +1070,12 @@ class BladesActor extends Actor { .map((aItem) => this.remSubItem(aItem))); } })); + /*~ @@DOUBLE-BLANK@@ ~*/ + // @ts-expect-error New method not defined in @league VTT types. await super._onCreateDescendantDocuments(parent, collection, docs, data, options, userId); + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog("actorTrigger", "_onCreateDescendantDocuments", { parent, collection, docs, data, options, userId }); + /*~ @@DOUBLE-BLANK@@ ~*/ docs.forEach((doc) => { if (BladesItem.IsType(doc, BladesItemType.vice) && BladesActor.IsType(this, BladesActorType.pc)) { this.activeSubActors @@ -872,6 +1084,7 @@ class BladesActor extends Actor { } }); } + /*~ @@DOUBLE-BLANK@@ ~*/ async update(updateData, context, isSkippingSubActorCheck = false) { if (!updateData) { return super.update(updateData); @@ -880,14 +1093,17 @@ class BladesActor extends Actor { if (!this.playbook) { return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog("actorTrigger", "Updating Crew", { updateData }); const playbookUpdateData = Object.fromEntries(Object.entries(flattenObject(updateData)) .filter(([key, _]) => key.startsWith("system.turfs."))); updateData = Object.fromEntries(Object.entries(flattenObject(updateData)) .filter(([key, _]) => !key.startsWith("system.turfs."))); eLog.checkLog("actorTrigger", "Updating Crew", { crewUpdateData: updateData, playbookUpdateData }); + /*~ @@DOUBLE-BLANK@@ ~*/ const diffPlaybookData = diffObject(flattenObject(this.playbook), playbookUpdateData); delete diffPlaybookData._id; + /*~ @@DOUBLE-BLANK@@ ~*/ if (!U.isEmpty(diffPlaybookData)) { await this.playbook.update(playbookUpdateData, context) .then(() => this.sheet?.render(false)); @@ -897,18 +1113,36 @@ class BladesActor extends Actor { || BladesActor.IsType(this, BladesActorType.faction)) && this.parentActor && !isSkippingSubActorCheck) { + // This is an embedded Actor: Update it as a subActor of parentActor. return this.parentActor.updateSubActor(this.id, updateData) .then(() => this); } + /*~ @@DOUBLE-BLANK@@ ~*/ return super.update(updateData, context); } - - - createListOfDiceMods(rs, re, s) { + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Rolling Dice ~ + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Creates modifiers for dice roll. + * + * @param {int} rs + * Min die modifier + * @param {int} re + * Max die modifier + * @param {int} s + * Selected die + */ + createListOfDiceMods(rs, re, s) { + /*~ @@DOUBLE-BLANK@@ ~*/ let text = ""; + /*~ @@DOUBLE-BLANK@@ ~*/ if (s === "") { s = 0; } + /*~ @@DOUBLE-BLANK@@ ~*/ for (let i = rs; i <= re; i++) { let plus = ""; if (i >= 0) { @@ -918,19 +1152,32 @@ class BladesActor extends Actor { if (i === s) { text += " selected"; } + /*~ @@DOUBLE-BLANK@@ ~*/ text += `>${plus}${i}d`; } + /*~ @@DOUBLE-BLANK@@ ~*/ return text; } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion Rolling Dice + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region NPC Randomizers ~ updateRandomizers() { if (!BladesActor.IsType(this, BladesActorType.npc)) { return; } const titleChance = 0.05; const suffixChance = 0.01; + /*~ @@DOUBLE-BLANK@@ ~*/ const { persona, secret, random } = this.system; - function sampleArray(arr, ...curVals) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Returns a random element from an array, optionally excluding all values + * passed as subsequent parameters. + * @param {string[]} arr The array to randomly select from. + * @param {...string} curVals The values to exclude from the sample. + */ + function sampleArray(arr, ...curVals) { arr = arr.filter((elem) => !curVals.includes(elem)); if (!arr.length) { return ""; @@ -969,6 +1216,7 @@ class BladesActor extends Actor { ...(gen.charAt(0).toLowerCase() !== "f" ? Randomizers.NPC.style.male : []) ], persona.style.value) }; + /*~ @@DOUBLE-BLANK@@ ~*/ const gender = persona.gender.isLocked ? persona.gender.value : randomGen.gender(); const updateKeys = [ ...Object.keys(persona).filter((key) => !persona[key]?.isLocked), @@ -976,8 +1224,10 @@ class BladesActor extends Actor { ...Object.keys(secret).filter((key) => !secret[key]?.isLocked) .map((secretKey) => `secret-${secretKey}`) ]; + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog("Update Keys", { updateKeys }); const updateData = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ updateKeys.forEach((key) => { switch (key) { case "name": @@ -1061,7 +1311,10 @@ class BladesActor extends Actor { } } }); + /*~ @@DOUBLE-BLANK@@ ~*/ this.update(updateData); } } -export default BladesActor; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesActor; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/BladesChat.js b/module/BladesChat.js new file mode 100644 index 00000000..cf8d0910 --- /dev/null +++ b/module/BladesChat.js @@ -0,0 +1,86 @@ +// #region IMPORTS ~ +/* import U from "./core/utilities.js"; +import C, { + BladesActorType, BladesItemType, RollPermissions, RollType, RollSubType, + RollModStatus, RollModSection, ActionTrait, DowntimeAction, AttributeTrait, + Position, Effect, Factor, RollResult, RollPhase, ConsequenceType, Tag +} from "./core/constants.js"; +import {BladesActor, BladesPC, BladesCrew} from "./documents/BladesActorProxy.js"; +import {BladesItem, BladesGMTracker} from "./documents/BladesItemProxy.js"; +import {ApplyTooltipListeners, ApplyConsequenceListeners} from "./core/gsap.js"; +import BladesDialog from "./BladesDialog.js"; */ +/*~ @@DOUBLE-BLANK@@ ~*/ +import U from "./core/utilities.js"; +import C, { BladesItemType } from "./core/constants.js"; +import { BladesPC } from "./documents/BladesActorProxy.js"; +import { BladesItem } from "./documents/BladesItemProxy.js"; +import { ApplyTooltipListeners, ApplyConsequenceListeners } from "./core/gsap.js"; +// #endregion +/*~ @@DOUBLE-BLANK@@ ~*/ +class BladesChat extends ChatMessage { + /*~ @@DOUBLE-BLANK@@ ~*/ + static Initialize() { + Hooks.on("renderChatMessage", (_msg, html) => { + ApplyTooltipListeners(html); + ApplyConsequenceListeners(html); + }); + return loadTemplates([ + "systems/eunos-blades/templates/chat/roll-result-action-roll.hbs", + "systems/eunos-blades/templates/chat/roll-result-resistance-roll.hbs", + "systems/eunos-blades/templates/chat/roll-result-fortune-roll.hbs", + "systems/eunos-blades/templates/chat/roll-result-indulgevice-roll.hbs" + ]); + } + /*~ @@DOUBLE-BLANK@@ ~*/ + static GetRollSpeaker(rollInst) { + /*~ @@DOUBLE-BLANK@@ ~*/ + // Get initial speaker data + const speaker = BladesChat.getSpeaker(); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Compare against rollInst.rollPrimary and modify accordingly. + const { rollPrimaryID, rollPrimaryName, rollPrimaryType, rollPrimaryDoc } = rollInst.rollPrimary; + /*~ @@DOUBLE-BLANK@@ ~*/ + speaker.alias = rollPrimaryName; + /*~ @@DOUBLE-BLANK@@ ~*/ + if (BladesItem.IsType(BladesItemType.cohort_gang, BladesItemType.cohort_expert)) { + speaker.actor = rollPrimaryDoc?.parent?.id ?? speaker.actor; + if (rollPrimaryDoc?.parent instanceof BladesPC) { + speaker.alias = `${speaker.alias} (${rollPrimaryDoc.parent.name})`; + } + } + else if (BladesItem.IsType(BladesItemType.gm_tracker, BladesItemType.score)) { + speaker.actor = null; + speaker.alias = "The Gamemaster"; + } + else if (rollPrimaryID) { + speaker.actor = rollPrimaryID; + } + /*~ @@DOUBLE-BLANK@@ ~*/ + speaker.alias = `${speaker.alias} Rolls ...`; + /*~ @@DOUBLE-BLANK@@ ~*/ + return speaker; + } + /*~ @@DOUBLE-BLANK@@ ~*/ + static async ConstructRollOutput(rollInst) { + const speaker = BladesChat.GetRollSpeaker(rollInst); + /*~ @@DOUBLE-BLANK@@ ~*/ + const template = `systems/eunos-blades/templates/chat/roll-result-${U.lCase(rollInst.rollType)}-roll.hbs`; + /*~ @@DOUBLE-BLANK@@ ~*/ + const templateData = rollInst; + if (rollInst.rollResult) { + templateData.rollResultDescription = C.RollResultDescriptions[rollInst.finalPosition][rollInst.rollResult]; + } + /*~ @@DOUBLE-BLANK@@ ~*/ + const renderedHTML = await renderTemplate(template, rollInst); + /*~ @@DOUBLE-BLANK@@ ~*/ + const messageData = { + speaker, + content: renderedHTML + }; + /*~ @@DOUBLE-BLANK@@ ~*/ + BladesChat.create(messageData, {}); + } +} +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesChat; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/BladesDialog.js b/module/BladesDialog.js index a1bb60bf..65da64f5 100644 --- a/module/BladesDialog.js +++ b/module/BladesDialog.js @@ -1,16 +1,10 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import { ApplyTooltipListeners } from "./core/gsap.js"; import U from "./core/utilities.js"; import { BladesActor, BladesPC } from "./documents/BladesActorProxy.js"; import BladesRoll from "./BladesRoll.js"; import C, { RollResult, AttributeTrait, Position } from "./core/constants.js"; import BladesAI, { AGENTS } from "./core/ai.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ const baseCsqData = { [RollResult.partial]: { 0: { name: "", type: "", attribute: "" }, @@ -23,6 +17,8 @@ const baseCsqData = { 2: { name: "", type: "", attribute: "" } } }; +/*~ @@DOUBLE-BLANK@@ ~*/ +// eslint-disable-next-line no-shadow export var SelectionCategory; (function (SelectionCategory) { SelectionCategory["Heritage"] = "Heritage"; @@ -47,12 +43,16 @@ export var SelectionCategory; SelectionCategory["Member"] = "Member"; SelectionCategory["Contact"] = "Contact"; })(SelectionCategory || (SelectionCategory = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ +// eslint-disable-next-line no-shadow export var BladesDialogType; (function (BladesDialogType) { BladesDialogType["Selection"] = "Selection"; BladesDialogType["Consequence"] = "Consequence"; })(BladesDialogType || (BladesDialogType = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesDialog extends Dialog { + /*~ @@DOUBLE-BLANK@@ ~*/ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["eunos-blades", "sheet", "dialog"], @@ -61,6 +61,7 @@ class BladesDialog extends Dialog { tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "front" }] }); } + /*~ @@DOUBLE-BLANK@@ ~*/ static Initialize() { return loadTemplates([ "systems/eunos-blades/templates/dialog-selection.hbs", @@ -68,7 +69,9 @@ class BladesDialog extends Dialog { "systems/eunos-blades/templates/parts/dialog-consequence-block.hbs" ]); } + /*~ @@DOUBLE-BLANK@@ ~*/ static async DisplaySelectionDialog(parent, title, docType, tabs, tags) { + /*~ @@DOUBLE-BLANK@@ ~*/ const app = new BladesDialog({ parent, title, @@ -88,9 +91,12 @@ class BladesDialog extends Dialog { }, default: "cancel" }); + /*~ @@DOUBLE-BLANK@@ ~*/ return app.hasItems ? app.render(true, { width: app.width }) : undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ static async DisplayRollConsequenceDialog(rollInst) { + /*~ @@DOUBLE-BLANK@@ ~*/ const app = new BladesDialog({ parent: rollInst, title: "Consequences", @@ -114,44 +120,58 @@ class BladesDialog extends Dialog { }, default: "apply" }, { classes: ["eunos-blades", "sheet", "dialog", "consequence-dialog"] }); + /*~ @@DOUBLE-BLANK@@ ~*/ return app._render(true, { width: app.width }).then(() => eLog.checkLog3("dialog", "Dialog Instance", { this: app })); } + /*~ @@DOUBLE-BLANK@@ ~*/ get template() { if (this.dialogType === BladesDialogType.Selection) { return "systems/eunos-blades/templates/dialog-selection.hbs"; } return "systems/eunos-blades/templates/dialog-consequence.hbs"; } + /*~ @@DOUBLE-BLANK@@ ~*/ get hasItems() { return Object.values(this.tabs ?? []).some((tabItems) => tabItems.length > 0); } + /*~ @@DOUBLE-BLANK@@ ~*/ parent; + /*~ @@DOUBLE-BLANK@@ ~*/ tabs; + /*~ @@DOUBLE-BLANK@@ ~*/ dialogType; + /*~ @@DOUBLE-BLANK@@ ~*/ tags = []; + /*~ @@DOUBLE-BLANK@@ ~*/ width; + /*~ @@DOUBLE-BLANK@@ ~*/ docType; + /*~ @@DOUBLE-BLANK@@ ~*/ csqData = { [Position.controlled]: { ...baseCsqData }, [Position.risky]: { ...baseCsqData }, [Position.desperate]: { ...baseCsqData } }; + /*~ @@DOUBLE-BLANK@@ ~*/ constructor(data, options) { super(data, options); + /*~ @@DOUBLE-BLANK@@ ~*/ this.dialogType = data.dialogType ?? BladesDialogType.Selection; this.parent = data.parent; this.width = 500; + /*~ @@DOUBLE-BLANK@@ ~*/ switch (this.dialogType) { case BladesDialogType.Selection: - this.constructSelectionData(data ); + this.constructSelectionData(data /* , options */); return; case BladesDialogType.Consequence: - this.constructConsequenceData(data ); + this.constructConsequenceData(data /* , options */); return; default: throw new Error(`Unrecognized type for BladesDialog constructor: '${this.dialogType}'`); } } - constructSelectionData(data ) { + /*~ @@DOUBLE-BLANK@@ ~*/ + constructSelectionData(data /* , options?: Partial */) { const validTabs = []; if (!data.tabs) { return; @@ -164,37 +184,46 @@ class BladesDialog extends Dialog { validTabs.push(tabName); } } + /*~ @@DOUBLE-BLANK@@ ~*/ if (validTabs.length === 1 && !("Main" in data.tabs)) { data.tabs.Main = [...data.tabs[validTabs[0]]]; delete data.tabs[validTabs[0]]; } + /*~ @@DOUBLE-BLANK@@ ~*/ this.docType = data.docType; this.tabs = data.tabs; this.tags = data.tags ?? []; this.width = 150 * Math.ceil(Math.sqrt(Object.values(data.tabs)[0].length)); } - constructConsequenceData(data ) { + /*~ @@DOUBLE-BLANK@@ ~*/ + constructConsequenceData(data /* , options?: Partial */) { eLog.checkLog3("dialog", "constructConsequenceData", { incoming: data }); if (this.parent instanceof BladesRoll) { + /*~ @@DOUBLE-BLANK@@ ~*/ this.csqData = U.objMerge(this.csqData, this.parent.getFlagVal("consequenceData") ?? {}); this._consequenceAI = new BladesAI(AGENTS.ConsequenceAdjuster); } } + /*~ @@DOUBLE-BLANK@@ ~*/ getData() { const data = super.getData(); + /*~ @@DOUBLE-BLANK@@ ~*/ switch (this.dialogType) { case BladesDialogType.Selection: return this.prepareSelectionData(data); case BladesDialogType.Consequence: return this.prepareConsequenceData(data); default: return null; } } + /*~ @@DOUBLE-BLANK@@ ~*/ prepareSelectionData(data) { data.title = this.title; data.tabs = this.tabs; data.docType = this.docType; data.tags = this.tags; + /*~ @@DOUBLE-BLANK@@ ~*/ return data; } + /*~ @@DOUBLE-BLANK@@ ~*/ prepareConsequenceData(data) { eLog.checkLog3("dialog", "prepareConsequenceData this.csqData", this.csqData); eLog.checkLog3("dialog", "prepareConsequenceData", { incoming: data }); @@ -210,6 +239,7 @@ class BladesDialog extends Dialog { eLog.checkLog3("dialog", "prepareConsequenceData", { outgoing: data }); return data; } + /*~ @@DOUBLE-BLANK@@ ~*/ get consequenceTypeOptions() { if (this.parent instanceof BladesRoll) { const returnData = {}; @@ -225,6 +255,7 @@ class BladesDialog extends Dialog { } return {}; } + /*~ @@DOUBLE-BLANK@@ ~*/ updateConsequenceDialog(html, isRendering = true) { if (!(this.parent instanceof BladesRoll)) { return; @@ -233,6 +264,7 @@ class BladesDialog extends Dialog { if (!(rollPrimaryDoc instanceof BladesPC)) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ [Position.controlled, Position.risky, Position.desperate].forEach((rollPos) => { const posCsqData = {}; [RollResult.partial, RollResult.fail].forEach((rollResult) => { @@ -283,17 +315,23 @@ class BladesDialog extends Dialog { this.render(); } } + /*~ @@DOUBLE-BLANK@@ ~*/ async writeToRollInstance(html) { if (this.parent instanceof BladesRoll) { this.updateConsequenceDialog(html, false); await this.parent.setFlagVal("consequenceData", this.csqData); } } + /*~ @@DOUBLE-BLANK@@ ~*/ _consequenceAI; + /*~ @@DOUBLE-BLANK@@ ~*/ async queryAI(event) { + // If the AI generator has not been initialized, do so. if (!this._consequenceAI) { this._consequenceAI = new BladesAI(AGENTS.ConsequenceAdjuster); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Get the name of the consequence. const dataAction = event.currentTarget.dataset.action; if (dataAction && dataAction.startsWith("ai-query")) { const [rollPosition, rollResult, csqIndex] = dataAction.split(/-/).slice(2); @@ -306,11 +344,13 @@ class BladesDialog extends Dialog { } } } + /*~ @@DOUBLE-BLANK@@ ~*/ async setFlagVal(target, value) { if (this.parent instanceof BladesRoll) { return this.parent.setFlagVal(target, value, false); } } + /*~ @@DOUBLE-BLANK@@ ~*/ async addResistanceOptions(rollPosition, rollResult, cIndex, rOptions) { const cData = this.csqData[rollPosition][rollResult][cIndex]; if (!cData) { @@ -333,21 +373,30 @@ class BladesDialog extends Dialog { eLog.checkLog3("dialog", "addResistanceOptions() this.csqData", this.csqData); this.render(); } + /*~ @@DOUBLE-BLANK@@ ~*/ async selectResistOption(event) { eLog.checkLog3("dialog", "Clicked Resistance Option", event); const dataAction = event.currentTarget.dataset.action; if (dataAction && dataAction.startsWith("gm-select-toggle")) { const [rollPosition, rollResult, csqIndex, resIndex] = dataAction.split(/-/).slice(3); eLog.checkLog3("dialog", "... Action Passed", { rollResult, csqIndex, resIndex }); + // Get consequence data const cData = this.csqData[rollPosition][rollResult][csqIndex]; cData.resistOptions ??= {}; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Toggle clicked resistance option cData.resistOptions[resIndex].isSelected = !cData.resistOptions[resIndex].isSelected; + /*~ @@DOUBLE-BLANK@@ ~*/ + // If resistance option is now selected... if (cData.resistOptions[resIndex].isSelected) { + // ... deselect other options Object.keys(cData.resistOptions) .filter((key) => key !== resIndex) .forEach((key) => { cData.resistOptions[key].isSelected = false; }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // ... and set 'resistedTo' to this consequence. cData.resistedTo = cData.resistOptions[resIndex]; if (cData.resistedTo.type) { cData.resistedTo.icon = C.ConsequenceIcons[cData.resistedTo.type]; @@ -355,15 +404,22 @@ class BladesDialog extends Dialog { } } else { + // Otherwise, set 'resistedTo' to false. cData.resistedTo = false; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Assign new cData instance. this.csqData[rollPosition][rollResult][csqIndex] = cData; this.render(); } } + /*~ @@DOUBLE-BLANK@@ ~*/ activateListeners(html) { super.activateListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Tooltips ApplyTooltipListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ switch (this.dialogType) { case BladesDialogType.Selection: this.activateSelectionListeners(html); @@ -373,8 +429,11 @@ class BladesDialog extends Dialog { break; } } + /*~ @@DOUBLE-BLANK@@ ~*/ activateSelectionListeners(html) { const self = this; + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Changing Width on Tab Change Depending on Number of Items html.find(".nav-tabs .tab-selector").on("click", (event) => { const tabIndex = U.pInt($(event.currentTarget).data("tab")); const numItems = Object.values(self.tabs ?? [])[tabIndex].length; @@ -382,6 +441,8 @@ class BladesDialog extends Dialog { eLog.checkLog3("nav", "Nav Tab Size Recalculation", { tabIndex, numItems, width }); this.render(false, { width }); }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Item Control html.find("[data-item-id]").on("click", function () { if ($(this).parent().hasClass("locked")) { return; @@ -399,7 +460,9 @@ class BladesDialog extends Dialog { } self.close(); }); + /*~ @@DOUBLE-BLANK@@ ~*/ } + /*~ @@DOUBLE-BLANK@@ ~*/ activateConsequenceListeners(html) { html.find("input").on({ blur: () => this.updateConsequenceDialog(html) }); html.find("select").on({ change: () => this.updateConsequenceDialog(html) }); @@ -407,4 +470,6 @@ class BladesDialog extends Dialog { html.find('[data-action^="gm-select-toggle"]').on({ click: (event) => this.selectResistOption(event) }); } } -export default BladesDialog; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesDialog; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/BladesItem.js b/module/BladesItem.js index 7ff70c87..9e8f79dd 100644 --- a/module/BladesItem.js +++ b/module/BladesItem.js @@ -1,27 +1,29 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import C, { BladesActorType, BladesItemType, Tag, Factor } from "./core/constants.js"; import U from "./core/utilities.js"; import { BladesActor } from "./documents/BladesActorProxy.js"; import { BladesRollMod } from "./BladesRoll.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesItem extends Item { - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Static Overrides: Create ~ static async create(data, options = {}) { if (Array.isArray(data)) { data = data[0]; } data.system = data.system ?? {}; + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog2("item", "BladesItem.create(data,options)", { data, options }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Create world_name data.system.world_name = data.system.world_name ?? data.name.replace(/[^A-Za-z_0-9 ]/g, "").trim().replace(/ /g, "_"); + /*~ @@DOUBLE-BLANK@@ ~*/ return super.create(data, options); } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesDocument Implementation static get All() { return game.items; } + /*~ @@DOUBLE-BLANK@@ ~*/ static Get(itemRef) { if (itemRef instanceof BladesItem) { return itemRef; @@ -32,6 +34,7 @@ class BladesItem extends Item { return BladesItem.All.find((a) => a.system.world_name === itemRef) || BladesItem.All.find((a) => a.name === itemRef); } + /*~ @@DOUBLE-BLANK@@ ~*/ static GetTypeWithTags(docType, ...tags) { if (Array.isArray(docType)) { return docType @@ -41,14 +44,18 @@ class BladesItem extends Item { return BladesItem.All.filter((item) => item.type === docType) .filter((item) => item.hasTag(...tags)); } + /*~ @@DOUBLE-BLANK@@ ~*/ static IsType(doc, ...types) { const typeSet = new Set(types); return doc instanceof BladesItem && typeSet.has(doc.type); } + /*~ @@DOUBLE-BLANK@@ ~*/ get tags() { return this.system.tags ?? []; } + /*~ @@DOUBLE-BLANK@@ ~*/ hasTag(...tags) { return tags.every((tag) => this.tags.includes(tag)); } + /*~ @@DOUBLE-BLANK@@ ~*/ async addTag(...tags) { const curTags = this.tags; tags.forEach((tag) => { @@ -59,10 +66,12 @@ class BladesItem extends Item { }); await this.update({ "system.tags": curTags }); } + /*~ @@DOUBLE-BLANK@@ ~*/ async remTag(...tags) { const curTags = this.tags.filter((tag) => !tags.includes(tag)); await this.update({ "system.tags": curTags }); } + /*~ @@DOUBLE-BLANK@@ ~*/ get tooltip() { const tooltipText = [ this.system.concept, @@ -74,7 +83,9 @@ class BladesItem extends Item { } return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ dialogCSSClasses = ""; + /*~ @@DOUBLE-BLANK@@ ~*/ getFactorTotal(factor) { switch (factor) { case Factor.tier: { @@ -128,17 +139,23 @@ class BladesItem extends Item { default: return 0; } } - + // #endregion + // #region BladesItemDocument Implementation + /*~ @@DOUBLE-BLANK@@ ~*/ async archive() { await this.addTag(Tag.System.Archived); return this; } + /*~ @@DOUBLE-BLANK@@ ~*/ async unarchive() { await this.remTag(Tag.System.Archived); return this; } - - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll Implementation + /*~ @@DOUBLE-BLANK@@ ~*/ get rollFactors() { const factorsMap = { [BladesItemType.cohort_gang]: [Factor.quality, Factor.scale], @@ -151,7 +168,9 @@ class BladesItem extends Item { if (!factorsMap[this.type]) { return {}; } + /*~ @@DOUBLE-BLANK@@ ~*/ const factors = factorsMap[this.type]; + /*~ @@DOUBLE-BLANK@@ ~*/ const factorData = {}; (factors ?? []).forEach((factor, i) => { const factorTotal = this.getFactorTotal(factor); @@ -168,34 +187,63 @@ class BladesItem extends Item { cssClasses: `factor-gold${i === 0 ? " factor-main" : ""}` }; }); + /*~ @@DOUBLE-BLANK@@ ~*/ return factorData; } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll.PrimaryDoc Implementation get rollPrimaryID() { return this.id; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimaryDoc() { return this; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimaryName() { return this.name; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimaryType() { return this.type; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimaryImg() { return this.img; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollModsData() { - + // Const rollModData = BladesRollMod.ParseDocRollMods(this); + // Add roll mods from COHORT harm + /*~ @@DOUBLE-BLANK@@ ~*/ return BladesRollMod.ParseDocRollMods(this); } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll.OppositionDoc Implementation get rollOppID() { return this.id; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppDoc() { return this; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppImg() { return this.img; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppName() { return this.name; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppSubName() { return ""; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppType() { return this.type; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppModsData() { return []; } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll.ParticipantDoc Implementation get rollParticipantID() { return this.id; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantDoc() { return this; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantIcon() { return this.img; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantName() { return this.name; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantType() { return this.type; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantModsData() { return []; } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region PREPARING DERIVED DATA prepareDerivedData() { super.prepareDerivedData(); if (BladesItem.IsType(this, BladesItemType.cohort_gang, BladesItemType.cohort_expert)) { @@ -211,11 +259,14 @@ class BladesItem extends Item { this._preparePlaybookData(this.system); } } + /*~ @@DOUBLE-BLANK@@ ~*/ _prepareCohortData(system) { if (!BladesItem.IsType(this, BladesItemType.cohort_gang, BladesItemType.cohort_expert)) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ system.tier.name = "Quality"; + /*~ @@DOUBLE-BLANK@@ ~*/ const subtypes = U.unique(Object.values(system.subtypes) .map((subtype) => subtype.trim()) .filter((subtype) => /[A-Za-z]/.test(subtype))); @@ -227,6 +278,7 @@ class BladesItem extends Item { ] .map((subtype) => subtype.trim()) .filter((subtype) => /[A-Za-z]/.test(subtype) && subtypes.includes(subtype))); + /*~ @@DOUBLE-BLANK@@ ~*/ system.subtypes = Object.fromEntries(subtypes.map((subtype, i) => [`${i + 1}`, subtype])); system.elite_subtypes = Object.fromEntries(eliteSubtypes.map((subtype, i) => [`${i + 1}`, subtype])); system.edges = Object.fromEntries(Object.values(system.edges ?? []) @@ -235,7 +287,9 @@ class BladesItem extends Item { system.flaws = Object.fromEntries(Object.values(system.flaws ?? []) .filter((flaw) => /[A-Za-z]/.test(flaw)) .map((flaw, i) => [`${i + 1}`, flaw.trim()])); + /*~ @@DOUBLE-BLANK@@ ~*/ system.quality = this.getFactorTotal(Factor.quality); + /*~ @@DOUBLE-BLANK@@ ~*/ if (BladesItem.IsType(this, BladesItemType.cohort_gang)) { if ([...subtypes, ...eliteSubtypes].includes(Tag.GangType.Vehicle)) { system.scale = this.getFactorTotal(Factor.scale); @@ -256,6 +310,7 @@ class BladesItem extends Item { system.scaleExample = [...subtypes, ...eliteSubtypes].includes("Pet") ? "(1 animal)" : "(1 person)"; system.subtitle = "An Expert"; } + /*~ @@DOUBLE-BLANK@@ ~*/ if (subtypes.length + eliteSubtypes.length > 0) { if ([...subtypes, ...eliteSubtypes].includes(Tag.GangType.Vehicle)) { system.subtitle = C.VehicleDescriptors[Math.min(6, this.getFactorTotal(Factor.tier))]; @@ -268,12 +323,14 @@ class BladesItem extends Item { } } } + /*~ @@DOUBLE-BLANK@@ ~*/ _prepareGearData(system) { if (!BladesItem.IsType(this, BladesItemType.gear)) { return; } system.tier.name = "Quality"; } + /*~ @@DOUBLE-BLANK@@ ~*/ _preparePlaybookData(system) { if (!BladesItem.IsType(this, BladesItemType.playbook, BladesItemType.crew_playbook)) { return; @@ -282,12 +339,16 @@ class BladesItem extends Item { [...Object.values(system.experience_clues).filter((clue) => /[A-Za-z]/.test(clue)), " "].forEach((clue, i) => { expClueData[(i + 1).toString()] = clue; }); system.experience_clues = expClueData; eLog.checkLog3("experienceClues", { expClueData }); + /*~ @@DOUBLE-BLANK@@ ~*/ if (BladesItem.IsType(this, BladesItemType.playbook)) { const gatherInfoData = {}; [...Object.values(system.gather_info_questions).filter((question) => /[A-Za-z]/.test(question)), " "].forEach((question, i) => { gatherInfoData[(i + 1).toString()] = question; }); system.gather_info_questions = gatherInfoData; eLog.checkLog3("gatherInfoQuestions", { gatherInfoData }); + /*~ @@DOUBLE-BLANK@@ ~*/ } } } -export default BladesItem; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesItem; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/BladesPushAlert.js b/module/BladesPushAlert.js index cc04f809..ff46a5ea 100644 --- a/module/BladesPushAlert.js +++ b/module/BladesPushAlert.js @@ -1,19 +1,17 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import U from "./core/utilities.js"; +import C from "./core/constants.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ export default class BladesPushAlert { + /*~ @@DOUBLE-BLANK@@ ~*/ static Get() { if (!game.eunoblades.PushController) { throw new Error("Attempt to Get BladesPushAlert before 'ready' hook."); } return game.eunoblades.PushController; } + /*~ @@DOUBLE-BLANK@@ ~*/ static isInitialized = false; + /*~ @@DOUBLE-BLANK@@ ~*/ static Initialize() { game.eunoblades ??= {}; Hooks.once("ready", async () => { @@ -26,6 +24,7 @@ export default class BladesPushAlert { }); Hooks.on("canvasReady", async () => { game.eunoblades.PushController?.initOverlay(); }); } + /*~ @@DOUBLE-BLANK@@ ~*/ static InitSockets() { if (game.eunoblades.PushController) { socketlib.system.register("pushNotice", game.eunoblades.PushController.push); @@ -33,13 +32,18 @@ export default class BladesPushAlert { } return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ initOverlay() { $("#sidebar").append($("
")); BladesPushAlert.isInitialized = true; } + /*~ @@DOUBLE-BLANK@@ ~*/ get elem$() { return $("#blades-push-notifications"); } + /*~ @@DOUBLE-BLANK@@ ~*/ get elem() { return this.elem$[0]; } + /*~ @@DOUBLE-BLANK@@ ~*/ activeNotifications = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ push(blockClass, charName, titleText, bodyText) { const pushController = BladesPushAlert.Get(); const pushID = randomID(); @@ -57,6 +61,7 @@ export default class BladesPushAlert { } pushLines.push(""); const pushElem$ = $(pushLines.join("\n")); + /*~ @@DOUBLE-BLANK@@ ~*/ pushController.elem$.append(pushElem$); pushElem$.on("click", () => pushController.removePush(pushElem$[0])); U.gsap.from(pushElem$[0], { @@ -66,19 +71,22 @@ export default class BladesPushAlert { ease: "power2" }); U.gsap.from(pushElem$[0], { - background: "rgb(255, 231, 92)", - borderColor: "rgb(255, 255, 255)", + background: C.Colors.bGOLD, + borderColor: C.Colors.WHITE, duration: 10, ease: "power2" }); } + /*~ @@DOUBLE-BLANK@@ ~*/ removePush(pushElem) { U.gsap.effects.slideUp(pushElem) .then(() => $(pushElem).remove()); } + /*~ @@DOUBLE-BLANK@@ ~*/ pushToAll(...args) { socketlib.system.executeForEveryone("pushNotice", "", ...args); } + /*~ @@DOUBLE-BLANK@@ ~*/ pushToSome(...args) { const users = (args.pop() ?? []) .filter((user) => Boolean(user?.id)); @@ -88,7 +96,9 @@ export default class BladesPushAlert { const pushArgs = args.slice(0, 3); socketlib.system.executeForUsers("pushNotice", users.map((user) => user.id), "", ...pushArgs); } + /*~ @@DOUBLE-BLANK@@ ~*/ pushToGM(...args) { socketlib.system.executeForAllGMs("pushNotice", "to-gm-notice", ...args); } -} \ No newline at end of file +} +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/BladesRoll.js b/module/BladesRoll.js index bee097e3..e74d33aa 100644 --- a/module/BladesRoll.js +++ b/module/BladesRoll.js @@ -1,32 +1,66 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - +// #region IMPORTS ~ import U from "./core/utilities.js"; import C, { BladesActorType, BladesItemType, RollPermissions, RollType, RollSubType, RollModStatus, RollModSection, ActionTrait, DowntimeAction, AttributeTrait, Position, Effect, Factor, RollResult, RollPhase, ConsequenceType, Tag } from "./core/constants.js"; import { BladesActor, BladesPC, BladesCrew } from "./documents/BladesActorProxy.js"; import { BladesItem, BladesGMTracker } from "./documents/BladesItemProxy.js"; import { ApplyTooltipListeners, ApplyConsequenceListeners } from "./core/gsap.js"; import BladesDialog from "./BladesDialog.js"; - +import BladesChat from "./BladesChat.js"; +// #endregion +// #region Types & Type Checking ~ +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Checks if the given string is a RollType. + * @param {unknown} str The string to check. + * @returns {boolean} True if the string is a RollType, false otherwise. + */ function isRollType(str) { return typeof str === "string" && str in RollType; } +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Checks if the given trait is an ActionTrait. + * @param {unknown} trait The trait to check. + * @returns {boolean} True if the trait is an ActionTrait, false otherwise. + */ function isAction(trait) { return Boolean(trait && typeof trait === "string" && U.lCase(trait) in ActionTrait); } +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Checks if the given trait is an AttributeTrait. + * @param {unknown} trait The trait to check. + * @returns {boolean} True if the trait is an AttributeTrait, false otherwise. + */ function isAttribute(trait) { return Boolean(trait && typeof trait === "string" && U.lCase(trait) in AttributeTrait); } +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Checks if the given trait is a Factor. + * @param {unknown} trait The trait to check. + * @returns {boolean} True if the trait is a Factor, false otherwise. + */ function isFactor(trait) { return Boolean(trait && typeof trait === "string" && U.lCase(trait) in Factor); } +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Checks if the given string is a RollModStatus. + * @param {unknown} str The string to check. + * @returns {boolean} True if the string is a RollModStatus, false otherwise. + */ function isModStatus(str) { return typeof str === "string" && str in RollModStatus; } +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Checks if the given value is valid consequence data for a Resistance Roll. + * @param {unknown} val The value to check. + * @param {boolean} [isCheckingResistedTo=false] If the check is being recursively + * applied to the 'resistedTo' value. + * @returns {boolean} True if the val is valid BladesRoll.ResistanceRollConsequenceData, false otherwise. + */ function isValidConsequenceData(val, isCheckingResistedTo = false) { if (!U.isList(val)) { return false; @@ -43,17 +77,25 @@ function isValidConsequenceData(val, isCheckingResistedTo = false) { if (typeof val.typeDisplay !== "string") { return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ if (isCheckingResistedTo) { return true; } + /*~ @@DOUBLE-BLANK@@ ~*/ if (typeof val.attribute !== "string" || !(val.attribute in AttributeTrait)) { return false; } if (!isValidConsequenceData(val.resistedTo, true)) { return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ return true; } +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Checks if the given section can contain BladesRollParticipant documents. + * @param {RollModSection} section + */ function isParticipantSection(section) { return [ RollModSection.roll, @@ -61,6 +103,11 @@ function isParticipantSection(section) { RollModSection.effect ].includes(section); } +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Checks if the given subSection can hold BladesRollParticipant documents. + * @param {string} subSection + */ function isParticipantSubSection(subSection) { if (subSection.startsWith("Group_")) { return true; @@ -70,15 +117,27 @@ function isParticipantSubSection(subSection) { } return false; } +// #endregion +// #region Utility Functions ~ +/** + * Prunes the configuration file of flag-incompatible direct document references. + * @param {BladesRoll.Config} cfg The configuration object to be pruned. + * @returns {BladesRoll.ConfigFlags} - The pruned configuration object. + */ function pruneConfig(cfg) { return expandObject(U.objFilter(flattenObject(cfg), (v) => !(v instanceof BladesActor) && !(v instanceof BladesItem))); } +// #endregion +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesRollMod { + /*~ @@DOUBLE-BLANK@@ ~*/ static ParseDocRollMods(doc) { + /*~ @@DOUBLE-BLANK@@ ~*/ const { roll_mods } = doc.system; if (!roll_mods || roll_mods.length === 0) { return []; } + /*~ @@DOUBLE-BLANK@@ ~*/ return roll_mods .filter((elem) => typeof elem === "string") .map((modString) => { @@ -95,6 +154,7 @@ class BladesRollMod { } const posNegString = (U.pullElement(pStrings, (v) => typeof v === "string" && /^p/i.test(v)) || "posNeg:positive"); const posNegVal = posNegString.replace(/^.*:/, ""); + /*~ @@DOUBLE-BLANK@@ ~*/ const rollModData = { id: `${nameVal}-${posNegVal}-${catVal}`, name: nameVal, @@ -105,6 +165,7 @@ class BladesRollMod { posNeg: posNegVal, tooltip: "" }; + /*~ @@DOUBLE-BLANK@@ ~*/ pStrings.forEach((pString) => { const [keyString, valString] = pString.split(/:/); let val = /\|/.test(valString) ? valString.split(/\|/) : valString; @@ -151,9 +212,11 @@ class BladesRollMod { else { throw new Error(`Bad Roll Mod Key: ${keyString}`); } + /*~ @@DOUBLE-BLANK@@ ~*/ if (key === "base_status" && val === "Conditional") { val = RollModStatus.Hidden; } + /*~ @@DOUBLE-BLANK@@ ~*/ let valProcessed; if (["value"].includes(key)) { valProcessed = U.pInt(val); @@ -164,11 +227,14 @@ class BladesRollMod { else { valProcessed = val.replace(/%COLON%/g, ":"); } + /*~ @@DOUBLE-BLANK@@ ~*/ Object.assign(rollModData, { [key]: valProcessed }); }); + /*~ @@DOUBLE-BLANK@@ ~*/ return rollModData; }); } + /*~ @@DOUBLE-BLANK@@ ~*/ get status() { if (this.userStatus && [RollModStatus.ForcedOn, RollModStatus.ForcedOff, RollModStatus.Hidden].includes(this.userStatus)) { @@ -179,17 +245,25 @@ class BladesRollMod { } return this.heldStatus ?? this.userStatus ?? this.baseStatus; } + /*~ @@DOUBLE-BLANK@@ ~*/ get isActive() { return [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(this.status); } + /*~ @@DOUBLE-BLANK@@ ~*/ get isVisible() { return this.status !== RollModStatus.Hidden; } + /*~ @@DOUBLE-BLANK@@ ~*/ _heldStatus; + /*~ @@DOUBLE-BLANK@@ ~*/ get heldStatus() { return this._heldStatus; } + /*~ @@DOUBLE-BLANK@@ ~*/ set heldStatus(val) { this._heldStatus = val; } + /*~ @@DOUBLE-BLANK@@ ~*/ get flagParams() { return [C.SYSTEM_ID, `rollCollab.rollModsData.${this.id}`]; } + /*~ @@DOUBLE-BLANK@@ ~*/ getUserStatusFlag() { return this.rollInstance.document.getFlag(...this.flagParams); } + /*~ @@DOUBLE-BLANK@@ ~*/ async setUserStatusFlag(val) { if (val === this.userStatus) { return; @@ -212,13 +286,17 @@ class BladesRollMod { } await socketlib.system.executeForEveryone("renderRollCollab", this.rollInstance.rollID); } + /*~ @@DOUBLE-BLANK@@ ~*/ get userStatus() { return this.getUserStatusFlag(); } + /*~ @@DOUBLE-BLANK@@ ~*/ set userStatus(val) { this.setUserStatusFlag(val); } + /*~ @@DOUBLE-BLANK@@ ~*/ get sourceName() { return this._sourceName; } + /*~ @@DOUBLE-BLANK@@ ~*/ get isConditional() { return [ ...this.conditionalRollTraits, @@ -229,6 +307,7 @@ class BladesRollMod { ...this.participantRollTypes ].length > 0; } + /*~ @@DOUBLE-BLANK@@ ~*/ get isInInactiveBlock() { if (game.user.isGM) { return [RollModStatus.Hidden, RollModStatus.ForcedOff, RollModStatus.ToggledOff].includes(this.status) @@ -237,11 +316,14 @@ class BladesRollMod { return [RollModStatus.ForcedOff, RollModStatus.ToggledOff].includes(this.status) && (this.isConditional || [BladesItemType.ability].includes(this.modType)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get isPush() { return Boolean(U.lCase(this.name) === "push" || this.effectKeys.find((eKey) => eKey === "Is-Push")); } + /*~ @@DOUBLE-BLANK@@ ~*/ get isBasicPush() { return U.lCase(this.name) === "push"; } + /*~ @@DOUBLE-BLANK@@ ~*/ get stressCost() { const costKeys = this.effectKeys.filter((key) => key.startsWith("Cost-Stress")); if (costKeys.length === 0) { @@ -255,6 +337,7 @@ class BladesRollMod { }); return stressCost; } + /*~ @@DOUBLE-BLANK@@ ~*/ isValidForRollType() { switch (this.rollInstance.rollType) { case RollType.Action: { @@ -272,39 +355,68 @@ class BladesRollMod { default: return false; } } - setConditionalStatus() { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Sets the conditional status of the roll instance. + * @returns {boolean} - Returns false if the status is ForcedOn or ToggledOff, true if the status is Hidden. + */ + setConditionalStatus() { + // If the roll instance is not conditional, return false if (!this.isConditional) { return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If any auto-Traits/Types apply, set status to ForcedOn and return false const autoTypesOrTraitsApply = this.autoRollTypes.includes(this.rollInstance.rollType) || (!this.rollInstance.rollTrait || this.autoRollTraits.includes(this.rollInstance.rollTrait)); if (autoTypesOrTraitsApply) { this.heldStatus = RollModStatus.ForcedOn; return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If any conditionalTypes apply and any conditionalTraits apply, set status to ToggledOff and return false const conditionalTypesOrTraitsApply = this.checkTypesOrTraits(this.conditionalRollTypes, this.conditionalRollTraits); if (conditionalTypesOrTraitsApply) { this.heldStatus = RollModStatus.ToggledOff; return false; } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // If this is a participant roll + // AND any participantTypes apply + // AND any participantTraits apply, + // ... set status to ToggledOff and return false const participantTypesOrTraitsApply = this.rollInstance.isParticipantRoll && this.checkTypesOrTraits(this.participantRollTypes, this.participantRollTraits); if (participantTypesOrTraitsApply) { this.heldStatus = RollModStatus.ToggledOff; return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If none of the above conditions apply, set status to Hidden and return true this.heldStatus = RollModStatus.Hidden; return true; } - checkTypesOrTraits(types, traits) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Checks if any types or traits apply to the roll instance. + * @param {AnyRollType[]} types The types to check. + * @param {RollTrait[]} traits The traits to check. + * @returns {boolean} - Returns true if any types or traits apply, false otherwise. + */ + checkTypesOrTraits(types, traits) { const typesApply = Boolean((!this.rollInstance.isParticipantRoll && types.length === 0) || types.includes(this.rollInstance.rollType)); const traitsApply = Boolean((!this.rollInstance.isParticipantRoll && traits.length === 0) || (this.rollInstance.rollTrait && traits.includes(this.rollInstance.rollTrait))); return typesApply && traitsApply; } - processKey(key) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Helper function to process each key + * @param {string} key The key to process + * @returns {boolean} - Whether the processing was successful + */ + processKey(key) { const [thisKey, thisParam] = key.split(/-/) ?? []; const positions = [Position.controlled, Position.risky, Position.desperate]; if (positions.includes(U.lCase(thisParam)) && this.rollInstance.finalPosition === U.lCase(thisParam)) { @@ -319,27 +431,34 @@ class BladesRollMod { } return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ setAutoStatus() { + // Check for AutoRevealOn and AutoEnableOn const holdKeys = this.effectKeys.filter((key) => key.startsWith("Auto")); if (holdKeys.length === 0) { return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ for (const key of holdKeys) { if (this.processKey(key)) { return false; } } + /*~ @@DOUBLE-BLANK@@ ~*/ this.heldStatus = RollModStatus.Hidden; return true; } + /*~ @@DOUBLE-BLANK@@ ~*/ setRelevancyStatus() { const holdKeys = this.effectKeys.filter((key) => /^Negate|^Increase/.test(key)); if (holdKeys.length === 0) { return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ const relevantKeys = holdKeys .filter((key) => { const [thisKey, thisParam] = key.split(/-/) ?? []; + /*~ @@DOUBLE-BLANK@@ ~*/ const negateOperations = { PushCost: () => this.rollInstance.isPushed(), PushCost0: () => this.rollInstance.isPushed(), @@ -370,6 +489,7 @@ class BladesRollMod { && (this.rollInstance.rollFactors.source[Factor.tier]?.value ?? 0) < (this.rollInstance.rollFactors.opposition[Factor.tier]?.value ?? 0) }; + /*~ @@DOUBLE-BLANK@@ ~*/ if (thisKey === "Negate") { if (Object.hasOwn(negateOperations, thisParam)) { return negateOperations[thisParam](); @@ -392,11 +512,13 @@ class BladesRollMod { } return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ setPayableStatus() { const holdKeys = this.effectKeys.filter((key) => key.startsWith("Cost")); if (holdKeys.length === 0) { return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ const payableKeys = holdKeys .filter((key) => { const [thisParam] = (key.split(/-/) ?? []).slice(1); @@ -419,22 +541,28 @@ class BladesRollMod { default: throw new Error(`Unrecognize Payable Key: ${traitStr}`); } }); + /*~ @@DOUBLE-BLANK@@ ~*/ if (payableKeys.length === 0) { this.heldStatus = RollModStatus.ForcedOff; return true; } return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ applyRollModEffectKeys() { if (!this.isActive) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ const holdKeys = this.effectKeys.filter((key) => /^Negate|^Increase/.test(key)); if (holdKeys.length === 0) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ holdKeys.forEach((key) => { + // Console.log({key, split: key.split(/-/)}) const [thisKey, thisParam] = key.split(/-/) ?? []; + /*~ @@DOUBLE-BLANK@@ ~*/ const negateOperations = { PushCost: () => { const costlyPushMod = this.rollInstance.getActiveRollMods() @@ -443,8 +571,37 @@ class BladesRollMod { U.pullElement(costlyPushMod.effectKeys, (k) => k.startsWith("Cost-Stress")); } }, + // PushCost0: negateOperations.PushCost, Consequence: () => { - }, + /*~ @@DOUBLE-BLANK@@ ~*/ + /* Should cancel roll entirely? */ + }, + // HarmLevel: () => { + // const harmLevels = [ + // [ConsequenceType.InsightHarm1, ConsequenceType.ProwessHarm1, ConsequenceType.ResolveHarm1], + // [ConsequenceType.InsightHarm2, ConsequenceType.ProwessHarm2, ConsequenceType.ResolveHarm2], + // [ConsequenceType.InsightHarm3, ConsequenceType.ProwessHarm3, ConsequenceType.ResolveHarm3], + // [ConsequenceType.InsightHarm4, ConsequenceType.ProwessHarm4, ConsequenceType.ResolveHarm4] + // ]; + // let harmConsequence: BladesRoll.ResistanceRollConsequenceData|undefined = undefined; + // while (!harmConsequence && harmLevels.length > 0) { + // harmConsequence = Object.values(this.rollInstance.rollConsequences) + // .find(({type}) => (harmLevels.pop() ?? []).includes(type as ConsequenceType)); + // } + // if (harmConsequence) { + // harmConsequence.resistedTo = { + // name: [ + // ConsequenceType.InsightHarm1, + // ConsequenceType.ProwessHarm1, + // ConsequenceType.ResolveHarm1 + // ].includes(harmConsequence.type as ConsequenceType) + // ? "Fully Negated" + // : (Object.values(harmConsequence.resistOptions ?? [])[0]?.name ?? harmConsequence.name), + // type: C.ResistedConsequenceTypes[harmConsequence.type as KeyOf], + // isSelected: true + // }; + // } + // }, QualityPenalty: () => { this.rollInstance.negateFactorPenalty(Factor.quality); }, @@ -455,6 +612,7 @@ class BladesRollMod { this.rollInstance.negateFactorPenalty(Factor.tier); } }; + /*~ @@DOUBLE-BLANK@@ ~*/ if (thisKey === "Negate") { if (Object.hasOwn(negateOperations, thisParam)) { return negateOperations[thisParam](); @@ -472,6 +630,7 @@ class BladesRollMod { } }); } + /*~ @@DOUBLE-BLANK@@ ~*/ get selectOptions() { if (this.modType !== "teamwork") { return null; @@ -484,12 +643,14 @@ class BladesRollMod { } return null; } + /*~ @@DOUBLE-BLANK@@ ~*/ get selectedParticipant() { if (this.modType !== "teamwork") { return null; } return this.rollInstance.getRollParticipant(this.section, this.name); } + /*~ @@DOUBLE-BLANK@@ ~*/ get tooltip() { let parsedTooltip = this._tooltip.replace(/%COLON%/g, ":"); if (parsedTooltip.includes("%DOC_NAME%")) { @@ -504,6 +665,7 @@ class BladesRollMod { } return parsedTooltip; } + /*~ @@DOUBLE-BLANK@@ ~*/ get sideString() { if (this._sideString) { return this._sideString; @@ -513,9 +675,11 @@ class BladesRollMod { } return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ get allFlagData() { return this.rollInstance.document.getFlag("eunos-blades", "rollCollab"); } + /*~ @@DOUBLE-BLANK@@ ~*/ get data() { return { id: this.id, @@ -535,6 +699,7 @@ class BladesRollMod { section: this.section }; } + /*~ @@DOUBLE-BLANK@@ ~*/ get costs() { if (!this.isActive) { return undefined; @@ -543,9 +708,11 @@ class BladesRollMod { if (holdKeys.length === 0) { return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ return holdKeys.map((key) => { const [thisParam] = (key.split(/-/) ?? []).slice(1); const [traitStr, valStr] = (/([A-Za-z]+)(\d*)/.exec(thisParam) ?? []).slice(1); + /*~ @@DOUBLE-BLANK@@ ~*/ let label = this.name; if (this.isBasicPush) { if (this.posNeg === "negative") { @@ -556,6 +723,7 @@ class BladesRollMod { label = `${this.name} (${effect})`; } } + /*~ @@DOUBLE-BLANK@@ ~*/ return { id: this.id, label, @@ -564,24 +732,43 @@ class BladesRollMod { }; }); } + /*~ @@DOUBLE-BLANK@@ ~*/ id; + /*~ @@DOUBLE-BLANK@@ ~*/ name; + /*~ @@DOUBLE-BLANK@@ ~*/ _sourceName; + /*~ @@DOUBLE-BLANK@@ ~*/ baseStatus; + /*~ @@DOUBLE-BLANK@@ ~*/ value; + /*~ @@DOUBLE-BLANK@@ ~*/ effectKeys; + /*~ @@DOUBLE-BLANK@@ ~*/ _sideString; + /*~ @@DOUBLE-BLANK@@ ~*/ _tooltip; + /*~ @@DOUBLE-BLANK@@ ~*/ posNeg; + /*~ @@DOUBLE-BLANK@@ ~*/ modType; + /*~ @@DOUBLE-BLANK@@ ~*/ conditionalRollTypes; + /*~ @@DOUBLE-BLANK@@ ~*/ autoRollTypes; + /*~ @@DOUBLE-BLANK@@ ~*/ participantRollTypes; + /*~ @@DOUBLE-BLANK@@ ~*/ conditionalRollTraits; + /*~ @@DOUBLE-BLANK@@ ~*/ autoRollTraits; + /*~ @@DOUBLE-BLANK@@ ~*/ participantRollTraits; + /*~ @@DOUBLE-BLANK@@ ~*/ section; + /*~ @@DOUBLE-BLANK@@ ~*/ rollInstance; + /*~ @@DOUBLE-BLANK@@ ~*/ constructor(modData, rollInstance) { this.rollInstance = rollInstance; this.id = modData.id; @@ -603,8 +790,10 @@ class BladesRollMod { this.section = modData.section; } } +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesRollPrimary { - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Static Methods ~ static IsValidData(data) { if (BladesRollPrimary.IsDoc(data)) { return true; @@ -618,13 +807,19 @@ class BladesRollPrimary { && (!data.rollPrimaryID || typeof data.rollPrimaryID === "string") && (!data.rollPrimaryDoc || BladesRollPrimary.IsDoc(data.rollPrimaryDoc)); } + /*~ @@DOUBLE-BLANK@@ ~*/ static IsDoc(doc) { return BladesActor.IsType(doc, BladesActorType.pc, BladesActorType.crew) || BladesItem.IsType(doc, BladesItemType.cohort_expert, BladesItemType.cohort_gang, BladesItemType.gm_tracker); } + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ rollInstance; + /*~ @@DOUBLE-BLANK@@ ~*/ rollPrimaryID; + /*~ @@DOUBLE-BLANK@@ ~*/ _rollPrimaryDoc; + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimaryDoc() { if (!this._rollPrimaryDoc) { let doc; @@ -642,25 +837,36 @@ class BladesRollPrimary { } return this._rollPrimaryDoc; } + /*~ @@DOUBLE-BLANK@@ ~*/ rollPrimaryName; + /*~ @@DOUBLE-BLANK@@ ~*/ rollPrimaryType; + /*~ @@DOUBLE-BLANK@@ ~*/ rollPrimaryImg; + /*~ @@DOUBLE-BLANK@@ ~*/ _rollModsData; + /*~ @@DOUBLE-BLANK@@ ~*/ get rollModsData() { return this.rollPrimaryDoc?.rollModsData ?? this._rollModsData ?? []; } + /*~ @@DOUBLE-BLANK@@ ~*/ rollFactors; - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Constructor ~ constructor(rollInstance, { rollPrimaryID, rollPrimaryName, rollPrimaryType, rollPrimaryImg, rollModsData, rollFactors }) { + // Identify ID, Doc, Name, SubName, Type & Image, to best of ability this.rollInstance = rollInstance; + /*~ @@DOUBLE-BLANK@@ ~*/ this.rollPrimaryID = rollPrimaryID ?? this.rollInstance.rollPrimary.rollPrimaryID ?? this.rollInstance.rollPrimary.rollPrimaryDoc?.rollPrimaryID; + /*~ @@DOUBLE-BLANK@@ ~*/ rollPrimaryName ??= this.rollInstance.rollPrimary.rollPrimaryName; rollPrimaryType ??= this.rollInstance.rollPrimary.rollPrimaryType; rollPrimaryImg ??= this.rollInstance.rollPrimary.rollPrimaryImg; rollModsData ??= this.rollInstance.rollPrimary.rollModsData; rollFactors ??= this.rollInstance.rollPrimary.rollFactors; + /*~ @@DOUBLE-BLANK@@ ~*/ if (BladesRollPrimary.IsDoc(this.rollPrimaryDoc)) { this.rollPrimaryName = rollPrimaryName ?? this.rollPrimaryDoc.rollPrimaryName; this.rollPrimaryType = this.rollPrimaryDoc.rollPrimaryType; @@ -684,6 +890,7 @@ class BladesRollPrimary { if (!rollFactors) { throw new Error("Must include a rollFactors when constructing a BladesRollPrimary object."); } + /*~ @@DOUBLE-BLANK@@ ~*/ this.rollPrimaryName = rollPrimaryName; this.rollPrimaryType = rollPrimaryType; this.rollPrimaryImg = rollPrimaryImg; @@ -692,8 +899,10 @@ class BladesRollPrimary { } } } +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesRollOpposition { - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Static Methods ~ static IsValidData(data) { if (BladesRollOpposition.IsDoc(data)) { return true; @@ -707,6 +916,7 @@ class BladesRollOpposition { && U.isList(data.rollFactors) && (!data.rollOppID || typeof data.rollOppID === "string"); } + /*~ @@DOUBLE-BLANK@@ ~*/ static GetDoc(docRef) { let doc = docRef; if (typeof docRef === "string") { @@ -720,6 +930,7 @@ class BladesRollOpposition { } return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ static IsDoc(doc) { return BladesActor.IsType(doc, BladesActorType.npc, BladesActorType.faction) || BladesItem.IsType(doc, ...[ @@ -731,9 +942,14 @@ class BladesRollOpposition { BladesItemType.ritual ]); } + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ rollInstance; + /*~ @@DOUBLE-BLANK@@ ~*/ _rollOppID; + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppID() { return this._rollOppID; } + /*~ @@DOUBLE-BLANK@@ ~*/ set rollOppID(val) { if (val) { const doc = BladesRollOpposition.GetDoc(val); @@ -743,18 +959,31 @@ class BladesRollOpposition { } this._rollOppID = val; } + /*~ @@DOUBLE-BLANK@@ ~*/ rollOppDoc; + /*~ @@DOUBLE-BLANK@@ ~*/ rollOppName; + /*~ @@DOUBLE-BLANK@@ ~*/ rollOppSubName; + /*~ @@DOUBLE-BLANK@@ ~*/ rollOppType; + /*~ @@DOUBLE-BLANK@@ ~*/ rollOppImg; + /*~ @@DOUBLE-BLANK@@ ~*/ rollOppModsData; + /*~ @@DOUBLE-BLANK@@ ~*/ rollFactors; - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Constructor ~ constructor(rollInstance, { rollOppID, rollOppDoc, rollOppName, rollOppSubName, rollOppType, rollOppImg, rollOppModsData, rollFactors } = {}) { + /*~ @@DOUBLE-BLANK@@ ~*/ this.rollInstance = rollInstance; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Attempt to fetch an associated BladesActor or BladesItem document const doc = BladesRollOpposition.GetDoc(rollOppDoc ?? rollOppID ?? rollOppName); + /*~ @@DOUBLE-BLANK@@ ~*/ if (doc) { + // Derive settings from valid Actor/Item document, unless explicitly set in constructor. rollOppID = doc.rollOppID; rollOppDoc = doc; rollOppName ??= doc.rollOppName; @@ -770,6 +999,8 @@ class BladesRollOpposition { ...rollFactors ?? {} }; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Confirm required settings if (!rollOppName) { throw new Error("Must include a rollOppName when constructing a BladesRollOpposition object."); } @@ -782,6 +1013,8 @@ class BladesRollOpposition { if (!rollFactors) { throw new Error("Must include a rollFactors when constructing a BladesRollOpposition object."); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize properties this.rollOppID = rollOppID; this.rollOppName = rollOppName; this.rollOppSubName = rollOppSubName; @@ -790,9 +1023,12 @@ class BladesRollOpposition { this.rollOppModsData = rollOppModsData ?? []; this.rollFactors = rollFactors; } + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ get flagParams() { return [C.SYSTEM_ID, "rollCollab.rollOppData"]; } + /*~ @@DOUBLE-BLANK@@ ~*/ get flagData() { return { rollOppID: this.rollOppID, @@ -800,14 +1036,17 @@ class BladesRollOpposition { rollOppSubName: this.rollOppSubName, rollOppType: this.rollOppType, rollOppImg: this.rollOppImg, + /*~ @@DOUBLE-BLANK@@ ~*/ rollOppModsData: this.rollOppModsData, rollFactors: this.rollFactors }; } + /*~ @@DOUBLE-BLANK@@ ~*/ async updateRollFlags() { await this.rollInstance.document.setFlag(...this.flagParams, this.flagData); socketlib.system.executeForEveryone("renderRollCollab", this.rollInstance.rollID); } + /*~ @@DOUBLE-BLANK@@ ~*/ refresh() { const rollOppFlags = this.rollInstance.flagData.rollOppData; if (rollOppFlags) { @@ -816,14 +1055,17 @@ class BladesRollOpposition { this.rollOppSubName = rollOppFlags.rollOppSubName; this.rollOppType = rollOppFlags.rollOppType; this.rollOppImg = rollOppFlags.rollOppImg; + /*~ @@DOUBLE-BLANK@@ ~*/ this.rollOppModsData = rollOppFlags.rollOppModsData; this.rollFactors = rollOppFlags.rollFactors; } return this; } } +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesRollParticipant { - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Static Methods ~ static IsValidData(data) { if (BladesRollParticipant.IsDoc(data)) { return true; @@ -837,6 +1079,7 @@ class BladesRollParticipant { && (!data.rollParticipantID || typeof data.rollParticipantID === "string") && (!data.rollParticipantDoc || BladesRollParticipant.IsDoc(data.rollParticipantDoc)); } + /*~ @@DOUBLE-BLANK@@ ~*/ static GetDoc(docRef) { let doc = docRef; if (typeof docRef === "string") { @@ -850,13 +1093,19 @@ class BladesRollParticipant { } return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ static IsDoc(doc) { return BladesActor.IsType(doc, BladesActorType.pc, BladesActorType.crew, BladesActorType.npc) || BladesItem.IsType(doc, BladesItemType.cohort_expert, BladesItemType.cohort_gang, BladesItemType.gm_tracker); } + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ rollInstance; + /*~ @@DOUBLE-BLANK@@ ~*/ _rollParticipantID; + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantID() { return this._rollParticipantID; } + /*~ @@DOUBLE-BLANK@@ ~*/ set rollParticipantID(val) { if (val) { const doc = BladesRollParticipant.GetDoc(val); @@ -866,17 +1115,28 @@ class BladesRollParticipant { } this._rollParticipantID = val; } + /*~ @@DOUBLE-BLANK@@ ~*/ rollParticipantDoc; + /*~ @@DOUBLE-BLANK@@ ~*/ rollParticipantName; + /*~ @@DOUBLE-BLANK@@ ~*/ rollParticipantType; + /*~ @@DOUBLE-BLANK@@ ~*/ rollParticipantIcon; + /*~ @@DOUBLE-BLANK@@ ~*/ rollParticipantSection; + /*~ @@DOUBLE-BLANK@@ ~*/ rollParticipantSubSection; - rollParticipantModsData; + /*~ @@DOUBLE-BLANK@@ ~*/ + rollParticipantModsData; // As applied to MAIN roll when this participant involved + /*~ @@DOUBLE-BLANK@@ ~*/ rollFactors; - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Constructor ~ constructor(rollInstance, { rollParticipantSection, rollParticipantSubSection, rollParticipantID, rollParticipantDoc, rollParticipantName, rollParticipantType, rollParticipantIcon, rollParticipantModsData, rollFactors }) { + /*~ @@DOUBLE-BLANK@@ ~*/ this.rollInstance = rollInstance; + /*~ @@DOUBLE-BLANK@@ ~*/ if (!rollParticipantSection) { throw new Error("Must include a rollParticipantSection when constructing a BladesRollParticipant object."); } @@ -885,8 +1145,12 @@ class BladesRollParticipant { } this.rollParticipantSection = rollParticipantSection; this.rollParticipantSubSection = rollParticipantSubSection; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Attempt to fetch an associated BladesActor or BladesItem document const doc = BladesRollParticipant.GetDoc(rollParticipantDoc ?? rollParticipantID ?? rollParticipantName); + /*~ @@DOUBLE-BLANK@@ ~*/ if (doc) { + // Derive settings from valid Actor/Item document, unless explicitly set in constructor. rollParticipantID = doc.rollParticipantID; rollParticipantDoc = doc; rollParticipantName ??= doc.rollParticipantName; @@ -901,6 +1165,8 @@ class BladesRollParticipant { ...rollFactors ?? {} }; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Confirm required settings if (!rollParticipantName) { throw new Error("Must include a rollParticipantName when constructing a BladesRollParticipant object."); } @@ -910,6 +1176,8 @@ class BladesRollParticipant { if (!rollFactors) { throw new Error("Must include a rollFactors when constructing a BladesRollParticipant object."); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize properties this.rollParticipantID = rollParticipantID; this.rollParticipantName = rollParticipantName; this.rollParticipantType = rollParticipantType; @@ -917,36 +1185,45 @@ class BladesRollParticipant { this.rollParticipantModsData = rollParticipantModsData ?? []; this.rollFactors = rollFactors; } + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ get flagParams() { return [C.SYSTEM_ID, `rollCollab.rollParticipantData.${this.rollParticipantSection}.${this.rollParticipantSubSection}`]; } + /*~ @@DOUBLE-BLANK@@ ~*/ get flagData() { return { rollParticipantSection: this.rollParticipantSection, rollParticipantSubSection: this.rollParticipantSubSection, + /*~ @@DOUBLE-BLANK@@ ~*/ rollParticipantID: this.rollParticipantID, rollParticipantName: this.rollParticipantName, rollParticipantType: this.rollParticipantType, rollParticipantIcon: this.rollParticipantIcon, + /*~ @@DOUBLE-BLANK@@ ~*/ rollParticipantModsData: this.rollParticipantModsData, rollFactors: this.rollFactors }; } + /*~ @@DOUBLE-BLANK@@ ~*/ async updateRollFlags() { await this.rollInstance.document.setFlag(...this.flagParams, this.flagData); socketlib.system.executeForEveryone("renderRollCollab", this.rollInstance.rollID); } + /*~ @@DOUBLE-BLANK@@ ~*/ refresh() { const rollParticipantFlagData = this.rollInstance.flagData.rollParticipantData?.[this.rollParticipantSection]; if (rollParticipantFlagData) { const rollParticipantFlags = rollParticipantFlagData[this.rollParticipantSubSection]; if (rollParticipantFlags) { this.rollParticipantID = rollParticipantFlags.rollParticipantID; + /*~ @@DOUBLE-BLANK@@ ~*/ this.rollParticipantName = rollParticipantFlags.rollParticipantName; this.rollParticipantType = rollParticipantFlags.rollParticipantType; this.rollParticipantIcon = rollParticipantFlags.rollParticipantIcon; this.rollParticipantSection = rollParticipantFlags.rollParticipantSection; this.rollParticipantSubSection = rollParticipantFlags.rollParticipantSubSection; + /*~ @@DOUBLE-BLANK@@ ~*/ this.rollParticipantModsData = rollParticipantFlags.rollParticipantModsData; this.rollFactors = rollParticipantFlags.rollFactors; } @@ -954,8 +1231,10 @@ class BladesRollParticipant { return this; } } +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesRoll extends DocumentSheet { - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region STATIC METHODS: INITIALIZATION & DEFAULTS ~ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["eunos-blades", "sheet", "roll-collab", game.user.isGM ? "gm-roll-collab" : ""], @@ -965,8 +1244,10 @@ class BladesRoll extends DocumentSheet { dragDrop: [ { dragSelector: null, dropSelector: "[data-action='gm-drop-opposition'" } ] + // Height: 500 }); } + /*~ @@DOUBLE-BLANK@@ ~*/ static Initialize() { return loadTemplates([ "systems/eunos-blades/templates/roll/roll-collab.hbs", @@ -986,11 +1267,13 @@ class BladesRoll extends DocumentSheet { "systems/eunos-blades/templates/roll/partials/roll-collab-incarceration-gm.hbs" ]); } + /*~ @@DOUBLE-BLANK@@ ~*/ static InitSockets() { socketlib.system.register("constructRollCollab", BladesRoll.ConstructRollCollab); socketlib.system.register("renderRollCollab", BladesRoll.RenderRollCollab); socketlib.system.register("closeRollCollab", BladesRoll.CloseRollCollab); } + /*~ @@DOUBLE-BLANK@@ ~*/ static get DefaultRollMods() { return [ { @@ -1078,6 +1361,7 @@ class BladesRoll extends DocumentSheet { } ]; } + /*~ @@DOUBLE-BLANK@@ ~*/ static get DefaultFlagData() { return { rollModsData: {}, @@ -1161,9 +1445,13 @@ class BladesRoll extends DocumentSheet { } }; } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region STATIC METHODS: New Roll Creation ~ static Current = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ static _Active; + /*~ @@DOUBLE-BLANK@@ ~*/ static get Active() { if (BladesRoll._Active) { return BladesRoll._Active; @@ -1173,43 +1461,59 @@ class BladesRoll extends DocumentSheet { } return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ static set Active(val) { BladesRoll._Active = val; } + /*~ @@DOUBLE-BLANK@@ ~*/ static async ConstructRollCollab({ userID, rollID, rollPermission }) { const rollInst = new BladesRoll(userID, rollID, rollPermission); eLog.checkLog3("rollCollab", "ConstructRollCollab()", { params: { userID, rollID, rollPermission }, rollInst }); await rollInst._render(true); } + /*~ @@DOUBLE-BLANK@@ ~*/ static RenderRollCollab(rollID) { BladesRoll.Current[rollID]?.prepareRollParticipantData(); BladesRoll.Current[rollID]?.render(); } + /*~ @@DOUBLE-BLANK@@ ~*/ static async CloseRollCollab(rollID) { eLog.checkLog3("rollCollab", "CloseRollCollab()", { rollID }); await BladesRoll.Current[rollID]?.close({ rollID }); delete BladesRoll.Current[rollID]; } + /*~ @@DOUBLE-BLANK@@ ~*/ static GetUserPermissions(config) { - + /*~ @@DOUBLE-BLANK@@ ~*/ + // === ONE === GET USER IDS + /*~ @@DOUBLE-BLANK@@ ~*/ + // Get user ID of GM const GMUserID = game.users.find((user) => user.isGM)?.id; if (!GMUserID) { throw new Error("[BladesRoll.GetUserPermissions()] No GM found!"); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Get user IDs of players const playerUserIDs = game.users .filter((user) => BladesPC.IsType(user.character) && !user.isGM && typeof user.id === "string") .map((user) => user.id); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare user ID permissions object const userIDs = { [RollPermissions.GM]: [GMUserID], [RollPermissions.Primary]: [], [RollPermissions.Participant]: [], [RollPermissions.Observer]: [] }; - + /*~ @@DOUBLE-BLANK@@ ~*/ + // === TWO === DETERMINE PRIMARY USER(S) + /*~ @@DOUBLE-BLANK@@ ~*/ + // Check RollPrimaryDoc to determine how to assign primary users const { rollPrimaryDoc } = config.rollPrimaryData; if (BladesPC.IsType(rollPrimaryDoc) && U.pullElement(playerUserIDs, rollPrimaryDoc.primaryUser?.id)) { userIDs[RollPermissions.Primary].push(rollPrimaryDoc.primaryUser?.id); + /*~ @@DOUBLE-BLANK@@ ~*/ } else if (BladesCrew.IsType(rollPrimaryDoc)) { userIDs[RollPermissions.Primary].push(...playerUserIDs); @@ -1225,14 +1529,25 @@ class BladesRoll extends DocumentSheet { else if (BladesGMTracker.IsType(rollPrimaryDoc)) { userIDs[RollPermissions.Primary].push(GMUserID); } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // === THREE === DETERMINE ROLL PARTICIPANT USER(S) + /*~ @@DOUBLE-BLANK@@ ~*/ + // Check config.rollParticipantData to determine if roll starts with any participants if (config.rollParticipantData) { userIDs[RollPermissions.Participant].push(...getParticipantDocUserIDs(config.rollParticipantData, playerUserIDs)); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Finally, add remaining players as observers. userIDs[RollPermissions.Observer] = playerUserIDs .filter((uID) => !userIDs[RollPermissions.Participant].includes(uID)); + /*~ @@DOUBLE-BLANK@@ ~*/ return userIDs; - function getParticipantDocs(participantData) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Generates BladesRollParticipant documents from the provided schema data. + * @param {BladesRoll.RollParticipantData} participantData + */ + function getParticipantDocs(participantData) { return Object.values(flattenObject(participantData)) .map((pData) => { if (BladesRollParticipant.IsDoc(pData)) { @@ -1252,7 +1567,13 @@ class BladesRoll extends DocumentSheet { return null; }); } - function getParticipantDocUserIDs(participantData, unassignedIDs) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Returns the user ids of potential BladesRollParticipants defined in the provided data schema. + * @param {BladesRoll.RollParticipantData} participantData + * @param {string[]} unassignedIDs + */ + function getParticipantDocUserIDs(participantData, unassignedIDs) { return getParticipantDocs(participantData) .map((pDoc) => { if (BladesPC.IsType(pDoc) && typeof pDoc.primaryUser?.id === "string") { @@ -1268,11 +1589,18 @@ class BladesRoll extends DocumentSheet { .filter((pUser) => pUser !== null && !userIDs[RollPermissions.Primary].includes(pUser)); } } + /*~ @@DOUBLE-BLANK@@ ~*/ static async PrepareActionRoll(rollID, config) { + /*~ @@DOUBLE-BLANK@@ ~*/ + // Validate the rollTrait if (!(U.isInt(config.rollTrait) || U.lCase(config.rollTrait) in { ...ActionTrait, ...Factor })) { throw new Error(`[PrepareActionRoll()] Bad RollTrait for Action Roll: ${config.rollTrait}`); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Retrieve the roll users const userIDs = BladesRoll.GetUserPermissions(config); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare roll user flag data const userFlagData = {}; Object.entries(userIDs) .forEach(([rollPermission, idsArray]) => { @@ -1280,24 +1608,38 @@ class BladesRoll extends DocumentSheet { userFlagData[id] = rollPermission; } }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare the flag data. const flagUpdateData = { ...BladesRoll.DefaultFlagData, ...pruneConfig(config), userPermissions: userFlagData, rollID }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Return flagData and roll users return { flagUpdateData, userIDs }; + /*~ @@DOUBLE-BLANK@@ ~*/ } + /*~ @@DOUBLE-BLANK@@ ~*/ static async PrepareResistanceRoll(rollID, config) { + /*~ @@DOUBLE-BLANK@@ ~*/ + // Validate consequenceData if (!config.resistanceData || !isValidConsequenceData(config.resistanceData?.consequence)) { eLog.error("rollCollab", "[PrepareResistanceRoll] Bad Roll Consequence Data.", config); throw new Error("[PrepareResistanceRoll()] Bad Consequence Data for Resistance Roll"); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Set rollTrait config.rollTrait = config.resistanceData.consequence.attribute; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Retrieve the roll users const userIDs = BladesRoll.GetUserPermissions(config); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare roll user flag data const userFlagData = {}; Object.entries(userIDs) .forEach(([rollPermission, idsArray]) => { @@ -1305,22 +1647,33 @@ class BladesRoll extends DocumentSheet { userFlagData[id] = rollPermission; } }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare the flag data. const flagUpdateData = { ...BladesRoll.DefaultFlagData, ...pruneConfig(config), userPermissions: userFlagData, rollID }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Return flagData and roll users return { flagUpdateData, userIDs }; } + /*~ @@DOUBLE-BLANK@@ ~*/ static async PrepareFortuneRoll(rollID, config) { + /*~ @@DOUBLE-BLANK@@ ~*/ + // Validate the rollTrait if (!(U.isInt(config.rollTrait) || U.lCase(config.rollTrait) in { ...ActionTrait, ...AttributeTrait, ...Factor })) { throw new Error(`[PrepareFortuneRoll()] Bad RollTrait for Fortune Roll: ${config.rollTrait}`); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Retrieve the roll users const userIDs = BladesRoll.GetUserPermissions(config); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare roll user flag data const userFlagData = {}; Object.entries(userIDs) .forEach(([rollPermission, idsArray]) => { @@ -1328,18 +1681,26 @@ class BladesRoll extends DocumentSheet { userFlagData[id] = rollPermission; } }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare the flag data. const flagUpdateData = { ...BladesRoll.DefaultFlagData, ...pruneConfig(config), userPermissions: userFlagData, rollID }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Return flagData and roll users return { flagUpdateData, userIDs }; + /*~ @@DOUBLE-BLANK@@ ~*/ } + /*~ @@DOUBLE-BLANK@@ ~*/ static async PrepareIndulgeViceRoll(rollID, config) { + /*~ @@DOUBLE-BLANK@@ ~*/ + // If primary doc is given, derive rollTrait from that (i.e. lowest Attribute) const { rollPrimaryDoc } = config.rollPrimaryData; if (BladesPC.IsType(rollPrimaryDoc)) { const minAttrVal = Math.min(...Object.values(rollPrimaryDoc.attributes)); @@ -1348,8 +1709,14 @@ class BladesRoll extends DocumentSheet { if (!(U.isInt(config.rollTrait) || U.lCase(config.rollTrait) in AttributeTrait)) { throw new Error(`[PrepareIndulgeViceRoll()] Bad RollTrait for Indulge Vice Roll: ${config.rollTrait}`); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Set other known config values config.rollDowntimeAction = DowntimeAction.IndulgeVice; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Retrieve the roll users const userIDs = BladesRoll.GetUserPermissions(config); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare roll user flag data const userFlagData = {}; Object.entries(userIDs) .forEach(([rollPermission, idsArray]) => { @@ -1357,25 +1724,43 @@ class BladesRoll extends DocumentSheet { userFlagData[id] = rollPermission; } }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare the flag data. const flagUpdateData = { ...BladesRoll.DefaultFlagData, ...pruneConfig(config), userPermissions: userFlagData, rollID }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Return flagData and roll users return { flagUpdateData, userIDs }; } - static async NewRoll(config) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * This static method accepts a partial version of the config options required + * to build a BladesRoll instance, sets the requisite flags on the storage + * document, then sends out a socket call to the relevant users to construct + * and display the roll instance. + * + * @param {BladesRoll.ConstructorConfig} config The configuration object for the new roll. + */ + static async NewRoll(config) { + // If no rollType is provided, throw an error. if (!isRollType(config.rollType)) { throw new Error("[BladesRoll.NewRoll()] You must provide a valid rollType in the config object."); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Get User document to serve as flag storage. const rollUser = game.users.get(config.rollUserID ?? game.user.id ?? ""); if (!rollUser?.id) { throw new Error("[BladesRoll.NewRoll()] You must provide a valid rollUserID in the config object."); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If roll flag data is already on user, throw an error. const flagData = rollUser.getFlag("eunos-blades", "rollCollab"); if (flagData) { const { rollID } = flagData; @@ -1385,9 +1770,12 @@ class BladesRoll extends DocumentSheet { await rollUser.setFlag("eunos-blades", `rollCollabArchive.${rollID}`, flagData); await rollUser.unsetFlag("eunos-blades", "rollCollab"); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If no rollPrimaryData is provided, attempt to derive it from the rest of the config object let { rollPrimaryData } = config; if (!BladesRollPrimary.IsValidData(rollPrimaryData)) { let rollPrimarySourceData; + // If the user is a player with a BladesPC character, assume that is rollPrimary. if (BladesPC.IsType(rollUser.character)) { rollPrimarySourceData = rollUser.character; rollPrimaryData = { @@ -1403,9 +1791,14 @@ class BladesRoll extends DocumentSheet { if (!BladesRollPrimary.IsValidData(rollPrimaryData)) { throw new Error("[BladesRoll.NewRoll()] A valid source of PrimaryDocData must be provided to construct a roll."); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Create a random ID for storing the roll instance const rID = randomID(); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Derive user flag data depending on given roll type and subtype let userIDs; let flagUpdateData; + /*~ @@DOUBLE-BLANK@@ ~*/ switch (config.rollType) { case RollType.Action: { ({ userIDs, flagUpdateData } = await BladesRoll.PrepareActionRoll(rID, { @@ -1440,19 +1833,32 @@ class BladesRoll extends DocumentSheet { break; } } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Log the roll data eLog.checkLog3("bladesRoll", "BladesRoll.NewRoll()", { userIDs, flagUpdateData, rollPrimaryData: flagUpdateData.rollPrimaryData }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Store the roll data on the storage document await rollUser.setFlag(C.SYSTEM_ID, "rollCollab", flagUpdateData); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Send out socket calls to all users to see the roll. socketlib.system.executeForUsers("constructRollCollab", userIDs[RollPermissions.GM], { userID: rollUser.id, rollID: rID, rollPermission: RollPermissions.GM }); socketlib.system.executeForUsers("constructRollCollab", userIDs[RollPermissions.Primary], { userID: rollUser.id, rollID: rID, rollPermission: RollPermissions.Primary }); socketlib.system.executeForUsers("constructRollCollab", userIDs[RollPermissions.Observer], { userID: rollUser.id, rollID: rID, rollPermission: RollPermissions.Observer }); socketlib.system.executeForUsers("constructRollCollab", userIDs[RollPermissions.Participant], { userID: rollUser.id, rollID: rID, rollPermission: RollPermissions.Participant }); } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Constructor ~ rollID; + /*~ @@DOUBLE-BLANK@@ ~*/ rollPermission; + /*~ @@DOUBLE-BLANK@@ ~*/ _rollPrimary; + /*~ @@DOUBLE-BLANK@@ ~*/ _rollOpposition; + /*~ @@DOUBLE-BLANK@@ ~*/ _rollParticipants; + /*~ @@DOUBLE-BLANK@@ ~*/ constructor(userID, rollID, rollPermission) { const rollUser = game.users.get(userID); if (!rollUser) { @@ -1481,13 +1887,19 @@ class BladesRoll extends DocumentSheet { } BladesRoll.Current[this.rollID] = this; } + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ async setRollSubType(subType) { await this.setFlagVal("rollSubType", subType); } + /*~ @@DOUBLE-BLANK@@ ~*/ async addRollParticipant(participantRef, rollSection, rollSubSection) { + /*~ @@DOUBLE-BLANK@@ ~*/ if (!rollSubSection) { - rollSubSection = "Assist"; + /* Insert logic to determine from rollSection and number of existing Group_X members */ + rollSubSection = "Assist"; } + /*~ @@DOUBLE-BLANK@@ ~*/ const participantData = typeof participantRef === "string" ? game.actors.get(participantRef) ?? game.actors.getName(participantRef) @@ -1502,24 +1914,31 @@ class BladesRoll extends DocumentSheet { rollParticipantSubSection: rollSubSection, rollParticipantID: participantData.rollParticipantID }); + /*~ @@DOUBLE-BLANK@@ ~*/ await rollParticipant.updateRollFlags(); socketlib.system.executeForEveryone("renderRollCollab", this.rollID); } + /*~ @@DOUBLE-BLANK@@ ~*/ async removeRollParticipant(rollSection, rollSubSection) { await this.clearFlagVal(`rollParticipantData.${rollSection}.${rollSubSection}`); } + /*~ @@DOUBLE-BLANK@@ ~*/ async updateUserPermission(_user, _permission) { - } - + /* Force-render roll with new permissions */ + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Basic User Flag Getters/Setters ~ get flagData() { if (!this.document.getFlag(C.SYSTEM_ID, "rollCollab")) { throw new Error("[get flags()] No RollCollab Flags Found on User Document"); } return this.document.getFlag(C.SYSTEM_ID, "rollCollab"); } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimary() { return this._rollPrimary; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimaryDoc() { if (BladesRollPrimary.IsDoc(this.rollPrimary.rollPrimaryDoc)) { return this.rollPrimary.rollPrimaryDoc; @@ -1529,12 +1948,14 @@ class BladesRoll extends DocumentSheet { } return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOpposition() { if (!this._rollOpposition && BladesRollOpposition.IsValidData(this.flagData.rollOppData)) { this._rollOpposition = new BladesRollOpposition(this, this.flagData.rollOppData); } return this._rollOpposition?.refresh(); } + /*~ @@DOUBLE-BLANK@@ ~*/ set rollOpposition(val) { if (val === undefined) { this._rollOpposition = undefined; @@ -1544,12 +1965,21 @@ class BladesRoll extends DocumentSheet { val.updateRollFlags(); } } - prepareRollParticipantData() { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * This method prepares the roll participant data. + * It iterates over the roll sections (roll, position, effect) and for each section, + * it creates a new BladesRollParticipant instance for each participant in that section. + * The created instances are stored in the rollParticipants object. + */ + prepareRollParticipantData() { const participantFlagData = this.flagData.rollParticipantData; if (!participantFlagData) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ const rollParticipants = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ [ RollModSection.roll, RollModSection.position, @@ -1571,11 +2001,14 @@ class BladesRoll extends DocumentSheet { rollParticipants[rollSection] = sectionParticipants; } }); + /*~ @@DOUBLE-BLANK@@ ~*/ this._rollParticipants = rollParticipants; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipants() { return this._rollParticipants; } + /*~ @@DOUBLE-BLANK@@ ~*/ getRollParticipant(section, subSection) { if (isParticipantSection(section) && isParticipantSubSection(subSection)) { const sectionData = this.rollParticipants?.[section]; @@ -1585,6 +2018,7 @@ class BladesRoll extends DocumentSheet { } return null; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantSelectOptions() { const nonPrimaryPCs = BladesPC.All .filter((actor) => actor.hasTag(Tag.PC.ActivePC) && actor.id !== this.rollPrimary.rollPrimaryID) @@ -1595,13 +2029,21 @@ class BladesRoll extends DocumentSheet { Group: nonPrimaryPCs }; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollType() { return this.flagData.rollType; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollSubType() { return this.flagData.rollSubType; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollDowntimeAction() { return this.flagData.rollDowntimeAction; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollTrait() { return this.flagData.rollTrait; } + /*~ @@DOUBLE-BLANK@@ ~*/ _rollTraitValOverride; + /*~ @@DOUBLE-BLANK@@ ~*/ get rollTraitValOverride() { return this._rollTraitValOverride; } + /*~ @@DOUBLE-BLANK@@ ~*/ set rollTraitValOverride(val) { this._rollTraitValOverride = val; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollTraitData() { if (BladesActor.IsType(this.rollPrimaryDoc, BladesActorType.pc)) { if (isAction(this.rollTrait)) { @@ -1639,6 +2081,7 @@ class BladesRoll extends DocumentSheet { } throw new Error(`[get rollTraitData] Invalid rollTrait: '${this.rollTrait}'`); } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollTraitOptions() { if (BladesActor.IsType(this.rollPrimaryDoc, BladesActorType.pc)) { if (isAction(this.rollTrait)) { @@ -1668,39 +2111,48 @@ class BladesRoll extends DocumentSheet { } throw new Error(`[get rollTraitOptions] Invalid rollTrait: '${this.rollTrait}'`); } + /*~ @@DOUBLE-BLANK@@ ~*/ get posEffectTrade() { return this.flagData?.rollPosEffectTrade ?? false; } + /*~ @@DOUBLE-BLANK@@ ~*/ getFlagVal(flagKey) { if (flagKey) { return this.document.getFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`); } return this.document.getFlag(C.SYSTEM_ID, "rollCollab"); } + /*~ @@DOUBLE-BLANK@@ ~*/ async setFlagVal(flagKey, flagVal, isRerendering = true) { await this.document.setFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`, flagVal); if (isRerendering) { socketlib.system.executeForEveryone("renderRollCollab", this.rollID); } } + /*~ @@DOUBLE-BLANK@@ ~*/ async clearFlagVal(flagKey, isRerendering = true) { await this.document.unsetFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`); if (isRerendering) { socketlib.system.executeForEveryone("renderRollCollab", this.rollID); } } + /*~ @@DOUBLE-BLANK@@ ~*/ get initialPosition() { return this.getFlagVal("rollPositionInitial") ?? Position.risky; } + /*~ @@DOUBLE-BLANK@@ ~*/ set initialPosition(val) { this.setFlagVal("rollPositionInitial", val ?? Position.risky); } + /*~ @@DOUBLE-BLANK@@ ~*/ get initialEffect() { return this.getFlagVal("rollEffectInitial") ?? Effect.standard; } + /*~ @@DOUBLE-BLANK@@ ~*/ set initialEffect(val) { this.setFlagVal("rollEffectInitial", val); } + /*~ @@DOUBLE-BLANK@@ ~*/ get isApplyingConsequences() { if (this.rollType !== RollType.Action) { return false; @@ -1713,12 +2165,19 @@ class BladesRoll extends DocumentSheet { } return true; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Get rollConsequence() --> For resistance rolls. get rollConsequence() { return this.getFlagVal("resistanceData.consequence"); } + /*~ @@DOUBLE-BLANK@@ ~*/ async applyConsequencesFromDialog(_html) { - } - + /* Convert values of dialog input fields to flag data */ + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region GETTERS: DERIVED DATA ~ get finalPosition() { return Object.values(Position)[U.clampNum(Object.values(Position) .indexOf(this.initialPosition) @@ -1726,6 +2185,7 @@ class BladesRoll extends DocumentSheet { + (this.posEffectTrade === "position" ? 1 : 0) + (this.posEffectTrade === "effect" ? -1 : 0), [0, 2])]; } + /*~ @@DOUBLE-BLANK@@ ~*/ get finalEffect() { return Object.values(Effect)[U.clampNum(Object.values(Effect) .indexOf(this.initialEffect) @@ -1733,29 +2193,36 @@ class BladesRoll extends DocumentSheet { + (this.posEffectTrade === "effect" ? 1 : 0) + (this.posEffectTrade === "position" ? -1 : 0), [0, 4])]; } + /*~ @@DOUBLE-BLANK@@ ~*/ get finalResult() { return this.getModsDelta(RollModSection.result) + (this.flagData?.GMBoosts.Result ?? 0) + (this.tempGMBoosts.Result ?? 0); } + /*~ @@DOUBLE-BLANK@@ ~*/ get finalDicePool() { return Math.max(0, this.rollTraitData.value + this.getModsDelta(RollModSection.roll) + (this.flagData.GMBoosts.Dice ?? 0) + (this.tempGMBoosts.Dice ?? 0)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get isRollingZero() { return Math.max(0, this.rollTraitData.value + this.getModsDelta(RollModSection.roll) + (this.flagData.GMBoosts.Dice ?? 0) + (this.tempGMBoosts.Dice ?? 0)) <= 0; } + /*~ @@DOUBLE-BLANK@@ ~*/ _roll; + /*~ @@DOUBLE-BLANK@@ ~*/ get roll() { this._roll ??= new Roll(`${this.isRollingZero ? 2 : this.finalDicePool}d6`, {}); return this._roll; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollFactors() { + /*~ @@DOUBLE-BLANK@@ ~*/ const defaultFactors = { [Factor.tier]: { name: "Tier", @@ -1806,10 +2273,13 @@ class BladesRoll extends DocumentSheet { cssClasses: "factor-gold" } }; + /*~ @@DOUBLE-BLANK@@ ~*/ const mergedSourceFactors = U.objMerge(U.objMerge(defaultFactors, this.rollPrimary.rollFactors, { isMutatingOk: false }), this.flagData.rollFactorToggles.source, { isMutatingOk: false }); + /*~ @@DOUBLE-BLANK@@ ~*/ const mergedOppFactors = this.rollOpposition ? U.objMerge(U.objMerge(defaultFactors, this.rollOpposition.rollFactors, { isMutatingOk: false }), this.flagData.rollFactorToggles.opposition, { isMutatingOk: false }) : {}; + /*~ @@DOUBLE-BLANK@@ ~*/ return { source: Object.fromEntries(Object.entries(mergedSourceFactors) .map(([factor, factorData]) => { @@ -1837,22 +2307,39 @@ class BladesRoll extends DocumentSheet { })) }; } + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region ROLL MODS: Getters & Update Method ~ + /*~ @@DOUBLE-BLANK@@ ~*/ initRollMods(modsData) { + // Reset override values previously enabled by rollmods this.rollTraitValOverride = undefined; this.rollFactorPenaltiesNegated = {}; this.tempGMBoosts = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ this.rollMods = modsData.map((modData) => new BladesRollMod(modData, this)); + /*~ @@DOUBLE-BLANK@@ ~*/ const initReport = {}; - this.rollMods = this.rollMods.filter((rollMod) => rollMod.isValidForRollType()); - this.rollMods + /*~ @@DOUBLE-BLANK@@ ~*/ + /* *** PASS ZERO: ROLLTYPE VALIDATION PASS *** */ + this.rollMods = this.rollMods.filter((rollMod) => rollMod.isValidForRollType()); + /*~ @@DOUBLE-BLANK@@ ~*/ + /* *** PASS ONE: DISABLE PASS *** */ + this.rollMods + // ... Conditional Status Pass .filter((rollMod) => !rollMod.setConditionalStatus()) + // ... AutoReveal/AutoEnable Pass .filter((rollMod) => !rollMod.setAutoStatus()) + // ... Payable Pass .forEach((rollMod) => { rollMod.setPayableStatus(); }); - const parseForceOnKeys = (mod) => { + /*~ @@DOUBLE-BLANK@@ ~*/ + /* *** PASS TWO: FORCE-ON PASS *** */ + const parseForceOnKeys = (mod) => { const holdKeys = mod.effectKeys.filter((key) => key.startsWith("ForceOn")); if (holdKeys.length === 0) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ while (holdKeys.length) { const thisTarget = holdKeys.pop()?.split(/-/)?.pop(); if (thisTarget === "BestAction") { @@ -1889,14 +2376,21 @@ class BladesRoll extends DocumentSheet { } }; this.getActiveRollMods().forEach((rollMod) => parseForceOnKeys(rollMod)); - + /*~ @@DOUBLE-BLANK@@ ~*/ + /* *** PASS THREE: PUSH-CHECK PASS *** */ + /*~ @@DOUBLE-BLANK@@ ~*/ + // IF ROLL FORCED ... if (this.isForcePushed()) { + // ... Force Off _ALL_ visible, inactive "Is-Push" mods. this.getInactivePushMods() .filter((mod) => !mod.isBasicPush) .forEach((mod) => { mod.heldStatus = RollModStatus.ForcedOff; }); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // ... BY CATEGORY ... [RollModSection.roll, RollModSection.effect].forEach((cat) => { if (this.isPushed(cat)) { + // ... if pushed by positive mod, Force Off any visible Bargain if (cat === RollModSection.roll && this.isPushed(cat, "positive")) { const bargainMod = this.getRollModByID("Bargain-positive-roll"); if (bargainMod?.isVisible) { @@ -1905,23 +2399,31 @@ class BladesRoll extends DocumentSheet { } } else { + // Otherwise, hide all Is-Push mods this.getInactivePushMods(cat) .filter((mod) => !mod.isBasicPush) .forEach((mod) => { mod.heldStatus = RollModStatus.Hidden; }); } }); - + /*~ @@DOUBLE-BLANK@@ ~*/ + /* *** PASS FOUR: Relevancy Pass *** */ + /*~ @@DOUBLE-BLANK@@ ~*/ this.getVisibleRollMods() .forEach((mod) => { mod.setRelevancyStatus(); }); - + /*~ @@DOUBLE-BLANK@@ ~*/ + /* *** PASS FIVE: Overpayment Pass *** */ + /*~ @@DOUBLE-BLANK@@ ~*/ + // ... If 'Cost-SpecialArmor' active, ForceOff other visible Cost-SpecialArmor mods const activeArmorCostMod = this.getActiveRollMods().find((mod) => mod.effectKeys.includes("Cost-SpecialArmor")); if (activeArmorCostMod) { this.getVisibleRollMods() .filter((mod) => !mod.isActive && mod.effectKeys.includes("Cost-SpecialArmor")) .forEach((mod) => { mod.heldStatus = RollModStatus.ForcedOff; }); } + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog2("rollMods", "*** initRollMods() PASS ***", initReport); } + /*~ @@DOUBLE-BLANK@@ ~*/ isTraitRelevant(trait) { if (trait in Factor) { const { source, opposition } = this.rollFactors; @@ -1929,18 +2431,26 @@ class BladesRoll extends DocumentSheet { } return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ get isParticipantRoll() { return (this.rollType === RollType.Fortune && !game.user.isGM) || (this.rollSubType === RollSubType.GroupParticipant); } + /*~ @@DOUBLE-BLANK@@ ~*/ rollFactorPenaltiesNegated = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ negateFactorPenalty(factor) { this.rollFactorPenaltiesNegated[factor] = true; } + /*~ @@DOUBLE-BLANK@@ ~*/ tempGMBoosts = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ isPushed(cat, posNeg) { return this.getActiveBasicPushMods(cat, posNeg).length > 0; } + /*~ @@DOUBLE-BLANK@@ ~*/ hasOpenPush(cat, posNeg) { return this.isPushed(cat) && this.getOpenPushMods(cat, posNeg).length > 0; } + /*~ @@DOUBLE-BLANK@@ ~*/ isForcePushed(cat, posNeg) { return this.isPushed(cat) && this.getForcedPushMods(cat, posNeg).length > 0; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollCosts() { if (!this.isPushed) { return 0; @@ -1955,11 +2465,13 @@ class BladesRoll extends DocumentSheet { + ((effectPush?.isActive && effectPush?.stressCost) || 0) - (negatePushCostMods.length * 2); } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollCostData() { return this.getActiveRollMods() .map((rollMod) => rollMod.costs ?? []) .flat(); } + /*~ @@DOUBLE-BLANK@@ ~*/ getRollModByName(name, cat, posNeg) { const modMatches = this.rollMods.filter((rollMod) => { if (U.lCase(rollMod.name) !== U.lCase(name)) { @@ -1981,99 +2493,149 @@ class BladesRoll extends DocumentSheet { } return modMatches[0]; } + /*~ @@DOUBLE-BLANK@@ ~*/ getRollModByID(id) { return this.rollMods.find((rollMod) => rollMod.id === id); } + /*~ @@DOUBLE-BLANK@@ ~*/ getRollMods(cat, posNeg) { return this.rollMods.filter((rollMod) => (!cat || rollMod.section === cat) && (!posNeg || rollMod.posNeg === posNeg)); } + /*~ @@DOUBLE-BLANK@@ ~*/ getVisibleRollMods(cat, posNeg) { return this.getRollMods(cat, posNeg).filter((rollMod) => rollMod.isVisible); } + /*~ @@DOUBLE-BLANK@@ ~*/ getActiveRollMods(cat, posNeg) { return this.getRollMods(cat, posNeg).filter((rollMod) => rollMod.isActive); } + /*~ @@DOUBLE-BLANK@@ ~*/ getVisibleInactiveRollMods(cat, posNeg) { return this.getVisibleRollMods(cat, posNeg).filter((rollMod) => !rollMod.isActive); } + /*~ @@DOUBLE-BLANK@@ ~*/ getPushMods(cat, posNeg) { return this.getRollMods(cat, posNeg).filter((rollMod) => rollMod.isPush); } + /*~ @@DOUBLE-BLANK@@ ~*/ getVisiblePushMods(cat, posNeg) { return this.getPushMods(cat, posNeg).filter((rollMod) => rollMod.isVisible); } + /*~ @@DOUBLE-BLANK@@ ~*/ getActivePushMods(cat, posNeg) { return this.getVisiblePushMods(cat, posNeg).filter((rollMod) => rollMod.isActive); } + /*~ @@DOUBLE-BLANK@@ ~*/ getActiveBasicPushMods(cat, posNeg) { return this.getActivePushMods(cat, posNeg).filter((rollMod) => rollMod.isBasicPush); } + /*~ @@DOUBLE-BLANK@@ ~*/ getInactivePushMods(cat, posNeg) { return this.getVisiblePushMods(cat, posNeg).filter((rollMod) => !rollMod.isActive); } + /*~ @@DOUBLE-BLANK@@ ~*/ getInactiveBasicPushMods(cat, posNeg) { return this.getInactivePushMods(cat, posNeg).filter((rollMod) => rollMod.isBasicPush); } + /*~ @@DOUBLE-BLANK@@ ~*/ getForcedPushMods(cat, posNeg) { return this.getActivePushMods(cat, posNeg) .filter((rollMod) => rollMod.isBasicPush && rollMod.status === RollModStatus.ForcedOn); } + /*~ @@DOUBLE-BLANK@@ ~*/ getOpenPushMods(cat, posNeg) { return this.getActivePushMods(cat, posNeg) .filter((rollMod) => rollMod.isBasicPush && rollMod.status === RollModStatus.ToggledOn); } + /*~ @@DOUBLE-BLANK@@ ~*/ getModsDelta = (cat) => { return U.sum([ ...this.getActiveRollMods(cat, "positive").map((mod) => mod.value), ...this.getActiveRollMods(cat, "negative").map((mod) => -mod.value) ]); }; + /*~ @@DOUBLE-BLANK@@ ~*/ _rollMods; - compareMods(modA, modB) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Compare function for sorting roll mods. + * @param {BladesRollMod} modA First mod to compare. + * @param {BladesRollMod} modB Second mod to compare. + * @returns {number} - Comparison result. + */ + compareMods(modA, modB) { + // Define the order of mod names for sorting const modOrder = ["Bargain", "Assist", "Setup"]; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Check for basic push if (modA.isBasicPush) { return -1; } if (modB.isBasicPush) { return 1; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Check for active Bargain if (modA.name === "Bargain" && modA.isActive) { return -1; } if (modB.name === "Bargain" && modB.isActive) { return 1; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Check for push if (modA.isPush) { return -1; } if (modB.isPush) { return 1; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Check for mod name order const modAIndex = modOrder.indexOf(modA.name); const modBIndex = modOrder.indexOf(modB.name); if (modAIndex !== -1 && modBIndex !== -1) { return modAIndex - modBIndex; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Default to alphabetical order return modA.name.localeCompare(modB.name); } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollMods() { if (!this._rollMods) { throw new Error("[get rollMods] No roll mods found!"); } return [...this._rollMods].sort((modA, modB) => this.compareMods(modA, modB)); } + /*~ @@DOUBLE-BLANK@@ ~*/ set rollMods(val) { this._rollMods = val; } - - - async getData() { + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region *** GETDATA *** ~ + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Retrieve the data for rendering the base RollCollab sheet. + * @returns {Promise} The data which can be used to render the HTML of the sheet. + */ + async getData() { const context = super.getData(); + /*~ @@DOUBLE-BLANK@@ ~*/ this.initRollMods(this.getRollModsData()); this.rollMods.forEach((rollMod) => rollMod.applyRollModEffectKeys()); + /*~ @@DOUBLE-BLANK@@ ~*/ const sheetData = this.getSheetData(this.getIsGM(), this.getRollCosts()); + /*~ @@DOUBLE-BLANK@@ ~*/ return { ...context, ...sheetData }; } - getRollModsData() { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Gets the roll modifications data. + * @returns {BladesRoll.RollModData[]} The roll modifications data. + */ + getRollModsData() { const defaultMods = [ ...BladesRoll.DefaultRollMods, ...this.rollPrimary.rollModsData @@ -2086,25 +2648,60 @@ class BladesRoll extends DocumentSheet { } return defaultMods; } - getIsGM() { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Determines if the user is a game master. + * @returns {boolean} Whether the user is a GM. + */ + getIsGM() { return game.eunoblades.Tracker?.system.is_spoofing_player ? false : game.user.isGM; } - getRollCosts() { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Gets the roll costs. + * @returns {BladesRoll.CostData[]} The roll costs. + */ + getRollCosts() { return this.getActiveRollMods() .map((rollMod) => rollMod.costs) .flat() .filter((costData) => costData !== undefined); } - getStressCosts(rollCosts) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Filters the roll costs to retrieve stress costs. + * @param {BladesRoll.CostData[]} rollCosts The roll costs. + * @returns {BladesRoll.CostData[]} The stress costs. + */ + getStressCosts(rollCosts) { return rollCosts.filter((costData) => costData.costType === "Stress"); } - getTotalStressCost(stressCosts) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Calculates the total stress cost. + * @param {BladesRoll.CostData[]} stressCosts The stress costs. + * @returns {number} The total stress cost. + */ + getTotalStressCost(stressCosts) { return U.sum(stressCosts.map((costData) => costData.costAmount)); } - getSpecArmorCost(rollCosts) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Searches for any special armor roll costs. + * @param {BladesRoll.CostData[]} rollCosts The roll costs. + * @returns {BladesRoll.CostData[]} The stress costs. + */ + getSpecArmorCost(rollCosts) { return rollCosts.find((costData) => costData.costType === "SpecialArmor"); } - getSheetData(isGM, rollCosts) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Constructs the sheet data. + * @param {boolean} isGM If the user is a GM. + * @param {BladesRoll.CostData[]} rollCosts The roll costs. + * @returns {BladesRoll.SheetData} The constructed sheet data. + */ + getSheetData(isGM, rollCosts) { const { flagData: rData, rollPrimary, rollTraitData, rollTraitOptions, finalDicePool, finalPosition, finalEffect, finalResult, rollMods, rollFactors } = this; if (!rollPrimary) { throw new Error("A primary roll source is required for BladesRoll."); @@ -2115,32 +2712,42 @@ class BladesRoll extends DocumentSheet { editable: this.options.editable, isGM, system: this.rollPrimaryDoc?.system, + /*~ @@DOUBLE-BLANK@@ ~*/ rollMods, rollPrimary, rollTraitData, rollTraitOptions, + /*~ @@DOUBLE-BLANK@@ ~*/ diceTotal: finalDicePool, + /*~ @@DOUBLE-BLANK@@ ~*/ rollOpposition: this.rollOpposition, rollParticipants: this.rollParticipants, rollParticipantOptions: this.rollParticipantSelectOptions, rollEffects: Object.values(Effect), rollTraitValOverride: this.rollTraitValOverride, rollFactorPenaltiesNegated: this.rollFactorPenaltiesNegated, + /*~ @@DOUBLE-BLANK@@ ~*/ posRollMods: Object.fromEntries(Object.values(RollModSection) .map((cat) => [cat, this.getRollMods(cat, "positive")])), negRollMods: Object.fromEntries(Object.values(RollModSection) .map((cat) => [cat, this.getRollMods(cat, "negative")])), hasInactiveConditionals: this.calculateHasInactiveConditionalsData(), + /*~ @@DOUBLE-BLANK@@ ~*/ rollFactors, ...this.calculateOddsHTML(finalDicePool, finalResult), costData: this.getCostsHTML(rollCosts) }; + /*~ @@DOUBLE-BLANK@@ ~*/ const rollPositionData = this.calculatePositionData(finalPosition); const rollEffectData = this.calculateEffectData(isGM, finalEffect); const rollResultData = this.calculateResultData(isGM, finalResult); + /*~ @@DOUBLE-BLANK@@ ~*/ const GMBoostsData = this.calculateGMBoostsData(rData); + /*~ @@DOUBLE-BLANK@@ ~*/ const positionEffectTradeData = this.calculatePositionEffectTradeData(); + /*~ @@DOUBLE-BLANK@@ ~*/ const userPermission = U.objFindKey(baseData.userPermissions, (v) => v.includes(game.user.id ?? "")); + /*~ @@DOUBLE-BLANK@@ ~*/ return { ...baseData, ...(this.rollPrimary.rollPrimaryDoc ? { rollPrimary: this.rollPrimary.rollPrimaryDoc } : {}), @@ -2152,12 +2759,14 @@ class BladesRoll extends DocumentSheet { userPermission }; } + /*~ @@DOUBLE-BLANK@@ ~*/ calculatePositionData(finalPosition) { return { rollPositions: Object.values(Position), rollPositionFinal: finalPosition }; } + /*~ @@DOUBLE-BLANK@@ ~*/ calculateEffectData(isGM, finalEffect) { return { rollEffects: Object.values(Effect), @@ -2166,6 +2775,7 @@ class BladesRoll extends DocumentSheet { || (isGM && this.getRollMods(RollModSection.after).length > 0) }; } + /*~ @@DOUBLE-BLANK@@ ~*/ calculateResultData(isGM, finalResult) { return { rollResultFinal: finalResult, @@ -2174,6 +2784,7 @@ class BladesRoll extends DocumentSheet { || (isGM && this.getRollMods(RollModSection.result).length > 0) }; } + /*~ @@DOUBLE-BLANK@@ ~*/ calculateGMBoostsData(flagData) { return { GMBoosts: { @@ -2192,13 +2803,21 @@ class BladesRoll extends DocumentSheet { } }; } + /*~ @@DOUBLE-BLANK@@ ~*/ calculateOddsHTML(diceTotal, finalResult) { if (this.rollType === RollType.Resistance) { return this.calculateOddsHTML_Resistance(diceTotal); } return this.calculateOddsHTML_Standard(diceTotal, finalResult); } - calculateOddsHTML_Standard(diceTotal, finalResult) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Calculate odds starting & ending HTML based on given dice total. + * @param {number} diceTotal Total number of dice. + * @param {number} finalResult + * @returns {{oddsHTMLStart: string, oddsHTMLStop: string}} Opening & Closing HTML for odds bar display + */ + calculateOddsHTML_Standard(diceTotal, finalResult) { const oddsColors = { crit: "var(--blades-gold)", success: "var(--blades-white-bright)", @@ -2206,6 +2825,7 @@ class BladesRoll extends DocumentSheet { fail: "var(--blades-black-dark)" }; const odds = { ...C.DiceOddsStandard[diceTotal] }; + /*~ @@DOUBLE-BLANK@@ ~*/ if (finalResult < 0) { for (let i = finalResult; i < 0; i++) { oddsColors.crit = oddsColors.success; @@ -2220,13 +2840,16 @@ class BladesRoll extends DocumentSheet { oddsColors.success = oddsColors.crit; } } + /*~ @@DOUBLE-BLANK@@ ~*/ const resultElements = []; + /*~ @@DOUBLE-BLANK@@ ~*/ Object.entries(odds).reverse().forEach(([result, chance]) => { if (chance === 0) { return; } resultElements.push(`
 
`); }); + /*~ @@DOUBLE-BLANK@@ ~*/ return { oddsHTMLStart: [ "
", @@ -2235,7 +2858,22 @@ class BladesRoll extends DocumentSheet { oddsHTMLStop: "
" }; } - calculateOddsHTML_Resistance(diceTotal) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Calculate odds starting & ending HTML based on given dice total. + * @param {number} diceTotal Total number of dice. + * @returns {{oddsHTMLStart: string, oddsHTMLStop: string}} Opening & Closing HTML for odds bar display + */ + calculateOddsHTML_Resistance(diceTotal) { + // Const oddsColors = [ + // "var(--blades-gold)", // -1 + // "var(--blades-white)", // 0 + // "var(--blades-red-bright)", // 1 + // "var(--blades-red-dark)", // 2 + // "var(--blades-red-bright)", // 3 + // "var(--blades-red-dark)", // 4 + // "var(--blades-red-bright)" // 5 + // ].reverse(); const oddsColors = [ "var(--blades-gold)", "var(--blades-white)", @@ -2243,7 +2881,7 @@ class BladesRoll extends DocumentSheet { "var(--blades-red)", "var(--blades-red)", "var(--blades-red)", - "var(--blades-red)" + "var(--blades-red)" // 5 ].reverse(); const oddsFilters = [ "none", @@ -2255,7 +2893,9 @@ class BladesRoll extends DocumentSheet { "none" ].reverse(); const odds = [...C.DiceOddsResistance[diceTotal]].reverse(); + /*~ @@DOUBLE-BLANK@@ ~*/ const resultElements = []; + /*~ @@DOUBLE-BLANK@@ ~*/ for (let index = 0; index < odds.length; index++) { const chance = odds[index]; if (chance > 0) { @@ -2266,6 +2906,7 @@ class BladesRoll extends DocumentSheet { ]); } } + /*~ @@DOUBLE-BLANK@@ ~*/ return { oddsHTMLStart: [ "
", @@ -2274,22 +2915,34 @@ class BladesRoll extends DocumentSheet { oddsHTMLStop: "
" }; } - calculatePositionEffectTradeData() { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Calculate data for position and effect trade. + * @returns {{canTradePosition: boolean, canTradeEffect: boolean}} + */ + calculatePositionEffectTradeData() { const canTradePosition = this.posEffectTrade === "position" || (this.posEffectTrade === false && this.finalPosition !== Position.desperate && this.finalEffect !== Effect.extreme); const canTradeEffect = this.posEffectTrade === "effect" || (this.posEffectTrade === false && this.finalPosition !== Position.controlled && this.finalEffect !== Effect.zero); + /*~ @@DOUBLE-BLANK@@ ~*/ return { canTradePosition, canTradeEffect }; } - calculateHasInactiveConditionalsData() { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Calculate data on whether there are any inactive conditionals. + * @returns {Record} - Data on inactive conditionals. + */ + calculateHasInactiveConditionalsData() { const hasInactive = {}; for (const section of Object.values(RollModSection)) { hasInactive[section] = this.getRollMods(section).filter((mod) => mod.isInInactiveBlock).length > 0; } return hasInactive; } + /*~ @@DOUBLE-BLANK@@ ~*/ getCostsHTML(rollCosts) { switch (this.rollType) { case RollType.Action: { @@ -2307,7 +2960,14 @@ class BladesRoll extends DocumentSheet { default: return false; } } - parseActionRollCostsHTML(stressCosts, specArmorCost) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Calculate data for costs of Action Roll. + * @param {BladesRoll.CostData[]} stressCosts Array of stress costs. + * @param {BladesRoll.CostData} [specArmorCost] Specific armor cost. + * @returns {{footerLabel: string, tooltip: string} | undefined} - Costs data or undefined. + */ + parseActionRollCostsHTML(stressCosts, specArmorCost) { if (specArmorCost || stressCosts.length > 0) { const totalStressCost = this.getTotalStressCost(stressCosts); return { @@ -2328,6 +2988,7 @@ class BladesRoll extends DocumentSheet { } return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ parseResistanceRollCostsHTML(stressCosts, specArmorCost) { const footerLabelStrings = [ "( Resisting Costs" @@ -2348,14 +3009,21 @@ class BladesRoll extends DocumentSheet { ].filter((line) => Boolean(line)).join("") }; } + /*~ @@DOUBLE-BLANK@@ ~*/ parseFortuneRollCostsHTML() { return { footerLabel: "Fortune Cost Label", tooltip: "Fortune Cost Tooltip" }; } + /*~ @@DOUBLE-BLANK@@ ~*/ parseIndulgeViceRollCostsHTML() { return { footerLabel: "Indulge Vice Cost Label", tooltip: "Indulge Vice Cost Tooltip" }; } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region *** EVALUATING & DISPLAYING ROLL TO CHAT *** ~ + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region DICE ~ _dieVals; + /*~ @@DOUBLE-BLANK@@ ~*/ get dieVals() { this._dieVals ??= this.roll.terms[0].results .map((result) => result.result) @@ -2363,6 +3031,7 @@ class BladesRoll extends DocumentSheet { .reverse(); return this._dieVals; } + /*~ @@DOUBLE-BLANK@@ ~*/ getDieClass(val, i) { eLog.checkLog3("rollCollab", `getDieClass(${val}, ${i})`, { inst: this }); if (val === 6 && i <= 1 && this.rollResult === RollResult.critical) { @@ -2379,10 +3048,12 @@ class BladesRoll extends DocumentSheet { "blades-die-critical" ][val]; } + /*~ @@DOUBLE-BLANK@@ ~*/ get dieValsHTML() { eLog.checkLog3("rollCollab", "[get dieValsHTML()]", { roll: this, dieVals: this.dieVals }); const dieVals = [...this.dieVals]; const ghostNum = this.isRollingZero ? dieVals.shift() : null; + /*~ @@DOUBLE-BLANK@@ ~*/ if (this.rollType === RollType.Resistance) { return [ ...dieVals.map((val, i) => ``), @@ -2400,76 +3071,50 @@ class BladesRoll extends DocumentSheet { .join(""); } } + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ get rollResult() { + /*~ @@DOUBLE-BLANK@@ ~*/ if ([RollPhase.Collaboration, RollPhase.AwaitingRoll].includes(this.rollPhase)) { return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If rollingZero, remove highest die. const dieVals = this.isRollingZero ? [[...this.dieVals].pop()] : this.dieVals; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Is this a critical success? if (dieVals.filter((val) => val === 6).length >= 2) { return RollResult.critical; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // A full success? if (dieVals.find((val) => val === 6)) { return RollResult.success; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // A partial? if (dieVals.find((val) => val && val >= 4)) { return RollResult.partial; } + /*~ @@DOUBLE-BLANK@@ ~*/ return RollResult.fail; + /*~ @@DOUBLE-BLANK@@ ~*/ } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPhase() { return this.getFlagVal("rollPhase") ?? RollPhase.Collaboration; } + /*~ @@DOUBLE-BLANK@@ ~*/ set rollPhase(phase) { this.setFlagVal("rollPhase", phase); } + /*~ @@DOUBLE-BLANK@@ ~*/ async outputRollToChat() { - const speaker = ChatMessage.getSpeaker(); - let renderedHTML = ""; - this.rollPhase = RollPhase.AwaitingChatInput; - switch (this.rollType) { - case RollType.Action: { - renderedHTML = - await renderTemplate("systems/eunos-blades/templates/chat/action-roll.hbs", { - sourceName: this.rollPrimary.rollPrimaryName, - oppName: this.rollOpposition?.rollOppName, - type: U.lCase(this.rollType), - subType: U.lCase(this.rollSubType), - downtimeAction: U.lCase(this.rollDowntimeAction), - position: this.finalPosition, - effect: this.finalEffect, - result: this.rollResult, - trait_label: typeof this.rollTrait === "number" ? `${this.rollTrait} Dice` : U.tCase(this.rollTrait), - dieVals: this.dieValsHTML - }); - break; - } - case RollType.Resistance: { - renderedHTML = await renderTemplate("systems/eunos-blades/templates/chat/resistance-roll.hbs", { - dieVals: this.dieValsHTML, - result: this.rollResult, - trait_label: typeof this.rollTrait === "number" ? `${this.rollTrait} Dice` : U.tCase(this.rollTrait), - stress: this.resistanceStressCost - }); - break; - } - case RollType.Fortune: { - break; - } - case RollType.IndulgeVice: { - break; - } - default: throw new Error(`Unrecognized RollType: ${this.rollType}`); - } - const messageData = { - speaker, - content: renderedHTML, - type: CONST.CHAT_MESSAGE_TYPES.ROLL, - roll: this.roll - }; - CONFIG.ChatMessage.documentClass.create(messageData, {}); + BladesChat.ConstructRollOutput(this); } + /*~ @@DOUBLE-BLANK@@ ~*/ async resolveRoll() { await this.roll.evaluate({ async: true }); if (this.isApplyingConsequences) { @@ -2479,6 +3124,10 @@ class BladesRoll extends DocumentSheet { await this.outputRollToChat(); this.close(); } + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region LISTENER FUNCTIONS ~ + /*~ @@DOUBLE-BLANK@@ ~*/ _toggleRollModClick(event) { event.preventDefault(); const elem$ = $(event.currentTarget); @@ -2487,6 +3136,7 @@ class BladesRoll extends DocumentSheet { if (!rollMod) { throw new Error(`Unable to find roll mod with id '${id}'`); } + /*~ @@DOUBLE-BLANK@@ ~*/ switch (rollMod.status) { case RollModStatus.Hidden: rollMod.userStatus = RollModStatus.ForcedOff; @@ -2508,7 +3158,12 @@ class BladesRoll extends DocumentSheet { default: throw new Error(`Unrecognized RollModStatus: ${rollMod.status}`); } } - _gmControlSet(event) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Handles setting of rollMod status via GM pop-out controls + * @param {ClickEvent} event JQuery click event sent to listener. + */ + _gmControlSet(event) { event.preventDefault(); if (!game.user.isGM) { return; @@ -2520,11 +3175,17 @@ class BladesRoll extends DocumentSheet { return; } const rollMod = this.getRollModByID(id); + /*~ @@DOUBLE-BLANK@@ ~*/ if (rollMod) { rollMod.userStatus = status === "Reset" ? undefined : status; } } - async _gmControlSetTargetToValue(event) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Handles setting values via GM number line (e.g. roll factor boosts/modifications). + * @param {ClickEvent} event JQuery click event sent to listener. + */ + async _gmControlSetTargetToValue(event) { event.preventDefault(); if (!game.user.isGM) { return; @@ -2532,18 +3193,55 @@ class BladesRoll extends DocumentSheet { const elem$ = $(event.currentTarget); const target = elem$.data("target").replace(/flags\.eunos-blades\./, ""); const value = elem$.data("value"); + /*~ @@DOUBLE-BLANK@@ ~*/ await this.document.setFlag(C.SYSTEM_ID, target, value).then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID)); } - async _gmControlResetTarget(event) { + /*~ @@DOUBLE-BLANK@@ ~*/ + async _gmControlCycleTarget(event) { + event.preventDefault(); + if (!game.user.isGM) { + return; + } + const elem$ = $(event.currentTarget); + const flagTarget = elem$.data("flagTarget"); + const curVal = elem$.data("curVal"); + const cycleVals = elem$.data("vals")?.split(/\|/); + if (!cycleVals) { + throw new Error(`Unable to parse cycle values from data-vals = ${elem$.data("vals")}`); + } + const curValIndex = cycleVals.indexOf(curVal); + if (curValIndex === -1) { + throw new Error(`Unable to find current value '${curVal}' in cycle values '${elem$.data("vals")}'`); + } + let newValIndex = curValIndex + 1; + if (newValIndex >= cycleVals.length) { + newValIndex = 0; + } + const newVal = cycleVals[newValIndex]; + eLog.checkLog3("gmControlCycleTarget", "gmControlCycleTarget", { flagTarget, curVal, cycleVals, curValIndex, newValIndex, newVal }); + await this.setFlagVal(flagTarget, newVal); + } + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Handles resetting value associated with GM number line on a right-click. + * @param {ClickEvent} event JQuery context menu event sent to listener. + */ + async _gmControlResetTarget(event) { event.preventDefault(); if (!game.user.isGM) { return; } const elem$ = $(event.currentTarget); const target = elem$.data("target").replace(/flags\.eunos-blades\./, ""); + /*~ @@DOUBLE-BLANK@@ ~*/ await this.document.unsetFlag(C.SYSTEM_ID, target).then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID)); } - _gmControlSetPosition(event) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Handles setting of baseline rollPosition via GM button line + * @param {ClickEvent} event JQuery click event sent to listener. + */ + _gmControlSetPosition(event) { event.preventDefault(); if (!game.user.isGM) { return; @@ -2552,7 +3250,12 @@ class BladesRoll extends DocumentSheet { const position = elem$.data("status"); this.initialPosition = position; } - _gmControlSetEffect(event) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Handles setting of baseline rollPosition via GM button line + * @param {ClickEvent} event JQuery click event sent to listener. + */ + _gmControlSetEffect(event) { event.preventDefault(); if (!game.user.isGM) { return; @@ -2561,7 +3264,12 @@ class BladesRoll extends DocumentSheet { const effect = elem$.data("status"); this.initialEffect = effect; } - async _gmControlToggleFactor(event) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Handles setting of Factor toggles: isActive, isPrimary, highFavorsPC, isDominant + * @param {ClickEvent} event JQuery click event sent to listener. + */ + async _gmControlToggleFactor(event) { event.preventDefault(); if (!game.user.isGM) { return; @@ -2569,20 +3277,31 @@ class BladesRoll extends DocumentSheet { const elem$ = $(event.currentTarget); const target = elem$.data("target"); const value = !elem$.data("value"); + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog3("toggleFactor", "_gmControlToggleFactor", { event, target, value }); + /*~ @@DOUBLE-BLANK@@ ~*/ const factorToggleData = this.document.getFlag(C.SYSTEM_ID, "rollCollab.rollFactorToggles"); + /*~ @@DOUBLE-BLANK@@ ~*/ const [thisSource, thisFactor, thisToggle] = target.split(/\./).slice(-3); + /*~ @@DOUBLE-BLANK@@ ~*/ + // If thisToggle is unrecognized, just toggle whatever value target points at if (!["isActive", "isPrimary", "isDominant", "highFavorsPC"].includes(thisToggle)) { await this.document.setFlag(C.SYSTEM_ID, `rollCollab.${target}`, value) .then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID)); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Otherwise, first toggle targeted factor to new value factorToggleData[thisSource][thisFactor] = { ...factorToggleData[thisSource][thisFactor] ?? { display: "" }, [thisToggle]: value }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Then perform specific logic depending on toggle targeted: switch (thisToggle) { case "isDominant": case "isPrimary": { + // Only one factor per sourceType can be declared Primary or Dominant: + // If one is being activated, must toggle off the others. if (value === true) { Object.values(Factor) .filter((factor) => factor !== thisFactor) @@ -2598,6 +3317,7 @@ class BladesRoll extends DocumentSheet { break; } case "isActive": { + // 'isActive' should be synchronized when 1) value is true, and 2) the other value is false if (value === true) { const otherSource = thisSource === "source" ? "opposition" : "source"; factorToggleData[otherSource][thisFactor] = { @@ -2609,9 +3329,11 @@ class BladesRoll extends DocumentSheet { } default: break; } + /*~ @@DOUBLE-BLANK@@ ~*/ await this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollFactorToggles", factorToggleData) .then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID)); } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onSelectChange(event) { event.preventDefault(); const elem = event.currentTarget; @@ -2625,11 +3347,27 @@ class BladesRoll extends DocumentSheet { socketlib.system.executeForEveryone("renderRollCollab", this.rollID); } } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onTextInputBlur(event) { await U.EventHandlers.onTextInputBlur(this, event); socketlib.system.executeForEveryone("renderRollCollab", this.rollID); } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // Async _gmControlSelect(event: SelectChangeEvent) { + // event.preventDefault(); + // const elem$ = $(event.currentTarget); + // const section = elem$.data("rollSection"); + // const subSection = elem$.data("rollSubSection"); + // const selectedOption = elem$.val(); + /*~ @@DOUBLE-BLANK@@ ~*/ + // if (typeof selectedOption !== "string") { return; } + // if (selectedOption === "false") { + // await this.document.unsetFlag(C.SYSTEM_ID, `rollCollab.rollParticipantData.${section}.${subSection}`); + // } + // await this.addRollParticipant(selectedOption, section, subSection); + /*~ @@DOUBLE-BLANK@@ ~*/ + // } + /*~ @@DOUBLE-BLANK@@ ~*/ get resistanceStressCost() { const dieVals = this.dieVals; if (this.rollResult === RollResult.critical) { @@ -2640,14 +3378,19 @@ class BladesRoll extends DocumentSheet { } return 6 - (dieVals.shift() ?? 0); } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + // #region ACTIVATE LISTENERS ~ activateListeners(html) { super.activateListeners(html); ApplyTooltipListeners(html); ApplyConsequenceListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ + // User-Toggleable Roll Mods html.find(".roll-mod[data-action='toggle']").on({ click: this._toggleRollModClick.bind(this) }); + /*~ @@DOUBLE-BLANK@@ ~*/ html.find("[data-action='tradePosition']").on({ click: (event) => { const curVal = `${$(event.currentTarget).data("value")}`; @@ -2670,19 +3413,27 @@ class BladesRoll extends DocumentSheet { } } }); + /*~ @@DOUBLE-BLANK@@ ~*/ html.find("[data-action='roll']").on({ click: () => this.resolveRoll() }); + /*~ @@DOUBLE-BLANK@@ ~*/ html .find("select[data-action='player-select']") .on({ change: this._onSelectChange.bind(this) }); + /*~ @@DOUBLE-BLANK@@ ~*/ if (!game.user.isGM) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // GM Controls html.on({ - focusin: () => { BladesRoll.Active = this; } + focusin: () => { BladesRoll.Active = this; } // Set reference to top-most, focused roll. }); - html.find(".controls-toggle").on({ + /** + * Handles setting of rollMod status via GM pop-out controls + */ + html.find(".controls-toggle").on({ click: (event) => { event.preventDefault(); $(event.currentTarget).parents(".controls-panel").toggleClass("active"); @@ -2691,33 +3442,60 @@ class BladesRoll extends DocumentSheet { html.find("[data-action=\"gm-set\"]").on({ click: this._gmControlSet.bind(this) }); - html.find("[data-action=\"gm-set-position\"]").on({ + /** + * Handles setting of baseline rollPosition via GM button line + */ + html.find("[data-action=\"gm-set-position\"]").on({ click: this._gmControlSetPosition.bind(this) }); - html.find("[data-action=\"gm-set-effect\"]").on({ + /** + * Handles setting of baseline rollEffect via GM button line + */ + html.find("[data-action=\"gm-set-effect\"]").on({ click: this._gmControlSetEffect.bind(this) }); - html.find("[data-action=\"gm-set-target\"]").on({ + /** + * Handles setting values via GM number line (e.g. roll factor boosts/modifications). + * Handles resetting value associated with GM number line on a right-click. + */ + html.find("[data-action=\"gm-set-target\"]").on({ click: this._gmControlSetTargetToValue.bind(this), contextmenu: this._gmControlResetTarget.bind(this) }); - html.find("[data-action=\"gm-toggle-factor\"]").on({ + /** + * Handles setting values via GM number line (e.g. roll factor boosts/modifications). + * Handles resetting value associated with GM number line on a right-click. + */ + html.find("[data-action=\"gm-cycle-target\"]").on({ + click: this._gmControlCycleTarget.bind(this) + }); + /** + * Handles setting of Factor toggles: isActive, isPrimary, highFavorsPC, isDominant + */ + html.find("[data-action=\"gm-toggle-factor\"]").on({ click: this._gmControlToggleFactor.bind(this) }); + /*~ @@DOUBLE-BLANK@@ ~*/ html .find("select[data-action='gm-select']") .on({ change: this._onSelectChange.bind(this) }); + /*~ @@DOUBLE-BLANK@@ ~*/ html .find("[data-action=\"gm-edit-consequences\"]") .on({ click: () => BladesDialog.DisplayRollConsequenceDialog(this) }); + /*~ @@DOUBLE-BLANK@@ ~*/ html .find("[data-action='gm-text-input']") .on({ blur: this._onTextInputBlur.bind(this) }); + /*~ @@DOUBLE-BLANK@@ ~*/ } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region OVERRIDES: _canDragDrop, _onDrop, _onSubmit, close, render ~ _canDragDrop() { return game.user.isGM; } + /*~ @@DOUBLE-BLANK@@ ~*/ _onDrop(event) { const { type, uuid } = TextEditor.getDragEventData(event); const [id] = (new RegExp(`${type}\\.(.+)`).exec(uuid) ?? []).slice(1); @@ -2726,12 +3504,15 @@ class BladesRoll extends DocumentSheet { this.rollOpposition = new BladesRollOpposition(this, { rollOppDoc: oppDoc }); } } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onSubmit(event, { updateData } = {}) { const returnVal = await super._onSubmit(event, { updateData, preventClose: true }); await socketlib.system.executeForEveryone("renderRollCollab", this.rollID); return returnVal; } + /*~ @@DOUBLE-BLANK@@ ~*/ async close(options = {}) { + /*~ @@DOUBLE-BLANK@@ ~*/ if (options.rollID) { return super.close({}); } @@ -2739,6 +3520,7 @@ class BladesRoll extends DocumentSheet { socketlib.system.executeForEveryone("closeRollCollab", this.rollID); return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ render(force = false, options) { if (!this.document.getFlag(C.SYSTEM_ID, "rollCollab")) { return this; @@ -2746,6 +3528,9 @@ class BladesRoll extends DocumentSheet { return super.render(force, options); } } - +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region EXPORTS ~ export { BladesRollMod, BladesRollPrimary, BladesRollOpposition, BladesRollParticipant }; -export default BladesRoll; \ No newline at end of file +export default BladesRoll; +// #endregion +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/blades.js b/module/blades.js index 784fd8d2..6ae9ad02 100644 --- a/module/blades.js +++ b/module/blades.js @@ -1,45 +1,56 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - +// #region ▮▮▮▮▮▮▮ IMPORTS ▮▮▮▮▮▮▮ ~ import C, { ActionTrait, AttributeTrait, RollType, ConsequenceType, Position, RollResult } from "./core/constants.js"; import registerSettings, { initTinyMCEStyles, initCanvasStyles } from "./core/settings.js"; import { registerHandlebarHelpers, preloadHandlebarsTemplates } from "./core/helpers.js"; import BladesPushAlert from "./BladesPushAlert.js"; +import BladesChat from "./BladesChat.js"; import U from "./core/utilities.js"; import logger from "./core/logger.js"; import G, { Initialize as GsapInitialize } from "./core/gsap.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ import BladesActorProxy, { BladesActor, BladesPC, BladesCrew, BladesNPC, BladesFaction } from "./documents/BladesActorProxy.js"; import BladesItemProxy, { BladesItem, BladesClockKeeper, BladesGMTracker, BladesLocation, BladesScore } from "./documents/BladesItemProxy.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ import BladesItemSheet from "./sheets/item/BladesItemSheet.js"; import BladesPCSheet from "./sheets/actor/BladesPCSheet.js"; import BladesCrewSheet from "./sheets/actor/BladesCrewSheet.js"; import BladesNPCSheet from "./sheets/actor/BladesNPCSheet.js"; import BladesFactionSheet from "./sheets/actor/BladesFactionSheet.js"; import BladesRoll, { BladesRollMod, BladesRollPrimary, BladesRollOpposition, BladesRollParticipant } from "./BladesRoll.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ import BladesDialog from "./BladesDialog.js"; import BladesAI, { AGENTS } from "./core/ai.js"; import BladesActiveEffect from "./BladesActiveEffect.js"; import BladesGMTrackerSheet from "./sheets/item/BladesGMTrackerSheet.js"; import BladesClockKeeperSheet from "./sheets/item/BladesClockKeeperSheet.js"; import { updateClaims, updateContacts, updateOps, updateFactions, updateDescriptions, updateRollMods } from "./data-import/data-import.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ CONFIG.debug.logging = false; -CONFIG.debug.logging = true; +/* DEVCODE*/ CONFIG.debug.logging = true; Object.assign(globalThis, { eLog: logger }); -Handlebars.registerHelper("eLog", logger.hbsLog); -let socket; +Handlebars.registerHelper("eLog", logger.hbsLog); /* !DEVCODE*/ +/*~ @@DOUBLE-BLANK@@ ~*/ +let socket; // ~ SocketLib interface +/*~ @@DOUBLE-BLANK@@ ~*/ +// #endregion ▮▮▮▮[IMPORTS]▮▮▮▮ +/*~ @@DOUBLE-BLANK@@ ~*/ class GlobalGetter { get roll() { return BladesRoll.Active; } + /*~ @@DOUBLE-BLANK@@ ~*/ get user() { return this.roll?.document; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollFlags() { return this.roll?.flagData; } + /*~ @@DOUBLE-BLANK@@ ~*/ get userFlags() { return this.user?.flags?.["eunos-blades"]?.rollCollab; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimary() { return this.roll?.rollPrimary; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollPrimaryDoc() { return this.roll?.rollPrimaryDoc; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOpposition() { return this.roll?.rollOpposition; } + /*~ @@DOUBLE-BLANK@@ ~*/ get sheetData() { return this.roll?.getData(); } + /*~ @@DOUBLE-BLANK@@ ~*/ newActionRoll() { const pc = game.actors.getName("Alistair"); if (!pc) { @@ -64,6 +75,7 @@ class GlobalGetter { 0: { type: ConsequenceType.ProwessHarm2, attribute: AttributeTrait.prowess, + attributeVal: 3, name: "Broken Leg", resistOptions: { 0: { @@ -98,47 +110,179 @@ class GlobalGetter { isSelected: true, type: ConsequenceType.None, icon: C.ConsequenceIcons[ConsequenceType.None], + footerMsg: "If vs. Physical Harm", typeDisplay: "" }, icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm2], typeDisplay: C.ConsequenceDisplay[ConsequenceType.ProwessHarm2] + }, + 1: { + type: ConsequenceType.ReducedEffect, + 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", + 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", + typeDisplay: "Level 1 Harm (Lesser)", + attributeVal: 4 } }, [RollResult.fail]: { 0: { - type: ConsequenceType.ProwessHarm2, + type: ConsequenceType.WorsePosition, + 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", + typeDisplay: "Worse Position" + }, + 1: { + type: ConsequenceType.ComplicationMajor, attribute: AttributeTrait.prowess, - name: "Broken Leg", + name: "Your pick snaps off inside the lock.", resistOptions: { 0: { - name: "Sprained Ankle", - isSelected: true, - type: ConsequenceType.ProwessHarm1, - icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm1], - typeDisplay: C.ConsequenceDisplay[ConsequenceType.ProwessHarm1] + name: "Lock remains intact but jammed", + isSelected: false, + type: ConsequenceType.ComplicationMinor, + icon: "main" }, 1: { - name: "Bruised Leg", + 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.ProwessHarm1, - icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm1] + type: ConsequenceType.ComplicationMinor, + icon: "main" + } + }, + resistedTo: { + name: "Pick breaks, but lock is still pickable", + isSelected: true, + type: ConsequenceType.ComplicationMinor, + icon: "main", + typeDisplay: "Minor Complication" + }, + attributeVal: 3, + icon: "main", + 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: "Fractured Foot", + name: "Given Partially Incorrect Information", isSelected: false, - type: ConsequenceType.ProwessHarm1, - icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm1] + type: ConsequenceType.InsightHarm1, + icon: "eye|iris" } }, resistedTo: { + name: "Confused by Deception", + isSelected: true, + type: ConsequenceType.InsightHarm1, + typeDisplay: "Level 1 Harm (Lesser)", + icon: "eye|iris" + }, + specialArmorTo: { name: "Sprained Ankle", isSelected: true, - type: ConsequenceType.ProwessHarm1, - typeDisplay: C.ConsequenceDisplay[ConsequenceType.ProwessHarm1], - icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm1] + type: ConsequenceType.InsightHarm1, + footerMsg: "If vs. Supernatural, Arcane Forces", + icon: C.ConsequenceIcons[ConsequenceType.InsightHarm1], + typeDisplay: "Level 1 Harm (Lesser)" }, - icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm2], - typeDisplay: C.ConsequenceDisplay[ConsequenceType.ProwessHarm2] + icon: "eye|iris", + typeDisplay: "Level 2 Harm (Moderate)", + attributeVal: 4 } } } @@ -146,6 +290,7 @@ class GlobalGetter { }; BladesRoll.NewRoll(conf); } + /*~ @@DOUBLE-BLANK@@ ~*/ newResistanceRoll() { const pc = game.actors.getName("Alistair"); if (!pc) { @@ -183,8 +328,9 @@ class GlobalGetter { BladesRoll.NewRoll(conf); } } - -Object.assign(globalThis, { +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region Globals: Exposing Functionality to Global Scope ~ +/* DEVCODE*/ Object.assign(globalThis, { get: new GlobalGetter(), updateClaims, updateContacts, @@ -208,6 +354,7 @@ Object.assign(globalThis, { BladesRollPrimary, BladesRollOpposition, BladesRollParticipant, + BladesChat, G, U, C, @@ -220,20 +367,32 @@ Object.assign(globalThis, { BladesGMTrackerSheet, BladesAI, AGENTS -}); - +}); /* !DEVCODE*/ +// #endregion Globals +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ SYSTEM INITIALIZATION: Initializing Blades In The Dark System on 'Init' Hook ████████ Hooks.once("init", async () => { + // Register System Settings registerSettings(); eLog.display("Initializing Blades In the Dark System"); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize Fonts & Gsap Animations GsapInitialize(); + /*~ @@DOUBLE-BLANK@@ ~*/ CONFIG.Item.documentClass = BladesItemProxy; CONFIG.Actor.documentClass = BladesActorProxy; + CONFIG.ChatMessage.documentClass = BladesChat; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Register sheet application classes Actors.unregisterSheet("core", ActorSheet); Actors.registerSheet("blades", BladesCrewSheet, { types: ["crew"], makeDefault: true }); Actors.registerSheet("blades", BladesFactionSheet, { types: ["faction"], makeDefault: true }); Actors.registerSheet("blades", BladesNPCSheet, { types: ["npc"], makeDefault: true }); + /*~ @@DOUBLE-BLANK@@ ~*/ Items.unregisterSheet("core", ItemSheet); Items.registerSheet("blades", BladesItemSheet, { types: C.ItemTypes, makeDefault: true }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize subclasses await Promise.all([ BladesPCSheet.Initialize(), BladesActiveEffect.Initialize(), @@ -245,20 +404,36 @@ Hooks.once("init", async () => { BladesRoll.Initialize(), preloadHandlebarsTemplates() ]); + /*~ @@DOUBLE-BLANK@@ ~*/ registerHandlebarHelpers(); }); +/*~ @@DOUBLE-BLANK@@ ~*/ Hooks.once("ready", () => { initCanvasStyles(); initTinyMCEStyles(); }); - +// #endregion ▄▄▄▄▄ SYSTEM INITIALIZATION ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ░░░░░░░[SocketLib]░░░░ SocketLib Initialization ░░░░░░░ ~ Hooks.once("socketlib.ready", () => { socket = socketlib.registerSystem("eunos-blades"); - Object.assign(globalThis, { socket, socketlib }); + /* DEVCODE*/ Object.assign(globalThis, { socket, socketlib }); /* !DEVCODE*/ + /*~ @@DOUBLE-BLANK@@ ~*/ BladesRoll.InitSockets(); + /*~ @@DOUBLE-BLANK@@ ~*/ let clockOverlayUp; let pushControllerUp; - function InitOverlaySockets() { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Initializes the overlay sockets for the BladesClockKeeperSheet and BladesPushAlert. + * It checks every 2 seconds if the overlays are up and running. + * If both overlays are up, it stops checking. + * + * @function + * @name InitOverlaySockets + * @returns {void} + */ + function InitOverlaySockets() { setTimeout(() => { clockOverlayUp = clockOverlayUp || BladesClockKeeperSheet.InitSockets(); pushControllerUp = pushControllerUp || BladesPushAlert.InitSockets(); @@ -280,4 +455,6 @@ Hooks.once("diceSoNiceReady", (dice3d) => { emissiveMaps: [undefined, undefined, undefined, undefined, undefined, "systems/eunos-blades/assets/dice/emission-maps/6.webp"], emissive: "#d89300" }); -}); \ No newline at end of file +}); +// #endregion ░░░░[Dice So Nice]░░░░ +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/core/ai.js b/module/core/ai.js index 7b1dc6f2..5b812231 100644 --- a/module/core/ai.js +++ b/module/core/ai.js @@ -1,13 +1,11 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - +// Import axios from "axios.js"; import C from "./constants.js"; import U from "./utilities.js"; +/** + * AI class for querying OpenAI API + */ class BladesAI { + /*~ @@DOUBLE-BLANK@@ ~*/ static async GetModels() { const apiKey = U.getSetting("openAPIKey"); if (!apiKey) { @@ -19,21 +17,41 @@ class BladesAI { Authorization: `Bearer ${apiKey}` } }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Send a POST request to the OpenAI API const response = await fetch("https://api.openai.com/v1/models", fetchRequest); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Check if the response status is not 200 (OK) if (!response.ok) { + // Throw an error with the status code throw new Error(`OpenAI API request failed with status ${response.status}`); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Parse the response body as JSON const data = await response.json(); + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog3("BladesAI", "Available Models", { response: data }); } + /*~ @@DOUBLE-BLANK@@ ~*/ apiKey; + /*~ @@DOUBLE-BLANK@@ ~*/ model; + /*~ @@DOUBLE-BLANK@@ ~*/ temperature = 0.5; + /*~ @@DOUBLE-BLANK@@ ~*/ frequency_penalty = 0.8; + /*~ @@DOUBLE-BLANK@@ ~*/ presence_penalty = 0.8; + /*~ @@DOUBLE-BLANK@@ ~*/ systemMessage; + /*~ @@DOUBLE-BLANK@@ ~*/ examplePrompts; - constructor(config) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * AI class constructor + * @param {BladesAI.Config} [config] Configuration settings for the API + */ + constructor(config) { const apiKey = U.getSetting("openAPIKey"); if (!apiKey) { throw new Error("You must configure your OpenAI API Key in Settings to use AI features."); @@ -50,7 +68,9 @@ class BladesAI { this.frequency_penalty = config.frequency_penalty ?? this.frequency_penalty; this.presence_penalty = config.presence_penalty ?? this.presence_penalty; } + /*~ @@DOUBLE-BLANK@@ ~*/ _initialMessages = []; + /*~ @@DOUBLE-BLANK@@ ~*/ get initialMessages() { if (this._initialMessages.length === 0) { this._initialMessages.push({ @@ -70,15 +90,30 @@ class BladesAI { } return this._initialMessages; } + /*~ @@DOUBLE-BLANK@@ ~*/ prompts = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ responses = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ getResponse(queryID) { return this.responses[queryID] ?? null; } + /*~ @@DOUBLE-BLANK@@ ~*/ hasQueried(queryID) { return this.prompts[queryID] !== undefined; } - async query(queryID, prompt, modelMod, extendedContext = false) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Query OpenAI API + * @param {string} queryID A label for later retrieval of the query data + * @param {string} prompt The prompt to send to the API + * @param {number} [modelMod] Optional modifier to the base model level. + * If provided, the final model quality will be adjusted by this number. + * @param {boolean} [extendedContext=false] Optional flag to indicate whether to use extended context models. + * If true, extended context models are used; otherwise, base context models are used. + * @returns {Promise} The API response + */ + async query(queryID, prompt, modelMod, extendedContext = false) { if (!prompt) { return; } @@ -89,6 +124,7 @@ class BladesAI { const model = extendedContext ? C.AI_MODELS.extendedContext[modelNum] : C.AI_MODELS.baseContext[modelNum]; + /*~ @@DOUBLE-BLANK@@ ~*/ const fetchRequest = { method: "POST", headers: { @@ -109,19 +145,72 @@ class BladesAI { ] }) }; - + /*~ @@DOUBLE-BLANK@@ ~*/ + // EeLog.checkLog3("BladesAI", "Fetch Request", fetchRequest); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Send a POST request to the OpenAI API const response = await fetch("https://api.openai.com/v1/chat/completions", fetchRequest); + // { + // method: "POST", + // headers: { + // // The content type of the request + // "Content-Type": "application/json", + // // The authorization header with the API key + // Authorization: `Bearer ${this.apiKey}` + // }, + // body: JSON.stringify({ + // model, + // messages: [ + // ...this.initialMessages, + // { + // role: "user", + // content: prompt + // } + // ], + // // Maximum number of tokens in the output. Min: 1, Max: 4096 + // // max_tokens: 60, + // // Controls randomness. Higher values mean the model will take more risks. + // temperature: 0.5, // 0 to 2.0 + // /* The 'top_p' parameter is an alternative to 'temperature' for controlling the randomness of + // the AI's responses. It represents the cumulative probability and its value ranges from 0 to 1. + // A lower value makes the AI's responses more deterministic, while a higher value makes them + // more diverse and unpredictable. */ + // // top_p: 1, // 0 to 1 + // /* The 'frequency_penalty' parameter is used to penalize new tokens based on their frequency in + // the training set. Its value ranges from 0 to 1. A higher value means the AI is less likely to + // use common phrases from its training set, leading to more unique responses. A lower value + // means the AI is more likely to use common phrases, leading to more predictable responses. */ + // frequency_penalty: 0.8, // -2.0 to 2.0 + // /* The 'presence_penalty' parameter is used to penalize tokens (words or phrases) that are out + // of context. Its value ranges from 0 to 1. A higher value means the AI is less likely to + // include out-of-context tokens in its responses, leading to more coherent and contextually + // appropriate responses. A lower value means the AI is more likely to include out-of-context + // tokens, which can lead to more creative but potentially less coherent responses. */ + // presence_penalty: 0.8 // -2.0 to 2.0 + // }) + // } + // ); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Check if the response status is not 200 (OK) if (!response.ok) { console.log("Failed AI Request:", JSON.parse(fetchRequest.body)); + // Throw an error with the status code throw new Error(`OpenAI API request failed with status ${response.status}`); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Parse the response body as JSON const data = await response.json(); + /*~ @@DOUBLE-BLANK@@ ~*/ fetchRequest.body = JSON.parse(fetchRequest.body); + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog3("BladesAI", "AI Query", { prompt: fetchRequest, response: data }); + /*~ @@DOUBLE-BLANK@@ ~*/ this.responses[queryID] = data.choices[0].message.content; + /*~ @@DOUBLE-BLANK@@ ~*/ return this.responses[queryID]; } } +/*~ @@DOUBLE-BLANK@@ ~*/ export const AGENTS = { GeneralContentGenerator: { systemMessage: "You will act as a creative content generator for a game of Blades In The Dark set in the city of Duskvol. You will be prompted with some element of the game world (a location, a character, an event, a faction, a dilemma) in the form of a JSON object. Your job is to analyze the JSON object and replace any values that equal \"\" with original content of your own creation. Original content must meet these requirements: (A) it should align with and be consistent with the provided contextual information, as well as your broader understanding of the game's themes. (B) It should be presented in a format that matches (in length and in style) other entries for that particular value, examples of which will also be provided. (C) It should be creative, interesting, and daring: Be bold with your creativity. Specific context for this prompt is as follows:", @@ -142,7 +231,13 @@ export const AGENTS = { human: "Setarra, a Demon. Patient, Defiant, Ruthless, Cold", ai: "[5 KEYWORDS]shadowy|sinister|unfathomable|enigmatic|tempting|[5 PHRASES]whispers that crawl under your skin|always watching, always plotting|in tones of silk and venom|intoxicating presence that draws you closer, despite your instincts urging you to run|eyes like black holes, swallowing all light around them|[3 QUIRKS/MOTIFFS]a disorienting mist clings to her form, obscuring her true shape|casually discusses the devastating acts of capricious revenge she has taken on those who crossed her|never forgets a slight or betrayal, no matter how small or insignificant it may seem at the time|[3 PLOT HOOKS]seeks revenge against Alistair for meddling in her affairs years ago|makes Ollie an offer he can't refuse: unlimited access to forbidden alchemical knowledge in exchange for a single favor, to be called in at some future time|tempts Spencer with forbidden knowledge about demons, promising answers to all their questions if they perform a dangerous ritual" } - ] + /* + "brutish,merciless,terrifying,savage,loyal, + bloody tools,hulking figures,blood-soaked alleys,grimy aprons,grisly displays, + never clean their tools,relishes the terror they inspire,occasional laughter among them, + recruiting a PC to perform a job for them from prison, + the gang blames one of the PCs for Tarvul's imprisonment and they're out for revenge" */ + ] }, ConsequenceAdjuster: { systemMessage: "You will act as a \"Setback Adjuster\" for a game of Blades In The Dark. You will be prompted with a short phrase describing an injury, lasting consequence or other setback. Your job is to respond with a pipe-delimited list of three possible alternative consequences that are less severe by one level, using the following scale as a rough guide: Level 1 = Lesser (e.g. 'Battered', 'Drained', 'Distracted', 'Scared', 'Confused'), Level 2 = Moderate (e.g. 'Exhausted', 'Deep Cut to Arm', 'Concussion', 'Panicked', 'Seduced'), Level 3 = Severe (e.g. 'Impaled', 'Broken Leg', 'Shot In Chest', 'Badly Burned', 'Terrified'), Level 4 = Fatal or Ruinous (e.g. 'Impaled Through Heart', 'Electrocuted', 'Headquarters Burned to the Ground'). So, if you determine that the consequence described in the prompt is severity level 3, you should respond with three narratively similar consequences that are severity level 2. Your three suggestions should be different from each other, but they should all logically follow from the initial harm described: You should not introduce new facts or make assumptions that are not indicated in the initial prompt. The consequences you suggest should always describe a NEGATIVE setback or complication, just one that is less severe than the one described in the prompt.", @@ -155,4 +250,6 @@ export const AGENTS = { ] } }; -export default BladesAI; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesAI; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/core/constants.js b/module/core/constants.js index 86e49e69..ac645884 100644 --- a/module/core/constants.js +++ b/module/core/constants.js @@ -1,24 +1,20 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - +/* eslint-disable no-shadow */ export var BladesPermissions; (function (BladesPermissions) { BladesPermissions[BladesPermissions["NONE"] = CONST.DOCUMENT_PERMISSION_LEVELS.NONE] = "NONE"; BladesPermissions[BladesPermissions["BASIC"] = CONST.DOCUMENT_PERMISSION_LEVELS.LIMITED] = "BASIC"; BladesPermissions[BladesPermissions["FULL"] = CONST.DOCUMENT_PERMISSION_LEVELS.OBSERVER] = "FULL"; BladesPermissions[BladesPermissions["OWNER"] = CONST.DOCUMENT_PERMISSION_LEVELS.OWNER] = "OWNER"; -})(BladesPermissions || (BladesPermissions = {})); +})(BladesPermissions || (BladesPermissions = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var BladesActorType; (function (BladesActorType) { BladesActorType["pc"] = "pc"; BladesActorType["npc"] = "npc"; BladesActorType["crew"] = "crew"; BladesActorType["faction"] = "faction"; -})(BladesActorType || (BladesActorType = {})); +})(BladesActorType || (BladesActorType = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var BladesItemType; (function (BladesItemType) { BladesItemType["ability"] = "ability"; @@ -43,20 +39,23 @@ export var BladesItemType; BladesItemType["design"] = "design"; BladesItemType["location"] = "location"; BladesItemType["score"] = "score"; -})(BladesItemType || (BladesItemType = {})); +})(BladesItemType || (BladesItemType = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var PrereqType; (function (PrereqType) { PrereqType["HasActiveItem"] = "HasActiveItem"; PrereqType["HasActiveItemsByTag"] = "HasActiveItemByTag"; PrereqType["AdvancedPlaybook"] = "AdvancedPlaybook"; PrereqType["HasAllTags"] = "HasAllTags"; - PrereqType["HasAnyTag"] = "HasAnyTag"; + PrereqType["HasAnyTag"] = "HasAnyTag"; + /*~ @@DOUBLE-BLANK@@ ~*/ PrereqType["Not_HasActiveItem"] = "Not_HasActiveItem"; PrereqType["Not_HasActiveItemsByTag"] = "Not_HasActiveItemsByTag"; PrereqType["Not_AdvancedPlaybook"] = "Not_AdvancedPlaybook"; PrereqType["Not_HasAllTags"] = "Not_HasAllTags"; PrereqType["Not_HasAnyTag"] = "Not_HasAnyTag"; -})(PrereqType || (PrereqType = {})); +})(PrereqType || (PrereqType = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var District; (function (District) { District["Barrowcleft"] = "Barrowcleft"; @@ -101,7 +100,8 @@ export var OtherDistrict; OtherDistrict["Ironhook Prison"] = "Ironhook Prison"; OtherDistrict["Old North Port"] = "Old North Port"; OtherDistrict["Deathlands"] = "Deathlands"; -})(OtherDistrict || (OtherDistrict = {})); +})(OtherDistrict || (OtherDistrict = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var AttributeTrait; (function (AttributeTrait) { AttributeTrait["insight"] = "insight"; @@ -143,7 +143,8 @@ export var ActionTrait; ActionTrait["command"] = "command"; ActionTrait["consort"] = "consort"; ActionTrait["sway"] = "sway"; -})(ActionTrait || (ActionTrait = {})); +})(ActionTrait || (ActionTrait = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var DowntimeAction; (function (DowntimeAction) { DowntimeAction["AcquireAsset"] = "AcquireAsset"; @@ -152,21 +153,24 @@ export var DowntimeAction; DowntimeAction["Recover"] = "Recover"; DowntimeAction["ReduceHeat"] = "ReduceHeat"; DowntimeAction["Train"] = "Train"; -})(DowntimeAction || (DowntimeAction = {})); +})(DowntimeAction || (DowntimeAction = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var RollPermissions; (function (RollPermissions) { RollPermissions["Primary"] = "Primary"; RollPermissions["Observer"] = "Observer"; RollPermissions["GM"] = "GM"; RollPermissions["Participant"] = "Participant"; -})(RollPermissions || (RollPermissions = {})); +})(RollPermissions || (RollPermissions = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var RollType; (function (RollType) { RollType["Action"] = "Action"; RollType["Resistance"] = "Resistance"; RollType["Fortune"] = "Fortune"; RollType["IndulgeVice"] = "IndulgeVice"; -})(RollType || (RollType = {})); +})(RollType || (RollType = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var RollSubType; (function (RollSubType) { RollSubType["Incarceration"] = "Incarceration"; @@ -174,7 +178,8 @@ export var RollSubType; RollSubType["GatherInfo"] = "GatherInfo"; RollSubType["GroupLead"] = "GroupLead"; RollSubType["GroupParticipant"] = "GroupParticipant"; -})(RollSubType || (RollSubType = {})); +})(RollSubType || (RollSubType = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var ConsequenceType; (function (ConsequenceType) { ConsequenceType["ReducedEffect"] = "ReducedEffect"; @@ -196,7 +201,8 @@ export var ConsequenceType; ConsequenceType["ResolveHarm3"] = "ResolveHarm3"; ConsequenceType["ResolveHarm4"] = "ResolveHarm4"; ConsequenceType["None"] = "None"; -})(ConsequenceType || (ConsequenceType = {})); +})(ConsequenceType || (ConsequenceType = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var RollModStatus; (function (RollModStatus) { RollModStatus["Hidden"] = "Hidden"; @@ -205,7 +211,8 @@ export var RollModStatus; RollModStatus["ToggledOn"] = "ToggledOn"; RollModStatus["ForcedOn"] = "ForcedOn"; RollModStatus["Dominant"] = "Dominant"; -})(RollModStatus || (RollModStatus = {})); +})(RollModStatus || (RollModStatus = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var RollModSection; (function (RollModSection) { RollModSection["roll"] = "roll"; @@ -234,29 +241,41 @@ export var Factor; Factor["quality"] = "quality"; Factor["scale"] = "scale"; Factor["magnitude"] = "magnitude"; -})(Factor || (Factor = {})); +})(Factor || (Factor = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var RollResult; (function (RollResult) { RollResult["critical"] = "critical"; RollResult["success"] = "success"; RollResult["partial"] = "partial"; RollResult["fail"] = "fail"; -})(RollResult || (RollResult = {})); +})(RollResult || (RollResult = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var RollPhase; -(function (RollPhase) { - RollPhase["Collaboration"] = "Collaboration"; - RollPhase["AwaitingRoll"] = "AwaitingRoll"; - RollPhase["ApplyingConsequences"] = "ApplyingConsequences"; - RollPhase["AwaitingChatInput"] = "AwaitingChatInput"; +(function (RollPhase) { + // Collaboration: Before GM toggles "Roll" button for player to click. + RollPhase["Collaboration"] = "Collaboration"; + // AwaitingRoll: Waiting for player to click "ROLL" + RollPhase["AwaitingRoll"] = "AwaitingRoll"; + // ApplyingConsequences: Waiting for GM to select consequence(s), AI to + // respond with resistance options, and GM to select + // resistance options. + RollPhase["ApplyingConsequences"] = "ApplyingConsequences"; + // AwaitingChatInput: Consequences and player options output to chat; + // awaiting player choice there + RollPhase["AwaitingChatInput"] = "AwaitingChatInput"; + // Complete: Roll finished (but may trigger another roll, e.g. resistance) RollPhase["Complete"] = "Complete"; -})(RollPhase || (RollPhase = {})); +})(RollPhase || (RollPhase = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var Harm; (function (Harm) { Harm["Weakened"] = "Weakened"; Harm["Impaired"] = "Impaired"; Harm["Broken"] = "Broken"; Harm["Dead"] = "Dead"; -})(Harm || (Harm = {})); +})(Harm || (Harm = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var Vice; (function (Vice) { Vice["Faith"] = "Faith"; @@ -271,7 +290,8 @@ export var Vice; Vice["Living_Essence"] = "Living_Essence"; Vice["Electroplasmic_Power"] = "Electroplasmic_Power"; Vice["Servitude"] = "Servitude"; -})(Vice || (Vice = {})); +})(Vice || (Vice = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var Playbook; (function (Playbook) { Playbook["Cutter"] = "Cutter"; @@ -291,7 +311,8 @@ export var Playbook; Playbook["Shadows"] = "Shadows"; Playbook["Smugglers"] = "Smugglers"; Playbook["Vigilantes"] = "Vigilantes"; -})(Playbook || (Playbook = {})); +})(Playbook || (Playbook = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var AdvancementPoint; (function (AdvancementPoint) { AdvancementPoint["UpgradeOrAbility"] = "UpgradeOrAbility"; @@ -315,7 +336,8 @@ export var AdvancementPoint; AdvancementPoint["command"] = "command"; AdvancementPoint["consort"] = "consort"; AdvancementPoint["sway"] = "sway"; -})(AdvancementPoint || (AdvancementPoint = {})); +})(AdvancementPoint || (AdvancementPoint = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ export var BladesPhase; (function (BladesPhase) { BladesPhase["CharGen"] = "CharGen"; @@ -324,7 +346,8 @@ export var BladesPhase; BladesPhase["Downtime"] = "Downtime"; })(BladesPhase || (BladesPhase = {})); export var Tag; -(function (Tag) { +(function (Tag) { + /*~ @@DOUBLE-BLANK@@ ~*/ let System; (function (System) { System["Archived"] = "Archived"; @@ -354,7 +377,7 @@ export var Tag; Invention["SparkCraft"] = "SparkCraft"; Invention["Alchemical"] = "Alchemical"; Invention["Mundane"] = "Mundane"; - Invention["Ritual"] = "Ritual"; + Invention["Ritual"] = "Ritual"; // Rituals })(Invention = Tag.Invention || (Tag.Invention = {})); let GearCategory; (function (GearCategory) { @@ -379,7 +402,8 @@ export var Tag; GangType["Skulks"] = "Skulks"; GangType["Vehicle"] = "Vehicle"; })(GangType = Tag.GangType || (Tag.GangType = {})); -})(Tag || (Tag = {})); +})(Tag || (Tag = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ const C = { SYSTEM_ID: "eunos-blades", SYSTEM_NAME: "Euno's Blades", @@ -517,27 +541,50 @@ const C = { [ConsequenceType.ResolveHarm4]: "spikes|eyeball|iris", [ConsequenceType.None]: "" }, + RollResultDescriptions: { + [Position.controlled]: { + [RollResult.critical]: "You critically succeed from a controlled position!", + [RollResult.success]: "You fully succeed from a controlled position!", + [RollResult.partial]: "You partially succeed from a controlled position!", + [RollResult.fail]: "You fail from a controlled position!" + }, + [Position.risky]: { + [RollResult.critical]: "You critically succeed from a risky position!", + [RollResult.success]: "You fully succeed from a risky position!", + [RollResult.partial]: "You partially succeed from a risky position!", + [RollResult.fail]: "You fail from a risky position!" + }, + [Position.desperate]: { + [RollResult.critical]: "You critically succeed from a desperate position!", + [RollResult.success]: "You fully succeed from a desperate position!", + [RollResult.partial]: "You partially succeed from a desperate position!", + [RollResult.fail]: "You fail from a desperate position!" + }, + }, // Colors: { bWHITE: "rgba(255, 255, 255, 1)", WHITE: "rgba(200, 200, 200, 1)", bGREY: "rgba(170, 170, 170, 1)", - GREY: "rgba(128, 128, 128, 1)", - dGREY: "rgba(78, 78, 78, 1)", - BLACK: "rgba(16, 16, 16, 1)", - dBLACK: "rgba(0, 0, 0, 1)", - gGOLD: "rgba(255, 254, 200, 1)", - bGOLD: "rgba(171, 146, 84, 1)", - GOLD: "rgba(253, 212, 112, 1)", - dGOLD: "rgba(65, 61, 46, 1)", - RED: "rgba(155, 32, 32, 1)", - dRED: "rgba(70, 14, 14, 1)", - bRED: "rgba(240, 50, 50, 1)", - gRED: "rgba(255, 0, 0, 1)", - BLUE: "rgba(43, 85, 139, 1)", - dBLUE: "rgba(17, 33, 54, 1)", - bBLUE: "rgba(69, 137, 224, 1)", - gBLUE: "rgba(128, 185, 255, 1)" + GREY: "rgba(119, 119, 119, 1)", + dGREY: "rgba(68, 68, 68, 1)", + BLACK: "rgba(32, 32, 32, 1)", + dBLACK: "rgba(0, 0, 0, 1)", + /*~ @@DOUBLE-BLANK@@ ~*/ + bGOLD: "rgba(255,216, 44, 1)", + GOLD: "rgba(215,175, 0, 1)", + dGOLD: "rgba(165,134, 0, 1)", + ddGOLD: "rgba(103, 83, 0, 1)", + /*~ @@DOUBLE-BLANK@@ ~*/ + bRED: "rgba(255, 0, 0, 1)", + RED: "rgba(200, 0, 0, 1)", + dRED: "rgba(150, 0, 0, 1)", + ddRED: "rgba(50, 0, 0, 1)", + /*~ @@DOUBLE-BLANK@@ ~*/ + bBLUE: "rgba( 0,224,224, 1)", + BLUE: "rgba(52,213,213, 1)", + dBLUE: "rgba(0,118,118, 1)", + ddBLUE: "rgba(0, 77, 77, 1)" }, Loadout: { selections: [ @@ -862,7 +909,8 @@ const C = { }, Hound: { "system.bgImg": "systems/eunos-blades/assets/icons/class-icons/hound-trans.svg", - "system.tagline": "A Deadly Sharpshooter & Tracker", + "system.tagline": "A Deadly Sharpshooter & Tracker", + // "system.acquaintances_name": "Deadly Friends & Rivals", "system.friends_name": "Deadly Friends", "system.rivals_name": "Deadlier Rivals", "system.starting_stats.chargen": { @@ -888,7 +936,8 @@ const C = { }, Leech: { "system.bgImg": "systems/eunos-blades/assets/icons/class-icons/leech-trans.svg", - "system.tagline": "A Saboteur & Technician", + "system.tagline": "A Saboteur & Technician", + // "system.acquaintances_name": "Clever Friends & Rivals", "system.friends_name": "Clever Friends", "system.rivals_name": "Cleverer Rivals", "system.starting_stats.chargen": { @@ -914,7 +963,8 @@ const C = { }, Lurk: { "system.bgImg": "systems/eunos-blades/assets/icons/class-icons/lurk-trans.svg", - "system.tagline": "A Stealthy Infiltrator & Burglar", + "system.tagline": "A Stealthy Infiltrator & Burglar", + // "system.acquaintances_name": "Shady Friends & Rivals", "system.friends_name": "Shady Friends", "system.rivals_name": "Shadier Rivals", "system.starting_stats.chargen": { @@ -940,7 +990,8 @@ const C = { }, Slide: { "system.bgImg": "systems/eunos-blades/assets/icons/class-icons/slide-trans.svg", - "system.tagline": "A Subtle Manipulator & Spy", + "system.tagline": "A Subtle Manipulator & Spy", + // "system.acquaintances_name": "Sly Friends & Rivals", "system.friends_name": "Sly Friends", "system.rivals_name": "Slyer Rivals", "system.starting_stats.chargen": { @@ -966,7 +1017,8 @@ const C = { }, Spider: { "system.bgImg": "systems/eunos-blades/assets/icons/class-icons/spider-trans.svg", - "system.tagline": "A Devious Mastermind", + "system.tagline": "A Devious Mastermind", + // "system.acquaintances_name": "Shrewd Friends & Rivals", "system.friends_name": "Shrewd Friends", "system.rivals_name": "Very Shrewd Rivals", "system.starting_stats.chargen": { @@ -992,7 +1044,8 @@ const C = { }, Whisper: { "system.bgImg": "systems/eunos-blades/assets/icons/class-icons/whisper-trans.svg", - "system.tagline": "An Arcane Adept & Channeler", + "system.tagline": "An Arcane Adept & Channeler", + // "system.acquaintances_name": "Strange Friends & Rivals", "system.friends_name": "Strange Friends", "system.rivals_name": "Stranger Rivals", "system.starting_stats.chargen": { @@ -1159,7 +1212,8 @@ const C = { Vice.Life_Essence, Vice.Electroplasmic_Power ] -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ export const Randomizers = { NPC: { heritage: [ @@ -4308,7 +4362,8 @@ export const Randomizers = { } ] } -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ export const SVGDATA = { overlayScale: 0.25, keys: { @@ -4396,7 +4451,8 @@ export const SVGDATA = { 10: "M317.557,38.197C284.557,14.18,243.938,0,200,0v200L317.557,38.197z", 12: "M300.014,26.771C270.593,9.748,236.436,0,200,0v200L300.014,26.771z" }, - glows: { + glows: { + /*~ @@DOUBLE-BLANK@@ ~*/ } }, teeth: { @@ -4628,5 +4684,7 @@ export const SVGDATA = { iris: "fill-bright" } } -}; -export default C; \ No newline at end of file +}; +/*~ @@DOUBLE-BLANK@@ ~*/ +export default C; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/core/gsap.js b/module/core/gsap.js index 6c2a16d1..52f4963f 100644 --- a/module/core/gsap.js +++ b/module/core/gsap.js @@ -1,16 +1,508 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import U from "./utilities.js"; +import C from "./constants.js"; +// eslint-disable-next-line import/no-unresolved import { TextPlugin } from "/scripts/greensock/esm/all.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ const gsapPlugins = [ TextPlugin ]; +/*~ @@DOUBLE-BLANK@@ ~*/ const gsapEffects = { + csqEnter: { + effect: (csqContainer, config) => { + const csqRoot = U.gsap.utils.selector(csqContainer); + // eLog.checkLog3("gsap", "gsapEffects.consequenceEnter -> THIS", {this: this, csqRoot}); + const csqIconCircle = csqRoot(".consequence-icon-circle.base-consequence"); + const csqBaseElems = csqRoot(".base-consequence:not(.consequence-icon-circle)"); + const csqAcceptElems = csqRoot(".accept-consequence:not(.consequence-icon-circle):not(.consequence-button-container)"); + /*~ @@DOUBLE-BLANK@@ ~*/ + const tl = U.gsap.timeline({ paused: true }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Fade out base-consequence components + tl.fromTo(csqBaseElems, { + opacity: 1 + }, { + opacity: 0, + duration: config.duration / 3, + ease: "none" + }, 0); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Fade in accept-consequence components + tl.fromTo(csqAcceptElems, { + opacity: 0 + }, { + opacity: 1, + duration: config.duration / 3, + ease: "none" + }, 0); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Brighten the entire container slightly + tl.fromTo(csqContainer, { + filter: "brightness(1)" + }, { + filter: `brightness(${config.brightness})`, + duration: config.duration / 3, + ease: "none" + }, 0); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + return tl; + }, + defaults: { + brightness: 1.5, + duration: 0.5, + scale: 1.5, + stagger: 0.05, + ease: "sine", + easeStrength: 1.5 + } + }, + csqClickIcon: { + effect: (csqIconContainer, config) => { + const csqRoot = U.gsap.utils.selector(csqIconContainer); + 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"); + /*~ @@DOUBLE-BLANK@@ ~*/ + const tl = U.gsap.timeline({ paused: true }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize interaction pads to display: none + if (csqInteractionPads.length) { + tl.set(csqInteractionPads, { display: "none" }); + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Fade out the base consequence icon circle + tl.fromTo(csqIconCircleBase, { + opacity: 1 + }, { + opacity: 0, + duration: 0.25, + ease: "none" + }, 0); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Finally, toggle on interaction pads + if (csqInteractionPads.length) { + tl.set(csqInteractionPads, { display: "block" }); + } + /*~ @@DOUBLE-BLANK@@ ~*/ + return tl; + }, + defaults: { + duration: 0.5, + scale: 1.5, + stagger: 0.05, + ease: "sine", + easeStrength: 1.5 + } + }, + csqEnterAccept: { + effect: (csqContainer) => { + const csqRoot = U.gsap.utils.selector(csqContainer); + const typeLine = csqRoot(".consequence-type-container .consequence-type.accept-consequence"); + const typeLineBg = csqRoot(".consequence-type-container .consequence-type-bg.accept-consequence"); + const buttonRoot = U.gsap.utils.selector(csqRoot(".consequence-button-container.consequence-accept-button-container")); + /*~ @@DOUBLE-BLANK@@ ~*/ + const buttonBg = buttonRoot(".consequence-button-bg"); + const buttonIcon = buttonRoot(".button-icon i"); + const buttonLabel = buttonRoot(".consequence-button-label"); + /*~ @@DOUBLE-BLANK@@ ~*/ + const tl = U.gsap.timeline({ paused: true }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Turn type line white + tl.fromTo(typeLine, { + color: C.Colors.RED + }, { + color: C.Colors.WHITE, + duration: 0.5, + ease: "sine.inOut" + }, 0); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + return tl; + }, + defaults: {} + }, + csqEnterResist: { + effect: (csqContainer) => { + const csqRoot = U.gsap.utils.selector(csqContainer); + /*~ @@DOUBLE-BLANK@@ ~*/ + const typeLine = csqRoot(".consequence-type-container .consequence-type.resist-consequence"); + /*~ @@DOUBLE-BLANK@@ ~*/ + const acceptElems = csqRoot(".accept-consequence"); + const specialArmorElems = csqRoot(".special-armor-consequence"); + /*~ @@DOUBLE-BLANK@@ ~*/ + const footerBg = csqRoot(".consequence-footer-container .consequence-footer-bg.resist-consequence"); + const attrElem = csqRoot(".consequence-footer-container .consequence-resist-attribute"); + const resistCsqName = csqRoot(".consequence-name.resist-consequence"); + const iconCircle = csqRoot(".consequence-icon-circle.resist-consequence"); + /*~ @@DOUBLE-BLANK@@ ~*/ + const buttonRoot = U.gsap.utils.selector(csqRoot(".consequence-button-container.consequence-resist-button-container")); + /*~ @@DOUBLE-BLANK@@ ~*/ + const buttonBg = buttonRoot(".consequence-button-bg"); + const buttonIcon = buttonRoot(".button-icon i"); + const buttonLabel = buttonRoot(".consequence-button-label"); + /*~ @@DOUBLE-BLANK@@ ~*/ + const tl = U.gsap.timeline({ paused: true }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Fade out all accept elems and special armor elems + tl.to([...acceptElems, ...specialArmorElems], { + opacity: 0, + duration: 0.25, + ease: "sine.out" + }); + /*~ @@DOUBLE-BLANK@@ ~*/ + if (typeLine.length) { + // Slide out .consequence-type.resist-consequence from left + tl.fromTo(typeLine, { + x: -15, + scaleX: 0, + opacity: 1, + color: C.Colors.dGOLD + }, { + x: 0, + scaleX: 1, + opacity: 1, + color: C.Colors.dGOLD, + duration: 0.5, + ease: "back.out" + }, 0); + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Fade in .consequence-icon-circle.resist-consequence + tl.fromTo(iconCircle, { + opacity: 0 + }, { + opacity: 1, + duration: 0.5, + ease: "back.out" + }, 0); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + return tl; + }, + defaults: {} + }, + csqEnterSpecialArmor: { + effect: (csqContainer) => { + const csqRoot = U.gsap.utils.selector(csqContainer); + /*~ @@DOUBLE-BLANK@@ ~*/ + const typeLine = csqRoot(".consequence-type-container .consequence-type.special-armor-consequence"); + /*~ @@DOUBLE-BLANK@@ ~*/ + const acceptElems = csqRoot(".accept-consequence"); + const resistElems = csqRoot(".resist-consequence"); + /*~ @@DOUBLE-BLANK@@ ~*/ + const footerBg = csqRoot(".consequence-footer-container .consequence-footer-bg.special-armor-consequence"); + const footerMsg = csqRoot(".consequence-footer-container .consequence-special-armor-message"); + const specialArmorCsqName = csqRoot(".consequence-name.special-armor-consequence"); + const iconCircle = csqRoot(".consequence-icon-circle.special-armor-consequence"); + /*~ @@DOUBLE-BLANK@@ ~*/ + const buttonRoot = U.gsap.utils.selector(csqRoot(".consequence-button-container.consequence-special-armor-button-container")); + /*~ @@DOUBLE-BLANK@@ ~*/ + const buttonBg = buttonRoot(".consequence-button-bg"); + const buttonIcon = buttonRoot(".button-icon i"); + const buttonLabel = buttonRoot(".consequence-button-label"); + /*~ @@DOUBLE-BLANK@@ ~*/ + const tl = U.gsap.timeline({ paused: true }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Fade out all accept elems and resist elems + tl.to([...acceptElems, ...resistElems], { + opacity: 0, + duration: 0.25, + ease: "sine.out" + }); + /*~ @@DOUBLE-BLANK@@ ~*/ + if (typeLine) { + // Slide out .consequence-type.special-armor-consequence from left + tl.fromTo(typeLine, { + x: -15, + scaleX: 0, + opacity: 1, + color: C.Colors.dBLUE + }, { + x: 0, + scaleX: 1, + opacity: 1, + color: C.Colors.dBLUE, + duration: 0.5, + ease: "back.out" + }, 0); + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + if (footerBg) { + // Slide out .consequence-footer-bg.special-armor-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); + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Slide out .consequence-special-armor-message from left + if (footerMsg) { + tl.fromTo(footerMsg, { + scaleX: 0, + opacity: 1 + }, { + scaleX: 1, + opacity: 1, + duration: 0.5, + ease: "back.inOut" + }, 0); + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Slide out .consequence-name.special-armor-consequence from left + if (specialArmorCsqName) { + tl.fromTo(specialArmorCsqName, { + scaleX: 0, + opacity: 1 + }, { + scaleX: 1, + opacity: 1, + duration: 0.5, + ease: "back.inOut" + }, 0); + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Fade in .consequence-icon-circle.special-armor-consequence + tl.fromTo(iconCircle, { + opacity: 0 + }, { + opacity: 1, + duration: 0.5, + ease: "back.out" + }, 0); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + // 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); + /*~ @@DOUBLE-BLANK@@ ~*/ + return tl; + }, + defaults: {} + }, blurRemove: { effect: (targets, config) => U.gsap.timeline() .to(targets, { @@ -46,6 +538,8 @@ const gsapEffects = { slideUp: { effect: (targets) => U.gsap.to(targets, { height: 0, + // PaddingTop: 0, + // paddingBottom: 0, duration: 0.5, ease: "power3" }), @@ -90,15 +584,21 @@ const gsapEffects = { extendTimeline: true }, pulseClockWedges: { - effect: (targets, config) => U.gsap.timeline({ duration: 0 }), + effect: () => U.gsap.timeline({ duration: 0 }), defaults: {} }, reversePulseClockWedges: { - effect: (targets, config) => U.gsap.timeline({ duration: 0 }), + effect: () => U.gsap.timeline({ duration: 0 }), defaults: {} }, fillCoins: { effect: (targets, config) => { + // Targets will be all coins from zero to where fill currently is + // Some will already be full, others not. + // Stagger in timeline + // Pulse in size and color + // Shimmer as they shrink back ? + /*~ @@DOUBLE-BLANK@@ ~*/ return U.gsap.effects.throb(targets, { stagger: { amount: 0.25, from: "start", @@ -114,16 +614,19 @@ const gsapEffects = { if (!tooltip) { return tl; } + // Tooltip = $(tooltip); + /*~ @@DOUBLE-BLANK@@ ~*/ if (config.scalingElems.length > 0) { tl.to(config.scalingElems, { scale: "+=0.2", filter: "none", - color: "rgba(255, 255, 255, 1)", + color: C.Colors.WHITE, opacity: 1, duration: 0.125, ease: "back" }, 0.5); } + /*~ @@DOUBLE-BLANK@@ ~*/ if (tooltip) { tl.fromTo(tooltip, { filter: "blur(50px)", @@ -138,6 +641,7 @@ const gsapEffects = { ease: "power2" }, 1); } + /*~ @@DOUBLE-BLANK@@ ~*/ return tl; }, defaults: { @@ -146,6 +650,10 @@ const gsapEffects = { } } }; +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Registers relevant GSAP plugins and effects. + */ export function Initialize() { if (gsapPlugins.length) { U.gsap.registerPlugin(...gsapPlugins); @@ -154,6 +662,11 @@ export function Initialize() { U.gsap.registerEffect(Object.assign(effect, { name })); }); } +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Applies listeners to '.tooltip-trigger' elements in the document. + * @param {JQuery} html The document to be searched. + */ export function ApplyTooltipListeners(html) { html.find(".tooltip-trigger").each((_, el) => { const tooltipElem = $(el).find(".tooltip")[0] ?? $(el).next(".tooltip")[0]; @@ -178,118 +691,146 @@ export function ApplyTooltipListeners(html) { }); }); } +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Applies listeners to .consequence-display-container and children found in document. + * @param {JQuery} html The document to be searched. + */ export function ApplyConsequenceListeners(html) { + /** + * TIMELINES + * .comp.consequence-display-container:mouseenter + * = fade in grey interaction buttons + * ...:mouseleave = reverse + * + * .consequence-accept-button-container:mouseenter + * = turn type line white, text shadow + * slide out .consequence-accept-button-bg from left + * turn .consequence-accept-button i black, and scale + * turn .consequence-accept-button-label black, add letter spacing, bold + * ...:mouseleave = reverse + * + * .consequence-resist-button-container:mouseenter + * = slide in .consequence-type-bg.base-consequence to left + * fade out all .base-consequence:not(.consequence-type-bg) + * slide out .consequence-type.resist-consequence from left + * slide out .consequence-resist-button-bg from right + * slide out .consequence-footer-bg.resist-consequence from left + * slide out .consequence-resist-attribute from left + * slide out .consequence-name.resist-consequence from left + * fade in .consequence-icon-circle.resist-consequence + * ...:mouseleave = reverse + * --> IF resistedTo.type === "None", blurRemove the base_consequence name and type instead of sliding them in, + * and don't slide the resistance ones out at all. + * */ + /*~ @@DOUBLE-BLANK@@ ~*/ html .find(".comp.consequence-display-container") .each((_i, csqContainer) => { - const resistButton = $(csqContainer).find(".consequence-resist-button-container")[0]; - const acceptButton = $(csqContainer).find(".consequence-accept-button-container")[0]; - const specialArmorButton = $(csqContainer).find(".consequence-special-armor-button-container")[0]; - const baseElems = Array.from($(csqContainer).find(".base-consequence")); - const resistElems = Array.from($(csqContainer).find(".resist-consequence")); - const specialArmorElems = Array.from($(csqContainer).find(".special-armor-consequence")); - if (resistButton) { - $(resistButton) - .on({ - mouseenter: function () { - $(resistButton).css("z-index", 10); - U.gsap.to(resistButton, { - scale: 1.25, - filter: "brightness(1.25)", - duration: 0.25, - ease: "sine.out" - }); - U.gsap.to(acceptButton, { - scale: 0.8, - filter: "greyscale(1)", - duration: 0.5, - ease: "sine.inOut" - }); - U.gsap.to(specialArmorButton, { - scale: 0.8, - filter: "greyscale(1)", - duration: 0.5, - ease: "sine.inOut" - }); - U.gsap.to(baseElems, { - opacity: 0, - duration: 0.5, - ease: "sine.out" - }); - U.gsap.to(resistElems, { - opacity: 1, - duration: 0.5, - ease: "sine.out" - }); - }, - mouseleave: function () { - U.gsap.to(resistButton, { - scale: 1, - filter: "brightness(1)", - duration: 0.25, - ease: "sine.out" - }).then(() => { - $(resistButton).css("z-index", ""); - }); - U.gsap.to(acceptButton, { - scale: 1, - filter: "", - duration: 0.5, - ease: "sine.inOut" - }); - U.gsap.to(specialArmorButton, { - scale: 1, - filter: "", - duration: 0.5, - ease: "sine.inOut" - }); - U.gsap.to(baseElems, { - opacity: 1, - duration: 0.5, - ease: "sine.out" - }); - U.gsap.to(resistElems, { - opacity: 0, - duration: 0.5, - ease: "sine.out" + /*~ @@DOUBLE-BLANK@@ ~*/ + const iconContainer$ = $(csqContainer).find(".consequence-icon-container"); + /*~ @@DOUBLE-BLANK@@ ~*/ + const acceptInteractionPad$ = $(csqContainer).find(".accept-consequence-pad"); + const resistInteractionPad$ = $(csqContainer).find(".resist-consequence-pad"); + const specialArmorInteractionPad$ = $(csqContainer).find(".special-armor-consequence-pad"); + /*~ @@DOUBLE-BLANK@@ ~*/ + $(csqContainer).data("hoverTimeline", U.gsap.effects.csqEnter(csqContainer)); + $(csqContainer).on({ + mouseenter: function () { + $(csqContainer).css("z-index", 10); + $(csqContainer).data("hoverTimeline").play(); + }, + mouseleave: function () { + if (!(iconContainer$.data("isToggled") || iconContainer$.data("isTogglingOn")) || iconContainer$.data("isTogglingOff")) { + $(csqContainer).data("hoverTimeline").reverse().then(() => { + $(csqContainer).css("z-index", ""); }); } - }); - } - if (acceptButton) { - $(acceptButton) - .on({ - mouseenter: function () { - $(acceptButton).css("z-index", 10); - U.gsap.to(acceptButton, { - scale: 1.25, - filter: "brightness(1.25)", - duration: 0.25, - ease: "sine.out" - }); - U.gsap.to(baseElems, { - filter: "brightness(1.25)", - duration: 0.5, - ease: "sine.out" + } + }); + /*~ @@DOUBLE-BLANK@@ ~*/ + iconContainer$.data("clickTimeline", U.gsap.effects.csqClickIcon(iconContainer$[0])); + iconContainer$.on({ + click: function () { + if (iconContainer$.data("isToggled") || iconContainer$.data("isTogglingOn")) { + iconContainer$.data("isTogglingOn", false); + iconContainer$.data("isTogglingOff", true); + iconContainer$.data("clickTimeline").reverse().then(() => { + iconContainer$.data("isTogglingOff", false); + iconContainer$.data("isToggled", false); }); - }, - mouseleave: function () { - U.gsap.to(acceptButton, { - scale: 1, - filter: "brightness(1)", - duration: 0.25, - ease: "sine.out" - }).then(() => { - $(acceptButton).css("z-index", ""); + } + else { + iconContainer$.data("isTogglingOn", true); + iconContainer$.data("isTogglingOff", false); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Find any siblings with toggled-on iconContainers, and toggle them off + Array.from($(csqContainer).siblings(".consequence-display-container")) + .forEach((containerElem) => { + const iContainer$ = $(containerElem).find(".consequence-icon-container"); + if (iContainer$?.data("isToggled") || iContainer$?.data("isTogglingOn")) { + iContainer$.data("isTogglingOn", false); + iContainer$.data("isTogglingOff", true); + iContainer$.data("clickTimeline").reverse().then(() => { + iContainer$.data("isTogglingOff", false); + iContainer$.data("isToggled", false); + $(containerElem).data("hoverTimeline").reverse().then(() => { + $(containerElem).css("z-index", ""); + }); + }); + } }); - U.gsap.to(baseElems, { - filter: "brightness(1)", - duration: 0.5, - ease: "sine.out" + iconContainer$.data("clickTimeline").play().then(() => { + iconContainer$.data("isTogglingOn", false); + iconContainer$.data("isToggled", true); }); } - }); - } + } + }); + /*~ @@DOUBLE-BLANK@@ ~*/ + acceptInteractionPad$.data("hoverTimeline", U.gsap.effects.csqEnterAccept(csqContainer)); + acceptInteractionPad$.on({ + mouseenter: function () { + if (iconContainer$.data("isToggled")) { + acceptInteractionPad$.data("hoverTimeline").play(); + } + }, + mouseleave: function () { + acceptInteractionPad$.data("hoverTimeline").reverse(); + } + }); + /*~ @@DOUBLE-BLANK@@ ~*/ + resistInteractionPad$.data("hoverTimeline", U.gsap.effects.csqEnterResist(csqContainer)); + resistInteractionPad$.on({ + mouseenter: function () { + if (iconContainer$.data("isToggled")) { + resistInteractionPad$.data("hoverTimeline").play(); + } + }, + mouseleave: function () { + if (iconContainer$.data("isToggled")) { + resistInteractionPad$.data("hoverTimeline").reverse(); + } + } + }); + /*~ @@DOUBLE-BLANK@@ ~*/ + specialArmorInteractionPad$.data("hoverTimeline", U.gsap.effects.csqEnterSpecialArmor(csqContainer)); + specialArmorInteractionPad$.on({ + mouseenter: function () { + if (iconContainer$.data("isToggled")) { + specialArmorInteractionPad$.data("hoverTimeline").play(); + } + }, + mouseleave: function () { + if (iconContainer$.data("isToggled")) { + specialArmorInteractionPad$.data("hoverTimeline").reverse(); + } + } + }); + /*~ @@DOUBLE-BLANK@@ ~*/ }); + /*~ @@DOUBLE-BLANK@@ ~*/ } -export default U.gsap; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default U.gsap; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/core/helpers.js b/module/core/helpers.js index 323c8ed8..19205338 100644 --- a/module/core/helpers.js +++ b/module/core/helpers.js @@ -1,15 +1,19 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import U from "./utilities.js"; -import { SVGDATA } from "./constants.js"; - -export async function preloadHandlebarsTemplates() { - const templatePaths = [ +// #region ▮▮▮▮▮▮▮ IMPORTS ▮▮▮▮▮▮▮ ~ +import U from "./utilities.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ +import { SVGDATA } from "./constants.js"; +// #endregion ▮▮▮▮[IMPORTS]▮▮▮▮ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ░░░░░░░[Templates]░░░░ Preload Partials, Components & Overlay Templates ░░░░░░░ ~ +/** + * Define a set of template paths to pre-load + * Pre-loaded templates are compiled and cached for fast access when rendering + */ +export async function preloadHandlebarsTemplates() { + // Define template paths to load + const templatePaths = [ + /*~ @@DOUBLE-BLANK@@ ~*/ + // General Components "systems/eunos-blades/templates/components/toggle-icon.hbs", "systems/eunos-blades/templates/components/button-icon.hbs", "systems/eunos-blades/templates/components/dotline.hbs", @@ -21,19 +25,27 @@ export async function preloadHandlebarsTemplates() { "systems/eunos-blades/templates/components/roll-collab-mod.hbs", "systems/eunos-blades/templates/components/roll-collab-opposition.hbs", "systems/eunos-blades/templates/components/slide-out-controls.hbs", - "systems/eunos-blades/templates/components/consequence.hbs", + "systems/eunos-blades/templates/components/consequence.hbs", + /*~ @@DOUBLE-BLANK@@ ~*/ + // Partials "systems/eunos-blades/templates/parts/tier-block.hbs", "systems/eunos-blades/templates/parts/turf-list.hbs", "systems/eunos-blades/templates/parts/cohort-block.hbs", "systems/eunos-blades/templates/parts/roll-opposition-creator.hbs", "systems/eunos-blades/templates/parts/active-effects.hbs", - "systems/eunos-blades/templates/parts/gm-pc-summary.hbs", + "systems/eunos-blades/templates/parts/gm-pc-summary.hbs", + /*~ @@DOUBLE-BLANK@@ ~*/ + // Overlays "systems/eunos-blades/templates/overlays/clock-overlay.hbs", "systems/eunos-blades/templates/overlays/clock-key.hbs" - ]; + ]; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Load the template parts return loadTemplates(templatePaths); -} - +} +// #endregion ░░░░[Preload Templates]░░░░ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ Handlebars: Handlebar Helpers Definitions ████████ ~ const handlebarHelpers = { randString(param1 = 10) { return U.randString(param1); @@ -151,7 +163,9 @@ const handlebarHelpers = { return param.length; } return param ? 1 : 0; - }, + }, + // Concat helper + // Usage: (concat 'first 'second') concat(...args) { let outStr = ""; for (const arg of args) { @@ -160,7 +174,8 @@ const handlebarHelpers = { } } return outStr; - }, + }, + // For loop: {{#for [from = 0, to, stepSize = 1]}}{{/for}} for: (...args) => { const options = args.pop(); let [from, to, stepSize] = args; @@ -202,8 +217,10 @@ const handlebarHelpers = { dbLevel = args.shift(); } eLog.hbsLog(...args, dbLevel); - }, - isTurfBlock: (name) => U.fuzzyMatch(name, "Turf"), + }, + // Does the name of this turf block represent a standard 'Turf' claim? + isTurfBlock: (name) => U.fuzzyMatch(name, "Turf"), + // Which other connection does this connector overlap with? getConnectorPartner: (index, direction) => { index = parseInt(`${index}`, 10); const partners = { @@ -229,7 +246,8 @@ const handlebarHelpers = { return `${partnerNum}-${partnerDir}`; } return null; - }, + }, + // Is the value Turf side. isTurfOnEdge: (index, direction) => { index = parseInt(`${index}`, 10); const edges = { @@ -253,38 +271,53 @@ const handlebarHelpers = { return true; } return edges[index].includes(direction); - }, + }, + // Multiboxes multiboxes(selected, options) { let html = options.fn(this); - selected = [selected].flat(1); + selected = [selected].flat(1); + /*~ @@DOUBLE-BLANK@@ ~*/ selected.forEach((selectedVal) => { if (selectedVal !== false) { const escapedValue = RegExp.escape(Handlebars.escapeExpression(String(selectedVal))); const rgx = new RegExp(` value="${escapedValue}"`); html = html.replace(rgx, "$& checked=\"checked\""); } - }); + }); + /*~ @@DOUBLE-BLANK@@ ~*/ return html; }, repturf: (turfsAmount, options) => { let html = options.fn(this); - let turfsAmountInt = parseInt(turfsAmount, 10); + let turfsAmountInt = parseInt(turfsAmount, 10); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Can't be more than 6. if (turfsAmountInt > 6) { turfsAmountInt = 6; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ for (let i = 13 - turfsAmountInt; i <= 12; i++) { const rgx = new RegExp(` value="${i}"`); html = html.replace(rgx, "$& disabled=\"disabled\""); - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ return html; } -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ handlebarHelpers.eLog1 = function (...args) { handlebarHelpers.eLog(...[1, ...args.slice(0, 7)]); }; handlebarHelpers.eLog2 = function (...args) { handlebarHelpers.eLog(...[2, ...args.slice(0, 7)]); }; handlebarHelpers.eLog3 = function (...args) { handlebarHelpers.eLog(...[3, ...args.slice(0, 7)]); }; handlebarHelpers.eLog4 = function (...args) { handlebarHelpers.eLog(...[4, ...args.slice(0, 7)]); }; -handlebarHelpers.eLog5 = function (...args) { handlebarHelpers.eLog(...[5, ...args.slice(0, 7)]); }; -Object.assign(handlebarHelpers); +handlebarHelpers.eLog5 = function (...args) { handlebarHelpers.eLog(...[5, ...args.slice(0, 7)]); }; +/*~ @@DOUBLE-BLANK@@ ~*/ +Object.assign(handlebarHelpers); +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * + */ export function registerHandlebarHelpers() { Object.entries(handlebarHelpers).forEach(([name, func]) => Handlebars.registerHelper(name, func)); -} \ No newline at end of file +} +// #endregion ▄▄▄▄▄ Handlebars ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/core/logger.js b/module/core/logger.js index fccebe47..72a05f52 100644 --- a/module/core/logger.js +++ b/module/core/logger.js @@ -1,40 +1,35 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import U from "./utilities.js"; -import C from "./constants.js"; +import C from "./constants.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ const LOGGERCONFIG = { fullName: "eLogger", aliases: ["dbLog"], stackTraceExclusions: { - handlebars: [/scripts\/handlebars/] + handlebars: [/scripts\/handlebars/] // From internal Handlebars module } -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ const STYLES = { base: { background: C.Colors.BLACK, - color: C.Colors.bGOLD, + color: C.Colors.dGOLD, "font-family": "Pragmata Pro", padding: "0 25px", "margin-right": "25px" }, log0: { - background: C.Colors.bGOLD, + background: C.Colors.dGOLD, color: C.Colors.dBLACK, "font-size": "16px" }, log1: { background: C.Colors.dBLACK, - color: C.Colors.gGOLD, + color: C.Colors.bGOLD, "font-size": "16px" }, log2: { background: C.Colors.dBLACK, - color: C.Colors.bGOLD, + color: C.Colors.dGOLD, "font-size": "16px" }, log3: { @@ -49,15 +44,15 @@ const STYLES = { "font-size": "10px" }, display: { - color: C.Colors.gGOLD, + color: C.Colors.bGOLD, "font-family": "Kirsty Rg", "font-size": "16px", "margin-left": "-100px", padding: "0 100px" }, error: { - color: C.Colors.gRED, - background: C.Colors.dRED, + color: C.Colors.bRED, + background: C.Colors.ddRED, "font-weight": 500 }, handlebars: { @@ -73,12 +68,14 @@ const STYLES = { "font-size": "10px", "font-family": "Pragmata Pro" } -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ const eLogger = (type = "base", ...content) => { if (!(type === "error" || CONFIG.debug.logging)) { return; } - const lastElem = U.getLast(content); + const lastElem = U.getLast(content); + /*~ @@DOUBLE-BLANK@@ ~*/ let dbLevel = typeof lastElem === "number" && [0, 1, 2, 3, 4, 5].includes(lastElem) ? content.pop() : 3; @@ -86,8 +83,10 @@ const eLogger = (type = "base", ...content) => { if (type === "checkLog") { key = content.shift(); type = `log${dbLevel}`; - } - const [message, ...data] = content; + } + /*~ @@DOUBLE-BLANK@@ ~*/ + const [message, ...data] = content; + /*~ @@DOUBLE-BLANK@@ ~*/ if (key) { const blacklist = (U.getSetting("blacklist") ?? "").split(/,/).map((pat) => new RegExp(`\\b${pat.trim()}\\b`, "igu")); const whitelist = (U.getSetting("whitelist") ?? "").split(/,/).map((pat) => new RegExp(`\\b${pat.trim()}\\b`, "igu")); @@ -112,7 +111,8 @@ const eLogger = (type = "base", ...content) => { const styleLine = Object.entries({ ...STYLES.base, ...STYLES[type] ?? {} - }).map(([prop, val]) => `${prop}: ${val};`).join(" "); + }).map(([prop, val]) => `${prop}: ${val};`).join(" "); + /*~ @@DOUBLE-BLANK@@ ~*/ let logFunc; if (stackTrace) { logFunc = console.groupCollapsed; @@ -122,7 +122,8 @@ const eLogger = (type = "base", ...content) => { } else { logFunc = console.group; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ if (data.length === 0) { if (typeof message === "string") { logFunc(`%c${message}`, styleLine); @@ -143,12 +144,17 @@ const eLogger = (type = "base", ...content) => { }); } if (stackTrace) { - console.group("%cSTACK TRACE", `color: ${C.Colors.bGOLD}; font-family: "Pragmata Pro"; font-size: 12px; background: ${C.Colors.BLACK}; font-weight: bold; padding: 0 10px;`); + console.group("%cSTACK TRACE", `color: ${C.Colors.dGOLD}; font-family: "Pragmata Pro"; font-size: 12px; background: ${C.Colors.BLACK}; font-weight: bold; padding: 0 10px;`); console.log(`%c${stackTrace}`, Object.entries(STYLES.stack).map(([prop, val]) => `${prop}: ${val};`).join(" ")); console.groupEnd(); } - console.groupEnd(); - function getStackTrace(regExpFilters = []) { + console.groupEnd(); + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * + * @param regExpFilters + */ + function getStackTrace(regExpFilters = []) { regExpFilters.push(new RegExp(`at (getStackTrace|${LOGGERCONFIG.fullName}|${LOGGERCONFIG.aliases.map(String).join("|")}|Object\\.(log|display|hbsLog|error))`), /^Error/); return ((new Error()).stack ?? "") .split(/\n/) @@ -175,5 +181,7 @@ const logger = { checkLog5: (...content) => eLogger("checkLog", ...content, 5), error: (...content) => eLogger("error", ...content), hbsLog: (...content) => eLogger("handlebars", ...content) -}; -export default logger; \ No newline at end of file +}; +/*~ @@DOUBLE-BLANK@@ ~*/ +export default logger; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/core/settings.js b/module/core/settings.js index 70af61a7..9f2523e1 100644 --- a/module/core/settings.js +++ b/module/core/settings.js @@ -1,12 +1,6 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import U from "./utilities.js"; -import C from "./constants.js"; +import C from "./constants.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ const registerSettings = function () { game.settings.register("eunos-blades", "debug", { name: "Debug Level", @@ -19,7 +13,7 @@ const registerSettings = function () { max: 5, step: 1 }, - default: 3 + default: 3 // The default value for the setting }); game.settings.register("eunos-blades", "openAPIModelLevel", { name: "AI Base Quality", @@ -39,7 +33,7 @@ const registerSettings = function () { scope: "client", config: true, type: String, - default: "" + default: "" // The default value for the setting }); game.settings.register("eunos-blades", "openAPIKey", { name: "OpenAI API Key", @@ -47,7 +41,7 @@ const registerSettings = function () { scope: "client", config: true, type: String, - default: "" + default: "" // The default value for the setting }); game.settings.register("eunos-blades", "whitelist", { name: "Debug Whitelist", @@ -55,16 +49,23 @@ const registerSettings = function () { scope: "client", config: true, type: String, - default: "" + default: "" // The default value for the setting }); - game.settings.register("eunos-blades", "systemMigrationVersion", { + /** + * Track the system version upon which point a migration was last applied + */ + game.settings.register("eunos-blades", "systemMigrationVersion", { name: "System Migration Version", scope: "world", config: false, type: Number, default: 0 }); -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * + */ export function initTinyMCEStyles() { CONFIG.TinyMCE = { ...CONFIG.TinyMCE, @@ -83,7 +84,8 @@ export function initTinyMCEStyles() { branding: false, resize: false, plugins: "lists image table code save autoresize searchreplace quickbars template", - save_enablewhendirty: false, + save_enablewhendirty: false, + // Table_default_styles: {}, style_formats: [ { title: "Headings", @@ -98,7 +100,8 @@ export function initTinyMCEStyles() { title: "Blocks", items: [ { title: "Paragraph", block: "p", wrapper: false }, - { title: "Block Quote", block: "blockquote", wrapper: true } + { title: "Block Quote", block: "blockquote", wrapper: true } + // {title: "Secret", block: "span", classes: "text-secret", attributes: {"data-is-secret": "true"}, wrapper: false} ] }, { @@ -142,7 +145,11 @@ export function initTinyMCEStyles() { quickbars_table_toolbar: "tableprops tabledelete | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol" } }; -} +} +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * + */ export function initCanvasStyles() { CONFIG.canvasTextStyle = new PIXI.TextStyle({ align: "center", @@ -173,5 +180,7 @@ export function initCanvasStyles() { wordWrap: true, wordWrapWidth: 0.1 }); -} -export default registerSettings; \ No newline at end of file +} +/*~ @@DOUBLE-BLANK@@ ~*/ +export default registerSettings; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/core/tags.js b/module/core/tags.js index b736ecea..85a68cf1 100644 --- a/module/core/tags.js +++ b/module/core/tags.js @@ -1,13 +1,7 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import Tagify from "../../lib/tagify/tagify.esm.js"; import { Tag, MainDistrict, OtherDistrict, Vice, Playbook, BladesActorType } from "./constants.js"; import U from "./utilities.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ const _onTagifyChange = (event, doc, targetKey) => { const tagString = event.target.value; if (tagString) { @@ -18,9 +12,18 @@ const _onTagifyChange = (event, doc, targetKey) => { doc.update({ [targetKey]: [] }); } }; +/*~ @@DOUBLE-BLANK@@ ~*/ const Tags = { InitListeners: (html, doc) => { - function makeTagInput(elem, tags) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Applies tags and Tagify functionality to a specified HTML element. + * @param {HTMLElement} elem The element to tagify. + * @param {Record} tags The tags, sorted into groups, to apply. + */ + function makeTagInput(elem, tags) { + /*~ @@DOUBLE-BLANK@@ ~*/ + // Create tagify instance; populate dropdown list with tags const tagify = new Tagify(elem, { enforceWhitelist: true, editTags: false, @@ -38,30 +41,45 @@ const Tags = { appendTarget: html[0] } }); + /*~ @@DOUBLE-BLANK@@ ~*/ tagify.dropdown.createListHTML = (optionsArr) => { const map = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ return structuredClone(optionsArr) .map((suggestion, idx) => { + /*~ @@DOUBLE-BLANK@@ ~*/ const value = tagify.dropdown.getMappedValue.call(tagify, suggestion); let tagHTMLString = ""; + /*~ @@DOUBLE-BLANK@@ ~*/ if (!map[suggestion["data-group"]]) { map[suggestion["data-group"]] = true; + /*~ @@DOUBLE-BLANK@@ ~*/ if (Object.keys(map).length) { tagHTMLString += ""; } + /*~ @@DOUBLE-BLANK@@ ~*/ tagHTMLString += `

${suggestion["data-group"]}

`; } + /*~ @@DOUBLE-BLANK@@ ~*/ suggestion.value = value && typeof value === "string" ? U.escapeHTML(value) : value; + /*~ @@DOUBLE-BLANK@@ ~*/ tagHTMLString += tagify.settings.templates.dropdownItem.apply(tagify, [suggestion, idx]); + /*~ @@DOUBLE-BLANK@@ ~*/ return tagHTMLString; }) .join(""); }; - function findDataGroup(tag) { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Returns the tag group to which a tag belongs, or false if no group found. + * @param {BladesTag|string} tag + * @returns {string|false} Either the group containing the given tag, or false if no group found. + */ + function findDataGroup(tag) { for (const [group, tagList] of Object.entries(tags)) { if (tagList.includes(tag)) { return group; @@ -69,6 +87,8 @@ const Tags = { } return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Check if element specifies an alternate schema target from doc.tags const targetKey = $(elem).data("tagTarget") ?? "system.tags"; const curTags = [getProperty(doc, targetKey) ?? []].flat().filter(Boolean); tagify.addTags(curTags @@ -77,9 +97,12 @@ const Tags = { value: (new Handlebars.SafeString(tag)).toString(), "data-group": findDataGroup(tag) })), true, true); - + /*~ @@DOUBLE-BLANK@@ ~*/ + // Add event listener for tag changes, setting defined target + // Wait briefly, so other tag elements' tags can be set before listener initializes setTimeout(() => elem.addEventListener("change", (event) => { _onTagifyChange(event, doc, targetKey); }), 1000); } + /*~ @@DOUBLE-BLANK@@ ~*/ const systemTags = { "System Tags": Object.values(Tag.System), "Gear Tags": [ @@ -102,9 +125,15 @@ const Tags = { const factionTags = { Factions: game.actors .filter((actor) => actor.type === BladesActorType.faction && actor.name !== null) .map((faction) => faction.name) }; + /*~ @@DOUBLE-BLANK@@ ~*/ $(html).find(".tags-gm").each((_, e) => makeTagInput(e, systemTags)); + /*~ @@DOUBLE-BLANK@@ ~*/ $(html).find(".tags-district").each((_, e) => makeTagInput(e, districtTags)); + /*~ @@DOUBLE-BLANK@@ ~*/ $(html).find(".tags-faction").each((_, e) => makeTagInput(e, factionTags)); + /*~ @@DOUBLE-BLANK@@ ~*/ } }; -export default Tags; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default Tags; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/core/utilities.js b/module/core/utilities.js index 47d4a5ef..18c7b4a2 100644 --- a/module/core/utilities.js +++ b/module/core/utilities.js @@ -1,18 +1,22 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import C from "./constants.js"; -import { gsap } from "/scripts/greensock/esm/all.js"; +// #region ▮▮▮▮▮▮▮ IMPORTS ▮▮▮▮▮▮▮ ~ +import C from "./constants.js"; +// eslint-disable-next-line import/no-unresolved +import { gsap } from "/scripts/greensock/esm/all.js"; +// #endregion ▮▮▮▮ IMPORTS ▮▮▮▮ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ▮▮▮▮▮▮▮ [HELPERS] Internal Functions, Data & References Used by Utility Functions ▮▮▮▮▮▮▮ ~ +/*~ @@DOUBLE-BLANK@@ ~*/ +// _noCapWords -- Patterns matching words that should NOT be capitalized when converting to TITLE case. const _noCapWords = "a|above|after|an|and|at|below|but|by|down|for|for|from|in|nor|of|off|on|onto|or|out|so|the|to|under|up|with|yet" .split("|") - .map((word) => new RegExp(`\\b${word}\\b`, "gui")); + .map((word) => new RegExp(`\\b${word}\\b`, "gui")); +/*~ @@DOUBLE-BLANK@@ ~*/ +// _capWords -- Patterns matching words that should ALWAYS be capitalized when converting to SENTENCE case. const _capWords = [ "I", /[^a-z]{3,}|[.0-9]/gu -].map((word) => (/RegExp/.test(Object.prototype.toString.call(word)) ? word : new RegExp(`\\b${word}\\b`, "gui"))); +].map((word) => (/RegExp/.test(Object.prototype.toString.call(word)) ? word : new RegExp(`\\b${word}\\b`, "gui"))); +/*~ @@DOUBLE-BLANK@@ ~*/ +// _loremIpsumText -- Boilerplate lorem ipsum const _loremIpsumText = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse ultricies nibh sed massa euismod lacinia. Aliquam nec est ac nunc ultricies scelerisque porta vulputate odio. Integer gravida mattis odio, semper volutpat tellus. Ut elit leo, auctor eget fermentum hendrerit, @@ -26,7 +30,9 @@ ut dui vel leo laoreet sodales nec ac tellus. In hac habitasse platea dictumst. sollicitudin interdum. Sed id lacus porttitor nisi vestibulum tincidunt. Nulla facilisi. Vestibulum feugiat finibus magna in pretium. Proin consectetur lectus nisi, non commodo lectus tempor et. Cras viverra, mi in consequat aliquet, justo mauris fringilla tellus, at accumsan magna metus in eros. Sed -vehicula, diam ut sagittis semper, purus massa mattis dolor, in posuere.`; +vehicula, diam ut sagittis semper, purus massa mattis dolor, in posuere.`; +/*~ @@DOUBLE-BLANK@@ ~*/ +// _randomWords -- A collection of random words for various debugging purposes. const _randomWords = ` aboveboard|account|achiever|acoustics|act|action|activity|actor|addition|adjustment|advertisement|advice|afterglow|afterimage|afterlife|aftermath|afternoon|afterthought|agreement air|aircraft|airfield|airlift|airline|airmen|airplane|airport|airtime|alarm|allover|allspice|alongside|also|amount|amusement|anger|angle|animal|another|ants|anyhow|anymore @@ -129,9 +135,17 @@ const _romanNumerals = { ["", "ↈ", "ↈↈ", "ↈↈↈ"] ] }; -const UUIDLOG = []; - -const GMID = () => game?.user?.find((user) => user.isGM)?.id ?? false; +const UUIDLOG = []; +/*~ @@DOUBLE-BLANK@@ ~*/ +// #endregion ▮▮▮▮[HELPERS]▮▮▮▮ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ GETTERS: Basic Data Lookup & Retrieval ████████ ~ +// @ts-expect-error Leauge of foundry developers is wrong about user not being on game. +const GMID = () => game?.user?.find((user) => user.isGM)?.id ?? false; +// #endregion ▄▄▄▄▄ GETTERS ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ TYPES: Type Checking, Validation, Conversion, Casting ████████ ~ +/*~ @@DOUBLE-BLANK@@ ~*/ const isNumber = (ref) => typeof ref === "number" && !isNaN(ref); const isNumberString = (ref) => typeof ref === "string" && !isNaN(parseFloat(ref)) @@ -156,57 +170,98 @@ const isEmpty = (ref) => Object.keys(ref).length === 0; const hasItems = (ref) => !isEmpty(ref); const isInstance = (classRef, ref) => ref instanceof classRef; const isNullish = (ref) => isUndefined(ref) || ref === null; +/** + * Asserts that a given value is of a specified type. + * Throws an error if the value is not of the expected type. + * + * @template T The expected type of the value. + * @param {unknown} val The value to check. + * @param {(new(...args: unknown[]) => T) | string} type The expected type of the value. + * @throws {Error} If the value is not of the expected type. + */ function assertNonNullType(val, type) { - let valStr; + let valStr; + // Attempt to convert the value to a string for error messaging. try { valStr = JSON.stringify(val); } catch { valStr = String(val); - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Check if the value is undefined if (val === undefined) { throw new Error(`Value ${valStr} is undefined!`); - } - if (typeof type === "string") { + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If the type is a string, compare the typeof the value to the type string. + if (typeof type === "string") { + // eslint-disable-next-line valid-typeof if (typeof val !== type) { throw new Error(`Value ${valStr} is not a ${type}!`); } } - else if (!(val instanceof type)) { + else if (!(val instanceof type)) { + // If the type is a function (constructor), check if the value is an instance of the type. throw new Error(`Value ${valStr} is not a ${type.name}!`); } } -const areFuzzyEqual = (val1, val2) => { +/** + * Checks if two values are "fuzzy" equal, simulating the behavior of the "==" operator. + * This function does not use the "==" operator directly to comply with linting rules. + * + * @param {unknown} val1 The first value to compare. + * @param {unknown} val2 The second value to compare. + * @returns {boolean} True if the values are "fuzzy" equal, false otherwise. + */ +const areFuzzyEqual = (val1, val2) => { + // If both values are null or undefined, they are considered equal if ([null, undefined].includes(val1) && [null, undefined].includes(val2)) { return true; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If only one of the values is null or undefined, they are not equal if ([null, undefined].includes(val1) || [null, undefined].includes(val2)) { return false; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If both values are numbers, they are considered equal if they are numerically equal if (typeof val1 === "number" && typeof val2 === "number") { return val1 === val2; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If both values are booleans, they are considered equal if they are both true or both false if (typeof val1 === "boolean" && typeof val2 === "boolean") { return val1 === val2; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If both values are strings, they are considered equal if they are identical if (typeof val1 === "string" && typeof val2 === "string") { return val1 === val2; - } - + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If one value is a number and the other is a string, they are considered + // equal if the string can be converted to the number if (typeof val1 === "number" && typeof val2 === "string") { return val1 === Number(val2); } if (typeof val1 === "string" && typeof val2 === "number") { return Number(val1) === val2; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If one value is a boolean and the other is a non-null object, they are not equal if (typeof val1 === "boolean" && typeof val2 === "object") { return false; } if (typeof val1 === "object" && typeof val2 === "boolean") { return false; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If one value is a boolean and the other is a string, they are considered equal ID: + // ... the boolean is true and the string is not empty, or + // ... the boolean is false and the string is empty if (typeof val1 === "boolean" && typeof val2 === "string") { return (val1 && val2 !== "") || (!val1 && val2 === ""); @@ -214,18 +269,25 @@ const areFuzzyEqual = (val1, val2) => { if (typeof val1 === "string" && typeof val2 === "boolean") { return (val2 && val1 !== "") || (!val2 && val1 === ""); - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If one value is a number or a string and the other is an object, they are not equal if ((typeof val1 === "number" || typeof val1 === "string") && typeof val2 === "object") { return false; } if (typeof val1 === "object" && (typeof val2 === "number" || typeof val2 === "string")) { return false; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If both values are objects, they are considered equal if they are identical if (typeof val1 === "object" && typeof val2 === "object") { return val1 === val2; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If none of the above conditions are met, the values are not equal return false; -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ const areEqual = (...refs) => { do { const ref = refs.pop(); @@ -266,10 +328,15 @@ const getKey = (key, obj) => { return obj[key]; } return null; -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ const FILTERS = { IsInstance: ((classRef) => ((item) => typeof classRef === "function" && item instanceof classRef)) -}; +}; +// #endregion ▄▄▄▄▄ TYPES ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ STRINGS: String Parsing, Manipulation, Conversion, Regular Expressions ████████ +// #region ░░░░░░░[Case Conversion]░░░░ Upper, Lower, Sentence & Title Case ░░░░░░░ ~ const uCase = (str) => String(str).toUpperCase(); const lCase = (str) => String(str).toLowerCase(); const sCase = (str) => { @@ -282,13 +349,18 @@ const sCase = (str) => { }; const tCase = (str) => String(str).split(/\s/) .map((word, i) => (i && testRegExp(word, _noCapWords) ? lCase(word) : sCase(word))) - .join(" ").trim(); + .join(" ").trim(); +// #endregion ░░░░[Case Conversion]░░░░ +// #region ░░░░░░░[RegExp]░░░░ Regular Expressions ░░░░░░░ ~ const testRegExp = (str, patterns = [], flags = "gui", isTestingAll = false) => patterns .map((pattern) => (pattern instanceof RegExp ? pattern : new RegExp(`\\b${pattern}\\b`, flags)))[isTestingAll ? "every" : "some"]((pattern) => pattern.test(`${str}`)); const regExtract = (ref, pattern, flags) => { - const splitFlags = []; + /* Wrapper around String.match() that removes the need to worry about match()'s different handling of the 'g' flag. + - IF your pattern contains unescaped parentheses -> Returns Array of all matching groups. + - OTHERWISE -> Returns string that matches the provided pattern. */ + const splitFlags = []; [...(flags ?? "").replace(/g/g, ""), "u"].forEach((flag) => { if (flag && !splitFlags.includes(flag)) { splitFlags.push(flag); @@ -302,8 +374,11 @@ const regExtract = (ref, pattern, flags) => { pattern = new RegExp(pattern, flags); const matches = `${ref}`.match(pattern) || []; return isGrouping ? Array.from(matches) : matches.pop(); -}; - +}; +/*~ @@DOUBLE-BLANK@@ ~*/ +// #endregion ░░░░[RegExp]░░░░ +// #region ░░░░░░░[Formatting]░░░░ Hyphenation, Pluralization, "a"/"an" Fixing ░░░░░░░ ~ +// const hyphenate = (str: unknown) => (/^<|\u00AD|\u200B/.test(`${str}`) ? `${str}` : _hyph(`${str}`)); const unhyphenate = (str) => `${str}`.replace(/[\u00AD\u200B]/gu, ""); const parseArticles = (str) => `${str}`.replace(/\b([aA])\s([aeiouAEIOU])/gu, "$1n $2"); const pluralize = (singular, num = 2, plural) => { @@ -338,7 +413,8 @@ const pad = (text, minLength, delim = " ") => { } return str; }; -const toKey = (text) => (text ?? "").toLowerCase().replace(/ /g, "-").replace(/default/, "DEFAULT"); +const toKey = (text) => (text ?? "").toLowerCase().replace(/ /g, "-").replace(/default/, "DEFAULT"); +// #region ========== Numbers: Formatting Numbers Into Strings =========== ~ const signNum = (num, delim = "", zeroSign = "+") => { let sign; const parsedNum = pFloat(num); @@ -369,7 +445,9 @@ const padNum = (num, numDecDigits, includePlus = false) => { } return `${prefix}${leftDigits}.${"0".repeat(numDecDigits)}`; }; -const stringifyNum = (num) => { +const stringifyNum = (num) => { + // Can take string representations of numbers, either in standard or scientific/engineering notation. + // Returns a string representation of the number in standard notation. if (pFloat(num) === 0) { return "0"; } @@ -408,7 +486,8 @@ const stringifyNum = (num) => { } return `${num}`; }; -const verbalizeNum = (num) => { +const verbalizeNum = (num) => { + // Converts a float with absolute magnitude <= 9.99e303 into words. num = stringifyNum(num); const getTier = (trioNum) => { if (trioNum < _numberWords.tiers.length) { @@ -507,8 +586,10 @@ const romanizeNum = (num, isUsingGroupedChars = true) => { return isUsingGroupedChars ? romanNum.replace(/ⅩⅠ/gu, "Ⅺ").replace(/ⅩⅡ/gu, "Ⅻ") : romanNum; -}; - +}; +// #endregion _______ Numbers _______ +// #endregion ░░░░[Formatting]░░░░ +// #region ░░░░░░░[Content]░░░░ Lorem Ipsum, Random Content Generation, Randum UUID ░░░░░░░ ~ const loremIpsum = (numWords = 200) => { const lrWordList = _loremIpsumText.split(/\n?\s+/g); const words = [...lrWordList[randNum(0, lrWordList.length - 1)]]; @@ -529,12 +610,19 @@ const getUID = (id) => { eLog.log(`UUIDify(${id}) --> [${uuid}, ${indexNum}]`); Object.assign(globalThis, { UUIDLOG }); return uuid; -}; +}; +// #endregion ░░░░[Content]░░░░ +// #endregion ▄▄▄▄▄ STRINGS ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ SEARCHING: Searching Various Data Types w/ Fuzzy Matching ████████ ~ const fuzzyMatch = (val1, val2) => { const [str1, str2] = [val1, val2].map((val) => lCase(String(val).replace(/[^a-zA-Z0-9.+-]/g, "").trim())); return str1.length > 0 && str1 === str2; }; -const isIn = (needle, haystack = [], fuzziness = 0) => { +const isIn = (needle, haystack = [], fuzziness = 0) => { + // Looks for needle in haystack using fuzzy matching, then returns value as it appears in haystack. + /*~ @@DOUBLE-BLANK@@ ~*/ + // STEP ONE: POPULATE SEARCH TESTS ACCORDING TO FUZZINESS SETTING const SearchTests = [ (ndl, item) => new RegExp(`^${ndl}$`, "gu").test(`${item}`), (ndl, item) => new RegExp(`^${ndl}$`, "gui").test(`${item}`) @@ -551,10 +639,12 @@ const isIn = (needle, haystack = [], fuzziness = 0) => { SearchTests.push(...fuzzyTests .map((func) => (ndl, item) => func(`${ndl}`.replace(/\W/g, ""), `${item}`.replace(/\W/gu, "")))); if (fuzziness >= 3) { - SearchTests.push(() => false); + SearchTests.push(() => false); // Have to implement Fuse matching } } - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // STEP TWO: PARSE NEEDLE & CONSTRUCT SEARCHABLE HAYSTACK. const searchNeedle = `${needle}`; const searchStack = (() => { if (isArray(haystack)) { @@ -572,7 +662,9 @@ const isIn = (needle, haystack = [], fuzziness = 0) => { })(); if (!isArray(searchStack)) { return false; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // STEP THREE: SEARCH HAY FOR NEEDLE USING PROGRESSIVELY MORE FUZZY SEARCH TESTS let matchIndex = -1; while (!isPosInt(matchIndex)) { const testFunc = SearchTests.shift(); @@ -586,8 +678,10 @@ const isIn = (needle, haystack = [], fuzziness = 0) => { } return false; }; -const isInExact = (needle, haystack) => isIn(needle, haystack, 0); - +const isInExact = (needle, haystack) => isIn(needle, haystack, 0); +// #endregion ▄▄▄▄▄ SEARCHING ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ NUMBERS: Number Casting, Mathematics, Conversion ████████ ~ const randNum = (min, max, snap = 0) => gsap.utils.random(min, max, snap); const randInt = (min, max) => randNum(min, max, 1); const coinFlip = () => randNum(0, 1, 1) === 1; @@ -596,7 +690,8 @@ const clampNum = (num, [min = 0, max = Infinity] = []) => gsap.utils.clamp(min, const cycleAngle = (angle, range = [0, 360]) => cycleNum(angle, range); const roundNum = (num, sigDigits = 0) => (sigDigits === 0 ? pInt(num) : pFloat(num, sigDigits)); const sum = (...nums) => Object.values(nums.flat()).reduce((num, tot) => tot + num, 0); -const average = (...nums) => sum(...nums) / nums.flat().length; +const average = (...nums) => sum(...nums) / nums.flat().length; +// #region ░░░░░░░[Positioning]░░░░ Relationships On 2D Cartesian Plane ░░░░░░░ ~ const getDistance = ({ x: x1, y: y1 }, { x: x2, y: y2 }) => (((x1 - x2) ** 2) + ((y1 - y2) ** 2)) ** 0.5; const getAngle = ({ x: x1, y: y1 }, { x: x2, y: y2 }, { x: xO, y: yO } = { x: 0, y: 0 }, range = [0, 360]) => { x1 -= xO; @@ -605,7 +700,11 @@ const getAngle = ({ x: x1, y: y1 }, { x: x2, y: y2 }, { x: xO, y: yO } = { x: 0, y2 -= yO; return cycleAngle(radToDeg(Math.atan2(y2 - y1, x2 - x1)), range); }; -const getAngleDelta = (angleStart, angleEnd, range = [0, 360]) => cycleAngle(angleEnd - angleStart, range); +const getAngleDelta = (angleStart, angleEnd, range = [0, 360]) => cycleAngle(angleEnd - angleStart, range); +// #endregion ░░░░[Positioning]░░░░ +// #endregion ▄▄▄▄▄ NUMBERS ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ ARRAYS: Array Manipulation ████████ ~ const randElem = (array) => gsap.utils.random(array); const randIndex = (array) => randInt(0, array.length - 1); const makeIntRange = (min, max) => { @@ -615,7 +714,9 @@ const makeIntRange = (min, max) => { } return intRange; }; -const makeCycler = (array, index = 0) => { +const makeCycler = (array, index = 0) => { + // Given an array and a starting index, returns a generator function that can be used + // to iterate over the array indefinitely, or wrap out-of-bounds index values const wrapper = gsap.utils.wrap(array); index--; return (function* () { @@ -624,11 +725,19 @@ const makeCycler = (array, index = 0) => { yield wrapper(index); } })(); -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Returns the last element of an array, or the last value of an object literal. + * + * @param {Index} array An array or object literal + * @returns {Type|undefined} The last element, or undefined if empty. + */ function getLast(array) { array = Object.values(array); return array.length === 0 ? undefined : array[array.length - 1]; -} +} +// Const getLast = (array: Type[]): typeof array extends [] ? undefined : Type => ; const unique = (array) => { const returnArray = []; array.forEach((item) => { if (!returnArray.includes(item)) { @@ -661,21 +770,42 @@ const sample = (array, numElems = 1, isUniqueOnly = true, uniqueTestFunc = (e, a } return elems; }; -const removeFirst = (array, element) => array.splice(array.findIndex((v) => v === element)); -function pullElement(array, checkFunc) { - let testFunction; +const removeFirst = (array, element) => array.splice(array.findIndex((v) => v === element)); +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * This function removes and returns the first element in an array that equals the provided value + * or satisfies the provided testing function. + * If no elements satisfy the testing function, the function will return undefined. + * + * @param {T[]} array The array to be searched. + * @param {(T|((_v: T, _i?: number, _a?: T[]) => boolean))} checkFunc The testing function or value to be searched for. + * @returns {T | undefined} The first element in the array that passes the test. + * If no elements pass the test, return undefined. + */ +function pullElement(array, checkFunc) { + // Define the test function + let testFunction; + /*~ @@DOUBLE-BLANK@@ ~*/ + // If checkFunc is not a function, create a function that checks for equality with checkFunc if (typeof checkFunc !== "function") { testFunction = (_v) => _v === checkFunc; } else { testFunction = checkFunc; - } - const index = array.findIndex((v, i, a) => testFunction(v, i, a)); + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Find the index of the first element that passes the test + const index = array.findIndex((v, i, a) => testFunction(v, i, a)); + /*~ @@DOUBLE-BLANK@@ ~*/ + // If no element passes the test, return undefined if (index === -1) { return undefined; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Remove the element from the array and return it return array.splice(index, 1).pop(); -} +} +/*~ @@DOUBLE-BLANK@@ ~*/ const pullIndex = (array, index) => pullElement(array, (_, i) => i === index); const subGroup = (array, groupSize) => { const subArrays = []; @@ -691,16 +821,27 @@ const subGroup = (array, groupSize) => { }; const shuffle = (array) => { let currentIndex = array.length; - let randomIndex; - while (currentIndex !== 0) { + let randomIndex; + /*~ @@DOUBLE-BLANK@@ ~*/ + // While there remain elements to shuffle. + while (currentIndex !== 0) { + /*~ @@DOUBLE-BLANK@@ ~*/ + // Pick a remaining element. randomIndex = Math.floor(Math.random() * currentIndex); - currentIndex--; + currentIndex--; + /*~ @@DOUBLE-BLANK@@ ~*/ + // And swap it with the current element. [array[currentIndex], array[randomIndex]] = [ array[randomIndex], array[currentIndex] ]; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ return array; -}; +}; +// #endregion ▄▄▄▄▄ ARRAYS ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ OBJECTS: Manipulation of Simple Key/Val Objects ████████ ~ +/*~ @@DOUBLE-BLANK@@ ~*/ const checkVal = ({ k, v }, checkTest) => { if (typeof checkTest === "function") { if (isDefined(v)) { @@ -713,6 +854,12 @@ const checkVal = ({ k, v }, checkTest) => { } return (new RegExp(checkTest)).test(`${v}`); }; +/** + * Given an array or list and a search function, will remove the first matching element and return it. + * @param {Index} obj The array or list to be searched. + * @param {testFunc | number | string} checkTest The search function. + * @returns {unknown | false} - The removed element or false if no element was found. + */ const remove = (obj, checkTest) => { if (isArray(obj)) { const index = obj.findIndex((v) => checkVal({ v }, checkTest)); @@ -727,7 +874,14 @@ const remove = (obj, checkTest) => { } } return false; -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Removes an element from an array at a given index and returns it. + * @param {unknown[]} array The array to remove the element from. + * @param {number} index The index of the element to remove. + * @returns {unknown} - The removed element. + */ const removeElementFromArray = (array, index) => { let remVal; for (let i = 0; i <= array.length; i++) { @@ -739,13 +893,22 @@ const removeElementFromArray = (array, index) => { } } return remVal; -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * Removes an element from a list at a given key and returns it. + * @param {List} list The list to remove the element from. + * @param {string} key The key of the element to remove. + * @returns {unknown} - The removed element. + */ const removeElementFromList = (list, key) => { const remVal = list[key]; delete list[key]; return remVal; }; -const replace = (obj, checkTest, repVal) => { +const replace = (obj, checkTest, repVal) => { + // As remove, except instead replaces the element with the provided value. + // Returns true/false to indicate whether the replace action succeeded. let repKey; if (isList(obj)) { [repKey] = Object.entries(obj).find((v) => checkVal({ v }, checkTest)) || [false]; @@ -762,14 +925,25 @@ const replace = (obj, checkTest, repVal) => { if (typeof repKey !== "number") { repKey = `${repKey}`; } - if (typeof repVal === "function") { + if (typeof repVal === "function") { + // @ts-expect-error Need to figure out how to properly define testFunc (keyFunc/valFunc types?) obj[repKey] = repVal(obj[repKey], repKey); } - else { + else { + // @ts-expect-error Need to figure out how to properly define testFunc (keyFunc/valFunc types?) obj[repKey] = repVal; } return true; }; +/** + * Cleans an object or value by removing specified values recursively. + * + * @template T - The type of the input object or value. + * @param {T} data The object or value to be cleaned. + * @param {Array} [remVals] An array of values to be removed during the cleaning process. + * @returns {T | Partial | "KILL"} - The cleaned version of the input object or value. + * If marked for removal, returns "KILL". + */ const objClean = (data, remVals = [undefined, null, "", {}, []]) => { const remStrings = remVals.map((rVal) => JSON.stringify(rVal)); if (remStrings.includes(JSON.stringify(data)) || remVals.includes(data)) { @@ -787,7 +961,13 @@ const objClean = (data, remVals = [undefined, null, "", {}, []]) => { return newData.length ? Object.fromEntries(newData) : "KILL"; } return data; -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * + * @param items + * @param key + */ export function toDict(items, key) { const dict = {}; const mappedItems = items @@ -806,9 +986,16 @@ export function toDict(items, key) { newKey = indexString(newKey); } dict[newKey] = iData; - }); - return dict; - function indexString(str) { + }); + // @ts-expect-error Oh it definitely does. + return dict; + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * 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) { if (/_\d+$/.test(str)) { const [curIndex, ...subStr] = [...str.split(/_/)].reverse(); return [ @@ -818,50 +1005,91 @@ export function toDict(items, key) { } 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, predicate = () => true) => [ objFilter(obj, predicate), objFilter(obj, (v, k) => !predicate(v, k)) ]; +/** + * 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. + * @param {Index} obj + * @param {mapFunc|false} keyFunc + * @param {mapFunc} [valFunc] + */ function objMap(obj, keyFunc, valFunc) { // let valFuncTyped = valFunc; - let keyFuncTyped = keyFunc; + let keyFuncTyped = keyFunc; + /*~ @@DOUBLE-BLANK@@ ~*/ if (!valFuncTyped) { valFuncTyped = keyFunc; keyFuncTyped = false; } if (!keyFuncTyped) { keyFuncTyped = ((k) => k); - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ if (isArray(obj)) { return obj.map(valFuncTyped); - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ return Object.fromEntries(Object.entries(obj).map(([key, val]) => { assertNonNullType(valFuncTyped, "function"); return [keyFuncTyped(key, val), valFuncTyped(val, key)]; })); } -const objSize = (obj) => Object.values(obj).filter((val) => val !== undefined && val !== null).length; -function objFindKey(obj, keyFunc, valFunc) { +const objSize = (obj) => Object.values(obj).filter((val) => val !== undefined && val !== null).length; +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * This function is an object-equivalent of Array.findIndex() function. + * It accepts check functions for both keys and/or values. + * If only one function is provided, it's assumed to be searching via values and will receive (v, k) args. + * + * @param {Type} obj The object to be searched. + * @param {testFunc | testFunc | false} keyFunc The testing function for keys. + * @param {testFunc} valFunc The testing function for values. + * @returns {KeyOf | false} The key of the first entry that passes the test. + * If no entries pass the test, return false. + */ +function objFindKey(obj, keyFunc, valFunc) { + // If valFunc is not provided, assume keyFunc is meant to be valFunc if (!valFunc) { valFunc = keyFunc; keyFunc = false; - } + } + // If keyFunc is not provided, create a function that returns the key if (!keyFunc) { keyFunc = ((k) => k); - } + } + // If obj is an array, find the index of the first element that passes the test if (isArray(obj)) { return obj.findIndex(valFunc); - } + } + // Define the testing functions for keys and values const kFunc = keyFunc || (() => true); - const vFunc = valFunc || (() => true); - const validEntry = Object.entries(obj).find(([k, v]) => kFunc(k, v) && vFunc(v, k)); + const vFunc = valFunc || (() => true); + // Find the first entry that passes the test + const validEntry = Object.entries(obj).find(([k, v]) => kFunc(k, v) && vFunc(v, k)); + // If an entry passes the test, return its key if (validEntry) { return validEntry[0]; - } + } + // If no entries pass the test, return false return false; -} +} +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * An object-equivalent Array.filter() function, which accepts filter functions for both keys and/or values. + * If only one function is provided, it's assumed to be mapping the values and will receive (v, k) args. + * + * @param {Type} obj The object to be searched. + * @param {testFunc | testFunc | false} keyFunc The testing function for keys. + * @param {testFunc} [valFunc] The testing function for values. + * @returns {Type} The filtered object. + */ const objFilter = (obj, keyFunc, valFunc) => { // if (!valFunc) { @@ -879,15 +1107,18 @@ const objFilter = (obj, keyFunc, valFunc) => { return Object.fromEntries(Object.entries(obj) .filter(([key, val]) => kFunc(key, val) && vFunc(val, key))); }; -const objForEach = (obj, func) => { +const objForEach = (obj, func) => { + // An object-equivalent Array.forEach() function, which accepts one function(val, key) to perform for each member. if (isArray(obj)) { obj.forEach(func); } else { Object.entries(obj).forEach(([key, val]) => func(val, key)); } -}; -const objCompact = (obj, removeWhiteList = [undefined, null]) => objFilter(obj, (val) => !removeWhiteList.includes(val)); +}; +// Prunes an object of given set of values, [undefined, null] default +const objCompact = (obj, removeWhiteList = [undefined, null]) => objFilter(obj, (val) => !removeWhiteList.includes(val)); +/*~ @@DOUBLE-BLANK@@ ~*/ const objClone = (obj, isStrictlySafe = false) => { const cloneArray = (arr) => [...arr]; const cloneObject = (o) => ({ ...o }); @@ -907,44 +1138,83 @@ const objClone = (obj, isStrictlySafe = false) => { } return obj; }; -function objMerge(target, source, { isMutatingOk = false, isStrictlySafe = false, isConcatenatingArrays = true, isReplacingArrays = false } = {}) { - target = isMutatingOk ? target : objClone(target, isStrictlySafe); +/** + * Returns a deep merge of source into target. Does not mutate target unless isMutatingOk = true. + * @param {Tx} target The target object to be merged. + * @param {Ty} source The source object to be merged. + * @param {object} options An object containing various options for the merge operation. + * @param {boolean} options.isMutatingOk + * @param {boolean} options.isStrictlySafe + * @param {boolean} options.isConcatenatingArrays + * @param {boolean} options.isReplacingArrays + * @returns {Tx & Ty} - The merged object. + */ +function objMerge(target, source, { isMutatingOk = false, isStrictlySafe = false, isConcatenatingArrays = true, isReplacingArrays = false } = {}) { + // Clone the target if mutation is not allowed + target = isMutatingOk ? target : objClone(target, isStrictlySafe); + /*~ @@DOUBLE-BLANK@@ ~*/ + // If source is an instance of Application or target is undefined, return source if (source instanceof Application || isUndefined(target)) { return source; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If source is undefined, return target if (isUndefined(source)) { return target; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If source is not an index, return target if (!isIndex(source)) { return target; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Iterate over each entry in the source object for (const [key, val] of Object.entries(source)) { - const targetVal = target[key]; - + const targetVal = target[key]; + /*~ @@DOUBLE-BLANK@@ ~*/ + // If replacing arrays is enabled and both target and source values are + // arrays, replace target value with source value if (isReplacingArrays && isArray(targetVal) && isArray(val)) { target[key] = val; } - else if (isConcatenatingArrays && isArray(targetVal) && isArray(val)) { - + else if (isConcatenatingArrays && isArray(targetVal) && isArray(val)) { + /*~ @@DOUBLE-BLANK@@ ~*/ + // If concatenating arrays is enabled and both target and source values + // are arrays, concatenate source value to target value target[key].push(...val); } - else if (val !== null && typeof val === "object") { + else if (val !== null && typeof val === "object") { + // If source value is an object and not null, merge it into target value if (isUndefined(targetVal) && !(val instanceof Application)) { target[key] = new (Object.getPrototypeOf(val).constructor)(); } target[key] = objMerge(target[key], val, { isMutatingOk: true, isStrictlySafe }); } - else { + else { + // For all other cases, assign source value to target target[key] = val; } - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Return the merged target return target; } +/** + * Deep-compares two objects and returns an object containing only the keys and values + * in the second object that differ from the first. + * If the second object is missing a key or value contained in the first, it sets the + * value in the returned object to null, and prefixes the key with "-=". + * @param {Tx} obj1 The first object to be compared. + * @param {Ty} obj2 The second object to be compared. + * @returns {Record} - An object containing the differences between the two input objects. + */ function objDiff(obj1, obj2) { const diff = {}; const bothObj1AndObj2Keys = Object.keys(obj2).filter((key) => Object.hasOwn(obj2, key) && Object.hasOwn(obj1, key)); - const onlyObj2Keys = Object.keys(obj2).filter((key) => Object.hasOwn(obj2, key) && !Object.hasOwn(obj1, key)); - for (const key of bothObj1AndObj2Keys) { + const onlyObj2Keys = Object.keys(obj2).filter((key) => Object.hasOwn(obj2, key) && !Object.hasOwn(obj1, key)); + /*~ @@DOUBLE-BLANK@@ ~*/ + for (const key of bothObj1AndObj2Keys) { + // If both values are non-array objects, recursively compare them if (typeof obj1[key] === "object" && typeof obj2[key] === "object" && !Array.isArray(obj1[key]) && !Array.isArray(obj2[key])) { const nestedDiff = objDiff(obj1[key], obj2[key]); if (Object.keys(nestedDiff).length > 0) { @@ -954,19 +1224,24 @@ function objDiff(obj1, obj2) { else if (Array.isArray(obj1[key]) && Array.isArray(obj2[key])) { const array1 = obj1[key]; const array2 = obj2[key]; - if (array1.toString() !== array2.toString()) { + if (array1.toString() !== array2.toString()) { + // If both values are arrays and they are not equal, add the second array to the diff diff[key] = obj2[key]; } } - else if (obj1[key] !== obj2[key]) { + else if (obj1[key] !== obj2[key]) { + // If the values are not equal, add the second value to the diff diff[key] = obj2[key]; } - } - for (const key of onlyObj2Keys) { + } + /*~ @@DOUBLE-BLANK@@ ~*/ + for (const key of onlyObj2Keys) { + // If the second object has a key that the first does not, add it to the diff with a "-=" prefix diff[`-=${key}`] = obj2[key]; } return diff; -} +} +/*~ @@DOUBLE-BLANK@@ ~*/ const objExpand = (obj) => { const expObj = {}; for (const [key, val] of Object.entries(obj)) { @@ -977,8 +1252,13 @@ const objExpand = (obj) => { else { setProperty(expObj, key, val); } - } - function arrayify(o) { + } + // Iterate through expanded Object, converting object literals to arrays where it makes sense + /** + * + * @param o + */ + function arrayify(o) { if (isList(o)) { if (/^\d+$/.test(Object.keys(o).join(""))) { return Object.values(o).map(arrayify); @@ -989,7 +1269,8 @@ const objExpand = (obj) => { return o.map(arrayify); } return o; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ return arrayify(expObj); }; const objFlatten = (obj) => { @@ -1006,23 +1287,44 @@ const objFlatten = (obj) => { } return flatObj; }; -function objNullify(obj) { +/** + * + * @param obj + */ +function objNullify(obj) { + // Check if the input is an object or an array if (!isIndex(obj)) { return obj; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If the input is an array, nullify all its elements if (Array.isArray(obj)) { obj.forEach((_, i) => { obj[i] = null; }); return obj; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If the input is an object, nullify all its properties Object.keys(obj).forEach((objKey) => { obj[objKey] = null; - }); + }); + /*~ @@DOUBLE-BLANK@@ ~*/ return obj; -} +} +/*~ @@DOUBLE-BLANK@@ ~*/ +/** + * This function freezes the properties of an object based on a provided schema or keys. + * If a property is missing, it throws an error. + * @param {Partial} data The object whose properties are to be frozen. + * @param {...Array | [T]} keysOrSchema The keys or schema to freeze the properties. + * @returns {T} - The object with frozen properties. + * @throws {Error} - Throws an error if a property is missing. + */ function objFreezeProps(data, ...keysOrSchema) { - const firstArg = keysOrSchema[0]; + const firstArg = keysOrSchema[0]; + /*~ @@DOUBLE-BLANK@@ ~*/ + // If the first argument is an object and not an array, treat it as a schema if (firstArg instanceof Object && !Array.isArray(firstArg)) { const schema = firstArg; for (const key in schema) { @@ -1031,31 +1333,47 @@ function objFreezeProps(data, ...keysOrSchema) { } } } - else { + else { + // If the first argument is not an object or is an array, treat it as an array of keys for (const key of keysOrSchema) { if (data[key] === undefined) { throw new Error(`Missing value for ${String(key)}`); } } - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Return the data as type T return data; -} - +} +// #endregion ▄▄▄▄▄ OBJECTS ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ FUNCTIONS: Function Wrapping, Queuing, Manipulation ████████ ~ const getDynamicFunc = (funcName, func, context) => { if (typeof func === "function") { const dFunc = { [funcName](...args) { return func(...args); } }[funcName]; return context ? dFunc.bind(context) : dFunc; } return false; -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ const withLog = (fn) => { return (...args) => { console.log(`calling ${fn.name}`); return fn(...args); }; -}; - +}; +// #endregion ▄▄▄▄▄ FUNCTIONS ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ HTML: Parsing HTML Code, Manipulating DOM Objects ████████ ~ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ░░░░░░░[GreenSock]░░░░ Wrappers for GreenSock Functions ░░░░░░░ ~ const set = (targets, vars) => gsap.set(targets, vars); +/** + * + * @param target + * @param property + * @param unit + */ function get(target, property, unit) { if (unit) { const propVal = regExtract(gsap.getProperty(target, property, unit), /[\d.]+/); @@ -1065,9 +1383,12 @@ function get(target, property, unit) { throw new Error(`Unable to extract property '${property}' in '${unit}' units from ${target}`); } return gsap.getProperty(target, property); -} -const getGSAngleDelta = (startAngle, endAngle) => signNum(roundNum(getAngleDelta(startAngle, endAngle), 2)).replace(/^(.)/, "$1="); - +} +/*~ @@DOUBLE-BLANK@@ ~*/ +const getGSAngleDelta = (startAngle, endAngle) => signNum(roundNum(getAngleDelta(startAngle, endAngle), 2)).replace(/^(.)/, "$1="); +// #endregion ░░░░[GreenSock]░░░░ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ░░░░░░░[SVG]░░░░ SVG Generation & Manipulation ░░░░░░░ ~ const getRawCirclePath = (r, { x: xO, y: yO } = { x: 0, y: 0 }) => { [r, xO, yO] = [r, xO, yO].map((val) => roundNum(val, 2)); const [b1, b2] = [0.4475 * r, (1 - 0.4475) * r]; @@ -1091,8 +1412,10 @@ const drawCirclePath = (radius, origin) => { }); path.push("z"); return path.join(" "); -}; - +}; +// #endregion ░░░░[SVG]░░░░ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ░░░░░░░[Colors]░░░░ Color Manipulation ░░░░░░░ ~ const getColorVals = (red, green, blue, alpha) => { if (isRGBColor(red)) { [red, green, blue, alpha] = red @@ -1130,7 +1453,11 @@ const getRGBString = (red, green, blue, alpha) => { return null; }; const getHEXString = (red, green, blue) => { - function componentToHex(c) { + /** + * + * @param c + */ + function componentToHex(c) { const hex = c.toString(16); return hex.length === 1 ? `0${hex}` : hex; } @@ -1153,20 +1480,27 @@ const getContrastingColor = (...colorVals) => { } return null; }; -const getRandomColor = () => getRGBString(gsap.utils.random(0, 255, 1), gsap.utils.random(0, 255, 1), gsap.utils.random(0, 255, 1)); - +const getRandomColor = () => getRGBString(gsap.utils.random(0, 255, 1), gsap.utils.random(0, 255, 1), gsap.utils.random(0, 255, 1)); +// #endregion ░░░░[Colors]░░░░ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ░░░░░░░[DOM]░░░░ DOM Manipulation ░░░░░░░ ~ const getSiblings = (elem) => { - const siblings = []; + const siblings = []; + // If no parent, return no sibling if (!elem.parentNode) { return siblings; - } + } + /*~ @@DOUBLE-BLANK@@ ~*/ Array.from(elem.parentNode.children).forEach((sibling) => { if (sibling !== elem) { siblings.push(sibling); } - }); + }); + /*~ @@DOUBLE-BLANK@@ ~*/ return siblings; -}; +}; +// #endregion ░░░░[DOM]░░░░ +/*~ @@DOUBLE-BLANK@@ ~*/ const escapeHTML = (str) => (typeof str === "string" ? str .replace(/&/g, "&") @@ -1174,11 +1508,16 @@ const escapeHTML = (str) => (typeof str === "string" .replace(/>/g, ">") .replace(/"/g, """) .replace(/[`']/g, "'") - : str); - + : str); +/*~ @@DOUBLE-BLANK@@ ~*/ +// #endregion ▄▄▄▄▄ HTML ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ ASYNC: Async Functions, Asynchronous Flow Control ████████ ~ const sleep = (duration) => new Promise((resolve) => { setTimeout(resolve, duration >= 100 ? duration : duration * 1000); -}); +}); +// #endregion ▄▄▄▄▄ ASYNC ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ const EventHandlers = { onTextInputBlur: async (inst, event) => { const elem = event.target; @@ -1203,7 +1542,8 @@ const EventHandlers = { }, onSelectChange: async (inst, event) => { const elem = event.currentTarget; - const { action, dtype, target, flagTarget } = elem.dataset; + const { action, dtype, target, flagTarget } = elem.dataset; + /*~ @@DOUBLE-BLANK@@ ~*/ if (!action) { throw new Error("Select elements require a data-action attribute."); } @@ -1245,14 +1585,18 @@ const EventHandlers = { } } } -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ +// #region ████████ FOUNDRY: Requires Configuration of System ID in constants.ts ████████ ~ +/*~ @@DOUBLE-BLANK@@ ~*/ const isDocID = (docRef, isUUIDok = true) => { return typeof docRef === "string" && (isUUIDok ? /^(.*\.)?[A-Za-z0-9]{16}$/.test(docRef) : /^[A-Za-z0-9]{16}$/.test(docRef)); -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ const loc = (locRef, formatDict = {}) => { - if (/[a-z]/.test(locRef)) { + if (/[a-z]/.test(locRef)) { // Reference contains lower-case characters: add system ID namespacing to dot notation locRef = locRef.replace(new RegExp(`^(${C.SYSTEM_ID}.)*`), `${C.SYSTEM_ID}.`); } if (typeof game.i18n.localize(locRef) === "string") { @@ -1262,14 +1606,30 @@ const loc = (locRef, formatDict = {}) => { return game.i18n.format(locRef, formatDict) || game.i18n.localize(locRef) || locRef; } return locRef; -}; +}; +/*~ @@DOUBLE-BLANK@@ ~*/ const getSetting = (setting) => game.settings.get(C.SYSTEM_ID, setting); +/** + * + * @param subFolder + * @param fileName + */ function getTemplatePath(subFolder, fileName) { if (typeof fileName === "string") { return `${C.TEMPLATE_ROOT}/${subFolder}/${fileName.replace(/\..*$/, "")}.hbs`; } return fileName.map((fName) => getTemplatePath(subFolder, fName)); -} +} +/*~ @@DOUBLE-BLANK@@ ~*/ +// DisplayImageSelector: Displays a file selector in tiles mode at the indicated path root. +/** + * + * @param callback + * @param pathRoot + * @param position + * @param position.top + * @param position.left + */ function displayImageSelector(callback, pathRoot = `systems/${C.SYSTEM_ID}/assets`, position = { top: 200, left: 200 }) { const fp = new FilePicker({ type: "image", @@ -1280,51 +1640,89 @@ function displayImageSelector(callback, pathRoot = `systems/${C.SYSTEM_ID}/asset left: position.left ?? 200 + 10 }); return fp.browse(pathRoot); -} -export default { - GMID, getUID, +} +/*~ @@DOUBLE-BLANK@@ ~*/ +// #endregion ▄▄▄▄▄ FOUNDRY ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ +export default { + // ████████ GETTERS: Basic Data Lookup & Retrieval ████████ + GMID, getUID, + /*~ @@DOUBLE-BLANK@@ ~*/ + // ████████ TYPES: Type Checking, Validation, Conversion, Casting ████████ isNumber, isNumberString, isBooleanString, isSimpleObj, isList, isArray, isFunc, isInt, isFloat, isPosInt, isIterable, isHTMLCode, isRGBColor, isHexColor, isUndefined, isDefined, isEmpty, hasItems, isInstance, isNullish, areEqual, areFuzzyEqual, pFloat, pInt, radToDeg, degToRad, getKey, - assertNonNullType, - FILTERS, + assertNonNullType, + /*~ @@DOUBLE-BLANK@@ ~*/ + FILTERS, + /*~ @@DOUBLE-BLANK@@ ~*/ + // ████████ REGEXP: Regular Expressions, Replacing, Matching ████████ testRegExp, - regExtract, - - uCase, lCase, sCase, tCase, - unhyphenate, pluralize, oxfordize, ellipsize, pad, + regExtract, + /*~ @@DOUBLE-BLANK@@ ~*/ + // ████████ STRINGS: String Parsing, Manipulation, Conversion ████████ + // ░░░░░░░ Case Conversion ░░░░░░░ + uCase, lCase, sCase, tCase, + // ░░░░░░░ Formatting ░░░░░░░ + /* hyphenate, */ unhyphenate, pluralize, oxfordize, ellipsize, pad, toKey, parseArticles, - signNum, padNum, stringifyNum, verbalizeNum, ordinalizeNum, romanizeNum, - loremIpsum, randString, randWord, - fuzzyMatch, isIn, isInExact, + signNum, padNum, stringifyNum, verbalizeNum, ordinalizeNum, romanizeNum, + // ░░░░░░░ Content ░░░░░░░ + loremIpsum, randString, randWord, + /*~ @@DOUBLE-BLANK@@ ~*/ + // ████████ SEARCHING: Searching Various Data Types w/ Fuzzy Matching ████████ + fuzzyMatch, isIn, isInExact, + /*~ @@DOUBLE-BLANK@@ ~*/ + // ████████ NUMBERS: Number Casting, Mathematics, Conversion ████████ randNum, randInt, coinFlip, cycleNum, cycleAngle, roundNum, clampNum, - sum, average, + sum, average, + // ░░░░░░░ Positioning ░░░░░░░ getDistance, - getAngle, getAngleDelta, + getAngle, getAngleDelta, + /*~ @@DOUBLE-BLANK@@ ~*/ + // ████████ ARRAYS: Array Manipulation ████████ randElem, randIndex, makeIntRange, makeCycler, unique, group, sample, getLast, removeFirst, pullElement, pullIndex, - subGroup, shuffle, + subGroup, shuffle, + /*~ @@DOUBLE-BLANK@@ ~*/ + // ████████ OBJECTS: Manipulation of Simple Key/Val Objects ████████ remove, replace, partition, objClean, objSize, objMap, objFindKey, objFilter, objForEach, objCompact, objClone, objMerge, objDiff, objExpand, objFlatten, objNullify, - objFreezeProps, - getDynamicFunc, withLog, - - gsap, get, set, getGSAngleDelta, - getRawCirclePath, drawCirclePath, - getColorVals, getRGBString, getHEXString, getContrastingColor, getRandomColor, - getSiblings, - escapeHTML, - sleep, - EventHandlers, - isDocID, loc, getSetting, getTemplatePath, displayImageSelector -}; \ No newline at end of file + objFreezeProps, + /*~ @@DOUBLE-BLANK@@ ~*/ + // ████████ FUNCTIONS: Function Wrapping, Queuing, Manipulation ████████ + getDynamicFunc, withLog, + /*~ @@DOUBLE-BLANK@@ ~*/ + // ████████ HTML: Parsing HTML Code, Manipulating DOM Objects ████████ + // ░░░░░░░ GreenSock ░░░░░░░ + gsap, get, set, getGSAngleDelta, + /*~ @@DOUBLE-BLANK@@ ~*/ + getRawCirclePath, drawCirclePath, + /*~ @@DOUBLE-BLANK@@ ~*/ + getColorVals, getRGBString, getHEXString, getContrastingColor, getRandomColor, + /*~ @@DOUBLE-BLANK@@ ~*/ + getSiblings, + /*~ @@DOUBLE-BLANK@@ ~*/ + escapeHTML, + /*~ @@DOUBLE-BLANK@@ ~*/ + // ████████ ASYNC: Async Functions, Asynchronous Flow Control ████████ + sleep, + /*~ @@DOUBLE-BLANK@@ ~*/ + // EVENT HANDLERS + EventHandlers, + // ░░░░░░░ SYSTEM: System-Specific Functions (Requires Configuration of System ID in constants.js) ░░░░░░░ + isDocID, loc, getSetting, getTemplatePath, displayImageSelector + /*~ @@DOUBLE-BLANK@@ ~*/ +}; +// #endregion ▄▄▄▄▄ EXPORTS ▄▄▄▄▄ +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/data-import/data-import.js b/module/data-import/data-import.js index f76b391b..9bf18863 100644 --- a/module/data-import/data-import.js +++ b/module/data-import/data-import.js @@ -1,13 +1,7 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import { BladesActorType, BladesItemType } from "../core/constants.js"; import U from "../core/utilities.js"; import { BladesItem } from "../documents/BladesItemProxy.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ const JSONDATA = { FACTIONS: { "the Billhooks": { @@ -3029,7 +3023,9 @@ const JSONDATA = { } } }; +/*~ @@DOUBLE-BLANK@@ ~*/ const problemLog = []; +/*~ @@DOUBLE-BLANK@@ ~*/ function parseColumnItem(item, isFaction = false) { if (isFaction) { const faction = game.actors.getName(item); @@ -3066,7 +3062,9 @@ function formatSplitColumns(elements, isFactionList = false) { "
" ].join(""); } +/*~ @@DOUBLE-BLANK@@ ~*/ const { claim, favoredOperation, contact } = U.group(JSONDATA.CREW_OBJECTS, "type"); +/*~ @@DOUBLE-BLANK@@ ~*/ export const updateClaims = async () => { const errorReport = []; const playbookUpdateData = {}; @@ -3089,11 +3087,14 @@ export const updateClaims = async () => { } playbookUpdateData[playbookName] = playbookData; }); + /*~ @@DOUBLE-BLANK@@ ~*/ await Promise.all(Object.entries(playbookUpdateData).map(async ([playbook, data]) => game.items.getName(playbook) ?.update(data) .then((item) => item?.addTag(playbook)))); + /*~ @@DOUBLE-BLANK@@ ~*/ console.log(errorReport); }; +/*~ @@DOUBLE-BLANK@@ ~*/ export const updateOps = async () => { const errorReport = []; await Promise.all((favoredOperation ?? []).map(async (op) => { @@ -3115,8 +3116,10 @@ export const updateOps = async () => { } return undefined; })); + /*~ @@DOUBLE-BLANK@@ ~*/ console.log(errorReport); }; +/*~ @@DOUBLE-BLANK@@ ~*/ export const updateContacts = async () => { const errorReport = []; await Promise.all((contact ?? []).map(async (ct) => { @@ -3135,8 +3138,10 @@ export const updateContacts = async () => { }); return actor.addTag(playbookObj.name); })); + /*~ @@DOUBLE-BLANK@@ ~*/ console.log(errorReport); }; +/*~ @@DOUBLE-BLANK@@ ~*/ const updateFactionData = async (factionData) => { const faction = game.actors.getName(factionData.name); const updateData = {}; @@ -3196,23 +3201,32 @@ const updateFactionData = async (factionData) => { problemLog.push(`Unable to find faction "${factionData.name}"`); } }; +/*~ @@DOUBLE-BLANK@@ ~*/ export const updateFactions = async () => { await Promise.all(Object.values(JSONDATA.FACTIONS).map(async (factionData) => updateFactionData(factionData))); console.log(problemLog); }; +/*~ @@DOUBLE-BLANK@@ ~*/ export const updateRollMods = async () => { await Promise.all([ ...Object.entries(JSONDATA.ABILITIES.RollMods) .map(async ([abilityName, eData]) => { + // Get ability doc const abilityDoc = game.items.getName(abilityName); if (!abilityDoc) { eLog.error("updateRollMods", `updateRollMods: Ability ${abilityName} Not Found.`); return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Get active effects on abilityDoc const abilityEffects = Array.from(abilityDoc.effects ?? []); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Separate out 'APPLYTOMEMBERS' and 'APPLYTOCOHORTS' ActiveEffects const toMemberEffects = abilityEffects.filter((effect) => effect.changes.some((change) => change.key === "APPLYTOMEMBERS")); const toCohortEffects = abilityEffects.filter((effect) => effect.changes.some((change) => change.key === "APPLYTOCOHORTS")); const standardEffects = abilityEffects.filter((effect) => effect.changes.every((change) => !["APPLYTOMEMBERS", "APPLYTOCOHORTS"].includes(change.key))); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Confirm eData.isMember and eData.isCohort are consistent across all changes. const testChange = eData[0]; if ((testChange.isMember && eData.some((change) => !change.isMember)) || (!testChange.isMember && eData.some((change) => change.isMember))) { @@ -3222,10 +3236,14 @@ export const updateRollMods = async () => { || (!testChange.isCohort && eData.some((change) => change.isCohort))) { return eLog.error("updateRollMods", `updateRollMods: Ability ${abilityName} has inconsistent 'isCohort' entries.`); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If eData.isMember or eData.isCohort, first see if there already is such an effect on the doc if (testChange.isMember) { if (toMemberEffects.length > 1) { return eLog.error("updateRollMods", `updateRollMods: Ability ${abilityName} Has Multiple 'APPLYTOMEMBERS' Active Effects`); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize new effect data const effectData = { name: abilityName, icon: abilityDoc.img ?? "", @@ -3234,6 +3252,8 @@ export const updateRollMods = async () => { return change; }) }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Derive new effect data from existing effect, if any, then delete existing effect if (toMemberEffects.length === 1) { const abilityEffect = toMemberEffects[0]; effectData.name = abilityEffect.name ?? effectData.name; @@ -3249,6 +3269,8 @@ export const updateRollMods = async () => { value: `${abilityName.replace(/\s*\([^()]*? (Ability|Upgrade)\)\s*$/, "")} (Scoundrel Ability)` }); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Create new ActiveEffect return abilityDoc.createEmbeddedDocuments("ActiveEffect", [effectData]); } else if (testChange.isCohort) { @@ -3256,6 +3278,8 @@ export const updateRollMods = async () => { eLog.error("updateRollMods", `updateRollMods: Ability ${abilityName} Has Multiple 'APPLYTOCOHORTS' Active Effects`); return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize new effect data const effectData = { name: abilityName, icon: abilityDoc.img ?? "", @@ -3264,6 +3288,8 @@ export const updateRollMods = async () => { return change; }) }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Derive new effect data from existing effect, if any, then delete existing effect if (toCohortEffects.length === 1) { const abilityEffect = toCohortEffects[0]; effectData.name = abilityEffect.name ?? effectData.name; @@ -3279,6 +3305,8 @@ export const updateRollMods = async () => { value: `${abilityName.replace(/\s*\([^()]*? (Ability|Upgrade)\)\s*$/, "")} (Scoundrel Ability)` }); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Create new ActiveEffect return abilityDoc.createEmbeddedDocuments("ActiveEffect", [effectData]); } else { @@ -3286,11 +3314,15 @@ export const updateRollMods = async () => { eLog.error("updateRollMods", `updateRollMods: Ability ${abilityName} Has Multiple Active Effects`); return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize new effect data const effectData = { name: abilityName, icon: abilityDoc.img ?? "", changes: eData }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Derive new effect data from existing effect, if any, then delete existing effect if (standardEffects.length === 1) { const abilityEffect = standardEffects[0]; effectData.name = abilityEffect.name ?? effectData.name; @@ -3298,20 +3330,29 @@ export const updateRollMods = async () => { effectData.changes.unshift(...abilityEffect.changes.filter((change) => change.key !== "system.roll_mods")); await abilityEffect.delete(); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Create new ActiveEffect return abilityDoc.createEmbeddedDocuments("ActiveEffect", [effectData]); } }), ...Object.entries(JSONDATA.CREW_ABILITIES.RollMods) .map(async ([aName, eData]) => { + // Get crew ability doc const crewAbilityDoc = game.items.getName(aName); if (!crewAbilityDoc) { eLog.error("updateRollMods", `updateRollMods: Crew Ability ${aName} Not Found.`); return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Get active effects on crewAbilityDoc const abilityEffects = Array.from(crewAbilityDoc.effects ?? []); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Separate out 'APPLYTOMEMBERS' and 'APPLYTOCOHORTS' ActiveEffects const toMemberEffects = abilityEffects.filter((effect) => effect.changes.some((change) => change.key === "APPLYTOMEMBERS")); const toCohortEffects = abilityEffects.filter((effect) => effect.changes.some((change) => change.key === "APPLYTOCOHORTS")); const standardEffects = abilityEffects.filter((effect) => effect.changes.every((change) => !["APPLYTOMEMBERS", "APPLYTOCOHORTS"].includes(change.key))); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Confirm eData.isMember and eData.isCohort are consistent across all changes. const testChange = eData[0]; if ((testChange.isMember && eData.some((change) => !change.isMember)) || (!testChange.isMember && eData.some((change) => change.isMember))) { @@ -3321,10 +3362,14 @@ export const updateRollMods = async () => { || (!testChange.isCohort && eData.some((change) => change.isCohort))) { return eLog.error("updateRollMods", `updateRollMods: Crew Ability ${aName} has inconsistent 'isCohort' entries.`); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If eData.isMember or eData.isCohort, first see if there already is such an effect on the doc if (testChange.isMember) { if (toMemberEffects.length > 1) { return eLog.error("updateRollMods", `updateRollMods: Crew Ability ${aName} Has Multiple 'APPLYTOMEMBERS' Active Effects`); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize new effect data const effectData = { name: aName, icon: crewAbilityDoc.img ?? "", @@ -3333,6 +3378,8 @@ export const updateRollMods = async () => { return change; }) }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Derive new effect data from existing effect, if any, then delete existing effect if (toMemberEffects.length === 1) { const abilityEffect = toMemberEffects[0]; effectData.name = abilityEffect.name ?? effectData.name; @@ -3348,6 +3395,8 @@ export const updateRollMods = async () => { value: `${aName.replace(/\s*\([^()]*? (Ability|Upgrade)\)\s*$/, "")} (Crew Ability)` }); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Create new ActiveEffect return crewAbilityDoc.createEmbeddedDocuments("ActiveEffect", [effectData]); } else if (testChange.isCohort) { @@ -3355,6 +3404,8 @@ export const updateRollMods = async () => { eLog.error("updateRollMods", `updateRollMods: Crew Ability ${aName} Has Multiple 'APPLYTOCOHORTS' Active Effects`); return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize new effect data const effectData = { name: aName, icon: crewAbilityDoc.img ?? "", @@ -3363,6 +3414,8 @@ export const updateRollMods = async () => { return change; }) }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Derive new effect data from existing effect, if any, then delete existing effect if (toCohortEffects.length === 1) { const abilityEffect = toCohortEffects[0]; effectData.name = abilityEffect.name ?? effectData.name; @@ -3378,6 +3431,8 @@ export const updateRollMods = async () => { value: `${aName.replace(/\s*\([^()]*? (Ability|Upgrade)\)\s*$/, "")} (Crew Ability)` }); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Create new ActiveEffect return crewAbilityDoc.createEmbeddedDocuments("ActiveEffect", [effectData]); } else { @@ -3385,11 +3440,15 @@ export const updateRollMods = async () => { eLog.error("updateRollMods", `updateRollMods: Crew Ability ${aName} Has Multiple Active Effects`); return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize new effect data const effectData = { name: aName, icon: crewAbilityDoc.img ?? "", changes: eData }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Derive new effect data from existing effect, if any, then delete existing effect if (standardEffects.length === 1) { const abilityEffect = standardEffects[0]; effectData.name = abilityEffect.name ?? effectData.name; @@ -3397,20 +3456,29 @@ export const updateRollMods = async () => { effectData.changes.unshift(...abilityEffect.changes.filter((change) => change.key !== "system.roll_mods")); await abilityEffect.delete(); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Create new ActiveEffect return crewAbilityDoc.createEmbeddedDocuments("ActiveEffect", [effectData]); } }), ...Object.entries(JSONDATA.CREW_UPGRADES.RollMods) .map(async ([aName, eData]) => { + // Get crew upgrade doc const crewUpgradeDoc = game.items.getName(aName); if (!crewUpgradeDoc) { eLog.error("updateRollMods", `updateRollMods: Crew Upgrade ${aName} Not Found.`); return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Get active effects on crewUpgradeDoc const abilityEffects = Array.from(crewUpgradeDoc.effects ?? []); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Separate out 'APPLYTOMEMBERS' and 'APPLYTOCOHORTS' ActiveEffects const toMemberEffects = abilityEffects.filter((effect) => effect.changes.some((change) => change.key === "APPLYTOMEMBERS")); const toCohortEffects = abilityEffects.filter((effect) => effect.changes.some((change) => change.key === "APPLYTOCOHORTS")); const standardEffects = abilityEffects.filter((effect) => effect.changes.every((change) => !["APPLYTOMEMBERS", "APPLYTOCOHORTS"].includes(change.key))); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Confirm eData.isMember and eData.isCohort are consistent across all changes. const testChange = eData[0]; if ((testChange.isMember && eData.some((change) => !change.isMember)) || (!testChange.isMember && eData.some((change) => change.isMember))) { @@ -3420,10 +3488,14 @@ export const updateRollMods = async () => { || (!testChange.isCohort && eData.some((change) => change.isCohort))) { return eLog.error("updateRollMods", `updateRollMods: Crew Upgrade ${aName} has inconsistent 'isCohort' entries.`); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // If eData.isMember or eData.isCohort, first see if there already is such an effect on the doc if (testChange.isMember) { if (toMemberEffects.length > 1) { return eLog.error("updateRollMods", `updateRollMods: Crew Upgrade ${aName} Has Multiple 'APPLYTOMEMBERS' Active Effects`); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize new effect data const effectData = { name: aName, icon: crewUpgradeDoc.img ?? "", @@ -3432,6 +3504,8 @@ export const updateRollMods = async () => { return change; }) }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Derive new effect data from existing effect, if any, then delete existing effect if (toMemberEffects.length === 1) { const abilityEffect = toMemberEffects[0]; effectData.name = abilityEffect.name ?? effectData.name; @@ -3447,6 +3521,8 @@ export const updateRollMods = async () => { value: `${aName.replace(/\s*\([^()]*? (Ability|Upgrade)\)\s*$/, "")} (Crew Upgrade)` }); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Create new ActiveEffect return crewUpgradeDoc.createEmbeddedDocuments("ActiveEffect", [effectData]); } else if (testChange.isCohort) { @@ -3454,6 +3530,8 @@ export const updateRollMods = async () => { eLog.error("updateRollMods", `updateRollMods: Crew Upgrade ${aName} Has Multiple 'APPLYTOCOHORTS' Active Effects`); return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize new effect data const effectData = { name: aName, icon: crewUpgradeDoc.img ?? "", @@ -3462,6 +3540,8 @@ export const updateRollMods = async () => { return change; }) }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Derive new effect data from existing effect, if any, then delete existing effect if (toCohortEffects.length === 1) { const abilityEffect = toCohortEffects[0]; effectData.name = abilityEffect.name ?? effectData.name; @@ -3477,6 +3557,8 @@ export const updateRollMods = async () => { value: `${aName.replace(/\s*\([^()]*? (Ability|Upgrade)\)\s*$/, "")} (Crew Upgrade)` }); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Create new ActiveEffect return crewUpgradeDoc.createEmbeddedDocuments("ActiveEffect", [effectData]); } else { @@ -3484,11 +3566,15 @@ export const updateRollMods = async () => { eLog.error("updateRollMods", `updateRollMods: Crew Upgrade ${aName} Has Multiple Active Effects`); return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Initialize new effect data const effectData = { name: aName, icon: crewUpgradeDoc.img ?? "", changes: eData }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Derive new effect data from existing effect, if any, then delete existing effect if (standardEffects.length === 1) { const abilityEffect = standardEffects[0]; effectData.name = abilityEffect.name ?? effectData.name; @@ -3496,11 +3582,14 @@ export const updateRollMods = async () => { effectData.changes.unshift(...abilityEffect.changes.filter((change) => change.key !== "system.roll_mods")); await abilityEffect.delete(); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Create new ActiveEffect return crewUpgradeDoc.createEmbeddedDocuments("ActiveEffect", [effectData]); } }) ]); }; +/*~ @@DOUBLE-BLANK@@ ~*/ export const updateDescriptions = async () => { return Promise.all(Object.entries({ ...JSONDATA.ABILITIES.Descriptions, @@ -3513,6 +3602,8 @@ export const updateDescriptions = async () => { eLog.error("applyRollEffects", `ApplyDescriptions: Item Doc ${aName} Not Found.`); return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Update system.notes return itemDoc.update({ "system.notes": desc }); })); -}; \ No newline at end of file +}; diff --git a/module/documents/BladesActorProxy.js b/module/documents/BladesActorProxy.js index aa3011be..be6f959a 100644 --- a/module/documents/BladesActorProxy.js +++ b/module/documents/BladesActorProxy.js @@ -1,10 +1,3 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import U from "../core/utilities.js"; import { BladesActorType } from "../core/constants.js"; import BladesActor from "../BladesActor.js"; @@ -12,13 +5,18 @@ import BladesPC from "./actors/BladesPC.js"; import BladesNPC from "./actors/BladesNPC.js"; import BladesFaction from "./actors/BladesFaction.js"; import BladesCrew from "./actors/BladesCrew.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ const ActorsMap = { [BladesActorType.pc]: BladesPC, [BladesActorType.npc]: BladesNPC, [BladesActorType.faction]: BladesFaction, [BladesActorType.crew]: BladesCrew + /*~ @@DOUBLE-BLANK@@ ~*/ }; +/*~ @@DOUBLE-BLANK@@ ~*/ +// eslint-disable-next-line @typescript-eslint/no-empty-function const BladesActorProxy = new Proxy(function () { }, { + /*~ @@DOUBLE-BLANK@@ ~*/ construct(_, args) { const [{ type }] = args; if (!type) { @@ -30,6 +28,7 @@ const BladesActorProxy = new Proxy(function () { }, { } return new MappedConstructor(...args); }, + /*~ @@DOUBLE-BLANK@@ ~*/ get(_, prop) { switch (prop) { case "create": @@ -38,6 +37,7 @@ const BladesActorProxy = new Proxy(function () { }, { if (U.isArray(data)) { return data.map((i) => CONFIG.Actor.documentClass.create(i, options)); } + /*~ @@DOUBLE-BLANK@@ ~*/ const MappedConstructor = ActorsMap[data.type]; if (!MappedConstructor) { return BladesActor.create(data, options); @@ -52,6 +52,8 @@ const BladesActorProxy = new Proxy(function () { }, { return BladesActor[prop]; } } + /*~ @@DOUBLE-BLANK@@ ~*/ }); +/*~ @@DOUBLE-BLANK@@ ~*/ export default BladesActorProxy; -export { BladesActor, BladesPC, BladesCrew, BladesNPC, BladesFaction }; \ No newline at end of file +export { BladesActor, BladesPC, BladesCrew, BladesNPC, BladesFaction }; diff --git a/module/documents/BladesItemProxy.js b/module/documents/BladesItemProxy.js index 227c84ea..5d699fef 100644 --- a/module/documents/BladesItemProxy.js +++ b/module/documents/BladesItemProxy.js @@ -1,10 +1,3 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import U from "../core/utilities.js"; import { BladesItemType } from "../core/constants.js"; import BladesItem from "../BladesItem.js"; @@ -12,13 +5,17 @@ import BladesLocation from "./items/BladesLocation.js"; import BladesClockKeeper from "./items/BladesClockKeeper.js"; import BladesGMTracker from "./items/BladesGMTracker.js"; import BladesScore from "./items/BladesScore.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ const ItemsMap = { [BladesItemType.clock_keeper]: BladesClockKeeper, [BladesItemType.gm_tracker]: BladesGMTracker, [BladesItemType.location]: BladesLocation, [BladesItemType.score]: BladesScore }; +/*~ @@DOUBLE-BLANK@@ ~*/ +// eslint-disable-next-line @typescript-eslint/no-empty-function const BladesItemProxy = new Proxy(function () { }, { + /*~ @@DOUBLE-BLANK@@ ~*/ construct(_, args) { const [{ type }] = args; if (!type) { @@ -30,6 +27,7 @@ const BladesItemProxy = new Proxy(function () { }, { } return new MappedConstructor(...args); }, + /*~ @@DOUBLE-BLANK@@ ~*/ get(_, prop) { switch (prop) { case "create": @@ -38,6 +36,7 @@ const BladesItemProxy = new Proxy(function () { }, { if (U.isArray(data)) { return data.map((i) => CONFIG.Item.documentClass.create(i, options)); } + /*~ @@DOUBLE-BLANK@@ ~*/ const MappedConstructor = ItemsMap[data.type]; if (!MappedConstructor) { return BladesItem.create(data, options); @@ -52,6 +51,8 @@ const BladesItemProxy = new Proxy(function () { }, { return BladesItem[prop]; } } + /*~ @@DOUBLE-BLANK@@ ~*/ }); +/*~ @@DOUBLE-BLANK@@ ~*/ export default BladesItemProxy; -export { BladesItem, BladesClockKeeper, BladesGMTracker, BladesLocation, BladesScore }; \ No newline at end of file +export { BladesItem, BladesClockKeeper, BladesGMTracker, BladesLocation, BladesScore }; diff --git a/module/documents/actors/BladesCrew.js b/module/documents/actors/BladesCrew.js index d208a1bc..0644d67e 100644 --- a/module/documents/actors/BladesCrew.js +++ b/module/documents/actors/BladesCrew.js @@ -1,35 +1,50 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import { BladesItemType } from "../../core/constants.js"; import BladesActor from "../../BladesActor.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesCrew extends BladesActor { - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Static Overrides: Create ~ static async create(data, options = {}) { data.token = data.token || {}; data.system = data.system ?? {}; + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog2("actor", "BladesActor.create(data,options)", { data, options }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ For Crew and PC set the Token to sync with charsheet. data.token.actorLink = true; + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Create world_name data.system.world_name = data.system.world_name ?? data.name.replace(/[^A-Za-z_0-9 ]/g, "").trim().replace(/ /g, "_"); + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Initialize generic experience clues. data.system.experience = { playbook: { value: 0, max: 8 }, clues: [], ...data.system.experience ?? {} }; + /*~ @@DOUBLE-BLANK@@ ~*/ return super.create(data, options); } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll Implementation + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll.ParticipantDoc Implementation get rollParticipantID() { return this.id; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantDoc() { return this; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantIcon() { return this.playbook?.img ?? this.img; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantName() { return this.name; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantType() { return this.type; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantModsData() { return []; } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ get abilities() { if (!this.playbook) { return []; @@ -37,12 +52,16 @@ class BladesCrew extends BladesActor { return this.activeSubItems .filter((item) => [BladesItemType.ability, BladesItemType.crew_ability].includes(item.type)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get playbookName() { return this.playbook?.name; } + /*~ @@DOUBLE-BLANK@@ ~*/ get playbook() { return this.activeSubItems .find((item) => item.type === BladesItemType.crew_playbook); } } -export default BladesCrew; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesCrew; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/documents/actors/BladesFaction.js b/module/documents/actors/BladesFaction.js index 4949a639..2f13e004 100644 --- a/module/documents/actors/BladesFaction.js +++ b/module/documents/actors/BladesFaction.js @@ -1,21 +1,27 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import BladesActor from "../../BladesActor.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesFaction extends BladesActor { - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll Implementation + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll.OppositionDoc Implementation get rollOppID() { return this.id; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppDoc() { return this; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppImg() { return this.img ?? ""; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppName() { return this.name ?? ""; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppSubName() { return this.system.subtitle || this.system.concept || " "; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppType() { return this.type; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppModsData() { return []; } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ async addClock(clockData = {}) { clockData.id ??= clockData.id ?? randomID(); clockData.color ??= "white"; @@ -26,10 +32,14 @@ class BladesFaction extends BladesActor { clockData.max ??= 4; clockData.target ??= `system.clocks.${clockData.id}.value`; clockData.value ??= 0; + /*~ @@DOUBLE-BLANK@@ ~*/ return this.update({ [`system.clocks.${clockData.id}`]: clockData }); } + /*~ @@DOUBLE-BLANK@@ ~*/ async deleteClock(clockID) { return this.update({ [`system.clocks.-=${clockID}`]: null }); } } -export default BladesFaction; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesFaction; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/documents/actors/BladesNPC.js b/module/documents/actors/BladesNPC.js index c67a732e..d781fd23 100644 --- a/module/documents/actors/BladesNPC.js +++ b/module/documents/actors/BladesNPC.js @@ -1,15 +1,14 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import { Factor } from "../../core/constants.js"; import BladesActor from "../../BladesActor.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesNPC extends BladesActor { + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll Implementation + /*~ @@DOUBLE-BLANK@@ ~*/ get rollFactors() { + /*~ @@DOUBLE-BLANK@@ ~*/ const factorData = super.rollFactors; + /*~ @@DOUBLE-BLANK@@ ~*/ factorData[Factor.scale] = { name: Factor.scale, display: "Scale", @@ -33,22 +32,39 @@ class BladesNPC extends BladesActor { isDominant: false, highFavorsPC: true }; + /*~ @@DOUBLE-BLANK@@ ~*/ return factorData; } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll.OppositionDoc Implementation get rollOppID() { return this.id; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppDoc() { return this; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppImg() { return this.img; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppName() { return this.name; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppSubName() { return this.system.subtitle || this.system.concept || " "; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppType() { return this.type; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppModsData() { return []; } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll.ParticipantDoc Implementation get rollParticipantID() { return this.id; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantDoc() { return this; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantIcon() { return this.img; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantName() { return this.name; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantType() { return this.type; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantModsData() { return []; } } -export default BladesNPC; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesNPC; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/documents/actors/BladesPC.js b/module/documents/actors/BladesPC.js index b52d2564..d05778d8 100644 --- a/module/documents/actors/BladesPC.js +++ b/module/documents/actors/BladesPC.js @@ -1,24 +1,25 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import C, { AttributeTrait, Harm, BladesActorType, BladesItemType, Tag, RollModSection, RollModStatus } from "../../core/constants.js"; import U from "../../core/utilities.js"; import { BladesActor } from "../BladesActorProxy.js"; import { BladesItem } from "../BladesItemProxy.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesPC extends BladesActor { - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Static Overrides: Create ~ static IsType(doc) { return super.IsType(doc, BladesActorType.pc); } + /*~ @@DOUBLE-BLANK@@ ~*/ static async create(data, options = {}) { data.token = data.token || {}; data.system = data.system ?? {}; + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog2("actor", "BladesPC.create(data,options)", { data, options }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Set the Token to sync with charsheet. data.token.actorLink = true; + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Initialize generic experience clues. data.system.experience = { playbook: { value: 0, max: 8 }, insight: { value: 0, max: 6 }, @@ -29,10 +30,13 @@ class BladesPC extends BladesActor { }; return super.create(data, options); } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesPrimaryActor Implementation ~ get primaryUser() { return game.users?.find((user) => user.character?.id === this?.id) || null; } + /*~ @@DOUBLE-BLANK@@ ~*/ async clearLoadout() { await this.update({ "system.loadout.selected": "" }); this.updateEmbeddedDocuments("Item", [ @@ -53,6 +57,8 @@ class BladesPC extends BladesActor { })) ]); } + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ getSubActor(actorRef) { const actor = super.getSubActor(actorRef); if (!actor) { @@ -63,6 +69,7 @@ class BladesPC extends BladesActor { } return actor; } + /*~ @@DOUBLE-BLANK@@ ~*/ get armorStatus() { const armorData = {}; if (this.system.armor.active.special) { @@ -95,17 +102,23 @@ class BladesPC extends BladesActor { } return armorData; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesScoundrel Implementation ~ + /*~ @@DOUBLE-BLANK@@ ~*/ isMember(crew) { return this.crew?.id === crew.id; } + /*~ @@DOUBLE-BLANK@@ ~*/ get vice() { if (this.type !== BladesActorType.pc) { return undefined; } return this.activeSubItems.find((item) => item.type === BladesItemType.vice); } + /*~ @@DOUBLE-BLANK@@ ~*/ get crew() { return this.activeSubActors .find((subActor) => BladesActor.IsType(subActor, BladesActorType.crew)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get abilities() { if (!this.playbook) { return []; @@ -113,13 +126,16 @@ class BladesPC extends BladesActor { return this.activeSubItems .filter((item) => [BladesItemType.ability, BladesItemType.crew_ability].includes(item.type)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get playbookName() { return this.playbook?.name; } + /*~ @@DOUBLE-BLANK@@ ~*/ get playbook() { return this.activeSubItems .find((item) => item.type === BladesItemType.playbook); } + /*~ @@DOUBLE-BLANK@@ ~*/ get attributes() { if (!BladesActor.IsType(this, BladesActorType.pc)) { return undefined; @@ -136,6 +152,7 @@ class BladesPC extends BladesActor { + this.system.resistance_bonus.resolve }; } + /*~ @@DOUBLE-BLANK@@ ~*/ get actions() { if (!BladesActor.IsType(this, BladesActorType.pc)) { return undefined; @@ -146,6 +163,7 @@ class BladesPC extends BladesActor { ...this.system.attributes.resolve }, ({ value, max }) => U.gsap.utils.clamp(0, max, value)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollable() { if (!BladesActor.IsType(this, BladesActorType.pc)) { return undefined; @@ -155,28 +173,35 @@ class BladesPC extends BladesActor { ...this.actions }; } + /*~ @@DOUBLE-BLANK@@ ~*/ get trauma() { if (!BladesActor.IsType(this, BladesActorType.pc)) { return 0; } return Object.keys(this.system.trauma.checked) .filter((traumaName) => + // @ts-ignore Compiler linter mismatch. this.system.trauma.active[traumaName] && this.system.trauma.checked[traumaName]) .length; } + /*~ @@DOUBLE-BLANK@@ ~*/ get traumaList() { + // @ts-ignore Compiler linter mismatch. return BladesActor.IsType(this, BladesActorType.pc) ? Object.keys(this.system.trauma.active).filter((key) => this.system.trauma.active[key]) : []; } + /*~ @@DOUBLE-BLANK@@ ~*/ get activeTraumaConditions() { if (!BladesActor.IsType(this, BladesActorType.pc)) { return {}; } return U.objFilter(this.system.trauma.checked, + // @ts-ignore Compiler linter mismatch. (_v, traumaName) => Boolean(traumaName in this.system.trauma.active && this.system.trauma.active[traumaName])); } + /*~ @@DOUBLE-BLANK@@ ~*/ get currentLoad() { if (!BladesActor.IsType(this, BladesActorType.pc)) { return 0; @@ -184,6 +209,7 @@ class BladesPC extends BladesActor { const activeLoadItems = this.activeSubItems.filter((item) => item.type === BladesItemType.gear); return U.gsap.utils.clamp(0, 10, activeLoadItems.reduce((tot, i) => tot + U.pInt(i.system.load), 0)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get remainingLoad() { if (!BladesActor.IsType(this, BladesActorType.pc)) { return 0; @@ -195,15 +221,21 @@ class BladesPC extends BladesActor { .toLowerCase()]; return Math.max(0, maxLoad - this.currentLoad); } + /*~ @@DOUBLE-BLANK@@ ~*/ async addStash(amount) { if (!BladesActor.IsType(this, BladesActorType.pc)) { return; } await this.update({ "system.stash.value": Math.min(this.system.stash.value + amount, this.system.stash.max) }); } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll.PrimaryDoc Implementation get rollModsData() { + /*~ @@DOUBLE-BLANK@@ ~*/ const rollModsData = super.rollModsData; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Add roll mods from harm [ [/1d/, RollModSection.roll], [/Less Effect/, RollModSection.effect] @@ -250,17 +282,28 @@ class BladesPC extends BladesActor { ].join("") }); } + /*~ @@DOUBLE-BLANK@@ ~*/ return rollModsData; } - - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll.ParticipantDoc Implementation + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantID() { return this.id; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantDoc() { return this; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantIcon() { return this.playbook?.img ?? this.img; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantName() { return this.name ?? ""; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantType() { return this.type; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollParticipantModsData() { return []; } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ get rollTraitPCTooltipActions() { const tooltipStrings = [""]; const actionRatings = this.actions; @@ -278,6 +321,7 @@ class BladesPC extends BladesActor { tooltipStrings.push("
"); return tooltipStrings.join(""); } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollTraitPCTooltipAttributes() { const tooltipStrings = [""]; const attributeRatings = this.attributes; @@ -294,4 +338,6 @@ class BladesPC extends BladesActor { return tooltipStrings.join(""); } } -export default BladesPC; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesPC; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/documents/actors/blades-crew.js b/module/documents/actors/blades-crew.js deleted file mode 100644 index 6e2dd973..00000000 --- a/module/documents/actors/blades-crew.js +++ /dev/null @@ -1,84 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import { BladesItemType, Factor } from "../../core/constants.js"; -import BladesActor from "../../blades-actor.js"; -import { BladesRollMod } from "../../blades-roll-collab.js"; -class BladesCrew extends BladesActor { - - static async create(data, options = {}) { - data.token = data.token || {}; - data.system = data.system ?? {}; - eLog.checkLog2("actor", "BladesActor.create(data,options)", { data, options }); - - data.token.actorLink = true; - - data.system.world_name = data.system.world_name ?? data.name.replace(/[^A-Za-z_0-9 ]/g, "").trim().replace(/ /g, "_"); - - data.system.experience = { - playbook: { value: 0, max: 8 }, - clues: [], - ...data.system.experience ?? {} - }; - return super.create(data, options); - } - get rollModsData() { - return BladesRollMod.ParseDocRollMods(this); - } - get rollFactors() { - const factorData = { - [Factor.tier]: { - name: Factor.tier, - value: this.getFactorTotal(Factor.tier), - max: this.getFactorTotal(Factor.tier), - baseVal: this.getFactorTotal(Factor.tier), - isActive: true, - isPrimary: true, - isDominant: false, - highFavorsPC: true - }, - [Factor.quality]: { - name: Factor.quality, - value: this.getFactorTotal(Factor.quality), - max: this.getFactorTotal(Factor.quality), - baseVal: this.getFactorTotal(Factor.quality), - isActive: false, - isPrimary: false, - isDominant: false, - highFavorsPC: true - } - }; - return factorData; - } - get rollPrimaryID() { return this.id; } - get rollPrimaryDoc() { return this; } - get rollPrimaryName() { return this.name; } - get rollPrimaryType() { return this.type; } - get rollPrimaryImg() { return this.img; } - - get rollParticipantID() { return this.id; } - get rollParticipantDoc() { return this; } - get rollParticipantIcon() { return this.playbook?.img ?? this.img; } - get rollParticipantName() { return this.name; } - get rollParticipantType() { return this.type; } - get rollParticipantModsData() { return []; } - - get abilities() { - if (!this.playbook) { - return []; - } - return this.activeSubItems.filter((item) => [BladesItemType.ability, BladesItemType.crew_ability].includes(item.type)); - } - get playbookName() { - return this.playbook?.name; - } - get playbook() { - return this.activeSubItems.find((item) => item.type === BladesItemType.crew_playbook); - } -} -export default BladesCrew; -//# sourceMappingURL=blades-crew.js.map \ No newline at end of file diff --git a/module/documents/actors/blades-faction.js b/module/documents/actors/blades-faction.js deleted file mode 100644 index cf8cd91c..00000000 --- a/module/documents/actors/blades-faction.js +++ /dev/null @@ -1,62 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import { Factor } from "../../core/constants.js"; -import BladesActor from "../../blades-actor.js"; -class BladesFaction extends BladesActor { - get rollFactors() { - const factorData = { - [Factor.tier]: { - name: Factor.tier, - value: this.getFactorTotal(Factor.tier), - max: this.getFactorTotal(Factor.tier), - baseVal: this.getFactorTotal(Factor.tier), - isActive: true, - isPrimary: true, - isDominant: false, - highFavorsPC: true - }, - [Factor.quality]: { - name: Factor.quality, - value: this.getFactorTotal(Factor.quality), - max: this.getFactorTotal(Factor.quality), - baseVal: this.getFactorTotal(Factor.quality), - isActive: false, - isPrimary: false, - isDominant: false, - highFavorsPC: true - } - }; - return factorData; - } - - get rollOppID() { return this.id; } - get rollOppDoc() { return this; } - get rollOppImg() { return this.img ?? ""; } - get rollOppName() { return this.name ?? ""; } - get rollOppSubName() { return ""; } - get rollOppType() { return this.type; } - get rollOppModsData() { return []; } - - async addClock(clockData = {}) { - clockData.id ??= clockData.id ?? randomID(); - clockData.color ??= "white"; - clockData.display ??= ""; - clockData.isVisible ??= false; - clockData.isNameVisible ??= false; - clockData.isActive ??= false; - clockData.max ??= 4; - clockData.target ??= `system.clocks.${clockData.id}.value`; - clockData.value ??= 0; - return this.update({ [`system.clocks.${clockData.id}`]: clockData }); - } - async deleteClock(clockID) { - return this.update({ [`system.clocks.-=${clockID}`]: null }); - } -} -export default BladesFaction; -//# sourceMappingURL=blades-faction.js.map \ No newline at end of file diff --git a/module/documents/actors/blades-npc.js b/module/documents/actors/blades-npc.js deleted file mode 100644 index ef313ca9..00000000 --- a/module/documents/actors/blades-npc.js +++ /dev/null @@ -1,76 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import { BladesActorType, Factor } from "../../core/constants.js"; -import BladesActor from "../../blades-actor.js"; -class BladesNPC extends BladesActor { - get rollFactors() { - const factorData = { - [Factor.tier]: { - name: Factor.tier, - value: this.getFactorTotal(Factor.tier), - max: this.getFactorTotal(Factor.tier), - baseVal: this.getFactorTotal(Factor.tier), - isActive: true, - isPrimary: true, - isDominant: false, - highFavorsPC: true - }, - [Factor.quality]: { - name: Factor.quality, - value: this.getFactorTotal(Factor.quality), - max: this.getFactorTotal(Factor.quality), - baseVal: this.getFactorTotal(Factor.quality), - isActive: false, - isPrimary: false, - isDominant: false, - highFavorsPC: true - } - }; - if (BladesActor.IsType(this, BladesActorType.npc)) { - factorData[Factor.scale] = { - name: Factor.scale, - value: this.getFactorTotal(Factor.scale), - max: this.getFactorTotal(Factor.scale), - baseVal: this.getFactorTotal(Factor.scale), - cssClasses: "factor-grey", - isActive: false, - isPrimary: false, - isDominant: false, - highFavorsPC: true - }; - factorData[Factor.magnitude] = { - name: Factor.magnitude, - value: this.getFactorTotal(Factor.magnitude), - max: this.getFactorTotal(Factor.magnitude), - baseVal: this.getFactorTotal(Factor.magnitude), - isActive: false, - isPrimary: false, - isDominant: false, - highFavorsPC: true - }; - } - return factorData; - } - - get rollOppID() { return this.id; } - get rollOppDoc() { return this; } - get rollOppImg() { return this.img ?? ""; } - get rollOppName() { return this.name ?? ""; } - get rollOppSubName() { return ""; } - get rollOppType() { return this.type; } - get rollOppModsData() { return []; } - - get rollParticipantID() { return this.id; } - get rollParticipantDoc() { return this; } - get rollParticipantIcon() { return this.img ?? ""; } - get rollParticipantName() { return this.name ?? ""; } - get rollParticipantType() { return this.type; } - get rollParticipantModsData() { return []; } -} -export default BladesNPC; -//# sourceMappingURL=blades-npc.js.map \ No newline at end of file diff --git a/module/documents/actors/blades-pc.js b/module/documents/actors/blades-pc.js deleted file mode 100644 index 93f1b83d..00000000 --- a/module/documents/actors/blades-pc.js +++ /dev/null @@ -1,306 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesItem from "../../blades-item.js"; -import C, { Attribute, Harm, BladesActorType, BladesItemType, Tag, RollModCategory, Factor, RollModStatus } from "../../core/constants.js"; -import U from "../../core/utilities.js"; -import BladesActor from "../../blades-actor.js"; -import { BladesRollMod } from "../../blades-roll-collab.js"; -class BladesPC extends BladesActor { - - static async create(data, options = {}) { - data.token = data.token || {}; - data.system = data.system ?? {}; - eLog.checkLog2("actor", "BladesPC.create(data,options)", { data, options }); - - data.token.actorLink = true; - - data.system.experience = { - playbook: { value: 0, max: 8 }, - insight: { value: 0, max: 6 }, - prowess: { value: 0, max: 6 }, - resolve: { value: 0, max: 6 }, - clues: [], - ...data.system.experience ?? {} - }; - return super.create(data, options); - } - - get primaryUser() { - return game.users?.find((user) => user.character?.id === this?.id) || null; - } - async clearLoadout() { - this.update({ "system.loadout.selected": "" }); - this.updateEmbeddedDocuments("Item", [ - ...this.activeSubItems.filter((item) => BladesItem.IsType(item, BladesItemType.gear) && !item.hasTag(Tag.System.Archived)) - .map((item) => ({ - "_id": item.id, - "system.tags": [...item.tags, Tag.System.Archived], - "system.uses_per_score.value": 0 - })), - ...this.activeSubItems.filter((item) => BladesItem.IsType(item, BladesItemType.ability) && item.system.uses_per_score.max) - .map((item) => ({ - "_id": item.id, - "system.uses_per_score.value": 0 - })) - ]); - } - getSubActor(actorRef) { - const actor = super.getSubActor(actorRef); - if (!actor) { - return undefined; - } - if (this.primaryUser?.id) { - actor.ownership[this.primaryUser.id] = CONST.DOCUMENT_PERMISSION_LEVELS.OWNER; - } - return actor; - } - get armorStatus() { - const armorData = {}; - if (this.system.armor.active.special) { - armorData.special = this.system.armor.checked.special; - } - if (this.system.armor.active.heavy) { - armorData.max = 2; - if (this.system.armor.checked.light) { - armorData.value = 0; - } - else if (this.system.armor.checked.heavy) { - armorData.value = 1; - } - else { - armorData.value = 2; - } - } - else if (this.system.armor.active.light) { - armorData.max = 1; - if (this.system.armor.checked.light) { - armorData.value = 0; - } - else { - armorData.value = 1; - } - } - else { - armorData.max = 0; - armorData.value = 0; - } - return armorData; - } - isMember(crew) { return this.crew?.id === crew.id; } - get vice() { - if (this.type !== BladesActorType.pc) { - return undefined; - } - return this.activeSubItems.find((item) => item.type === BladesItemType.vice); - } - get crew() { - return this.activeSubActors.find((subActor) => BladesActor.IsType(subActor, BladesActorType.crew)); - } - get abilities() { - if (!this.playbook) { - return []; - } - return this.activeSubItems.filter((item) => [BladesItemType.ability, BladesItemType.crew_ability].includes(item.type)); - } - get playbookName() { - return this.playbook?.name; - } - get playbook() { - return this.activeSubItems.find((item) => item.type === BladesItemType.playbook); - } - get attributes() { - if (!BladesActor.IsType(this, BladesActorType.pc)) { - return undefined; - } - return { - insight: Object.values(this.system.attributes.insight).filter(({ value }) => value > 0).length + this.system.resistance_bonus.insight, - prowess: Object.values(this.system.attributes.prowess).filter(({ value }) => value > 0).length + this.system.resistance_bonus.prowess, - resolve: Object.values(this.system.attributes.resolve).filter(({ value }) => value > 0).length + this.system.resistance_bonus.resolve - }; - } - get actions() { - if (!BladesActor.IsType(this, BladesActorType.pc)) { - return undefined; - } - return U.objMap({ - ...this.system.attributes.insight, - ...this.system.attributes.prowess, - ...this.system.attributes.resolve - }, ({ value, max }) => U.gsap.utils.clamp(0, max, value)); - } - get rollable() { - if (!BladesActor.IsType(this, BladesActorType.pc)) { - return undefined; - } - return { - ...this.attributes, - ...this.actions - }; - } - get trauma() { - if (!BladesActor.IsType(this, BladesActorType.pc)) { - return 0; - } - return Object.keys(this.system.trauma.checked) - .filter((traumaName) => - this.system.trauma.active[traumaName] && this.system.trauma.checked[traumaName]) - .length; - } - get traumaList() { - return BladesActor.IsType(this, BladesActorType.pc) ? Object.keys(this.system.trauma.active).filter((key) => this.system.trauma.active[key]) : []; - } - get activeTraumaConditions() { - if (!BladesActor.IsType(this, BladesActorType.pc)) { - return {}; - } - return U.objFilter(this.system.trauma.checked, - (_v, traumaName) => Boolean(traumaName in this.system.trauma.active && this.system.trauma.active[traumaName])); - } - get currentLoad() { - if (!BladesActor.IsType(this, BladesActorType.pc)) { - return 0; - } - const activeLoadItems = this.activeSubItems.filter((item) => item.type === BladesItemType.gear); - return U.gsap.utils.clamp(0, 10, activeLoadItems.reduce((tot, i) => tot + U.pInt(i.system.load), 0)); - } - get remainingLoad() { - if (!BladesActor.IsType(this, BladesActorType.pc)) { - return 0; - } - if (!this.system.loadout.selected) { - return 0; - } - const maxLoad = this.system.loadout.levels[game.i18n.localize(this.system.loadout.selected.toString()).toLowerCase()]; - return Math.max(0, maxLoad - this.currentLoad); - } - async addStash(amount) { - if (!BladesActor.IsType(this, BladesActorType.pc)) { - return; - } - this.update({ "system.stash.value": Math.min(this.system.stash.value + amount, this.system.stash.max) }); - } - get rollFactors() { - const factorData = { - [Factor.tier]: { - name: Factor.tier, - value: this.getFactorTotal(Factor.tier), - max: this.getFactorTotal(Factor.tier), - baseVal: this.getFactorTotal(Factor.tier), - isActive: true, - isPrimary: true, - isDominant: false, - highFavorsPC: true - }, - [Factor.quality]: { - name: Factor.quality, - value: this.getFactorTotal(Factor.quality), - max: this.getFactorTotal(Factor.quality), - baseVal: this.getFactorTotal(Factor.quality), - isActive: false, - isPrimary: false, - isDominant: false, - highFavorsPC: true - } - }; - return factorData; - } - get rollPrimaryID() { return this.id; } - get rollPrimaryDoc() { return this; } - get rollPrimaryName() { return this.name; } - get rollPrimaryType() { return this.type; } - get rollPrimaryImg() { return this.img; } - get rollModsData() { - const rollModsData = BladesRollMod.ParseDocRollMods(this); - [[/1d/, RollModCategory.roll], [/Less Effect/, RollModCategory.effect]].forEach(([effectPat, effectCat]) => { - const { one: harmConditionOne, two: harmConditionTwo } = Object.values(this.system.harm) - .find((harmData) => effectPat.test(harmData.effect)) ?? {}; - const harmString = U.objCompact([harmConditionOne, harmConditionTwo === "" ? null : harmConditionTwo]).join(" & "); - if (harmString.length > 0) { - rollModsData.push({ - id: `Harm-negative-${effectCat}`, - name: harmString, - category: effectCat, - posNeg: "negative", - base_status: RollModStatus.ToggledOn, - modType: "harm", - value: 1, - tooltip: [ - `

${effectCat === RollModCategory.roll ? Harm.Impaired : Harm.Weakened} (Harm)

`, - `

${harmString}

`, - effectCat === RollModCategory.roll - ? "

If your injuries apply to the situation at hand, you suffer −1d to your roll.

" - : "

If your injuries apply to the situation at hand, you suffer −1 effect." - ].join("") - }); - } - }); - const { one: harmCondition } = Object.values(this.system.harm).find((harmData) => /Need Help/.test(harmData.effect)) ?? {}; - if (harmCondition && harmCondition.trim() !== "") { - rollModsData.push({ - id: "Push-negative-roll", - name: "PUSH", - sideString: harmCondition.trim(), - category: RollModCategory.roll, - posNeg: "negative", - base_status: RollModStatus.ToggledOn, - modType: "harm", - value: 0, - effectKeys: ["Cost-Stress2"], - tooltip: [ - "

Broken (Harm)

", - `

${harmCondition.trim()}

`, - "

If your injuries apply to the situation at hand, you must Push to act.

" - ].join("") - }); - } - return rollModsData; - } - - - get rollParticipantID() { return this.id; } - get rollParticipantDoc() { return this; } - get rollParticipantIcon() { return this.playbook?.img ?? this.img; } - get rollParticipantName() { return this.name ?? ""; } - get rollParticipantType() { return this.type; } - get rollParticipantModsData() { return []; } - - get rollTraitPCTooltipActions() { - const tooltipStrings = ["
"]; - const actionRatings = this.actions; - Object.values(Attribute).forEach((attribute) => { - C.Action[attribute].forEach((action) => { - tooltipStrings.push([ - "", - ``, - ``, - ``, - "" - ].join("")); - }); - }); - tooltipStrings.push("
${U.uCase(action)}${"⚪".repeat(actionRatings[action])}(${C.ShortActionTooltips[action]})
"); - return tooltipStrings.join(""); - } - get rollTraitPCTooltipAttributes() { - const tooltipStrings = [""]; - const attributeRatings = this.attributes; - Object.values(Attribute).forEach((attribute) => { - tooltipStrings.push([ - "", - ``, - ``, - ``, - "" - ].join("")); - }); - tooltipStrings.push("
${U.uCase(attribute)}${"⚪".repeat(attributeRatings[attribute])}(${C.ShortAttributeTooltips[attribute]})
"); - return tooltipStrings.join(""); - } -} -export default BladesPC; -//# sourceMappingURL=blades-pc.js.map \ No newline at end of file diff --git a/module/documents/blades-actor-proxy.js b/module/documents/blades-actor-proxy.js deleted file mode 100644 index 9a508959..00000000 --- a/module/documents/blades-actor-proxy.js +++ /dev/null @@ -1,58 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import U from "../core/utilities.js"; -import { BladesActorType } from "../core/constants.js"; -import BladesActor from "../blades-actor.js"; -import BladesPC from "./actors/blades-pc.js"; -import BladesNPC from "./actors/blades-npc.js"; -import BladesFaction from "./actors/blades-faction.js"; -import BladesCrew from "./actors/blades-crew.js"; -const ActorsMap = { - [BladesActorType.pc]: BladesPC, - [BladesActorType.npc]: BladesNPC, - [BladesActorType.faction]: BladesFaction, - [BladesActorType.crew]: BladesCrew -}; -const BladesActorProxy = new Proxy(function () { }, { - construct(_, args) { - const [{ type }] = args; - if (!type) { - throw new Error(`Invalid Actor Type: ${String(type)}`); - } - const MappedConstructor = ActorsMap[type]; - if (!MappedConstructor) { - return new BladesActor(...args); - } - return new MappedConstructor(...args); - }, - get(_, prop) { - switch (prop) { - case "create": - case "createDocuments": - return function (data, options = {}) { - if (U.isArray(data)) { - return data.map((i) => CONFIG.Actor.documentClass.create(i, options)); - } - const MappedConstructor = ActorsMap[data.type]; - if (!MappedConstructor) { - return BladesActor.create(data, options); - } - return MappedConstructor.create(data, options); - }; - case Symbol.hasInstance: - return function (instance) { - return Object.values(ActorsMap).some((i) => instance instanceof i); - }; - default: - return BladesActor[prop]; - } - } -}); -export default BladesActorProxy; -export { BladesActor, BladesPC, BladesCrew, BladesNPC, BladesFaction }; -//# sourceMappingURL=blades-actor-proxy.js.map \ No newline at end of file diff --git a/module/documents/blades-item-location.js b/module/documents/blades-item-location.js deleted file mode 100644 index ad9c1066..00000000 --- a/module/documents/blades-item-location.js +++ /dev/null @@ -1,8 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -"use strict"; \ No newline at end of file diff --git a/module/documents/blades-item-proxy.js b/module/documents/blades-item-proxy.js deleted file mode 100644 index 1109d613..00000000 --- a/module/documents/blades-item-proxy.js +++ /dev/null @@ -1,58 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import U from "../core/utilities.js"; -import { BladesItemType } from "../core/constants.js"; -import BladesItem from "../blades-item.js"; -import BladesLocation from "./items/blades-location.js"; -import BladesClockKeeper from "./items/blades-clock-keeper.js"; -import BladesGMTracker from "./items/blades-gm-tracker.js"; -import BladesScore from "./items/blades-score.js"; -const ItemsMap = { - [BladesItemType.clock_keeper]: BladesClockKeeper, - [BladesItemType.gm_tracker]: BladesGMTracker, - [BladesItemType.location]: BladesLocation, - [BladesItemType.score]: BladesScore -}; -const BladesItemProxy = new Proxy(function () { }, { - construct(_, args) { - const [{ type }] = args; - if (!type) { - throw new Error(`Invalid Item Type: ${String(type)}`); - } - const MappedConstructor = ItemsMap[type]; - if (!MappedConstructor) { - return new BladesItem(...args); - } - return new MappedConstructor(...args); - }, - get(_, prop) { - switch (prop) { - case "create": - case "createDocuments": - return function (data, options = {}) { - if (U.isArray(data)) { - return data.map((i) => CONFIG.Item.documentClass.create(i, options)); - } - const MappedConstructor = ItemsMap[data.type]; - if (!MappedConstructor) { - return BladesItem.create(data, options); - } - return MappedConstructor.create(data, options); - }; - case Symbol.hasInstance: - return function (instance) { - return Object.values(ItemsMap).some((i) => instance instanceof i); - }; - default: - return BladesItem[prop]; - } - } -}); -export default BladesItemProxy; -export { BladesItem, BladesClockKeeper, BladesGMTracker, BladesLocation, BladesScore }; -//# sourceMappingURL=blades-item-proxy.js.map \ No newline at end of file diff --git a/module/documents/items/BladesClockKeeper.js b/module/documents/items/BladesClockKeeper.js index 68feb4e9..1a225b39 100644 --- a/module/documents/items/BladesClockKeeper.js +++ b/module/documents/items/BladesClockKeeper.js @@ -1,16 +1,11 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import BladesItem from "../../BladesItem.js"; import C, { SVGDATA, BladesActorType, BladesItemType } from "../../core/constants.js"; import U from "../../core/utilities.js"; import BladesActor from "../../BladesActor.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesClockKeeper extends BladesItem { - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region CLOCKS OVERLAY _overlayElement; get overlayElement() { this._overlayElement ??= $("#clocks-overlay")[0]; @@ -20,6 +15,7 @@ class BladesClockKeeper extends BladesItem { } return this._overlayElement; } + /*~ @@DOUBLE-BLANK@@ ~*/ async renderOverlay() { if (!game.scenes?.current) { return; @@ -53,25 +49,33 @@ class BladesClockKeeper extends BladesItem { if (!(event.originalEvent instanceof WheelEvent)) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ event.preventDefault(); + /*~ @@DOUBLE-BLANK@@ ~*/ const clock$ = $(event.currentTarget).closest(".clock"); const [key] = clock$.closest(".clock-key"); + /*~ @@DOUBLE-BLANK@@ ~*/ if (!(key instanceof HTMLElement)) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ const keyID = key.id; const clockNum = clock$.data("index"); const curClockVal = U.pInt(clock$.data("value")); const delta = event.originalEvent.deltaY < 0 ? 1 : -1; const max = U.pInt(clock$.data("size")); + /*~ @@DOUBLE-BLANK@@ ~*/ const newClockVal = U.gsap.utils.clamp(0, max, curClockVal + delta); + /*~ @@DOUBLE-BLANK@@ ~*/ if (curClockVal === newClockVal) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ await game.eunoblades.ClockKeeper.update({ [`system.clock_keys.${keyID}.clocks.${clockNum}.value`]: `${newClockVal}` }); }); + /*~ @@DOUBLE-BLANK@@ ~*/ $("#clocks-overlay").find(".key-label").on({ click: async (event) => { if (!event.currentTarget) { @@ -80,13 +84,16 @@ class BladesClockKeeper extends BladesItem { if (!BladesItem.IsType(game.eunoblades.ClockKeeper, BladesItemType.clock_keeper)) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ event.preventDefault(); + /*~ @@DOUBLE-BLANK@@ ~*/ const keyID = $(event.currentTarget).data("keyId"); eLog.checkLog3("clocksOverlay", "Updating Key isActive", { current: game.eunoblades.ClockKeeper.system.clock_keys[keyID]?.isActive, update: !(game.eunoblades.ClockKeeper.system.clock_keys[keyID]?.isActive) }); await game.eunoblades.ClockKeeper.update({ [`system.clock_keys.${keyID}.isActive`]: !(game.eunoblades.ClockKeeper.system.clock_keys[keyID]?.isActive) }); + /*~ @@DOUBLE-BLANK@@ ~*/ }, contextmenu: () => { if (!game.user.isGM) { @@ -96,6 +103,7 @@ class BladesClockKeeper extends BladesItem { } }); } + /*~ @@DOUBLE-BLANK@@ ~*/ async addClockKey() { if (!BladesItem.IsType(game.eunoblades.ClockKeeper, BladesItemType.clock_keeper)) { return undefined; @@ -122,12 +130,14 @@ class BladesClockKeeper extends BladesItem { } } }); } + /*~ @@DOUBLE-BLANK@@ ~*/ async deleteClockKey(keyID) { if (!BladesItem.IsType(game.eunoblades.ClockKeeper, BladesItemType.clock_keeper)) { return undefined; } return game.eunoblades.ClockKeeper.update({ [`system.clock_keys.-=${keyID}`]: null }); } + /*~ @@DOUBLE-BLANK@@ ~*/ async setKeySize(keyID, keySize = 1) { if (!BladesItem.IsType(game.eunoblades.ClockKeeper, BladesItemType.clock_keeper)) { return undefined; @@ -161,10 +171,14 @@ class BladesClockKeeper extends BladesItem { } eLog.checkLog("clock_key", "Clock Key Update Data", { clockKey, updateData }); return game.eunoblades.ClockKeeper.update(updateData); + /*~ @@DOUBLE-BLANK@@ ~*/ } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region OVERRIDES: prepareDerivedData, _onUpdate prepareDerivedData() { super.prepareDerivedData(); + /*~ @@DOUBLE-BLANK@@ ~*/ this.system.scenes = game.scenes.map((scene) => ({ id: scene.id, name: scene.name ?? "" })); this.system.targetScene ??= game.scenes.current?.id || null; this.system.clock_keys = Object.fromEntries(Object.entries(this.system.clock_keys ?? {}) @@ -178,10 +192,12 @@ class BladesClockKeeper extends BladesItem { return [keyID, keyData]; })); } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onUpdate(changed, options, userId) { super._onUpdate(changed, options, userId); BladesActor.GetTypeWithTags(BladesActorType.pc).forEach((actor) => actor.render()); socketlib.system.executeForEveryone("renderOverlay"); } } -export default BladesClockKeeper; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesClockKeeper; diff --git a/module/documents/items/BladesGMTracker.js b/module/documents/items/BladesGMTracker.js index ec3fb5c8..c1eb3257 100644 --- a/module/documents/items/BladesGMTracker.js +++ b/module/documents/items/BladesGMTracker.js @@ -1,27 +1,25 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import BladesItem from "../../BladesItem.js"; import { BladesActorType, BladesItemType, BladesPhase } from "../../core/constants.js"; import BladesActor from "../../BladesActor.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesGMTracker extends BladesItem { + /*~ @@DOUBLE-BLANK@@ ~*/ get phase() { return BladesItem.IsType(this, BladesItemType.gm_tracker) && this.system.phase; } set phase(phase) { if (phase && BladesItem.IsType(this, BladesItemType.gm_tracker)) { this.update({ "system.phase": phase }); } } + /*~ @@DOUBLE-BLANK@@ ~*/ prepareDerivedData() { this.system.phases = Object.values(BladesPhase); } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region OVERRIDES: prepareDerivedData, _onUpdate async _onUpdate(changed, options, userId) { await super._onUpdate(changed, options, userId); BladesActor.GetTypeWithTags(BladesActorType.pc).forEach((actor) => actor.render()); } } -export default BladesGMTracker; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesGMTracker; diff --git a/module/documents/items/BladesLocation.js b/module/documents/items/BladesLocation.js index 43fe1d99..53ce8137 100644 --- a/module/documents/items/BladesLocation.js +++ b/module/documents/items/BladesLocation.js @@ -1,16 +1,12 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import BladesItem from "../../BladesItem.js"; import { BladesActorType, Factor } from "../../core/constants.js"; import U from "../../core/utilities.js"; import BladesActor from "../../BladesActor.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesLocation extends BladesItem { + /*~ @@DOUBLE-BLANK@@ ~*/ get rollFactors() { + /*~ @@DOUBLE-BLANK@@ ~*/ const factorData = {}; [ Factor.tier, @@ -33,19 +29,24 @@ class BladesLocation extends BladesItem { }); return factorData; } + /*~ @@DOUBLE-BLANK@@ ~*/ getFactorTotal(factor) { switch (factor) { case Factor.tier: return this.system.tier.value; case Factor.quality: return this.getFactorTotal(Factor.tier); case Factor.scale: return this.system.scale; + // no default } return 0; } + /*~ @@DOUBLE-BLANK@@ ~*/ get rollOppImg() { return this.img ?? ""; } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region OVERRIDES: _onUpdate async _onUpdate(changed, options, userId) { await super._onUpdate(changed, options, userId); BladesActor.GetTypeWithTags(BladesActorType.pc).forEach((actor) => actor.render()); } } -export default BladesLocation; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesLocation; diff --git a/module/documents/items/BladesScore.js b/module/documents/items/BladesScore.js index 4627fd5b..50c2a2bb 100644 --- a/module/documents/items/BladesScore.js +++ b/module/documents/items/BladesScore.js @@ -1,23 +1,20 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import BladesItem from "../../BladesItem.js"; import { BladesActorType, BladesItemType, Factor } from "../../core/constants.js"; import U from "../../core/utilities.js"; import BladesActor from "../../BladesActor.js"; import BladesScoreSheet from "../../sheets/item/BladesScoreSheet.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesScore extends BladesItem { - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region INITIALIZATION ~ static async Initialize() { game.eunoblades ??= {}; Object.assign(globalThis, { BladesScore, BladesScoreSheet }); Items.registerSheet("blades", BladesScoreSheet, { types: ["score"], makeDefault: true }); return loadTemplates(["systems/eunos-blades/templates/items/score-sheet.hbs"]); } + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ static get Active() { return BladesItem.GetTypeWithTags(BladesItemType.score).find((score) => score.system.isActive); } @@ -30,8 +27,10 @@ class BladesScore extends BladesItem { } }); } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region BladesRoll.OppositionDocData Implementation get rollFactors() { + /*~ @@DOUBLE-BLANK@@ ~*/ const tierTotal = this.getFactorTotal(Factor.tier); return { [Factor.tier]: { @@ -58,10 +57,13 @@ class BladesScore extends BladesItem { default: return 0; } } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region OVERRIDES: _onUpdate async _onUpdate(changed, options, userId) { super._onUpdate(changed, options, userId); BladesActor.GetTypeWithTags(BladesActorType.pc).forEach((actor) => actor.render()); } } -export default BladesScore; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesScore; diff --git a/module/documents/items/blades-clock-keeper.js b/module/documents/items/blades-clock-keeper.js deleted file mode 100644 index c12b129e..00000000 --- a/module/documents/items/blades-clock-keeper.js +++ /dev/null @@ -1,188 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesItem from "../../blades-item.js"; -import C, { SVGDATA, BladesActorType, BladesItemType } from "../../core/constants.js"; -import U from "../../core/utilities.js"; -import BladesActor from "../../blades-actor.js"; -class BladesClockKeeper extends BladesItem { - - _overlayElement; - get overlayElement() { - this._overlayElement ??= $("#clocks-overlay")[0]; - if (!this._overlayElement) { - $("body.vtt.game.system-eunos-blades").append("
"); - [this._overlayElement] = $("#clocks-overlay"); - } - return this._overlayElement; - } - async renderOverlay() { - if (!game.scenes?.current) { - return; - } - if (!game.eunoblades.ClockKeeper) { - return; - } - if (!game.eunoblades.ClockKeeper.overlayElement) { - eLog.error("clocksOverlay", "[ClocksOverlay] Cannot locate overlay element."); - return; - } - game.eunoblades.ClockKeeper.overlayElement.innerHTML = (await getTemplate("systems/eunos-blades/templates/overlays/clock-overlay.hbs"))({ - ...game.eunoblades.ClockKeeper.system, - currentScene: game.scenes?.current.id, - clockSizes: C.ClockSizes, - svgData: SVGDATA - }); - game.eunoblades.ClockKeeper.activateOverlayListeners(); - } - async activateOverlayListeners() { - if (!game?.user?.isGM) { - return; - } - $("#clocks-overlay").find(".clock-frame").on("wheel", async (event) => { - if (!event.currentTarget) { - return; - } - if (!BladesItem.IsType(game.eunoblades.ClockKeeper, BladesItemType.clock_keeper)) { - return; - } - if (!(event.originalEvent instanceof WheelEvent)) { - return; - } - event.preventDefault(); - const clock$ = $(event.currentTarget).closest(".clock"); - const [key] = clock$.closest(".clock-key"); - if (!(key instanceof HTMLElement)) { - return; - } - const keyID = key.id; - const clockNum = clock$.data("index"); - const curClockVal = U.pInt(clock$.data("value")); - const delta = event.originalEvent.deltaY < 0 ? 1 : -1; - const max = U.pInt(clock$.data("size")); - const newClockVal = U.gsap.utils.clamp(0, max, curClockVal + delta); - if (curClockVal === newClockVal) { - return; - } - await game.eunoblades.ClockKeeper.update({ - [`system.clock_keys.${keyID}.clocks.${clockNum}.value`]: `${newClockVal}` - }); - }); - $("#clocks-overlay").find(".key-label").on({ - click: async (event) => { - if (!event.currentTarget) { - return; - } - if (!BladesItem.IsType(game.eunoblades.ClockKeeper, BladesItemType.clock_keeper)) { - return; - } - event.preventDefault(); - const keyID = $(event.currentTarget).data("keyId"); - eLog.checkLog3("clocksOverlay", "Updating Key isActive", { - current: game.eunoblades.ClockKeeper.system.clock_keys[keyID]?.isActive, - update: !(game.eunoblades.ClockKeeper.system.clock_keys[keyID]?.isActive) - }); - await game.eunoblades.ClockKeeper.update({ [`system.clock_keys.${keyID}.isActive`]: !(game.eunoblades.ClockKeeper.system.clock_keys[keyID]?.isActive) }); - }, - contextmenu: () => { - if (!game.user.isGM) { - return; - } - game.eunoblades.ClockKeeper?.sheet?.render(true); - } - }); - } - async addClockKey() { - if (!BladesItem.IsType(game.eunoblades.ClockKeeper, BladesItemType.clock_keeper)) { - return undefined; - } - const keyID = randomID(); - return game.eunoblades.ClockKeeper.update({ [`system.clock_keys.${keyID}`]: { - id: keyID, - display: "", - isVisible: false, - isNameVisible: true, - isActive: true, - scene: game.eunoblades.ClockKeeper.system.targetScene, - numClocks: 1, - clocks: { - 1: { - display: "", - isVisible: false, - isNameVisible: false, - isActive: false, - color: "yellow", - max: 4, - value: 0 - } - } - } }); - } - async deleteClockKey(keyID) { - if (!BladesItem.IsType(game.eunoblades.ClockKeeper, BladesItemType.clock_keeper)) { - return undefined; - } - return game.eunoblades.ClockKeeper.update({ [`system.clock_keys.-=${keyID}`]: null }); - } - async setKeySize(keyID, keySize = 1) { - if (!BladesItem.IsType(game.eunoblades.ClockKeeper, BladesItemType.clock_keeper)) { - return undefined; - } - keySize = parseInt(`${keySize}`, 10); - const updateData = { - [`system.clock_keys.${keyID}.numClocks`]: keySize - }; - const clockKey = game.eunoblades.ClockKeeper.system.clock_keys[keyID]; - if (!clockKey) { - return game.eunoblades.ClockKeeper; - } - const currentSize = Object.values(clockKey.clocks).length; - if (currentSize < keySize) { - for (let i = (currentSize + 1); i <= keySize; i++) { - updateData[`system.clock_keys.${keyID}.clocks.${i}`] = { - display: "", - value: 0, - max: 4, - color: "yellow", - isVisible: false, - isNameVisible: true, - isActive: false - }; - } - } - else if (currentSize > keySize) { - for (let i = (keySize + 1); i <= currentSize; i++) { - updateData[`system.clock_keys.${keyID}.clocks.-=${i}`] = null; - } - } - eLog.checkLog("clock_key", "Clock Key Update Data", { clockKey, updateData }); - return game.eunoblades.ClockKeeper.update(updateData); - } - - prepareDerivedData() { - super.prepareDerivedData(); - this.system.scenes = game.scenes.map((scene) => ({ id: scene.id, name: scene.name ?? "" })); - this.system.targetScene ??= game.scenes.current?.id || null; - this.system.clock_keys = Object.fromEntries(Object.entries(this.system.clock_keys ?? {}) - .filter(([_, keyData]) => keyData?.id) - .map(([keyID, keyData]) => { - if (keyData === null) { - return [keyID, null]; - } - keyData.clocks = Object.fromEntries(Object.entries(keyData.clocks ?? {}) - .filter(([_, clockData]) => Boolean(clockData))); - return [keyID, keyData]; - })); - } - async _onUpdate(changed, options, userId) { - await super._onUpdate(changed, options, userId); - BladesActor.GetTypeWithTags(BladesActorType.pc).forEach((actor) => actor.render()); - socketlib.system.executeForEveryone("renderOverlay"); - } -} -export default BladesClockKeeper; -//# sourceMappingURL=blades-clock-keeper.js.map \ No newline at end of file diff --git a/module/documents/items/blades-gm-tracker.js b/module/documents/items/blades-gm-tracker.js deleted file mode 100644 index 4de9eec7..00000000 --- a/module/documents/items/blades-gm-tracker.js +++ /dev/null @@ -1,28 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesItem from "../../blades-item.js"; -import { BladesActorType, BladesItemType, BladesPhase } from "../../core/constants.js"; -import BladesActor from "../../blades-actor.js"; -class BladesGMTracker extends BladesItem { - get phase() { return BladesItem.IsType(this, BladesItemType.gm_tracker) && this.system.phase; } - set phase(phase) { - if (phase && BladesItem.IsType(this, BladesItemType.gm_tracker)) { - this.update({ "system.phase": phase }); - } - } - prepareDerivedData() { - this.system.phases = Object.values(BladesPhase); - } - - async _onUpdate(changed, options, userId) { - await super._onUpdate(changed, options, userId); - BladesActor.GetTypeWithTags(BladesActorType.pc).forEach((actor) => actor.render()); - } -} -export default BladesGMTracker; -//# sourceMappingURL=blades-gm-tracker.js.map \ No newline at end of file diff --git a/module/documents/items/blades-item-location.js b/module/documents/items/blades-item-location.js deleted file mode 100644 index ad9c1066..00000000 --- a/module/documents/items/blades-item-location.js +++ /dev/null @@ -1,8 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -"use strict"; \ No newline at end of file diff --git a/module/documents/items/blades-location copy.js b/module/documents/items/blades-location copy.js deleted file mode 100644 index 19935eda..00000000 --- a/module/documents/items/blades-location copy.js +++ /dev/null @@ -1,11 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesItem from "../../blades-item.js"; -class BladesLocation extends BladesItem { -} -export default BladesLocation; \ No newline at end of file diff --git a/module/documents/items/blades-location.js b/module/documents/items/blades-location.js deleted file mode 100644 index 134c3743..00000000 --- a/module/documents/items/blades-location.js +++ /dev/null @@ -1,52 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesItem from "../../blades-item.js"; -import { BladesActorType, Factor } from "../../core/constants.js"; -import U from "../../core/utilities.js"; -import BladesActor from "../../blades-actor.js"; -class BladesLocation extends BladesItem { - get rollFactors() { - const factorData = {}; - [ - Factor.tier, - Factor.quality, - Factor.scale - ].forEach((factor, i) => { - const factorTotal = this.getFactorTotal(factor); - factorData[factor] = { - name: factor, - value: factorTotal, - max: factorTotal, - baseVal: factorTotal, - display: factor === Factor.tier ? U.romanizeNum(factorTotal) : `${factorTotal}`, - isActive: i === 0, - isPrimary: i === 0, - isDominant: false, - highFavorsPC: true, - cssClasses: `factor-gold${i === 0 ? " factor-main" : ""}` - }; - }); - return factorData; - } - getFactorTotal(factor) { - switch (factor) { - case Factor.tier: return this.system.tier.value; - case Factor.quality: return this.getFactorTotal(Factor.tier); - case Factor.scale: return this.system.scale; - } - return 0; - } - get rollOppImg() { return this.img ?? ""; } - - async _onUpdate(changed, options, userId) { - await super._onUpdate(changed, options, userId); - BladesActor.GetTypeWithTags(BladesActorType.pc).forEach((actor) => actor.render()); - } -} -export default BladesLocation; -//# sourceMappingURL=blades-location.js.map \ No newline at end of file diff --git a/module/documents/items/blades-score.js b/module/documents/items/blades-score.js deleted file mode 100644 index 8029412c..00000000 --- a/module/documents/items/blades-score.js +++ /dev/null @@ -1,68 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesItem from "../../blades-item.js"; -import { BladesActorType, BladesItemType, Factor } from "../../core/constants.js"; -import U from "../../core/utilities.js"; -import BladesActor from "../../blades-actor.js"; -import BladesScoreSheet from "../../sheets/item/blades-score-sheet.js"; -class BladesScore extends BladesItem { - - static async Initialize() { - game.eunoblades ??= {}; - Object.assign(globalThis, { BladesScore, BladesScoreSheet }); - Items.registerSheet("blades", BladesScoreSheet, { types: ["score"], makeDefault: true }); - return loadTemplates(["systems/eunos-blades/templates/items/score-sheet.hbs"]); - } - static get Active() { - return BladesItem.GetTypeWithTags(BladesItemType.score).find((score) => score.system.isActive); - } - static set Active(val) { - BladesItem.GetTypeWithTags(BladesItemType.score) - .find((score) => score.system.isActive)?.update({ "system.isActive": false }) - .then(() => { - if (val) { - val.update({ "system.isActive": true }); - } - }); - } - - get rollFactors() { - const tierTotal = this.getFactorTotal(Factor.tier); - return { - [Factor.tier]: { - name: "Tier", - value: tierTotal, - max: tierTotal, - baseVal: tierTotal, - display: U.romanizeNum(tierTotal), - isActive: true, - isPrimary: true, - isDominant: false, - highFavorsPC: true, - cssClasses: "factor-gold factor-main" - } - }; - } - get rollOppImg() { return this.img ?? ""; } - getFactorTotal(factor) { - switch (factor) { - case Factor.tier: return this.system.tier.value; - case Factor.quality: return this.getFactorTotal(Factor.tier); - case Factor.scale: return 0; - case Factor.magnitude: return 0; - } - return 0; - } - - async _onUpdate(changed, options, userId) { - await super._onUpdate(changed, options, userId); - BladesActor.GetTypeWithTags(BladesActorType.pc).forEach((actor) => actor.render()); - } -} -export default BladesScore; -//# sourceMappingURL=blades-score.js.map \ No newline at end of file diff --git a/module/sheets/BladesSheet.js b/module/sheets/BladesSheet.js deleted file mode 100644 index ad9c1066..00000000 --- a/module/sheets/BladesSheet.js +++ /dev/null @@ -1,8 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -"use strict"; \ No newline at end of file diff --git a/module/sheets/actor/BladesActorSheet.js b/module/sheets/actor/BladesActorSheet.js index 1116e847..b9c0b56b 100644 --- a/module/sheets/actor/BladesActorSheet.js +++ b/module/sheets/actor/BladesActorSheet.js @@ -1,11 +1,5 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - - +// #region IMPORTS~ +/*~ @@DOUBLE-BLANK@@ ~*/ import U from "../../core/utilities.js"; import G, { ApplyTooltipListeners } from "../../core/gsap.js"; import C, { BladesActorType, BladesItemType, AttributeTrait, ActionTrait, Factor, RollType } from "../../core/constants.js"; @@ -15,10 +9,25 @@ import BladesItem from "../../BladesItem.js"; import BladesDialog from "../../BladesDialog.js"; import BladesActiveEffect from "../../BladesActiveEffect.js"; import BladesRoll, { BladesRollPrimary, BladesRollOpposition } from "../../BladesRoll.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ +// #endregion +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesActorSheet extends ActorSheet { - getData() { + /*~ @@DOUBLE-BLANK@@ ~*/ + /** + * Override the default getData method to provide additional data for the actor sheet. + * This includes: cssClass, editable, isGM, actor, system, tierTotal, rollData, activeEffects, + * hasFullVision, hasLimitedVision, hasControl, preparedItems. + * @returns {BladesActorSheetData} The data object for the actor sheet. + */ + getData() { + /*~ @@DOUBLE-BLANK@@ ~*/ + // Get the base data context from the parent class. const context = super.getData(); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare additional data specific to this actor's sheet. const sheetData = { + // Basic actor data. cssClass: this.actor.type, editable: this.options.editable, isGM: game.eunoblades.Tracker?.system.is_spoofing_player ? false : game.user.isGM, @@ -32,11 +41,14 @@ class BladesActorSheet extends ActorSheet { hasLimitedVision: game.user.isGM || this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.LIMITED), hasControl: game.user.isGM || this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER), + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare items for display on the actor sheet. preparedItems: { cohorts: { gang: this.actor.activeSubItems .filter((item) => item.type === BladesItemType.cohort_gang) .map((item) => { + // Prepare gang cohort items. const subtypes = U.unique(Object.values(item.system.subtypes) .map((subtype) => subtype.trim()) .filter((subtype) => /[A-Za-z]/.test(subtype))); @@ -48,6 +60,8 @@ class BladesActorSheet extends ActorSheet { .map((subtype) => subtype.trim()) .filter((subtype) => /[A-Za-z]/ .test(subtype) && subtypes.includes(subtype))); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare images for gang cohort items. const imgTypes = [...eliteSubtypes]; if (imgTypes.length < 2) { imgTypes.push(...subtypes.filter((subtype) => !imgTypes.includes(subtype))); @@ -60,6 +74,8 @@ class BladesActorSheet extends ActorSheet { item.system.imageLeft = Object.values(item.system.elite_subtypes).includes(leftType) ? `elite-${U.lCase(leftType)}.svg` : `${U.lCase(leftType)}.svg`; item.system.imageRight = Object.values(item.system.elite_subtypes).includes(rightType) ? `elite-${U.lCase(rightType)}.svg` : `${U.lCase(rightType)}.svg`; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare additional data for gang cohort items. Object.assign(item.system, { tierTotal: item.getFactorTotal(Factor.tier) > 0 ? U.romanizeNum(item.getFactorTotal(Factor.tier)) : "0", cohortRollData: [ @@ -77,6 +93,7 @@ class BladesActorSheet extends ActorSheet { expert: this.actor.activeSubItems .filter((item) => item.type === BladesItemType.cohort_expert) .map((item) => { + // Prepare expert cohort items. Object.assign(item.system, { tierTotal: item.getFactorTotal(Factor.tier) > 0 ? U.romanizeNum(item.getFactorTotal(Factor.tier)) : "0", cohortRollData: [ @@ -94,6 +111,8 @@ class BladesActorSheet extends ActorSheet { } } }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prepare additional data for PC and Crew actors. if (BladesActor.IsType(this.actor, BladesActorType.pc) || BladesActor.IsType(this.actor, BladesActorType.crew)) { sheetData.playbookData = { dotline: { @@ -114,6 +133,7 @@ class BladesActorSheet extends ActorSheet { "" ].join(""))).toString(); } + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.coinsData = { dotline: { data: this.actor.system.coins, @@ -123,31 +143,49 @@ class BladesActorSheet extends ActorSheet { } }; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Return the combined data context for the actor sheet. return { ...context, ...sheetData }; + /*~ @@DOUBLE-BLANK@@ ~*/ } + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region LISTENERS & EVENT HANDLERS + /*~ @@DOUBLE-BLANK@@ ~*/ activateListeners(html) { super.activateListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Handle removal or revealing of secret information content. if (game.user.isGM) { html.attr("style", "--secret-text-display: initial"); } else { html.find('.editor:not(.tinymce) [data-is-secret="true"]').remove(); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Tooltips ApplyTooltipListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ Tags.InitListeners(html, this.actor); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Everything below here is only needed if the sheet is editable if (!this.options.editable) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Add dotline functionality html.find(".dotline").each((__, elem) => { if ($(elem).hasClass("locked")) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ let targetDoc = this.actor; let targetField = $(elem).data("target"); + /*~ @@DOUBLE-BLANK@@ ~*/ const comp$ = $(elem).closest("comp"); + /*~ @@DOUBLE-BLANK@@ ~*/ if (targetField.startsWith("item")) { targetField = targetField.replace(/^item\./, ""); const itemId = $(elem).closest("[data-comp-id]").data("compId"); @@ -160,6 +198,7 @@ class BladesActorSheet extends ActorSheet { } targetDoc = item; } + /*~ @@DOUBLE-BLANK@@ ~*/ const curValue = U.pInt($(elem).data("value")); $(elem) .find(".dot") @@ -188,12 +227,16 @@ class BladesActorSheet extends ActorSheet { }); }); }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Clock Functionality html .find(".clock-container") .on({ click: this._onClockLeftClick.bind(this) }); html .find(".clock-container") .on({ contextmenu: this._onClockRightClick.bind(this) }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Component Functionality: Open, Add (via SelectorDialog), Archive, Delete, Toggle, Select html .find("[data-comp-id]") .find(".comp-title") @@ -216,19 +259,27 @@ class BladesActorSheet extends ActorSheet { select[data-action='gm-select'] `) .on({ change: this._onSelectChange.bind(this) }); + /*~ @@DOUBLE-BLANK@@ ~*/ html .find(".advance-button") .on({ click: this._onAdvanceClick.bind(this) }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Active Effects Functionality html .find(".effect-control") .on({ click: this._onActiveEffectControlClick.bind(this) }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Roll Functionality html .find("[data-roll-trait]") .on({ click: this._onRollTraitClick.bind(this) }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // This is a workaround until is being fixed in FoundryVTT. if (this.options.submitOnChange) { - html.on("change", "textarea", this._onChangeInput.bind(this)); + html.on("change", "textarea", this._onChangeInput.bind(this)); // Use delegated listener on the form } } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onSubmit(event, params = {}) { if (!game.user.isGM && !this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER)) { eLog.checkLog("actorSheetTrigger", "User does not have permission to edit this actor", { user: game.user, actor: this.actor }); @@ -236,6 +287,7 @@ class BladesActorSheet extends ActorSheet { } return super._onSubmit(event, params); } + /*~ @@DOUBLE-BLANK@@ ~*/ async close(options) { if (this.actor.type === BladesActorType.pc) { return super.close(options).then(() => this.actor.clearSubActors()); @@ -245,7 +297,8 @@ class BladesActorSheet extends ActorSheet { } return super.close(options); } - + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Clock Handlers ~ async _onClockLeftClick(event) { event.preventDefault(); const clock$ = $(event.currentTarget).find(".clock[data-target]"); @@ -255,10 +308,12 @@ class BladesActorSheet extends ActorSheet { const target = clock$.data("target"); const curValue = U.pInt(clock$.data("value")); const maxValue = U.pInt(clock$.data("size")); + /*~ @@DOUBLE-BLANK@@ ~*/ await G.effects.pulseClockWedges(clock$.find("wedges")).then(async () => await this.actor.update({ [target]: G.utils.wrap(0, maxValue + 1, curValue + 1) })); } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onClockRightClick(event) { event.preventDefault(); const clock$ = $(event.currentTarget).find(".clock[data-target]"); @@ -267,11 +322,14 @@ class BladesActorSheet extends ActorSheet { } const target = clock$.data("target"); const curValue = U.pInt(clock$.data("value")); + /*~ @@DOUBLE-BLANK@@ ~*/ await G.effects.reversePulseClockWedges(clock$.find("wedges")).then(async () => await this.actor.update({ [target]: Math.max(0, curValue - 1) })); } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Component Handlers _getCompData(event) { const elem$ = $(event.currentTarget).closest(".comp"); const compData = { @@ -282,6 +340,7 @@ class BladesActorSheet extends ActorSheet { docTags: (elem$.data("compTags") ?? "").split(/\s+/g) }; eLog.checkLog2("dialog", "Component Data", { elem: elem$, ...compData }); + /*~ @@DOUBLE-BLANK@@ ~*/ if (compData.docID && compData.docType) { compData.doc = { Actor: this.actor.getSubActor(compData.docID), @@ -294,8 +353,10 @@ class BladesActorSheet extends ActorSheet { Item: this.actor.getDialogItems(compData.docCat) }[compData.docType]; } + /*~ @@DOUBLE-BLANK@@ ~*/ return compData; } + /*~ @@DOUBLE-BLANK@@ ~*/ _onItemOpenClick(event) { event.preventDefault(); const { doc } = this._getCompData(event); @@ -304,6 +365,7 @@ class BladesActorSheet extends ActorSheet { } doc.sheet?.render(true); } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onItemAddClick(event) { event.preventDefault(); const addType = $(event.currentTarget).closest(".comp").data("addType"); @@ -325,6 +387,7 @@ class BladesActorSheet extends ActorSheet { } await BladesDialog.DisplaySelectionDialog(this.actor, U.tCase(`Add ${docCat.replace(/_/g, " ")}`), docType, dialogDocs, docTags); } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onItemRemoveClick(event) { event.preventDefault(); const { elem$, doc } = this._getCompData(event); @@ -340,6 +403,7 @@ class BladesActorSheet extends ActorSheet { } }); } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onItemFullRemoveClick(event) { event.preventDefault(); const { elem$, doc } = this._getCompData(event); @@ -348,6 +412,7 @@ class BladesActorSheet extends ActorSheet { } await G.effects.blurRemove(elem$).then(async () => await doc.delete()); } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onItemToggleClick(event) { event.preventDefault(); const target = $(event.currentTarget).data("target"); @@ -355,17 +420,21 @@ class BladesActorSheet extends ActorSheet { [target]: !getProperty(this.actor, target) }); } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onSelectChange(event) { event.preventDefault(); await U.EventHandlers.onSelectChange(this, event); } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onAdvanceClick(event) { event.preventDefault(); if ($(event.currentTarget).data("action") === "advance-playbook") { await this.actor.advancePlaybook(); } } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Roll Handlers async _onRollTraitClick(event) { const traitName = $(event.currentTarget).data("rollTrait"); const rollType = $(event.currentTarget).data("rollType"); @@ -376,6 +445,7 @@ class BladesActorSheet extends ActorSheet { else if (U.isInt(traitName)) { rollData.rollTrait = U.pInt(traitName); } + /*~ @@DOUBLE-BLANK@@ ~*/ if (U.tCase(rollType) in RollType) { rollData.rollType = U.tCase(rollType); } @@ -387,6 +457,7 @@ class BladesActorSheet extends ActorSheet { rollData.rollType = RollType.Action; } } + /*~ @@DOUBLE-BLANK@@ ~*/ if (game.user.isGM) { if (BladesRollPrimary.IsDoc(this.actor)) { rollData.rollPrimaryData = this.actor; @@ -395,11 +466,16 @@ class BladesActorSheet extends ActorSheet { rollData.rollOppData = this.actor; } } + /*~ @@DOUBLE-BLANK@@ ~*/ await BladesRoll.NewRoll(rollData); } - + // #endregion + /*~ @@DOUBLE-BLANK@@ ~*/ + // #region Active Effect Handlers _onActiveEffectControlClick(event) { BladesActiveEffect.onManageActiveEffect(event, this.actor); } } -export default BladesActorSheet; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesActorSheet; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/sheets/actor/BladesCrewSheet.js b/module/sheets/actor/BladesCrewSheet.js index 6f85c376..d4b0b007 100644 --- a/module/sheets/actor/BladesCrewSheet.js +++ b/module/sheets/actor/BladesCrewSheet.js @@ -1,13 +1,8 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import BladesActorSheet from "./BladesActorSheet.js"; import { BladesItemType } from "../../core/constants.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesCrewSheet extends BladesActorSheet { + /*~ @@DOUBLE-BLANK@@ ~*/ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["eunos-blades", "sheet", "actor", "crew"], @@ -17,12 +12,16 @@ class BladesCrewSheet extends BladesActorSheet { tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "claims" }] }); } + /*~ @@DOUBLE-BLANK@@ ~*/ getData() { const context = super.getData(); + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog("actor", "[BladesCrewSheet] super.getData()", { ...context }); const { activeSubItems } = this.actor; + /*~ @@DOUBLE-BLANK@@ ~*/ const sheetData = {}; - + /*~ @@DOUBLE-BLANK@@ ~*/ + //~ Assemble embedded actors and items sheetData.preparedItems = Object.assign(context.preparedItems ?? {}, { abilities: activeSubItems.filter((item) => item.type === BladesItemType.crew_ability), playbook: this.actor.playbook, @@ -30,10 +29,12 @@ class BladesCrewSheet extends BladesActorSheet { upgrades: activeSubItems.filter((item) => item.type === BladesItemType.crew_upgrade), preferredOp: activeSubItems.find((item) => item.type === BladesItemType.preferred_op) }); + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.preparedActors = { members: this.actor.members, contacts: this.actor.contacts }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.tierData = { label: "Tier", dotline: { @@ -45,6 +46,7 @@ class BladesCrewSheet extends BladesActorSheet { iconFullHover: "dot-full-hover.svg" } }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.upgradeData = { dotline: { dotlineClass: "dotline-right", @@ -57,6 +59,7 @@ class BladesCrewSheet extends BladesActorSheet { iconFull: "dot-full.svg" } }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.abilityData = { dotline: { dotlineClass: "dotline-right", @@ -69,6 +72,7 @@ class BladesCrewSheet extends BladesActorSheet { iconFull: "dot-full.svg" } }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.cohortData = { dotline: { dotlineClass: "dotline-right", @@ -81,6 +85,7 @@ class BladesCrewSheet extends BladesActorSheet { iconFull: "dot-full.svg" } }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.repData = { label: "Rep", dotlines: [ @@ -108,6 +113,7 @@ class BladesCrewSheet extends BladesActorSheet { } ] }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.heatData = { label: "Heat", dotline: { @@ -118,6 +124,7 @@ class BladesCrewSheet extends BladesActorSheet { svgEmpty: "full|half|frame" } }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.wantedData = { label: "Wanted", dotline: { @@ -128,23 +135,34 @@ class BladesCrewSheet extends BladesActorSheet { svgEmpty: "frame" } }; + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog("actor", "[BladesCrewSheet] return getData()", { ...context, ...sheetData }); return { ...context, ...sheetData }; } + /*~ @@DOUBLE-BLANK@@ ~*/ activateListeners(html) { super.activateListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Everything below here is only needed if the sheet is editable if (!this.options.editable) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Update Inventory Item html.find(".item-sheet-open").on("click", (event) => { const element = $(event.currentTarget).parents(".item"); const item = this.actor.items.get(element.data("itemId")); item?.sheet?.render(true); }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Toggle Hold html.find(".hold-toggle").on("click", () => { this.actor.update({ "system.hold": this.actor.system.hold === "weak" ? "strong" : "weak" }); }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Toggle Turf html.find(".turf-select").on("click", async (event) => { + /*~ @@DOUBLE-BLANK@@ ~*/ const turf_id = $(event.currentTarget).data("turfId"); const turf_current_status = $(event.currentTarget).data("turfStatus"); this.actor.playbook?.update({ ["system.turfs." + turf_id + ".value"]: !turf_current_status }) @@ -152,4 +170,5 @@ class BladesCrewSheet extends BladesActorSheet { }); } } -export default BladesCrewSheet; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesCrewSheet; diff --git a/module/sheets/actor/BladesFactionSheet.js b/module/sheets/actor/BladesFactionSheet.js index 3acaf313..28aa5824 100644 --- a/module/sheets/actor/BladesFactionSheet.js +++ b/module/sheets/actor/BladesFactionSheet.js @@ -1,14 +1,10 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - +/*~ @@DOUBLE-BLANK@@ ~*/ import BladesActor from "../../BladesActor.js"; import BladesActorSheet from "./BladesActorSheet.js"; import { BladesActorType } from "../../core/constants.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesFactionSheet extends BladesActorSheet { + /*~ @@DOUBLE-BLANK@@ ~*/ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["eunos-blades", "sheet", "actor", "faction"], @@ -18,11 +14,13 @@ class BladesFactionSheet extends BladesActorSheet { tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "overview" }] }); } + /*~ @@DOUBLE-BLANK@@ ~*/ getData() { const context = super.getData(); if (!BladesActor.IsType(this.actor, BladesActorType.faction)) { return context; } + /*~ @@DOUBLE-BLANK@@ ~*/ const sheetData = { tierData: { "class": "comp-tier comp-vertical comp-teeth", @@ -37,15 +35,18 @@ class BladesFactionSheet extends BladesActorSheet { } } }; + /*~ @@DOUBLE-BLANK@@ ~*/ return { ...context, ...sheetData }; } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onClockAddClick(event) { event.preventDefault(); this.actor.addClock(); } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onClockDeleteClick(event) { event.preventDefault(); const clockID = $(event.currentTarget).data("clockId"); @@ -54,11 +55,16 @@ class BladesFactionSheet extends BladesActorSheet { } this.actor.deleteClock(clockID); } + /*~ @@DOUBLE-BLANK@@ ~*/ activateListeners(html) { super.activateListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Everything below here is only needed if the sheet is editable if (!this.options.editable) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Update Inventory Item html.find(".item-body").on("click", (event) => { const element = $(event.currentTarget).parents(".item"); const item = this.actor.items.get(element.data("itemId")); @@ -70,6 +76,8 @@ class BladesFactionSheet extends BladesActorSheet { html .find(".comp-control.comp-delete-clock") .on("click", this._onClockDeleteClick.bind(this)); + /*~ @@DOUBLE-BLANK@@ ~*/ } } -export default BladesFactionSheet; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesFactionSheet; diff --git a/module/sheets/actor/BladesNPCSheet.js b/module/sheets/actor/BladesNPCSheet.js index c63a8a86..88d7d81f 100644 --- a/module/sheets/actor/BladesNPCSheet.js +++ b/module/sheets/actor/BladesNPCSheet.js @@ -1,41 +1,45 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - +/*~ @@DOUBLE-BLANK@@ ~*/ import BladesActorSheet from "./BladesActorSheet.js"; import U from "../../core/utilities.js"; class BladesNPCSheet extends BladesActorSheet { + /*~ @@DOUBLE-BLANK@@ ~*/ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["eunos-blades", "sheet", "actor", "npc"], template: "systems/eunos-blades/templates/npc-sheet.hbs", width: 500, height: 400, + // height: "auto", tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "description" }] }); } + /*~ @@DOUBLE-BLANK@@ ~*/ getData() { const context = super.getData(); + /*~ @@DOUBLE-BLANK@@ ~*/ context.isSubActor = context.actor.isSubActor; context.parentActor = context.actor.parentActor; context.persona = context.actor.system.persona; context.random = context.actor.system.random; context.secret = context.actor.system.secret; + /*~ @@DOUBLE-BLANK@@ ~*/ const rStatus = { name: { size: 3, label: "Name" }, gender: { size: "half", label: "Gender" }, + /*~ @@DOUBLE-BLANK@@ ~*/ heritage: { size: "third", label: "Heritage" }, background: { size: "third", label: "Background" }, profession: { size: "third", label: "Profession" }, + /*~ @@DOUBLE-BLANK@@ ~*/ appearance: { size: 2, label: "Appearance" }, style: { size: 2, label: "Style" }, quirk: { size: 4, label: "Quirk" }, + /*~ @@DOUBLE-BLANK@@ ~*/ goal: { size: 2, label: "Goal" }, method: { size: 2, label: "Method" }, + /*~ @@DOUBLE-BLANK@@ ~*/ interests: { size: 4, label: "Interests" }, + /*~ @@DOUBLE-BLANK@@ ~*/ trait: { size: "half", label: "Trait" }, trait1: { size: "half", label: null }, trait2: { size: "half", label: null }, @@ -48,23 +52,36 @@ class BladesNPCSheet extends BladesActorSheet { } } } + /*~ @@DOUBLE-BLANK@@ ~*/ console.log({ persona: context.persona, random: context.random, secret: context.secret }); + /*~ @@DOUBLE-BLANK@@ ~*/ return context; } + /*~ @@DOUBLE-BLANK@@ ~*/ activateListeners(html) { super.activateListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Everything below here is only needed if the sheet is editable if (!this.options.editable) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ html.find(".gm-alert-header").on("click", async (event) => { event.preventDefault(); + /*~ @@DOUBLE-BLANK@@ ~*/ this.actor.clearParentActor(); }); - + /*~ @@DOUBLE-BLANK@@ ~*/ + //~ Configure Tagify input elements + // const inputElement = document.querySelector('input[name="system.harm.heavy.one"]'); + // if (inputElement instanceof HTMLInputElement) { new Tagify(inputElement, {}) } else { console.log("Not an HTMLInputElement")} + /*~ @@DOUBLE-BLANK@@ ~*/ + //~ Enable Randomize Button for NPCs html.find("[data-action=\"randomize\"").on("click", (event) => { this.actor.updateRandomizers(); }); - + /*~ @@DOUBLE-BLANK@@ ~*/ + //~ Enable status toggles for NPC subactors html.find(".comp-status-toggle") .on("click", () => { const { tags } = this.actor; @@ -88,6 +105,8 @@ class BladesNPCSheet extends BladesActorSheet { .on("contextmenu", () => { this.actor.update({ "system.status": 0 }); }); + /*~ @@DOUBLE-BLANK@@ ~*/ } } -export default BladesNPCSheet; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesNPCSheet; diff --git a/module/sheets/actor/BladesPCSheet.js b/module/sheets/actor/BladesPCSheet.js index 22b83e88..3ffee4d0 100644 --- a/module/sheets/actor/BladesPCSheet.js +++ b/module/sheets/actor/BladesPCSheet.js @@ -1,16 +1,12 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - +/*~ @@DOUBLE-BLANK@@ ~*/ import C, { BladesActorType, BladesItemType, AttributeTrait, Tag, BladesPhase } from "../../core/constants.js"; import U from "../../core/utilities.js"; import BladesActorSheet from "./BladesActorSheet.js"; import { BladesActor } from "../../documents/BladesActorProxy.js"; import BladesGMTrackerSheet from "../item/BladesGMTrackerSheet.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesPCSheet extends BladesActorSheet { + /*~ @@DOUBLE-BLANK@@ ~*/ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["eunos-blades", "sheet", "actor", "pc"], @@ -20,15 +16,19 @@ class BladesPCSheet extends BladesActorSheet { tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "abilities" }] }); } + /*~ @@DOUBLE-BLANK@@ ~*/ static Initialize() { Actors.registerSheet("blades", BladesPCSheet, { types: ["pc"], makeDefault: true }); + /*~ @@DOUBLE-BLANK@@ ~*/ Hooks.on("dropActorSheetData", async (parentActor, _, { uuid }) => { const doc = await fromUuid(uuid); if (doc instanceof BladesActor) { if (parentActor.type === BladesActorType.crew && doc.type === BladesActorType.pc) { + // Dropping a PC onto a Crew Sheet: Add Crew to PC doc.addSubActor(parentActor); } else if (parentActor.type === BladesActorType.pc && doc.type === BladesActorType.crew) { + // Dropping a Crew onto a PC Sheet: Add parentActor.addSubActor(doc); } } @@ -38,14 +38,20 @@ class BladesPCSheet extends BladesActorSheet { "systems/eunos-blades/templates/parts/clock-sheet-row.hbs" ]); } + /*~ @@DOUBLE-BLANK@@ ~*/ getData() { const context = super.getData(); + /*~ @@DOUBLE-BLANK@@ ~*/ const { activeSubItems, activeSubActors } = this.actor; + /*~ @@DOUBLE-BLANK@@ ~*/ const sheetData = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Assemble embedded actors and items sheetData.preparedItems = Object.assign(context.preparedItems ?? {}, { abilities: activeSubItems .filter((item) => item.type === BladesItemType.ability) .map((item) => { + // ~ Assign dotlines to abilities with usage data if (item.system.uses_per_score.max) { Object.assign(item, { inRuleDotline: { @@ -67,6 +73,7 @@ class BladesPCSheet extends BladesActorSheet { loadout: activeSubItems .filter((item) => item.type === BladesItemType.gear) .map((item) => { + // Assign load and usage data to gear if (item.system.load) { Object.assign(item, { numberCircle: item.system.load, @@ -90,6 +97,7 @@ class BladesPCSheet extends BladesActorSheet { }), playbook: this.actor.playbook }); + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.preparedActors = { crew: activeSubActors .find((actor) => actor.type === BladesActorType.crew), @@ -98,8 +106,10 @@ class BladesPCSheet extends BladesActorSheet { acquaintances: activeSubActors .filter((actor) => actor.hasTag(Tag.NPC.Acquaintance)) }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.hasVicePurveyor = Boolean(this.actor.playbook?.hasTag(Tag.Gear.Advanced) === false && activeSubItems.find((item) => item.type === BladesItemType.vice)); + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.healing_clock = { display: "Healing", target: "system.healing.value", @@ -110,6 +120,7 @@ class BladesPCSheet extends BladesActorSheet { ...this.actor.system.healing, id: randomID() }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.stashData = { label: "Stash:", dotline: { @@ -124,6 +135,7 @@ class BladesPCSheet extends BladesActorSheet { altIconStep: 10 } }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.stressData = { label: this.actor.system.stress.name, dotline: { @@ -135,6 +147,7 @@ class BladesPCSheet extends BladesActorSheet { svgEmpty: "full|half|frame" } }; + /*~ @@DOUBLE-BLANK@@ ~*/ if (BladesActor.IsType(this.actor, BladesActorType.pc)) { sheetData.traumaData = { label: this.actor.system.trauma.name, @@ -176,6 +189,7 @@ class BladesPCSheet extends BladesActorSheet { } }; } + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.abilityData = { dotline: { dotlineClass: "dotline-right dotline-glow", @@ -188,18 +202,21 @@ class BladesPCSheet extends BladesActorSheet { iconFull: "dot-full.svg" } }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.loadData = { curLoad: this.actor.currentLoad, selLoadCount: this.actor.system.loadout.levels[U.lCase(this.actor.system.loadout.selected)], options: C.Loadout.selections, selected: this.actor.system.loadout.selected ?? "" }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.armor = Object.fromEntries(Object.entries(this.actor.system.armor.active) .filter(([, isActive]) => isActive) .map(([armor]) => [ armor, this.actor.system.armor.checked[armor] ])); + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.attributeData = {}; const attrEntries = Object.entries(this.actor.system.attributes); for (const [attribute, attrData] of attrEntries) { @@ -218,27 +235,35 @@ class BladesPCSheet extends BladesActorSheet { }; } } + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.gatherInfoTooltip = (new Handlebars.SafeString([ "

Gathering Information: Questions to Consider

", "
    ", ...Object.values(this.actor.system.gather_info ?? []).map((line) => `
  • ${line}
  • `) ?? [], "
" ].join(""))).toString(); + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog("Attribute", "[BladesPCSheet] attributeData", { attributeData: sheetData.attributeData }); + /*~ @@DOUBLE-BLANK@@ ~*/ eLog.checkLog("actor", "[BladesPCSheet] getData()", { ...context, ...sheetData }); + /*~ @@DOUBLE-BLANK@@ ~*/ return { ...context, ...sheetData }; } + /*~ @@DOUBLE-BLANK@@ ~*/ get activeArmor() { return Object.keys(U.objFilter(this.actor.system.armor.active, (val) => val === true)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get checkedArmor() { return Object.keys(U.objFilter(this.actor.system.armor.checked, (val, key) => val === true && this.actor.system.armor.active[key] === true)); } + /*~ @@DOUBLE-BLANK@@ ~*/ get uncheckedArmor() { return Object.keys(U.objFilter(this.actor.system.armor.active, (val, key) => val === true && this.actor.system.armor.checked[key] === false)); } + /*~ @@DOUBLE-BLANK@@ ~*/ _getHoverArmor() { if (!this.activeArmor.length) { return false; @@ -251,6 +276,7 @@ class BladesPCSheet extends BladesActorSheet { } return "special"; } + /*~ @@DOUBLE-BLANK@@ ~*/ _getClickArmor() { if (!this.uncheckedArmor.length) { return false; @@ -263,6 +289,7 @@ class BladesPCSheet extends BladesActorSheet { } return "special"; } + /*~ @@DOUBLE-BLANK@@ ~*/ _getContextMenuArmor() { if (!this.checkedArmor.length) { return false; @@ -275,6 +302,7 @@ class BladesPCSheet extends BladesActorSheet { } return "special"; } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onAdvanceClick(event) { event.preventDefault(); super._onAdvanceClick(event); @@ -283,12 +311,19 @@ class BladesPCSheet extends BladesActorSheet { await this.actor.advanceAttribute(action); } } + /*~ @@DOUBLE-BLANK@@ ~*/ activateListeners(html) { + /*~ @@DOUBLE-BLANK@@ ~*/ super.activateListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Everything below here is only needed if the sheet is editable if (!this.options.editable) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ const self = this; + /*~ @@DOUBLE-BLANK@@ ~*/ + // ~ Armor Control html.find(".main-armor-control").on({ click() { const targetArmor = self._getClickArmor(); @@ -346,6 +381,9 @@ class BladesPCSheet extends BladesActorSheet { $(this).siblings(".svg-armor.armor-special").removeClass("hover-over"); } }); + /*~ @@DOUBLE-BLANK@@ ~*/ } } -export default BladesPCSheet; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesPCSheet; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/sheets/actor/blades-actor-sheet.js b/module/sheets/actor/blades-actor-sheet.js deleted file mode 100644 index f0bd4399..00000000 --- a/module/sheets/actor/blades-actor-sheet.js +++ /dev/null @@ -1,346 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import C, { BladesActorType, BladesItemType, Attribute, Tag, BladesPhase } from "../../core/constants.js"; -import U from "../../core/utilities.js"; -import BladesSheet from "./blades-sheet.js"; -import BladesActor from "../../blades-actor.js"; -import BladesTrackerSheet from "../item/blades-tracker-sheet.js"; - -class BladesActorSheet extends BladesSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "actor", "pc"], - template: "systems/eunos-blades/templates/actor-sheet.hbs", - width: 775, - height: 775, - tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "abilities" }] - }); - } - static Initialize() { - Actors.registerSheet("blades", BladesActorSheet, { types: ["pc"], makeDefault: true }); - Hooks.on("dropActorSheetData", async (parentActor, _, { uuid }) => { - const doc = await fromUuid(uuid); - if (doc instanceof BladesActor) { - if (parentActor.type === BladesActorType.crew && doc.type === BladesActorType.pc) { - doc.addSubActor(parentActor); - } - else if (parentActor.type === BladesActorType.pc && doc.type === BladesActorType.crew) { - parentActor.addSubActor(doc); - } - } - }); - return loadTemplates([ - "systems/eunos-blades/templates/items/clock_keeper-sheet.hbs", - "systems/eunos-blades/templates/parts/clock-sheet-row.hbs" - ]); - } - getData() { - const context = super.getData(); - const { activeSubItems, activeSubActors } = this.actor; - const sheetData = {}; - - sheetData.preparedItems = Object.assign(context.preparedItems ?? {}, { - abilities: activeSubItems - .filter((item) => item.type === BladesItemType.ability) - .map((item) => { - if (item.system.uses_per_score.max) { - Object.assign(item, { - inRuleDotline: { - data: item.system.uses_per_score, - dotlineLabel: "Uses", - target: "item.system.uses_per_score.value", - iconEmpty: "dot-empty.svg", - iconEmptyHover: "dot-empty-hover.svg", - iconFull: "dot-full.svg", - iconFullHover: "dot-full-hover.svg" - } - }); - } - return item; - }), - background: activeSubItems.find((item) => item.type === BladesItemType.background), - heritage: activeSubItems.find((item) => item.type === BladesItemType.heritage), - vice: activeSubItems.find((item) => item.type === BladesItemType.vice), - loadout: activeSubItems.filter((item) => item.type === BladesItemType.gear).map((item) => { - if (item.system.load) { - Object.assign(item, { - numberCircle: item.system.load, - numberCircleClass: "item-load" - }); - } - if (item.system.uses_per_score.max) { - Object.assign(item, { - inRuleDotline: { - data: item.system.uses_per_score, - dotlineLabel: "Uses", - target: "item.system.uses_per_score.value", - iconEmpty: "dot-empty.svg", - iconEmptyHover: "dot-empty-hover.svg", - iconFull: "dot-full.svg", - iconFullHover: "dot-full-hover.svg" - } - }); - } - return item; - }), - playbook: this.actor.playbook - }); - sheetData.preparedActors = { - crew: activeSubActors.find((actor) => actor.type === BladesActorType.crew), - vice_purveyor: activeSubActors.find((actor) => actor.hasTag(Tag.NPC.VicePurveyor)), - acquaintances: activeSubActors.filter((actor) => actor.hasTag(Tag.NPC.Acquaintance)) - }; - sheetData.hasVicePurveyor = Boolean(this.actor.playbook?.hasTag(Tag.Gear.Advanced) === false - && activeSubItems.find((item) => item.type === BladesItemType.vice)); - sheetData.healing_clock = { - display: "Healing", - target: "system.healing.value", - color: "white", - isVisible: true, - isNameVisible: false, - isActive: false, - ...this.actor.system.healing, - id: randomID() - }; - sheetData.stashData = { - label: "Stash:", - dotline: { - data: this.actor.system.stash, - target: "system.stash.value", - iconEmpty: "coin-empty.svg", - iconEmptyHover: "coin-empty-hover.svg", - iconFull: "coin-full.svg", - iconFullHover: "coin-full-hover.svg", - altIconFull: "coin-ten.svg", - altIconFullHover: "coin-ten-hover.svg", - altIconStep: 10 - } - }; - sheetData.stressData = { - label: this.actor.system.stress.name, - dotline: { - data: this.actor.system.stress, - dotlineClass: this.actor.system.stress.max >= 13 ? "narrow-stress" : "", - target: "system.stress.value", - svgKey: "teeth.tall", - svgFull: "full|half|frame", - svgEmpty: "full|half|frame" - } - }; - if (BladesActor.IsType(this.actor, BladesActorType.pc)) { - sheetData.traumaData = { - label: this.actor.system.trauma.name, - dotline: { - data: { value: this.actor.trauma, max: this.actor.system.trauma.max }, - svgKey: "teeth.short", - svgFull: "full|frame", - svgEmpty: "frame", - isLocked: true - }, - compContainer: { - "class": "comp-trauma-conditions comp-vertical full-width", - "blocks": [ - this.actor.traumaList.slice(0, Math.ceil(this.actor.traumaList.length / 2)) - .map((tName) => ({ - checkLabel: tName, - checkClasses: { - active: "comp-toggle-red", - inactive: "comp-toggle-grey" - }, - checkTarget: `system.trauma.checked.${tName}`, - checkValue: this.actor.system.trauma.checked[tName] ?? false, - tooltip: C.TraumaTooltips[tName], - tooltipClass: "tooltip-trauma" - })), - this.actor.traumaList.slice(Math.ceil(this.actor.traumaList.length / 2)) - .map((tName) => ({ - checkLabel: tName, - checkClasses: { - active: "comp-toggle-red", - inactive: "comp-toggle-grey" - }, - checkTarget: `system.trauma.checked.${tName}`, - checkValue: this.actor.system.trauma.checked[tName] ?? false, - tooltip: C.TraumaTooltips[tName], - tooltipClass: "tooltip-trauma" - })) - ] - } - }; - } - sheetData.abilityData = { - dotline: { - dotlineClass: "dotline-right dotline-glow", - data: { - value: this.actor.getAvailableAdvancements("Ability"), - max: this.actor.getAvailableAdvancements("Ability") - }, - dotlineLabel: "Available Abilities", - isLocked: true, - iconFull: "dot-full.svg" - } - }; - sheetData.loadData = { - curLoad: this.actor.currentLoad, - selLoadCount: this.actor.system.loadout.levels[U.lCase(game.i18n.localize(this.actor.system.loadout.selected.toString()))], - selections: C.Loadout.selections, - selLoadLevel: this.actor.system.loadout.selected.toString() - }; - sheetData.armor = Object.fromEntries(Object.entries(this.actor.system.armor.active) - .filter(([, isActive]) => isActive) - .map(([armor]) => [armor, this.actor.system.armor.checked[armor]])); - sheetData.attributeData = {}; - const attrEntries = Object.entries(this.actor.system.attributes); - for (const [attribute, attrData] of attrEntries) { - sheetData.attributeData[attribute] = { - tooltip: C.AttributeTooltips[attribute], - actions: {} - }; - const actionEntries = Object.entries(attrData); - for (const [action, actionData] of actionEntries) { - sheetData.attributeData[attribute].actions[action] = { - tooltip: C.ActionTooltips[action], - value: actionData.value, - max: BladesTrackerSheet.Get().phase === BladesPhase.CharGen ? 2 : this.actor.system.attributes[attribute][action].max - }; - } - } - sheetData.gatherInfoTooltip = (new Handlebars.SafeString([ - "

Gathering Information: Questions to Consider

", - "
    ", - ...Object.values(this.actor.system.gather_info ?? []).map((line) => `
  • ${line}
  • `) ?? [], - "
" - ].join(""))).toString(); - eLog.checkLog("Attribute", "[BladesActorSheet] attributeData", { attributeData: sheetData.attributeData }); - eLog.checkLog("actor", "[BladesActorSheet] getData()", { ...context, ...sheetData }); - return { ...context, ...sheetData }; - } - get activeArmor() { - return Object.keys(U.objFilter(this.actor.system.armor.active, (val) => val === true)); - } - get checkedArmor() { - return Object.keys(U.objFilter(this.actor.system.armor.checked, (val, key) => val === true - && this.actor.system.armor.active[key] === true)); - } - get uncheckedArmor() { - return Object.keys(U.objFilter(this.actor.system.armor.active, (val, key) => val === true - && this.actor.system.armor.checked[key] === false)); - } - _getHoverArmor() { - if (!this.activeArmor.length) { - return false; - } - if (this.activeArmor.includes("heavy")) { - return this.checkedArmor.includes("heavy") ? "light" : "heavy"; - } - else if (this.activeArmor.includes("light")) { - return "light"; - } - return "special"; - } - _getClickArmor() { - if (!this.uncheckedArmor.length) { - return false; - } - if (this.uncheckedArmor.includes("heavy")) { - return "heavy"; - } - if (this.uncheckedArmor.includes("light")) { - return "light"; - } - return "special"; - } - _getContextMenuArmor() { - if (!this.checkedArmor.length) { - return false; - } - if (this.checkedArmor.includes("light")) { - return "light"; - } - if (this.checkedArmor.includes("heavy")) { - return "heavy"; - } - return "special"; - } - async _onAdvanceClick(event) { - event.preventDefault(); - super._onAdvanceClick(event); - const action = $(event.currentTarget).data("action").replace(/^advance-/, ""); - if (action in Attribute) { - this.actor.advanceAttribute(action); - } - } - activateListeners(html) { - super.activateListeners(html); - - if (!this.options.editable) { - return; - } - const self = this; - - html.find(".main-armor-control").on({ - click: function () { - const targetArmor = self._getClickArmor(); - if (!targetArmor) { - return; - } - self.actor.update({ [`system.armor.checked.${targetArmor}`]: true }); - }, - contextmenu: function () { - const targetArmor = self._getContextMenuArmor(); - if (!targetArmor) { - return; - } - self.actor.update({ [`system.armor.checked.${targetArmor}`]: false }); - }, - mouseenter: function () { - const targetArmor = self._getHoverArmor(); - eLog.log4("Mouse Enter", targetArmor, this, $(this), $(this).next()); - if (!targetArmor) { - return; - } - $(this).siblings(`.svg-armor.armor-${targetArmor}`).addClass("hover-over"); - }, - mouseleave: function () { - const targetArmor = self._getHoverArmor(); - if (!targetArmor) { - return; - } - $(this).siblings(`.svg-armor.armor-${targetArmor}`).removeClass("hover-over"); - } - }); - html.find(".special-armor-control").on({ - click: function () { - if (!self.activeArmor.includes("special")) { - return; - } - self.actor.update({ ["system.armor.checked.special"]: self.uncheckedArmor.includes("special") }); - }, - contextmenu: function () { - if (!self.activeArmor.includes("special")) { - return; - } - self.actor.update({ ["system.armor.checked.special"]: self.uncheckedArmor.includes("special") }); - }, - mouseenter: function () { - if (!self.activeArmor.includes("special") || self.activeArmor.length === 1) { - return; - } - $(this).siblings(".svg-armor.armor-special").addClass("hover-over"); - }, - mouseleave: function () { - if (!self.activeArmor.includes("special") || self.activeArmor.length === 1) { - return; - } - $(this).siblings(".svg-armor.armor-special").removeClass("hover-over"); - } - }); - } -} -export default BladesActorSheet; -//# sourceMappingURL=blades-actor-sheet.js.map \ No newline at end of file diff --git a/module/sheets/actor/blades-crew-sheet.js b/module/sheets/actor/blades-crew-sheet.js deleted file mode 100644 index 26d5f18d..00000000 --- a/module/sheets/actor/blades-crew-sheet.js +++ /dev/null @@ -1,156 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesSheet from "./blades-sheet.js"; -import { BladesItemType } from "../../core/constants.js"; -class BladesCrewSheet extends BladesSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "actor", "crew"], - template: "systems/eunos-blades/templates/crew-sheet.hbs", - width: 940, - height: 820, - tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "claims" }] - }); - } - getData() { - const context = super.getData(); - eLog.checkLog("actor", "[BladesCrewSheet] super.getData()", { ...context }); - const { activeSubItems } = this.actor; - const sheetData = {}; - - sheetData.preparedItems = Object.assign(context.preparedItems ?? {}, { - abilities: activeSubItems.filter((item) => item.type === BladesItemType.crew_ability), - playbook: this.actor.playbook, - reputation: activeSubItems.find((item) => item.type === BladesItemType.crew_reputation), - upgrades: activeSubItems.filter((item) => item.type === BladesItemType.crew_upgrade), - preferredOp: activeSubItems.find((item) => item.type === BladesItemType.preferred_op) - }); - sheetData.preparedActors = { - members: this.actor.members, - contacts: this.actor.contacts - }; - sheetData.tierData = { - label: "Tier", - dotline: { - data: this.actor.system.tier, - target: "system.tier.value", - iconEmpty: "dot-empty.svg", - iconEmptyHover: "dot-empty-hover.svg", - iconFull: "dot-full.svg", - iconFullHover: "dot-full-hover.svg" - } - }; - sheetData.upgradeData = { - dotline: { - dotlineClass: "dotline-right", - data: { - value: this.actor.availableUpgradePoints, - max: this.actor.availableUpgradePoints - }, - dotlineLabel: "Available Upgrade Points", - isLocked: true, - iconFull: "dot-full.svg" - } - }; - sheetData.abilityData = { - dotline: { - dotlineClass: "dotline-right", - data: { - value: this.actor.availableAbilityPoints, - max: this.actor.availableAbilityPoints - }, - dotlineLabel: "Available Ability Points", - isLocked: true, - iconFull: "dot-full.svg" - } - }; - sheetData.cohortData = { - dotline: { - dotlineClass: "dotline-right", - data: { - value: this.actor.availableCohortPoints, - max: this.actor.availableCohortPoints - }, - dotlineLabel: "Available Cohort Points", - isLocked: true, - iconFull: "dot-full.svg" - } - }; - sheetData.repData = { - label: "Rep", - dotlines: [ - { - data: { - value: Math.min(this.actor.system.rep.value, this.actor.system.rep.max - this.actor.turfCount), - max: this.actor.system.rep.max - this.actor.turfCount - }, - target: "system.rep.value", - svgKey: "teeth.tall", - svgFull: "full|half|frame", - svgEmpty: "full|half|frame" - }, - { - data: { - value: this.actor.turfCount, - max: this.actor.turfCount - }, - target: "none", - svgKey: "teeth.tall", - svgFull: "full|half|frame", - svgEmpty: "full|half|frame", - dotlineClass: "flex-row-reverse", - isLocked: true - } - ] - }; - sheetData.heatData = { - label: "Heat", - dotline: { - data: this.actor.system.heat, - target: "system.heat.value", - svgKey: "teeth.tall", - svgFull: "full|half|frame", - svgEmpty: "full|half|frame" - } - }; - sheetData.wantedData = { - label: "Wanted", - dotline: { - data: this.actor.system.wanted, - target: "system.wanted.value", - svgKey: "teeth.short", - svgFull: "full|frame", - svgEmpty: "frame" - } - }; - eLog.checkLog("actor", "[BladesCrewSheet] return getData()", { ...context, ...sheetData }); - return { ...context, ...sheetData }; - } - activateListeners(html) { - super.activateListeners(html); - if (!this.options.editable) { - return; - } - html.find(".item-sheet-open").on("click", (event) => { - const element = $(event.currentTarget).parents(".item"); - const item = this.actor.items.get(element.data("itemId")); - item?.sheet?.render(true); - }); - html.find(".hold-toggle").on("click", () => { - this.actor.update({ "system.hold": this.actor.system.hold === "weak" ? "strong" : "weak" }); - }); - html.find(".turf-select").on("click", async (event) => { - const turf_id = $(event.currentTarget).data("turfId"); - const turf_current_status = $(event.currentTarget).data("turfStatus"); - this.actor.playbook?.update({ ["system.turfs." + turf_id + ".value"]: !turf_current_status }) - .then(() => this.render(false)); - }); - } -} -export default BladesCrewSheet; -//# sourceMappingURL=blades-crew-sheet.js.map \ No newline at end of file diff --git a/module/sheets/actor/blades-faction-sheet.js b/module/sheets/actor/blades-faction-sheet.js deleted file mode 100644 index 05d8ba73..00000000 --- a/module/sheets/actor/blades-faction-sheet.js +++ /dev/null @@ -1,76 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesActor from "../../blades-actor.js"; -import BladesSheet from "./blades-sheet.js"; -import { BladesActorType } from "../../core/constants.js"; -class BladesFactionSheet extends BladesSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "actor", "faction"], - template: "systems/eunos-blades/templates/faction-sheet.hbs", - width: 900, - height: "auto", - tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "overview" }] - }); - } - getData() { - const context = super.getData(); - if (!BladesActor.IsType(this.actor, BladesActorType.faction)) { - return context; - } - const sheetData = { - tierData: { - "class": "comp-tier comp-vertical comp-teeth", - "label": "Tier", - "labelClass": "filled-label full-width", - "dotline": { - data: this.actor.system.tier, - target: "system.tier.value", - svgKey: "teeth.tall", - svgFull: "full|half|frame", - svgEmpty: "full|half|frame" - } - } - }; - return { - ...context, - ...sheetData - }; - } - async _onClockAddClick(event) { - event.preventDefault(); - this.actor.addClock(); - } - async _onClockDeleteClick(event) { - event.preventDefault(); - const clockID = $(event.currentTarget).data("clockId"); - if (!clockID) { - return; - } - this.actor.deleteClock(clockID); - } - activateListeners(html) { - super.activateListeners(html); - if (!this.options.editable) { - return; - } - html.find(".item-body").on("click", (event) => { - const element = $(event.currentTarget).parents(".item"); - const item = this.actor.items.get(element.data("itemId")); - item?.sheet?.render(true); - }); - html - .find(".comp-control.comp-add-clock") - .on("click", this._onClockAddClick.bind(this)); - html - .find(".comp-control.comp-delete-clock") - .on("click", this._onClockDeleteClick.bind(this)); - } -} -export default BladesFactionSheet; -//# sourceMappingURL=blades-faction-sheet.js.map \ No newline at end of file diff --git a/module/sheets/actor/blades-npc-sheet.js b/module/sheets/actor/blades-npc-sheet.js deleted file mode 100644 index e9a3913d..00000000 --- a/module/sheets/actor/blades-npc-sheet.js +++ /dev/null @@ -1,94 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesSheet from "./blades-sheet.js"; -import U from "../../core/utilities.js"; -class BladesNPCSheet extends BladesSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "actor", "npc"], - template: "systems/eunos-blades/templates/npc-sheet.hbs", - width: 500, - height: 400, - tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "description" }] - }); - } - getData() { - const context = super.getData(); - context.isSubActor = context.actor.isSubActor; - context.parentActor = context.actor.parentActor; - context.persona = context.actor.system.persona; - context.random = context.actor.system.random; - context.secret = context.actor.system.secret; - const rStatus = { - name: { size: 3, label: "Name" }, - gender: { size: "half", label: "Gender" }, - heritage: { size: "third", label: "Heritage" }, - background: { size: "third", label: "Background" }, - profession: { size: "third", label: "Profession" }, - appearance: { size: 2, label: "Appearance" }, - style: { size: 2, label: "Style" }, - quirk: { size: 4, label: "Quirk" }, - goal: { size: 2, label: "Goal" }, - method: { size: 2, label: "Method" }, - interests: { size: 4, label: "Interests" }, - trait: { size: "half", label: "Trait" }, - trait1: { size: "half", label: null }, - trait2: { size: "half", label: null }, - trait3: { size: "half", label: null } - }; - for (const cat of ["persona", "random", "secret"]) { - for (const [key] of Object.entries(context[cat])) { - if (key in rStatus) { - Object.assign(context[cat][key], rStatus[key]); - } - } - } - console.log({ persona: context.persona, random: context.random, secret: context.secret }); - return context; - } - activateListeners(html) { - super.activateListeners(html); - if (!this.options.editable) { - return; - } - html.find(".gm-alert-header").on("click", async (event) => { - event.preventDefault(); - this.actor.clearParentActor(); - }); - - html.find("[data-action=\"randomize\"").on("click", (event) => { - this.actor.updateRandomizers(); - }); - - html.find(".comp-status-toggle") - .on("click", () => { - const { tags } = this.actor; - if (this.actor.system.status === 1) { - U.remove(tags, "Friend"); - tags.push("Rival"); - this.actor.update({ - "system.status": -1, - "system.tags": U.unique(tags) - }); - } - else { - U.remove(tags, "Rival"); - tags.push("Friend"); - this.actor.update({ - "system.status": 1, - "system.tags": U.unique(tags) - }); - } - }) - .on("contextmenu", () => { - this.actor.update({ "system.status": 0 }); - }); - } -} -export default BladesNPCSheet; -//# sourceMappingURL=blades-npc-sheet.js.map \ No newline at end of file diff --git a/module/sheets/actor/blades-pc-sheet.js b/module/sheets/actor/blades-pc-sheet.js deleted file mode 100644 index d783bf3b..00000000 --- a/module/sheets/actor/blades-pc-sheet.js +++ /dev/null @@ -1,345 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import C, { BladesActorType, BladesItemType, Attribute, Tag, BladesPhase } from "../../core/constants.js"; -import U from "../../core/utilities.js"; -import BladesSheet from "./blades-sheet.js"; -import { BladesActor } from "../../documents/blades-actor-proxy.js"; -import BladesTrackerSheet from "../item/blades-tracker-sheet.js"; -class BladesPCSheet extends BladesSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "actor", "pc"], - template: "systems/eunos-blades/templates/actor-sheet.hbs", - width: 775, - height: 775, - tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "abilities" }] - }); - } - static Initialize() { - Actors.registerSheet("blades", BladesPCSheet, { types: ["pc"], makeDefault: true }); - Hooks.on("dropActorSheetData", async (parentActor, _, { uuid }) => { - const doc = await fromUuid(uuid); - if (doc instanceof BladesActor) { - if (parentActor.type === BladesActorType.crew && doc.type === BladesActorType.pc) { - doc.addSubActor(parentActor); - } - else if (parentActor.type === BladesActorType.pc && doc.type === BladesActorType.crew) { - parentActor.addSubActor(doc); - } - } - }); - return loadTemplates([ - "systems/eunos-blades/templates/items/clock_keeper-sheet.hbs", - "systems/eunos-blades/templates/parts/clock-sheet-row.hbs" - ]); - } - getData() { - const context = super.getData(); - const { activeSubItems, activeSubActors } = this.actor; - const sheetData = {}; - - sheetData.preparedItems = Object.assign(context.preparedItems ?? {}, { - abilities: activeSubItems - .filter((item) => item.type === BladesItemType.ability) - .map((item) => { - if (item.system.uses_per_score.max) { - Object.assign(item, { - inRuleDotline: { - data: item.system.uses_per_score, - dotlineLabel: "Uses", - target: "item.system.uses_per_score.value", - iconEmpty: "dot-empty.svg", - iconEmptyHover: "dot-empty-hover.svg", - iconFull: "dot-full.svg", - iconFullHover: "dot-full-hover.svg" - } - }); - } - return item; - }), - background: activeSubItems.find((item) => item.type === BladesItemType.background), - heritage: activeSubItems.find((item) => item.type === BladesItemType.heritage), - vice: activeSubItems.find((item) => item.type === BladesItemType.vice), - loadout: activeSubItems.filter((item) => item.type === BladesItemType.gear).map((item) => { - if (item.system.load) { - Object.assign(item, { - numberCircle: item.system.load, - numberCircleClass: "item-load" - }); - } - if (item.system.uses_per_score.max) { - Object.assign(item, { - inRuleDotline: { - data: item.system.uses_per_score, - dotlineLabel: "Uses", - target: "item.system.uses_per_score.value", - iconEmpty: "dot-empty.svg", - iconEmptyHover: "dot-empty-hover.svg", - iconFull: "dot-full.svg", - iconFullHover: "dot-full-hover.svg" - } - }); - } - return item; - }), - playbook: this.actor.playbook - }); - sheetData.preparedActors = { - crew: activeSubActors.find((actor) => actor.type === BladesActorType.crew), - vice_purveyor: activeSubActors.find((actor) => actor.hasTag(Tag.NPC.VicePurveyor)), - acquaintances: activeSubActors.filter((actor) => actor.hasTag(Tag.NPC.Acquaintance)) - }; - sheetData.hasVicePurveyor = Boolean(this.actor.playbook?.hasTag(Tag.Gear.Advanced) === false - && activeSubItems.find((item) => item.type === BladesItemType.vice)); - sheetData.healing_clock = { - display: "Healing", - target: "system.healing.value", - color: "white", - isVisible: true, - isNameVisible: false, - isActive: false, - ...this.actor.system.healing, - id: randomID() - }; - sheetData.stashData = { - label: "Stash:", - dotline: { - data: this.actor.system.stash, - target: "system.stash.value", - iconEmpty: "coin-empty.svg", - iconEmptyHover: "coin-empty-hover.svg", - iconFull: "coin-full.svg", - iconFullHover: "coin-full-hover.svg", - altIconFull: "coin-ten.svg", - altIconFullHover: "coin-ten-hover.svg", - altIconStep: 10 - } - }; - sheetData.stressData = { - label: this.actor.system.stress.name, - dotline: { - data: this.actor.system.stress, - dotlineClass: this.actor.system.stress.max >= 13 ? "narrow-stress" : "", - target: "system.stress.value", - svgKey: "teeth.tall", - svgFull: "full|half|frame", - svgEmpty: "full|half|frame" - } - }; - if (BladesActor.IsType(this.actor, BladesActorType.pc)) { - sheetData.traumaData = { - label: this.actor.system.trauma.name, - dotline: { - data: { value: this.actor.trauma, max: this.actor.system.trauma.max }, - svgKey: "teeth.short", - svgFull: "full|frame", - svgEmpty: "frame", - isLocked: true - }, - compContainer: { - "class": "comp-trauma-conditions comp-vertical full-width", - "blocks": [ - this.actor.traumaList.slice(0, Math.ceil(this.actor.traumaList.length / 2)) - .map((tName) => ({ - checkLabel: tName, - checkClasses: { - active: "comp-toggle-red", - inactive: "comp-toggle-grey" - }, - checkTarget: `system.trauma.checked.${tName}`, - checkValue: this.actor.system.trauma.checked[tName] ?? false, - tooltip: C.TraumaTooltips[tName], - tooltipClass: "tooltip-trauma" - })), - this.actor.traumaList.slice(Math.ceil(this.actor.traumaList.length / 2)) - .map((tName) => ({ - checkLabel: tName, - checkClasses: { - active: "comp-toggle-red", - inactive: "comp-toggle-grey" - }, - checkTarget: `system.trauma.checked.${tName}`, - checkValue: this.actor.system.trauma.checked[tName] ?? false, - tooltip: C.TraumaTooltips[tName], - tooltipClass: "tooltip-trauma" - })) - ] - } - }; - } - sheetData.abilityData = { - dotline: { - dotlineClass: "dotline-right dotline-glow", - data: { - value: this.actor.getAvailableAdvancements("Ability"), - max: this.actor.getAvailableAdvancements("Ability") - }, - dotlineLabel: "Available Abilities", - isLocked: true, - iconFull: "dot-full.svg" - } - }; - sheetData.loadData = { - curLoad: this.actor.currentLoad, - selLoadCount: this.actor.system.loadout.levels[U.lCase(game.i18n.localize(this.actor.system.loadout.selected.toString()))], - selections: C.Loadout.selections, - selLoadLevel: this.actor.system.loadout.selected.toString() - }; - sheetData.armor = Object.fromEntries(Object.entries(this.actor.system.armor.active) - .filter(([, isActive]) => isActive) - .map(([armor]) => [armor, this.actor.system.armor.checked[armor]])); - sheetData.attributeData = {}; - const attrEntries = Object.entries(this.actor.system.attributes); - for (const [attribute, attrData] of attrEntries) { - sheetData.attributeData[attribute] = { - tooltip: C.AttributeTooltips[attribute], - actions: {} - }; - const actionEntries = Object.entries(attrData); - for (const [action, actionData] of actionEntries) { - sheetData.attributeData[attribute].actions[action] = { - tooltip: C.ActionTooltips[action], - value: actionData.value, - max: BladesTrackerSheet.Get().phase === BladesPhase.CharGen ? 2 : this.actor.system.attributes[attribute][action].max - }; - } - } - sheetData.gatherInfoTooltip = (new Handlebars.SafeString([ - "

Gathering Information: Questions to Consider

", - "
    ", - ...Object.values(this.actor.system.gather_info ?? []).map((line) => `
  • ${line}
  • `) ?? [], - "
" - ].join(""))).toString(); - eLog.checkLog("Attribute", "[BladesPCSheet] attributeData", { attributeData: sheetData.attributeData }); - eLog.checkLog("actor", "[BladesPCSheet] getData()", { ...context, ...sheetData }); - return { ...context, ...sheetData }; - } - get activeArmor() { - return Object.keys(U.objFilter(this.actor.system.armor.active, (val) => val === true)); - } - get checkedArmor() { - return Object.keys(U.objFilter(this.actor.system.armor.checked, (val, key) => val === true - && this.actor.system.armor.active[key] === true)); - } - get uncheckedArmor() { - return Object.keys(U.objFilter(this.actor.system.armor.active, (val, key) => val === true - && this.actor.system.armor.checked[key] === false)); - } - _getHoverArmor() { - if (!this.activeArmor.length) { - return false; - } - if (this.activeArmor.includes("heavy")) { - return this.checkedArmor.includes("heavy") ? "light" : "heavy"; - } - else if (this.activeArmor.includes("light")) { - return "light"; - } - return "special"; - } - _getClickArmor() { - if (!this.uncheckedArmor.length) { - return false; - } - if (this.uncheckedArmor.includes("heavy")) { - return "heavy"; - } - if (this.uncheckedArmor.includes("light")) { - return "light"; - } - return "special"; - } - _getContextMenuArmor() { - if (!this.checkedArmor.length) { - return false; - } - if (this.checkedArmor.includes("light")) { - return "light"; - } - if (this.checkedArmor.includes("heavy")) { - return "heavy"; - } - return "special"; - } - async _onAdvanceClick(event) { - event.preventDefault(); - super._onAdvanceClick(event); - const action = $(event.currentTarget).data("action").replace(/^advance-/, ""); - if (action in Attribute) { - this.actor.advanceAttribute(action); - } - } - activateListeners(html) { - super.activateListeners(html); - - if (!this.options.editable) { - return; - } - const self = this; - - html.find(".main-armor-control").on({ - click: function () { - const targetArmor = self._getClickArmor(); - if (!targetArmor) { - return; - } - self.actor.update({ [`system.armor.checked.${targetArmor}`]: true }); - }, - contextmenu: function () { - const targetArmor = self._getContextMenuArmor(); - if (!targetArmor) { - return; - } - self.actor.update({ [`system.armor.checked.${targetArmor}`]: false }); - }, - mouseenter: function () { - const targetArmor = self._getHoverArmor(); - eLog.log4("Mouse Enter", targetArmor, this, $(this), $(this).next()); - if (!targetArmor) { - return; - } - $(this).siblings(`.svg-armor.armor-${targetArmor}`).addClass("hover-over"); - }, - mouseleave: function () { - const targetArmor = self._getHoverArmor(); - if (!targetArmor) { - return; - } - $(this).siblings(`.svg-armor.armor-${targetArmor}`).removeClass("hover-over"); - } - }); - html.find(".special-armor-control").on({ - click: function () { - if (!self.activeArmor.includes("special")) { - return; - } - self.actor.update({ ["system.armor.checked.special"]: self.uncheckedArmor.includes("special") }); - }, - contextmenu: function () { - if (!self.activeArmor.includes("special")) { - return; - } - self.actor.update({ ["system.armor.checked.special"]: self.uncheckedArmor.includes("special") }); - }, - mouseenter: function () { - if (!self.activeArmor.includes("special") || self.activeArmor.length === 1) { - return; - } - $(this).siblings(".svg-armor.armor-special").addClass("hover-over"); - }, - mouseleave: function () { - if (!self.activeArmor.includes("special") || self.activeArmor.length === 1) { - return; - } - $(this).siblings(".svg-armor.armor-special").removeClass("hover-over"); - } - }); - } -} -export default BladesPCSheet; -//# sourceMappingURL=blades-pc-sheet.js.map \ No newline at end of file diff --git a/module/sheets/actor/blades-sheet.js b/module/sheets/actor/blades-sheet.js deleted file mode 100644 index d6361922..00000000 --- a/module/sheets/actor/blades-sheet.js +++ /dev/null @@ -1,399 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - - -import U from "../../core/utilities.js"; -import G, { ApplyTooltipListeners } from "../../core/gsap.js"; -import C, { BladesActorType, BladesItemType, Attribute, Action, Factor, RollType } from "../../core/constants.js"; -import Tags from "../../core/tags.js"; -import BladesActor from "../../blades-actor.js"; -import BladesItem from "../../blades-item.js"; -import BladesSelectorDialog from "../../blades-dialog.js"; -import BladesActiveEffect from "../../blades-active-effect.js"; -import BladesRollCollab, { BladesRollCollabComps } from "../../blades-roll-collab.js"; -class BladesSheet extends ActorSheet { - getData() { - const context = super.getData(); - const sheetData = { - cssClass: this.actor.type, - editable: this.options.editable, - isGM: game.eunoblades.Tracker?.system.is_spoofing_player ? false : game.user.isGM, - actor: this.actor, - system: this.actor.system, - tierTotal: this.actor.getFactorTotal(Factor.tier) > 0 ? U.romanizeNum(this.actor.getFactorTotal(Factor.tier)) : "0", - rollData: this.actor.getRollData(), - activeEffects: Array.from(this.actor.effects), - hasFullVision: game.user.isGM || this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OBSERVER), - hasLimitedVision: game.user.isGM || this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.LIMITED), - hasControl: game.user.isGM || this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER), - preparedItems: { - cohorts: { - gang: this.actor.activeSubItems - .filter((item) => item.type === BladesItemType.cohort_gang) - .map((item) => { - const subtypes = U.unique(Object.values(item.system.subtypes) - .map((subtype) => subtype.trim()) - .filter((subtype) => /[A-Za-z]/.test(subtype))); - const eliteSubtypes = U.unique([ - ...Object.values(item.system.elite_subtypes), - ...(item.parent?.upgrades ?? []) - .map((upgrade) => (upgrade.name ?? "").trim().replace(/^Elite /, "")) - ] - .map((subtype) => subtype.trim()) - .filter((subtype) => /[A-Za-z]/.test(subtype) && subtypes.includes(subtype))); - const imgTypes = [...eliteSubtypes]; - if (imgTypes.length < 2) { - imgTypes.push(...subtypes.filter((subtype) => !imgTypes.includes(subtype))); - } - if (U.unique(imgTypes).length === 1) { - item.system.image = Object.values(item.system.elite_subtypes).includes(imgTypes[0]) ? `elite-${U.lCase(imgTypes[0])}.svg` : `${U.lCase(imgTypes[0])}.svg`; - } - else if (U.unique(imgTypes).length > 1) { - const [rightType, leftType] = imgTypes; - item.system.imageLeft = Object.values(item.system.elite_subtypes).includes(leftType) ? `elite-${U.lCase(leftType)}.svg` : `${U.lCase(leftType)}.svg`; - item.system.imageRight = Object.values(item.system.elite_subtypes).includes(rightType) ? `elite-${U.lCase(rightType)}.svg` : `${U.lCase(rightType)}.svg`; - } - Object.assign(item.system, { - tierTotal: item.getFactorTotal(Factor.tier) > 0 ? U.romanizeNum(item.getFactorTotal(Factor.tier)) : "0", - cohortRollData: [ - { mode: "untrained", label: "Untrained", color: "transparent", tooltip: "

Roll Untrained

" } - ], - edgeData: Object.fromEntries(Object.values(item.system.edges ?? []) - .filter((edge) => /[A-Za-z]/.test(edge)) - .map((edge) => [edge.trim(), C.EdgeTooltips[edge]])), - flawData: Object.fromEntries(Object.values(item.system.flaws ?? []) - .filter((flaw) => /[A-Za-z]/.test(flaw)) - .map((flaw) => [flaw.trim(), C.FlawTooltips[flaw]])) - }); - return item; - }), - expert: this.actor.activeSubItems - .filter((item) => item.type === BladesItemType.cohort_expert) - .map((item) => { - Object.assign(item.system, { - tierTotal: item.getFactorTotal(Factor.tier) > 0 ? U.romanizeNum(item.getFactorTotal(Factor.tier)) : "0", - cohortRollData: [ - { mode: "untrained", label: "Untrained", tooltip: "

Roll Untrained

" } - ], - edgeData: Object.fromEntries(Object.values(item.system.edges ?? []) - .filter((edge) => /[A-Za-z]/.test(edge)) - .map((edge) => [edge.trim(), C.EdgeTooltips[edge]])), - flawData: Object.fromEntries(Object.values(item.system.flaws ?? []) - .filter((flaw) => /[A-Za-z]/.test(flaw)) - .map((flaw) => [flaw.trim(), C.FlawTooltips[flaw]])) - }); - return item; - }) - } - } - }; - if (BladesActor.IsType(this.actor, BladesActorType.pc) || BladesActor.IsType(this.actor, BladesActorType.crew)) { - sheetData.playbookData = { - dotline: { - data: this.actor.system.experience.playbook, - dotlineClass: "xp-playbook", - target: "system.experience.playbook.value", - svgKey: "teeth.tall", - svgFull: "full|frame", - svgEmpty: "full|half|frame", - advanceButton: "advance-playbook" - } - }; - if (this.actor.system.experience.playbook.value !== this.actor.system.experience.playbook.max) { - sheetData.playbookData.tooltip = (new Handlebars.SafeString([ - "

At the End of the Session, Gain XP If ...

", - "
    ", - ...Object.values(this.actor.system.experience.clues ?? []).map((line) => `
  • ${line.replace(/^Y/, "... y")}
  • `) ?? [], - "
" - ].join(""))).toString(); - } - sheetData.coinsData = { - dotline: { - data: this.actor.system.coins, - target: "system.coins.value", - iconEmpty: "coin-full.svg", - iconFull: "coin-full.svg" - } - }; - } - return { - ...context, - ...sheetData - }; - } - activateListeners(html) { - super.activateListeners(html); - if (game.user.isGM) { - html.attr("style", "--secret-text-display: initial"); - } - else { - html.find('.editor:not(.tinymce) [data-is-secret="true"]').remove(); - } - - ApplyTooltipListeners(html); - Tags.InitListeners(html, this.actor); - if (!this.options.editable) { - return; - } - html.find(".dotline").each((__, elem) => { - if ($(elem).hasClass("locked")) { - return; - } - let targetDoc = this.actor; - let targetField = $(elem).data("target"); - const comp$ = $(elem).closest("comp"); - if (targetField.startsWith("item")) { - targetField = targetField.replace(/^item\./, ""); - const itemId = $(elem).closest("[data-comp-id]").data("compId"); - if (!itemId) { - return; - } - const item = this.actor.items.get(itemId); - if (!item) { - return; - } - targetDoc = item; - } - const curValue = U.pInt($(elem).data("value")); - $(elem) - .find(".dot") - .each((_, dot) => { - $(dot).on("click", (event) => { - event.preventDefault(); - const thisValue = U.pInt($(dot).data("value")); - if (thisValue !== curValue) { - if (comp$.hasClass("comp-coins") - || comp$.hasClass("comp-stash")) { - G.effects - .fillCoins($(dot).prevAll(".dot")) - .then(() => targetDoc.update({ [targetField]: thisValue })); - } - else { - targetDoc.update({ [targetField]: thisValue }); - } - } - }); - $(dot).on("contextmenu", (event) => { - event.preventDefault(); - const thisValue = U.pInt($(dot).data("value")) - 1; - if (thisValue !== curValue) { - targetDoc.update({ [targetField]: thisValue }); - } - }); - }); - }); - html - .find(".clock-container") - .on("click", this._onClockLeftClick.bind(this)); - html - .find(".clock-container") - .on("contextmenu", this._onClockRightClick.bind(this)); - html - .find("[data-comp-id]") - .find(".comp-title") - .on("click", this._onItemOpenClick.bind(this)); - html - .find(".comp-control.comp-add") - .on("click", this._onItemAddClick.bind(this)); - html - .find(".comp-control.comp-delete") - .on("click", this._onItemRemoveClick.bind(this)); - html - .find(".comp-control.comp-delete-full") - .on("click", this._onItemFullRemoveClick.bind(this)); - html - .find(".comp-control.comp-toggle") - .on("click", this._onItemToggleClick.bind(this)); - html - .find(".advance-button") - .on("click", this._onAdvanceClick.bind(this)); - html - .find(".effect-control") - .on("click", this._onActiveEffectControlClick.bind(this)); - html - .find("[data-roll-trait]") - .on("click", this._onRollTraitClick.bind(this)); - if (this.options.submitOnChange) { - html.on("change", "textarea", this._onChangeInput.bind(this)); - } - } - async _onSubmit(event, params = {}) { - if (!game.user.isGM && !this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER)) { - eLog.checkLog("actorSheetTrigger", "User does not have permission to edit this actor", { user: game.user, actor: this.actor }); - return {}; - } - return super._onSubmit(event, params); - } - async close(options) { - if (this.actor.type === BladesActorType.pc) { - return super.close(options).then(() => this.actor.clearSubActors()); - } - else if (this.actor.type === BladesActorType.npc && this.actor.parentActor) { - return super.close(options).then(() => this.actor.clearParentActor(false)); - } - return super.close(options); - } - - async _onClockLeftClick(event) { - event.preventDefault(); - const clock$ = $(event.currentTarget).find(".clock[data-target]"); - if (!clock$[0]) { - return; - } - const target = clock$.data("target"); - const curValue = U.pInt(clock$.data("value")); - const maxValue = U.pInt(clock$.data("size")); - G.effects.pulseClockWedges(clock$.find("wedges")).then(() => this.actor.update({ - [target]: G.utils.wrap(0, maxValue + 1, curValue + 1) - })); - } - async _onClockRightClick(event) { - event.preventDefault(); - const clock$ = $(event.currentTarget).find(".clock[data-target]"); - if (!clock$[0]) { - return; - } - const target = clock$.data("target"); - const curValue = U.pInt(clock$.data("value")); - G.effects.reversePulseClockWedges(clock$.find("wedges")).then(() => this.actor.update({ - [target]: Math.max(0, curValue - 1) - })); - } - - _getCompData(event) { - const elem$ = $(event.currentTarget).closest(".comp"); - const compData = { - elem$, - docID: elem$.data("compId"), - docCat: elem$.data("compCat"), - docType: elem$.data("compType"), - docTags: (elem$.data("compTags") ?? "").split(/\s+/g) - }; - eLog.checkLog2("dialog", "Component Data", { elem: elem$, ...compData }); - if (compData.docID && compData.docType) { - compData.doc = { - Actor: this.actor.getSubActor(compData.docID), - Item: this.actor.getSubItem(compData.docID) - }[compData.docType]; - } - if (compData.docCat && compData.docType) { - compData.dialogDocs = { - Actor: this.actor.getDialogActors(compData.docCat), - Item: this.actor.getDialogItems(compData.docCat) - }[compData.docType]; - } - return compData; - } - async _onItemOpenClick(event) { - event.preventDefault(); - const { doc } = this._getCompData(event); - if (!doc) { - return; - } - doc.sheet?.render(true); - } - async _onItemAddClick(event) { - event.preventDefault(); - const addType = $(event.currentTarget).closest(".comp").data("addType"); - if (addType && addType in BladesItemType) { - await this.actor.createEmbeddedDocuments("Item", [ - { - name: { - [BladesItemType.cohort_gang]: "A Gang", - [BladesItemType.cohort_expert]: "An Expert" - }[addType] ?? randomID(), - type: addType - } - ]); - return; - } - const { docCat, docType, dialogDocs, docTags } = this._getCompData(event); - if (!dialogDocs || !docCat || !docType) { - return; - } - await BladesSelectorDialog.Display(this.actor, U.tCase(`Add ${docCat.replace(/_/g, " ")}`), docType, dialogDocs, docTags); - } - async _onItemRemoveClick(event) { - event.preventDefault(); - const { elem$, doc } = this._getCompData(event); - if (!doc) { - return; - } - G.effects.blurRemove(elem$).then(() => { - if (doc instanceof BladesItem) { - this.actor.remSubItem(doc); - } - else { - this.actor.remSubActor(doc); - } - }); - } - async _onItemFullRemoveClick(event) { - event.preventDefault(); - const { elem$, doc } = this._getCompData(event); - if (!doc) { - return; - } - G.effects.blurRemove(elem$).then(() => doc.delete()); - } - async _onItemToggleClick(event) { - event.preventDefault(); - const target = $(event.currentTarget).data("target"); - this.actor.update({ - [target]: !getProperty(this.actor, target) - }); - } - async _onAdvanceClick(event) { - event.preventDefault(); - if ($(event.currentTarget).data("action") === "advance-playbook") { - this.actor.advancePlaybook(); - } - } - - async _onRollTraitClick(event) { - const traitName = $(event.currentTarget).data("rollTrait"); - const rollType = $(event.currentTarget).data("rollType"); - const rollData = {}; - if (U.lCase(traitName) in { ...Action, ...Attribute, ...Factor }) { - rollData.rollTrait = U.lCase(traitName); - } - else if (U.isInt(traitName)) { - rollData.rollTrait = U.pInt(traitName); - } - if (U.tCase(rollType) in RollType) { - rollData.rollType = U.tCase(rollType); - } - else if (typeof rollData.rollTrait === "string") { - if (rollData.rollTrait in Attribute) { - rollData.rollType = RollType.Resistance; - } - else if (rollData.rollTrait in Action) { - rollData.rollType = RollType.Action; - } - } - if (game.user.isGM) { - if (BladesRollCollabComps.Primary.IsDoc(this.actor)) { - rollData.rollPrimary = this.actor; - } - else if (BladesRollCollabComps.Opposition.IsDoc(this.actor)) { - rollData.rollOpposition = this.actor; - } - } - if (rollData.rollType) { - BladesRollCollab.NewRoll(rollData); - } - else { - throw new Error("Unable to determine roll type of roll."); - } - } - - async _onActiveEffectControlClick(event) { - BladesActiveEffect.onManageActiveEffect(event, this.actor); - } -} -export default BladesSheet; -//# sourceMappingURL=blades-sheet.js.map \ No newline at end of file diff --git a/module/sheets/blades-actor-sheet.js b/module/sheets/blades-actor-sheet.js deleted file mode 100644 index 3766dbd1..00000000 --- a/module/sheets/blades-actor-sheet.js +++ /dev/null @@ -1,315 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import C, { BladesActorType, BladesItemType, Tag } from "../core/constants.js"; -import { BladesPhase } from "./blades-tracker-sheet.js"; -import U from "../core/utilities.js"; -import BladesSheet from "./blades-sheet.js"; -import BladesItem from "../blades-item.js"; -import BladesActor from "../blades-actor.js"; -class BladesActorSheet extends BladesSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "actor", "pc"], - template: "systems/eunos-blades/templates/actor-sheet.hbs", - width: 775, - height: 775, - tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "abilities" }] - }); - } - static Initialize() { - Actors.registerSheet("blades", BladesActorSheet, { types: ["pc"], makeDefault: true }); - Hooks.on("dropActorSheetData", async (parentActor, _, { uuid }) => { - const doc = await fromUuid(uuid); - if (doc instanceof BladesActor) { - if (parentActor.type === BladesActorType.crew && doc.type === BladesActorType.pc) { - doc.addSubActor(parentActor); - } - else if (parentActor.type === BladesActorType.pc && doc.type === BladesActorType.crew) { - parentActor.addSubActor(doc); - } - } - if (doc instanceof BladesItem) { - BladesItem.create(doc, { parent: parentActor }); - return; - } - }); - return loadTemplates([ - "systems/eunos-blades/templates/items/clock_keeper-sheet.hbs", - "systems/eunos-blades/templates/parts/clock-sheet-row.hbs" - ]); - } - getData() { - const context = super.getData(); - const sheetData = {}; - sheetData.isOwner = this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER); - context.attributes = U.objMap(context.system.attributes, (attrData) => U.objMap(attrData, (value) => ({ - value: value.value, - max: game.eunoblades.Tracker.system.game_phase === BladesPhase.CharGen ? 2 : value.max - }))); - const { activeSubItems, activeSubActors } = this.actor; - Object.assign(sheetData, { - phases: Object.values(BladesPhase), - items: { - abilities: activeSubItems.filter((item) => item.type === BladesItemType.ability).map((item) => { - if (item.system.uses?.max) { - Object.assign(item, { - inRuleDotline: { - data: item.system.uses, - dotlineLabel: "Uses", - target: "item.system.uses.value", - iconEmpty: "dot-empty.svg", - iconEmptyHover: "dot-empty-hover.svg", - iconFull: "dot-full.svg", - iconFullHover: "dot-full-hover.svg" - } - }); - } - return item; - }), - background: activeSubItems.find((item) => item.type === BladesItemType.background), - heritage: activeSubItems.find((item) => item.type === BladesItemType.heritage), - vice: activeSubItems.find((item) => item.type === BladesItemType.vice), - loadout: activeSubItems.filter((item) => item.type === BladesItemType.item).map((item) => { - if (item.system.load) { - Object.assign(item, { - numberCircle: item.system.load, - numberCircleClass: "item-load" - }); - } - if (item.system.uses?.max) { - Object.assign(item, { - inRuleDotline: { - data: item.system.uses, - dotlineLabel: "Uses", - target: "item.system.uses.value", - iconEmpty: "dot-empty.svg", - iconEmptyHover: "dot-empty-hover.svg", - iconFull: "dot-full.svg", - iconFullHover: "dot-full-hover.svg" - } - }); - } - return item; - }), - playbook: this.actor.playbook - }, - actors: { - crew: activeSubActors.find((actor) => actor.type === BladesActorType.crew), - vice_purveyor: activeSubActors.find((actor) => actor.hasTag(Tag.NPC.VicePurveyor)), - friends: activeSubActors.filter((actor) => actor.hasTag(Tag.NPC.Friend)), - rivals: activeSubActors.filter((actor) => actor.hasTag(Tag.NPC.Rival)) - }, - stashData: { - label: "Stash:", - dotline: { - data: this.actor.system.stash, - target: "system.stash.value", - iconEmpty: "coin-empty.svg", - iconEmptyHover: "coin-empty-hover.svg", - iconFull: "coin-full.svg", - iconFullHover: "coin-full-hover.svg", - altIconFull: "coin-ten.svg", - altIconFullHover: "coin-ten-hover.svg", - altIconStep: 10 - } - }, - healing_clock: { - value: this.actor.system.healing.value, - size: this.actor.system.healing.max - }, - armor: Object.fromEntries(Object.entries(this.actor.system.armor.active) - .filter(([, isActive]) => isActive) - .map(([armor]) => [armor, this.actor.system.armor.checked[armor]])), - loadData: { - curLoad: this.actor.currentLoad, - selLoadCount: this.actor.system.loadout.levels[U.lCase(game.i18n.localize(this.actor.system.loadout.selected.toString()))], - selections: C.Loadout.selections, - selLoadLevel: this.actor.system.loadout.selected.toString() - }, - stressData: { - name: this.actor.system.stress.name, - dotline: { - data: this.actor.system.stress, - target: "system.stress.value", - svgKey: "teeth.tall", - svgFull: "full|half|frame", - svgEmpty: "full|half|frame" - } - }, - traumaData: { - name: this.actor.system.trauma.name, - dotline: { - data: { value: this.actor.trauma, max: this.actor.system.trauma.max }, - svgKey: "teeth.short", - svgFull: "full|frame", - svgEmpty: "frame", - isLocked: true - }, - compContainer: { - "class": "comp-trauma-conditions comp-vertical full-width", - "blocks": [ - this.actor.traumaList.slice(0, Math.ceil(this.actor.traumaList.length / 2)) - .map((tName) => ({ - checkLabel: tName, - checkClasses: { - active: "comp-toggle-red", - inactive: "comp-toggle-grey" - }, - checkTarget: `system.trauma.checked.${tName}`, - checkValue: this.actor.system.trauma.checked[tName] ?? false - })), - this.actor.traumaList.slice(Math.ceil(this.actor.traumaList.length / 2)) - .map((tName) => ({ - checkLabel: tName, - checkClasses: { - active: "comp-toggle-red", - inactive: "comp-toggle-grey" - }, - checkTarget: `system.trauma.checked.${tName}`, - checkValue: this.actor.system.trauma.checked[tName] ?? false - })) - ] - } - }, - acquaintancesName: this.actor.system.acquaintances_name ?? "Friends & Rivals", - friendsName: this.actor.system.friends_name, - rivalsName: this.actor.system.rivals_name, - abilityData: { - dotline: { - "class": "dotline-right", - "data": { - value: this.actor.availableAbilityPoints, - max: this.actor.availableAbilityPoints - }, - "dotlineLabel": "Available Abilities", - "isLocked": true, - "iconFull": "dot-full.svg" - } - } - }); - eLog.checkLog("actor", "[BladesActorSheet] getData()", { ...context, ...sheetData }); - return { - ...context, - ...sheetData - }; - } - get activeArmor() { - return Object.keys(U.objFilter(this.actor.system.armor.active, (val) => val === true)); - } - get checkedArmor() { - return Object.keys(U.objFilter(this.actor.system.armor.checked, (val, key) => val === true - && this.actor.system.armor.active[key] === true)); - } - get uncheckedArmor() { - return Object.keys(U.objFilter(this.actor.system.armor.active, (val, key) => val === true - && this.actor.system.armor.checked[key] === false)); - } - _getHoverArmor() { - if (!this.activeArmor.length) { - return false; - } - if (this.activeArmor.includes("heavy")) { - return this.checkedArmor.includes("heavy") ? "light" : "heavy"; - } - else if (this.activeArmor.includes("light")) { - return "light"; - } - return "special"; - } - _getClickArmor() { - if (!this.uncheckedArmor.length) { - return false; - } - if (this.uncheckedArmor.includes("heavy")) { - return "heavy"; - } - if (this.uncheckedArmor.includes("light")) { - return "light"; - } - return "special"; - } - _getContextMenuArmor() { - if (!this.checkedArmor.length) { - return false; - } - if (this.checkedArmor.includes("light")) { - return "light"; - } - if (this.checkedArmor.includes("heavy")) { - return "heavy"; - } - return "special"; - } - activateListeners(html) { - super.activateListeners(html); - - if (!this.options.editable) { - return; - } - const self = this; - - html.find(".main-armor-control").on({ - click: function () { - const targetArmor = self._getClickArmor(); - if (!targetArmor) { - return; - } - self.actor.update({ [`system.armor.checked.${targetArmor}`]: true }); - }, - contextmenu: function () { - const targetArmor = self._getContextMenuArmor(); - if (!targetArmor) { - return; - } - self.actor.update({ [`system.armor.checked.${targetArmor}`]: false }); - }, - mouseenter: function () { - const targetArmor = self._getHoverArmor(); - eLog.log4("Mouse Enter", targetArmor, this, $(this), $(this).next()); - if (!targetArmor) { - return; - } - $(this).siblings(`.svg-armor.armor-${targetArmor}`).addClass("hover-over"); - }, - mouseleave: function () { - const targetArmor = self._getHoverArmor(); - if (!targetArmor) { - return; - } - $(this).siblings(`.svg-armor.armor-${targetArmor}`).removeClass("hover-over"); - } - }); - html.find(".special-armor-control").on({ - click: function () { - if (!self.activeArmor.includes("special")) { - return; - } - self.actor.update({ ["system.armor.checked.special"]: self.uncheckedArmor.includes("special") }); - }, - contextmenu: function () { - if (!self.activeArmor.includes("special")) { - return; - } - self.actor.update({ ["system.armor.checked.special"]: self.uncheckedArmor.includes("special") }); - }, - mouseenter: function () { - if (!self.activeArmor.includes("special") || self.activeArmor.length === 1) { - return; - } - $(this).siblings(".svg-armor.armor-special").addClass("hover-over"); - }, - mouseleave: function () { - if (!self.activeArmor.includes("special") || self.activeArmor.length === 1) { - return; - } - $(this).siblings(".svg-armor.armor-special").removeClass("hover-over"); - } - }); - } -} -export default BladesActorSheet; \ No newline at end of file diff --git a/module/sheets/blades-clock-keeper-sheet.js b/module/sheets/blades-clock-keeper-sheet.js deleted file mode 100644 index fd18e3d2..00000000 --- a/module/sheets/blades-clock-keeper-sheet.js +++ /dev/null @@ -1,75 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesItemSheet from "./blades-item-sheet.js"; -import BladesItem from "../blades-item.js"; -export default class BladesClockKeeperSheet extends BladesItemSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "item", "clock-keeper"], - template: "systems/eunos-blades/templates/items/clock_keeper-sheet.hbs", - width: 700, - height: 970 - }); - } - static async Initialize() { - game.eunoblades ??= {}; - Items.registerSheet("blades", BladesClockKeeperSheet, { types: ["clock_keeper"], makeDefault: true }); - Hooks.once("ready", async () => { - let clockKeeper = game.items.find((item) => item.type === "clock_keeper"); - if (!(clockKeeper instanceof BladesItem)) { - clockKeeper = (await BladesItem.create({ - name: "Clock Keeper", - type: "clock_keeper", - img: "systems/eunos-blades/assets/icons/clock-keeper.svg" - })); - } - game.eunoblades.ClockKeeper = clockKeeper; - game.eunoblades.ClockKeeper.renderOverlay(); - }); - Hooks.on("canvasReady", async () => { game.eunoblades.ClockKeeper?.renderOverlay(); }); - return loadTemplates([ - "systems/eunos-blades/templates/items/clock_keeper-sheet.hbs", - "systems/eunos-blades/templates/parts/clock-sheet-row.hbs" - ]); - } - async _updateObject(event, formData) { - const updateData = await this.object.update(formData); - socketlib.system.executeForEveryone("renderOverlay"); - return updateData; - } - async getData() { - const context = super.getData(); - context.clock_keys = Object.fromEntries(Object.entries(context.clock_keys) - .filter(([keyID, keyData]) => Boolean(keyData && keyData.scene === context.system.targetScene))); - return context; - } - addKey(event) { - event.preventDefault(); - this.item.addClockKey(); - } - deleteKey(event) { - event.preventDefault(); - const keyID = event.currentTarget.dataset.id; - if (keyID) { - this.item.deleteClockKey(keyID); - } - } - setKeySize(event) { - event.preventDefault(); - const keyID = event.target.dataset.id; - if (keyID) { - this.item.setKeySize(keyID, parseInt(event.target.value)); - } - } - async activateListeners(html) { - super.activateListeners(html); - html.find("[data-action=\"add-key\"").on("click", this.addKey.bind(this)); - html.find("[data-action=\"delete-key\"").on("click", this.deleteKey.bind(this)); - html.find(".clock-counter").on("change", this.setKeySize.bind(this)); - } -} \ No newline at end of file diff --git a/module/sheets/blades-crew-sheet.js b/module/sheets/blades-crew-sheet.js deleted file mode 100644 index be6a3039..00000000 --- a/module/sheets/blades-crew-sheet.js +++ /dev/null @@ -1,177 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesSheet from "./blades-sheet.js"; -import { BladesItemType } from "../core/constants.js"; -class BladesCrewSheet extends BladesSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "actor", "crew"], - template: "systems/eunos-blades/templates/crew-sheet.hbs", - width: 940, - height: 820, - tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "claims" }] - }); - } - getData() { - const context = super.getData(); - eLog.checkLog("actor", "[BladesCrewSheet] super.getData()", { ...context }); - context.actor = this.actor; - context.system = this.actor.system; - const { activeSubItems } = this.actor; - - context.items = { - abilities: activeSubItems.filter((item) => item.type === BladesItemType.crew_ability), - playbook: this.actor.playbook, - reputation: activeSubItems.find((item) => item.type === BladesItemType.crew_reputation), - upgrades: activeSubItems.filter((item) => item.type === BladesItemType.crew_upgrade), - cohorts: activeSubItems.filter((item) => item.type === BladesItemType.cohort), - preferredOp: activeSubItems.find((item) => item.type === BladesItemType.preferred_op) - }; - context.actors = { - members: this.actor.members, - contacts: this.actor.contacts - }; - context.tierData = { - label: "Tier", - dotline: { - data: this.actor.system.tier, - target: "system.tier.value", - iconEmpty: "dot-empty.svg", - iconEmptyHover: "dot-empty-hover.svg", - iconFull: "dot-full.svg", - iconFullHover: "dot-full-hover.svg" - } - }; - context.upgradeData = { - dotline: { - "class": "dotline-right", - "data": { - value: this.actor.availableUpgradePoints, - max: this.actor.availableUpgradePoints - }, - "dotlineLabel": "Available Upgrade Points", - "isLocked": true, - "iconFull": "dot-full.svg" - } - }; - context.abilityData = { - dotline: { - "class": "dotline-right", - "data": { - value: this.actor.availableAbilityPoints, - max: this.actor.availableAbilityPoints - }, - "dotlineLabel": "Available Ability Points", - "isLocked": true, - "iconFull": "dot-full.svg" - } - }; - context.cohortData = { - dotline: { - "class": "dotline-right", - "data": { - value: this.actor.availableCohortPoints, - max: this.actor.availableCohortPoints - }, - "dotlineLabel": "Available Cohort Points", - "isLocked": true, - "iconFull": "dot-full.svg" - } - }; - context.repData = { - name: "Rep", - dotlines: [ - { - data: { - value: Math.min(this.actor.system.rep.value, this.actor.system.rep.max - this.actor.turfCount), - max: this.actor.system.rep.max - this.actor.turfCount - }, - target: "system.rep.value", - svgKey: "teeth.tall", - svgFull: "full|half|frame", - svgEmpty: "full|half|frame" - }, - { - "data": { - value: this.actor.turfCount, - max: this.actor.turfCount - }, - "target": "none", - "svgKey": "teeth.tall", - "svgFull": "full|half|frame", - "svgEmpty": "full|half|frame", - "class": "flex-row-reverse", - "isLocked": true - } - ] - }; - context.heatData = { - name: "Heat", - dotline: { - data: this.actor.system.heat, - target: "system.heat.value", - svgKey: "teeth.tall", - svgFull: "full|half|frame", - svgEmpty: "full|half|frame" - } - }; - context.wantedData = { - name: "Wanted", - dotline: { - data: this.actor.system.wanted, - target: "system.wanted.value", - svgKey: "teeth.short", - svgFull: "full|frame", - svgEmpty: "frame" - } - }; - eLog.checkLog("actor", "[BladesCrewSheet] return getData()", { ...context }); - return context; - } - activateListeners(html) { - super.activateListeners(html); - if (!this.options.editable) { - return; - } - html.find(".item-sheet-open").on("click", (event) => { - const element = $(event.currentTarget).parents(".item"); - const item = this.actor.items.get(element.data("itemId")); - item?.sheet?.render(true); - }); - html.find(".add-item").on("click", (event) => { - event.preventDefault(); - const a = event.currentTarget; - const item_type = a.dataset.itemType; - const data = { - name: randomID(), - type: item_type - }; - return this.actor.createEmbeddedDocuments("Item", [data]); - }); - html.find(".hold-toggle").on("click", () => { - this.actor.update({ "system.hold": this.actor.system.hold === "weak" ? "strong" : "weak" }); - }); - html.find(".turf-select").on("click", async (event) => { - const turf_id = $(event.currentTarget).data("turfId"); - const turf_current_status = $(event.currentTarget).data("turfStatus"); - this.actor.playbook?.update({ ["system.turfs." + turf_id + ".value"]: !turf_current_status }) - .then(() => this.render(false)); - }); - html.find('.cohort-block-harm input[type="radio"]').change(async (ev) => { - const element = $(ev.currentTarget).parents(".item"); - const item_id = element.data("itemId"); - const harm_id = $(ev.currentTarget).val(); - await this.actor.updateEmbeddedDocuments("Item", [{ - "_id": item_id, - "system.harm": [harm_id] - }]); - this.render(false); - }); - } -} -export default BladesCrewSheet; \ No newline at end of file diff --git a/module/sheets/blades-faction-sheet.js b/module/sheets/blades-faction-sheet.js deleted file mode 100644 index dfc02701..00000000 --- a/module/sheets/blades-faction-sheet.js +++ /dev/null @@ -1,31 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesSheet from "./blades-sheet.js"; -class BladesFactionSheet extends BladesSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "actor", "faction"], - template: "systems/eunos-blades/templates/faction-sheet.hbs", - width: 900, - height: "auto", - tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content" }] - }); - } - activateListeners(html) { - super.activateListeners(html); - if (!this.options.editable) { - return; - } - html.find(".item-body").on("click", (event) => { - const element = $(event.currentTarget).parents(".item"); - const item = this.actor.items.get(element.data("itemId")); - item?.sheet?.render(true); - }); - } -} -export default BladesFactionSheet; \ No newline at end of file diff --git a/module/sheets/blades-item-sheet.js b/module/sheets/blades-item-sheet.js deleted file mode 100644 index 5be81f26..00000000 --- a/module/sheets/blades-item-sheet.js +++ /dev/null @@ -1,186 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import C, { Tag, District, Playbook, Vice } from "../core/constants.js"; -import U from "../core/utilities.js"; -import BladesActiveEffect from "../blades-active-effect.js"; -import Tagify from "../../lib/tagify/tagify.esm.js"; -class BladesItemSheet extends ItemSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "item"], - width: 560, - height: 500, - tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }] - }); - } - - constructor(item, options = {}) { - options.classes = [...options.classes ?? [], "eunos-blades", "sheet", "item", item.type]; - super(item, options); - } - get template() { - const pathComps = [ - "systems/eunos-blades/templates/items" - ]; - if (C.SimpleItemTypes.includes(this.item.type)) { - pathComps.push("simple-sheet.hbs"); - } - else { - pathComps.push(`${this.item.type}-sheet.hbs`); - } - return pathComps.join("/"); - } - - activateListeners(html) { - super.activateListeners(html); - const self = this; - if (!this.options.editable) { - return; - } - const tagElem = html.find(".tag-entry")[0]; - if (tagElem) { - const tagify = new Tagify(tagElem, { - enforceWhitelist: true, - editTags: false, - whitelist: [ - ...Object.values(Tag.System).map((tag) => ({ - "value": tag, - "data-group": "System Tags" - })), - ...Object.values(Tag.Item).map((tag) => ({ - "value": tag, - "data-group": "Item Tags" - })), - ...Object.values(Tag.PC).map((tag) => ({ - "value": tag, - "data-group": "Actor Tags" - })), - ...Object.values(Tag.NPC).map((tag) => ({ - "value": tag, - "data-group": "Actor Tags" - })), - ...Object.values(District).map((tag) => ({ - "value": tag, - "data-group": "Districts" - })), - ...Object.values(Vice).map((tag) => ({ - "value": tag, - "data-group": "Vices" - })), - ...Object.values(Playbook).map((tag) => ({ - "value": tag, - "data-group": "Playbooks" - })) - ], - dropdown: { - enabled: 0, - maxItems: 10000, - placeAbove: false, - appendTarget: html[0] - } - }); - tagify.dropdown.createListHTML = (optionsArr) => { - const map = {}; - return structuredClone(optionsArr) - .map((suggestion, idx) => { - const value = tagify.dropdown.getMappedValue.call(tagify, suggestion); - let tagHTMLString = ""; - if (!map[suggestion["data-group"]]) { - map[suggestion["data-group"]] = true; - if (Object.keys(map).length) { - tagHTMLString += ""; - } - tagHTMLString += ` -
-

${suggestion["data-group"]}

- `; - } - suggestion.value - = value && typeof value === "string" ? U.escapeHTML(value.replace(/_/g, " ")) : value; - tagHTMLString += tagify.settings.templates.dropdownItem.apply(tagify, [suggestion, idx]); - return tagHTMLString; - }) - .join(""); - }; - tagify.addTags(this.item.tags.map((tag) => { - if (Object.values(Tag.System).includes(tag)) { - return { "value": tag, "data-group": "System Tags" }; - } - if (Object.values(Tag.Item).includes(tag)) { - return { "value": tag, "data-group": "Item Tags" }; - } - if (Object.values(Tag.PC).includes(tag) || Object.values(Tag.NPC).includes(tag)) { - return { "value": tag, "data-group": "Actor Tags" }; - } - if (Object.values(District).includes(tag)) { - return { "value": tag, "data-group": "Districts" }; - } - if (Object.values(Playbook).includes(tag)) { - return { "value": tag, "data-group": "Playbooks" }; - } - if (Object.values(Vice).includes(tag)) { - return { "value": tag, "data-group": "Vices" }; - } - return { "value": tag, "data-group": "Other" }; - }), false, false); - tagElem.addEventListener("change", this._onTagifyChange.bind(this)); - } - if (this.options.submitOnChange) { - html.on("change", "textarea", this._onChangeInput.bind(this)); - } - html.find(".effect-control").on("click", (ev) => { - if (self.item.isOwned) { - ui.notifications.warn(game.i18n.localize("BITD.EffectWarning")); - return; - } - BladesActiveEffect.onManageActiveEffect(ev, self.item); - }); - html.find("[data-action=\"toggle-turf-connection\"").on("click", this.toggleTurfConnection.bind(this)); - } - async _onTagifyChange(event) { - const tagString = event.target.value; - if (tagString) { - const tags = JSON.parse(tagString) - .map(({ value }) => value); - this.item.update({ "system.tags": tags }); - } - else { - this.item.update({ "system.tags": [] }); - } - } - toggleTurfConnection(event) { - const button$ = $(event.currentTarget); - const connector$ = button$.parent(); - const turfNum = parseInt(connector$.data("index") ?? 0); - const turfDir = connector$.data("dir"); - if (!turfNum || !turfDir) { - return; - } - const toggleState = connector$.hasClass("no-connect"); - const updateData = { - [`system.turfs.${turfNum}.connects.${turfDir}`]: toggleState - }; - const partner = connector$.data("partner"); - if (typeof partner === "string" && /-/.test(partner)) { - const [partnerNum, partnerDir] = partner.split("-"); - updateData[`system.turfs.${partnerNum}.connects.${partnerDir}`] = toggleState; - } - this.item.update(updateData); - } - async getData() { - const context = (await super.getData()); - context.editable = this.options.editable; - context.isGM = game.user.isGM; - context.isEmbeddedItem = this.item.parent !== null; - context.item = this.item; - context.system = this.item.system; - context.effects = this.item.effects; - return context; - } -} -export default BladesItemSheet; \ No newline at end of file diff --git a/module/sheets/blades-npc-sheet.js b/module/sheets/blades-npc-sheet.js deleted file mode 100644 index c3835664..00000000 --- a/module/sheets/blades-npc-sheet.js +++ /dev/null @@ -1,41 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesSheet from "./blades-sheet.js"; -class BladesNPCSheet extends BladesSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "actor", "npc"], - template: "systems/eunos-blades/templates/npc-sheet.hbs", - width: 500, - height: 350, - tabs: [{ navSelector: ".nav-tabs", contentSelector: ".tab-content", initial: "description" }] - }); - } - getData() { - const context = super.getData(); - context.isSubActor = context.actor.isSubActor; - context.parentActor = context.actor.parentActor; - context.randomizers = context.actor.system.randomizers; - return context; - } - activateListeners(html) { - super.activateListeners(html); - if (!this.options.editable) { - return; - } - html.find(".gm-alert-header").on("click", async (event) => { - event.preventDefault(); - this.actor.clearParentActor(); - }); - - html.find("[data-action=\"randomize\"").on("click", (event) => { - this.actor.updateRandomizers(); - }); - } -} -export default BladesNPCSheet; \ No newline at end of file diff --git a/module/sheets/blades-roll-collab-sheet.js b/module/sheets/blades-roll-collab-sheet.js deleted file mode 100644 index 8d519006..00000000 --- a/module/sheets/blades-roll-collab-sheet.js +++ /dev/null @@ -1,570 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import U from "../core/utilities.js"; -import C, { BladesActorType, RollType, RollModStatus, RollModCategory, Action, Attribute, Position, Effect, Factor, Harm } from "../core/constants.js"; -import BladesActor from "../blades-actor.js"; -import BladesItem from "../blades-item.js"; -import { ApplyTooltipListeners } from "../core/gsap.js"; -function isAction(trait) { - return Boolean(trait && typeof trait === "string" && trait in Action); -} -function isAttribute(trait) { - return Boolean(trait && typeof trait === "string" && trait in Attribute); -} -function isTier(trait) { return U.lCase(trait) === "tier"; } -function isNumber(trait) { return U.isInt(trait); } -class BladesRollCollabSheet extends DocumentSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "roll-collab"], - template: `systems/eunos-blades/templates/roll-collab${game.user.isGM ? "-gm" : ""}.hbs`, - submitOnChange: true, - width: 500 - }); - } - static Initialize() { - return loadTemplates([ - "systems/eunos-blades/templates/roll-collab-gm.hbs", - "systems/eunos-blades/templates/roll-collab.hbs" - ]); - } - static InitSockets() { - socketlib.system.register("renderRollCollab", BladesRollCollabSheet.RenderRollCollab); - socketlib.system.register("closeRollCollab", BladesRollCollabSheet.CloseRollCollab); - } - static Current = {}; - static get DefaultFlagData() { - return { - rollID: randomID(), - rollType: RollType.Action, - rollSourceType: "Actor", - rollSourceID: "", - rollTrait: Factor.tier, - rollMods: { - [RollModCategory.roll]: { - positive: { - Push: { - status: RollModStatus.ToggledOff, - value: 1, - tooltip: "

Take 2 Stress to add 1 die to your pool. (You cannot also accept a Devil's Bargain to increase your dice pool: It's one or the other.)

" - }, - Bargain: { - status: RollModStatus.ForcedOff, - value: 1, - tooltip: "

Accept a Devil's Bargain from the GM to add 1 die to your pool (You cannot also Push to increase your dice pool: It's one or the other. You can, however, Push to increase your Effect.)

" - }, - Assist: { - status: RollModStatus.Hidden, - value: 1, - sideString: "", - tooltip: "

Another character is Assisting your efforts, adding 1 die to your pool. (It costs them 1 Stress to do so.)

" - } - }, - negative: {} - }, - [RollModCategory.position]: { - positive: { - Setup: { - status: RollModStatus.Hidden, - value: 1, - sideString: undefined, - tooltip: "

Another character has set you up for success, increasing your Position by one level.

" - } - }, - negative: {} - }, - [RollModCategory.effect]: { - positive: { - Push: { - status: RollModStatus.ToggledOff, - value: 1, - tooltip: "

Take 2 Stress to increase your Effect by one level. (You can both Push for Effect and Push for +1d if you like, for a total cost of 4 Stress.)

" - }, - Setup: { - status: RollModStatus.Hidden, - value: 1, - sideString: undefined, - tooltip: "

Another character has set you up for success, increasing your Effect by one level.

" - }, - Potency: { - status: RollModStatus.Hidden, - value: 1, - tooltip: "" - } - }, - negative: {} - }, - [RollModCategory.result]: { positive: {}, negative: {} }, - [RollModCategory.after]: { positive: {}, negative: {} } - }, - rollPositionInitial: Position.risky, - rollEffectInitial: Effect.standard, - rollPosEffectTrade: false, - rollFactors: { - [Factor.tier]: { name: "Tier", value: 0, max: 0, isActive: false, isDominant: false, highFavorsPC: true }, - [Factor.quality]: { name: "Quality", value: 0, max: 0, isActive: false, isDominant: false, highFavorsPC: true }, - [Factor.force]: { name: "Force", value: 0, max: 0, isActive: false, isDominant: false, highFavorsPC: true }, - [Factor.scale]: { name: "Scale", value: 0, max: 0, isActive: false, isDominant: false, highFavorsPC: true }, - [Factor.area]: { name: "Area", value: 0, max: 0, isActive: false, isDominant: false, highFavorsPC: true }, - [Factor.duration]: { name: "Duration", value: 0, max: 0, isActive: false, isDominant: false, highFavorsPC: true }, - [Factor.range]: { name: "Range", value: 0, max: 0, isActive: false, isDominant: false, highFavorsPC: true }, - [Factor.magnitude]: { name: "Magnitude", value: 0, max: 0, isActive: false, isDominant: false, highFavorsPC: true } - }, - isGMReady: false, - GMBoosts: { - Dice: 0, - [Factor.tier]: 0, - [Factor.quality]: 0, - [Factor.force]: 0, - [Factor.scale]: 0, - [Factor.area]: 0, - [Factor.duration]: 0, - [Factor.range]: 0, - [Factor.magnitude]: 0, - Result: 0 - } - }; - } - static async RenderRollCollab({ userID, rollID }) { - const user = game.users.get(userID); - if (!user) { - return; - } - BladesRollCollabSheet.Current[rollID] = new BladesRollCollabSheet(user, rollID); - BladesRollCollabSheet.Current[rollID].render(true); - } - static async CloseRollCollab(rollID) { - eLog.checkLog3("rollCollab", "CloseRollCollab()", { rollID }); - await BladesRollCollabSheet.Current[rollID]?.close({ rollID }); - delete BladesRollCollabSheet.Current[rollID]; - } - static async NewRoll(config) { - if (game.user.isGM) { - eLog.error("rollCollab", "GM Cannot Use New Roll!"); - return; - } - const user = game.users.get(config.userID ?? game.user._id); - if (!(user instanceof User)) { - eLog.error("rollCollab", `[NewRoll()] Can't Find User '${config.userID}'`, config); - return; - } - const flagUpdateData = BladesRollCollabSheet.DefaultFlagData; - flagUpdateData.rollType = config.rollType; - if (!(flagUpdateData.rollType in RollType)) { - eLog.error("rollCollab", `[RenderRollCollab()] Invalid rollType: ${flagUpdateData.rollType}`, config); - return; - } - const rollSource = config.rollSource ?? user.character; - if (!(rollSource instanceof BladesActor || rollSource instanceof BladesItem)) { - eLog.error("rollCollab", "[RenderRollCollab()] Invalid rollSource", { rollSource, config }); - return; - } - flagUpdateData.rollSourceID = rollSource.id; - flagUpdateData.rollSourceType = rollSource instanceof BladesActor ? "Actor" : "Item"; - if (U.isInt(config.rollTrait)) { - flagUpdateData.rollTrait = config.rollTrait; - } - else if (!config.rollTrait) { - eLog.error("rollCollab", "[RenderRollCollab()] No RollTrait in Config", config); - return; - } - else { - switch (flagUpdateData.rollType) { - case RollType.Action: { - if (!(U.lCase(config.rollTrait) in { ...Action, ...Factor })) { - eLog.error("rollCollab", `[RenderRollCollab()] Bad RollTrait for Action Roll: ${config.rollTrait}`, config); - return; - } - flagUpdateData.rollTrait = U.lCase(config.rollTrait); - break; - } - case RollType.Downtime: { - if (!(U.lCase(config.rollTrait) in { ...Action, ...Factor })) { - eLog.error("rollCollab", `[RenderRollCollab()] Bad RollTrait for Downtime Roll: ${config.rollTrait}`, config); - return; - } - flagUpdateData.rollTrait = U.lCase(config.rollTrait); - break; - } - case RollType.Fortune: { - if (!(U.lCase(config.rollTrait) in { ...Action, ...Attribute, ...Factor })) { - eLog.error("rollCollab", `[RenderRollCollab()] Bad RollTrait for Fortune Roll: ${config.rollTrait}`, config); - return; - } - flagUpdateData.rollTrait = U.lCase(config.rollTrait); - break; - } - case RollType.Resistance: { - if (!(U.lCase(config.rollTrait) in Attribute)) { - eLog.error("rollCollab", `[RenderRollCollab()] Bad RollTrait for Resistance Roll: ${config.rollTrait}`, config); - return; - } - break; - } - } - flagUpdateData.rollTrait = U.lCase(config.rollTrait); - } - - flagUpdateData.rollMods = { - [RollModCategory.roll]: { - positive: { - "Push": { - status: RollModStatus.ToggledOff, - value: 1, - tooltip: "

Take 2 Stress to add 1 die to your pool.

(You cannot also accept a Devil's Bargain to increase your dice pool: It's one or the other.)

" - }, - "Bargain": { - status: RollModStatus.Hidden, - value: 1, - tooltip: "

Accept a Devil's Bargain from the GM to add 1 die to your pool.

(You cannot also Push to increase your dice pool: It's one or the other. You can, however, Push to increase your Effect.)

" - }, - "Assist": { - status: RollModStatus.ForcedOn, - value: 1, - sideString: "Ollie", - tooltip: "

Ollie is Assisting your efforts, adding 1 die to your pool. (It costs them 1 Stress to do so.)

" - }, - "Mastermind": { - status: RollModStatus.ToggledOff, - value: 1, - isAbility: true, - tooltip: "

You may expend your special armor to protect a teammate, or to push yourself when you gather information or work on a long-term project.

" - }, - "Trust In Me": { - status: RollModStatus.ToggledOn, - value: 1, - isAbility: true, - tooltip: "

You may expend your special armor to protect a teammate, or to push yourself when you gather information or work on a long-term project.

" - }, - "Forged in the Fire": { - status: RollModStatus.ToggledOff, - value: 1, - isAbility: true, - tooltip: "

You may expend your special armor to protect a teammate, or to push yourself when you gather information or work on a long-term project.

" - }, - "A Little Something on the Side": { - status: RollModStatus.ToggledOff, - value: 1, - isAbility: true, - tooltip: "

You may expend your special armor to protect a teammate, or to push yourself when you gather information or work on a long-term project.

" - }, - "Ghost Hunter (Arrow-Swift)": { - status: RollModStatus.ToggledOff, - value: 1, - isAbility: true, - tooltip: "

You may expend your special armor to protect a teammate, or to push yourself when you gather information or work on a long-term project.

" - } - }, - negative: { - [Harm.Impaired]: { - status: RollModStatus.ForcedOn, - value: 1, - tooltip: `

${Harm.Impaired}: Your injuries reduce your dice pool by one.

` - } - } - }, - [RollModCategory.position]: { - positive: { - Setup: { - status: RollModStatus.ForcedOn, - value: 1, - sideString: "Jax", - tooltip: "

Jax has set you up for success with a preceding action, increasing your Position by one level.

" - } - }, - negative: {} - }, - [RollModCategory.effect]: { - positive: { - "Push": { - status: RollModStatus.ToggledOn, - value: 1, - tooltip: "

Take 2 Stress to increase your Effect by one level.

(You can both Push for Effect and Push for +1d, for a total cost of 4 Stress.)

" - }, - "Setup": { - status: RollModStatus.ForcedOn, - value: 1, - sideString: "High-Flyer", - tooltip: "

High-Flyer has set you up for success with a preceding action, increasing your Effect by one level.

" - }, - "Potency": { - status: RollModStatus.Hidden, - value: 1, - tooltip: "

Circumstances in your favor make this action especially Potent, increasing your Effect by one level.

" - }, - "Cloak & Dagger": { - status: RollModStatus.ToggledOff, - value: 1, - isAbility: true, - tooltip: "

You may expend your special armor to protect a teammate, or to push yourself when you gather information or work on a long-term project.

" - } - }, - negative: { - [Harm.Impaired]: { - status: RollModStatus.ForcedOn, - value: 1, - tooltip: `

${Harm.Impaired}: Your injuries reduce your Effect by one level.

` - }, - Opposition: { - status: RollModStatus.ForcedOn, - value: 1, - tooltip: "

The following Factors combine to reduce your Effect by one level:

  • Inferior Quality
  • Detrimental Scale
" - } - } - }, - [RollModCategory.result]: { - positive: { - Mastermind: { - status: RollModStatus.ToggledOff, - value: 1, - isAbility: true, - tooltip: "

You may expend your special armor to protect a teammate, or to push yourself when you gather information or work on a long-term project.

" - } - }, negative: {} - }, - [RollModCategory.after]: { - positive: { - Mesmerism: { - status: RollModStatus.ToggledOff, - value: 1, - isAbility: true, - tooltip: "

You may expend your special armor to protect a teammate, or to push yourself when you gather information or work on a long-term project.

" - } - }, - negative: {} - } - }; - await user.setFlag(C.SYSTEM_ID, "rollCollab", flagUpdateData); - BladesRollCollabSheet.RenderRollCollab({ userID: user._id, rollID: flagUpdateData.rollID }); - socketlib.system.executeForAllGMs("renderRollCollab", { userID: user._id, rollID: flagUpdateData.rollID }); - } - rollID; - constructor(user, rollID) { - super(user); - this.rollID = rollID; - } - get rData() { - if (!this.document.getFlag(C.SYSTEM_ID, "rollCollab")) { - eLog.error("rollCollab", "[get flags()] No RollCollab Flags Found on User", { user: this.document, flags: this.document.flags }); - return null; - } - return this.document.flags["eunos-blades"].rollCollab; - } - get rollSource() { - if (!this.rData) { - return undefined; - } - return this.rData.rollSourceType === "Actor" - ? game.actors.get(this.rData.rollSourceID) - : game.items.get(this.rData.rollSourceID); - } - getData() { - const context = super.getData(); - const { rData } = this; - if (!rData) { - return context; - } - const sheetData = { - cssClass: "roll-collab", - editable: this.options.editable, - isGM: game.eunoblades.Tracker.system.is_spoofing_player ? false : game.user.isGM, - ...rData - }; - if (!this.rollSource) { - eLog.error("rollCollab", `[getData()] No '${rData.rollSourceType}' Found with ID '${rData.rollSourceID}'`, { user: this.document, rData: rData }); - return null; - } - sheetData.system = this.rollSource.system; - sheetData.rollSource = this.rollSource; - if (BladesActor.IsType(this.rollSource, BladesActorType.pc) && isAction(rData.rollTrait)) { - const { rollSource } = this; - sheetData.rollTraitData = { - name: rData.rollTrait, - value: rollSource.actions[rData.rollTrait], - max: rollSource.actions[rData.rollTrait] - }; - sheetData.rollTraitOptions = Object.values(Action) - .map((action) => ({ - name: U.uCase(action), - value: action - })); - } - else if (BladesActor.IsType(this.rollSource, BladesActorType.pc) && isAttribute(rData.rollTrait)) { - const { rollSource } = this; - sheetData.rollTraitData = { - name: rData.rollTrait, - value: rollSource.attributes[rData.rollTrait], - max: rollSource.attributes[rData.rollTrait] - }; - sheetData.rollTraitOptions = Object.values(Attribute) - .map((attribute) => ({ - name: U.uCase(attribute), - value: attribute - })); - } - else if (rData.rollTrait === "tier") { - const { rollSource } = this; - sheetData.rollTraitData = { - name: "Tier", - value: rollSource.getTierTotal(), - max: rollSource.getTierTotal() - }; - sheetData.rollTraitOptions = false; - } - else if (U.isInt(rData.rollTrait)) { - sheetData.rollTraitData = { - name: `+${rData.rollTrait}`, - value: rData.rollTrait, - max: rData.rollTrait - }; - sheetData.rollTraitOptions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - .map((num) => ({ - name: `+${num}`, - value: num - })); - } - - sheetData.rollFactorData = [ - { name: Factor.quality, value: U.romanizeNum(2), cssClasses: "factor-gold factor-main" }, - { name: Factor.scale, value: `${2}`, cssClasses: "factor-gold" } - ]; - sheetData.rollOpposition = undefined; - sheetData.stressData = { cost: 4, tooltip: "
  • 2 Stress from Pushing for +1d
  • 2 Stress from Pushing for Effect
" }; - const getModsDelta = (cat) => { - const activePosMods = Object.values(sheetData.rollMods?.[cat]?.positive ?? {}) - .filter((mod) => [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(mod.status)); - const posModVals = activePosMods.map((mod) => mod.value); - const posModSum = U.sum(posModVals); - const activeNegMods = Object.values(sheetData.rollMods?.[cat]?.negative ?? {}) - .filter((mod) => [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(mod.status)); - const negModVals = activeNegMods.map((mod) => mod.value); - const negModSum = U.sum(negModVals); - eLog.checkLog3("rollMods", `getModsDelta(${cat}`, { activePosMods, posModVals, posModSum, activeNegMods, negModVals, negModSum, returnVal: U.sum(Object.values(sheetData.rollMods?.[cat]?.positive ?? {}) - .filter((mod) => [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(mod.status)) - .map((mod) => mod.value)) - - U.sum(Object.values(sheetData.rollMods?.[cat]?.negative ?? {}) - .filter((mod) => [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(mod.status)) - .map((mod) => mod.value)) }); - return U.sum(Object.values(sheetData.rollMods?.[cat]?.positive ?? {}) - .filter((mod) => [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(mod.status)) - .map((mod) => mod.value)) - - U.sum(Object.values(sheetData.rollMods?.[cat]?.negative ?? {}) - .filter((mod) => [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(mod.status)) - .map((mod) => mod.value)); - }; - sheetData.diceTotal = Math.max(0, (sheetData.rollTraitData?.value ?? 0) - + getModsDelta(RollModCategory.roll) - + (rData.GMBoosts.Dice ?? 0)); - let finalPosIndex = Object.values(Position).indexOf(sheetData.rollPositionInitial ?? Position.risky) - + getModsDelta(RollModCategory.position); - let finalEffectIndex = Object.values(Effect).indexOf(sheetData.rollEffectInitial ?? Effect.standard) - + getModsDelta(RollModCategory.effect); - const isPosEffectTradeValid = rData.rollPosEffectTrade === "position" - ? (finalPosIndex < 2 && finalEffectIndex > 0) - : (rData.rollPosEffectTrade === "effect" - ? (finalPosIndex > 0 && finalEffectIndex < 4) - : true); - if (isPosEffectTradeValid) { - if (rData.rollPosEffectTrade === "position") { - finalPosIndex++; - finalEffectIndex--; - } - if (rData.rollPosEffectTrade === "effect") { - finalPosIndex--; - finalEffectIndex++; - } - } - sheetData.rollPositionFinal = Object.values(Position)[U.clampNum(finalPosIndex, [0, 2])]; - sheetData.rollEffectFinal = Object.values(Effect)[U.clampNum(finalEffectIndex, [0, 4])]; - sheetData.isAffectingResult = getModsDelta(RollModCategory.result) !== 0 - || (rData.GMBoosts.Result ?? 0) !== 0; - if (sheetData.isAffectingResult) { - sheetData.rollResultFinal = getModsDelta(RollModCategory.result) - + (rData.GMBoosts.Result ?? 0); - } - sheetData.hasInactiveAbilities = { - [RollModCategory.roll]: Object.values(rData.rollMods.roll?.positive ?? {}) - .filter((mod) => mod.isAbility && mod.status === RollModStatus.ToggledOff) - .length > 0, - [RollModCategory.position]: Object.values(rData.rollMods.position?.positive ?? {}) - .filter((mod) => mod.isAbility && mod.status === RollModStatus.ToggledOff) - .length > 0, - [RollModCategory.effect]: Object.values(rData.rollMods.effect?.positive ?? {}) - .filter((mod) => mod.isAbility && mod.status === RollModStatus.ToggledOff) - .length > 0, - [RollModCategory.result]: Object.values(rData.rollMods.result?.positive ?? {}) - .filter((mod) => mod.isAbility && mod.status === RollModStatus.ToggledOff) - .length > 0, - [RollModCategory.after]: Object.values(rData.rollMods.after?.positive ?? {}) - .filter((mod) => mod.isAbility && mod.status === RollModStatus.ToggledOff) - .length > 0 - }; - const { success, partial, fail } = C.DiceOdds[sheetData.diceTotal ?? 0]; - sheetData.oddsGradient = [ - "linear-gradient(to right", - `var(--blades-black-dark) ${fail}%`, - `var(--blades-grey) ${fail + partial}%`, - `var(--blades-white-bright) ${fail + partial + success}%`, - "var(--blades-gold-bright))" - ].join(", "); - - eLog.checkLog3("getData", "RollCollab.getData()", { ...context, ...sheetData }); - return { - ...context, - ...sheetData - }; - } - activateListeners(html) { - super.activateListeners(html); - ApplyTooltipListeners(html); - const trade$ = html.find("[data-action='trade']"); - eLog.checkLog3("rollCollab", "Trade$", { trade: trade$ }); - html.find("[data-action='trade']").on({ - click: (event) => { - const curVal = `${$(event.currentTarget).data("value")}`; - eLog.checkLog3("rollCollab", "Click Event", { event, curVal }); - if (curVal === "false") { - this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", "position"); - } - else { - this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", false); - } - }, - contextmenu: (event) => { - const curVal = `${$(event.currentTarget).data("value")}`; - eLog.checkLog3("rollCollab", "Context Event", { event, curVal }); - if (curVal === "false") { - this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", "effect"); - } - else { - this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", false); - } - } - }); - } - async _onSubmit(event, { updateData } = {}) { - return super._onSubmit(event, { updateData, preventClose: true }) - .then((returnVal) => { this.render(); return returnVal; }); - } - async close(options = {}) { - eLog.checkLog3("rollCollab", "RollCollab.close()", { options }); - if (options.rollID) { - return super.close({}); - } - this.document.setFlag(C.SYSTEM_ID, "rollCollab", null); - socketlib.system.executeForEveryone("closeRollCollab", this.rollID); - return undefined; - } - render(force, options) { - if (!this.document.getFlag(C.SYSTEM_ID, "rollCollab")) { - return this; - } - return super.render(force, options); - } -} -export default BladesRollCollabSheet; \ No newline at end of file diff --git a/module/sheets/blades-sheet.js b/module/sheets/blades-sheet.js deleted file mode 100644 index 23b34d6e..00000000 --- a/module/sheets/blades-sheet.js +++ /dev/null @@ -1,377 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - - -import U from "../core/utilities.js"; -import G from "../core/gsap.js"; -import { Tag, District, Playbook, Vice, BladesActorType } from "../core/constants.js"; -import Tagify from "../../lib/tagify/tagify.esm.js"; -import BladesSelectorDialog from "../blades-dialog.js"; -import BladesActiveEffect from "../blades-active-effect.js"; -class BladesSheet extends ActorSheet { - getData() { - const context = super.getData(); - const sheetData = { - editable: this.options.editable, - isGM: game.user.isGM, - actor: this.actor, - system: this.actor.system, - activeEffects: Array.from(this.actor.effects), - hasFullVision: game.user.isGM || this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OBSERVER), - hasLimitedVision: game.user.isGM || this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.LIMITED), - hasControl: game.user.isGM || this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER), - playbookData: { - tooltip: (new Handlebars.SafeString([ - "
    ", - ...this.actor.playbook?.system.experience_clues?.map((line) => `
  • ${line}
  • `) ?? [], - "
" - ].join(""))).toString(), - dotline: { - data: this.actor.system.experience?.playbook, - target: "system.experience.playbook.value", - svgKey: "teeth.tall", - svgFull: "full|frame", - svgEmpty: "full|half|frame" - } - }, - coinsData: { - dotline: { - data: this.actor.system.coins, - target: "system.coins.value", - iconEmpty: "coin-full.svg", - iconFull: "coin-full.svg" - } - } - }; - return { - ...context, - ...sheetData - }; - } - activateListeners(html) { - super.activateListeners(html); - if (game.user.isGM) { - html.attr("style", "--secret-text-display: initial"); - } - else { - html.find('.editor:not(.tinymce) [data-is-secret="true"]').remove(); - } - - html.find(".tooltip").siblings(".comp-body") - .each(function (i, elem) { - $(elem).data("hoverTimeline", G.effects.hoverTooltip(elem)); - }) - .on({ - mouseenter: function () { - $(this).parent().css("z-index", 1); - $(this).data("hoverTimeline").play(); - }, - mouseleave: function () { - $(this).data("hoverTimeline").reverse().then(() => { - $(this).parent().removeAttr("style"); - }); - } - }); - if (!this.options.editable) { - return; - } - html.find(".dotline").each((_, elem) => { - if ($(elem).hasClass("locked")) { - return; - } - let targetDoc = this.actor; - let targetField = $(elem).data("target"); - const comp$ = $(elem).closest("comp"); - if (targetField.startsWith("item")) { - targetField = targetField.replace(/^item\./, ""); - const itemId = $(elem).closest("[data-comp-id]").data("compId"); - if (!itemId) { - return; - } - const item = this.actor.items.get(itemId); - if (!item) { - return; - } - targetDoc = item; - } - const curValue = U.pInt($(elem).data("value")); - $(elem) - .find(".dot") - .each((j, dot) => { - $(dot).on("click", (event) => { - event.preventDefault(); - const thisValue = U.pInt($(dot).data("value")); - if (thisValue !== curValue) { - if (comp$.hasClass("comp-coins") - || comp$.hasClass("comp-stash")) { - G.effects - .fillCoins($(dot).prevAll(".dot")) - .then(() => targetDoc.update({ [targetField]: thisValue })); - } - else { - targetDoc.update({ [targetField]: thisValue }); - } - } - }); - $(dot).on("contextmenu", (event) => { - event.preventDefault(); - const thisValue = U.pInt($(dot).data("value")) - 1; - if (thisValue !== curValue) { - targetDoc.update({ [targetField]: thisValue }); - } - }); - }); - }); - html - .find(".clock-container") - .on("click", this._onClockLeftClick.bind(this)); - html - .find(".clock-container") - .on("contextmenu", this._onClockRightClick.bind(this)); - html - .find("[data-comp-id]") - .find(".comp-title") - .on("click", this._onItemOpenClick.bind(this)); - html - .find(".comp-control.comp-add") - .on("click", this._onItemAddClick.bind(this)); - html - .find(".comp-control.comp-delete") - .on("click", this._onItemRemoveClick.bind(this)); - html - .find(".comp-control.comp-delete-full") - .on("click", this._onItemFullRemoveClick.bind(this)); - html - .find(".comp-control.comp-toggle") - .on("click", this._onItemToggleClick.bind(this)); - html - .find("[data-roll-attribute]") - .on("click", this._onRollAttributeDieClick.bind(this)); - html - .find(".effect-control") - .on("click", this._onActiveEffectControlClick.bind(this)); - const tagElem = html.find(".tag-entry")[0]; - if (tagElem) { - const tagify = new Tagify(tagElem, { - enforceWhitelist: false, - editTags: true, - whitelist: [ - ...Object.values(Tag.System).map((tag) => ({ - "value": (new Handlebars.SafeString(tag)).toString(), - "data-group": "System Tags" - })), - ...Object.values(Tag.Item).map((tag) => ({ - "value": (new Handlebars.SafeString(tag)).toString(), - "data-group": "Item Tags" - })), - ...Object.values(Tag.PC).map((tag) => ({ - "value": (new Handlebars.SafeString(tag)).toString(), - "data-group": "Actor Tags" - })), - ...Object.values(Tag.NPC).map((tag) => ({ - "value": (new Handlebars.SafeString(tag)).toString(), - "data-group": "Actor Tags" - })), - ...Object.values(District).map((tag) => ({ - "value": (new Handlebars.SafeString(tag)).toString(), - "data-group": "Districts" - })), - ...Object.values(Vice).map((tag) => ({ - "value": (new Handlebars.SafeString(tag)).toString(), - "data-group": "Vices" - })), - ...Object.values(Playbook).map((tag) => ({ - "value": (new Handlebars.SafeString(tag)).toString(), - "data-group": "Playbooks" - })) - ], - dropdown: { - enabled: 0, - maxItems: 10000, - placeAbove: false, - appendTarget: html[0] - } - }); - tagify.dropdown.createListHTML = (optionsArr) => { - const map = {}; - return structuredClone(optionsArr) - .map((suggestion, idx) => { - const value = tagify.dropdown.getMappedValue.call(tagify, suggestion); - let tagHTMLString = ""; - if (!map[suggestion["data-group"]]) { - map[suggestion["data-group"]] = true; - if (Object.keys(map).length) { - tagHTMLString += "
"; - } - tagHTMLString += ` -
-

${suggestion["data-group"]}

- `; - } - suggestion.value - = value && typeof value === "string" ? U.escapeHTML(value) : value; - tagHTMLString += tagify.settings.templates.dropdownItem.apply(tagify, [suggestion, idx]); - return tagHTMLString; - }) - .join(""); - }; - tagify.addTags(this.actor.tags.map((tag) => { - if (Object.values(Tag.System).includes(tag)) { - return { "value": (new Handlebars.SafeString(tag)).toString(), "data-group": "System Tags" }; - } - if (Object.values(Tag.Item).includes(tag)) { - return { "value": (new Handlebars.SafeString(tag)).toString(), "data-group": "Item Tags" }; - } - if (Object.values(Tag.PC).includes(tag) || Object.values(Tag.NPC).includes(tag)) { - return { "value": (new Handlebars.SafeString(tag)).toString(), "data-group": "Actor Tags" }; - } - if (Object.values(District).includes(tag)) { - return { "value": (new Handlebars.SafeString(tag)).toString(), "data-group": "Districts" }; - } - if (Object.values(Playbook).includes(tag)) { - return { "value": (new Handlebars.SafeString(tag)).toString(), "data-group": "Playbooks" }; - } - if (Object.values(Vice).includes(tag)) { - return { "value": (new Handlebars.SafeString(tag)).toString(), "data-group": "Vices" }; - } - return { "value": (new Handlebars.SafeString(tag)).toString(), "data-group": "Other" }; - }), false, false); - tagElem.addEventListener("change", this._onTagifyChange.bind(this)); - } - if (this.options.submitOnChange) { - html.on("change", "textarea", this._onChangeInput.bind(this)); - } - } - async _onSubmit(event, params = {}) { - if (!game.user.isGM && !this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER)) { - eLog.checkLog("actorSheetTrigger", "User does not have permission to edit this actor", { user: game.user, actor: this.actor }); - return {}; - } - eLog.checkLog("actorSheetTrigger", "Submitting Form Data", { parentActor: this.actor.parentActor, systemTags: this.actor.system.tags, sourceTags: this.actor._source.system.tags, params }); - return super._onSubmit(event, params); - } - async close(options) { - if (this.actor.type === BladesActorType.pc) { - return super.close(options).then(() => this.actor.clearSubActors()); - } - else if (this.actor.type === BladesActorType.npc && this.actor.parentActor) { - return super.close(options).then(() => this.actor.clearParentActor(false)); - } - return super.close(options); - } - - async _onClockLeftClick(event) { - event.preventDefault(); - const clock$ = $(event.currentTarget).find(".clock[data-target]"); - if (!clock$[0]) { - return; - } - const target = clock$.data("target"); - const curValue = U.pInt(clock$.data("value")); - const maxValue = U.pInt(clock$.data("size")); - G.effects.pulseClockWedges(clock$.find("wedges")).then(() => this.actor.update({ - [target]: G.utils.wrap(0, maxValue + 1, curValue + 1) - })); - } - async _onClockRightClick(event) { - event.preventDefault(); - const clock$ = $(event.currentTarget).find(".clock[data-target]"); - if (!clock$[0]) { - return; - } - const target = clock$.data("target"); - const curValue = U.pInt(clock$.data("value")); - G.effects.reversePulseClockWedges(clock$.find("wedges")).then(() => this.actor.update({ - [target]: Math.max(0, curValue - 1) - })); - } - async _onTagifyChange(event) { - const tagString = event.target.value; - if (tagString) { - const tags = JSON.parse(tagString).map(({ value }) => value); - this.actor.update({ "system.tags": tags }); - } - else { - this.actor.update({ "system.tags": [] }); - } - } - - _getCompData(event) { - const elem$ = $(event.currentTarget).closest(".comp"); - const compData = { - elem$, - docID: elem$.data("compId"), - docCat: elem$.data("compCat"), - docType: elem$.data("compType"), - docTags: (elem$.data("compTags") ?? "").split(/\s+/g) - }; - if (compData.docID && compData.docType) { - compData.doc = { - Actor: this.actor.getSubActor(compData.docID), - Item: this.actor.getSubItem(compData.docID) - }[compData.docType]; - } - if (compData.docCat && compData.docType) { - compData.dialogDocs = { - Actor: this.actor.getDialogActors(compData.docCat), - Item: this.actor.getDialogItems(compData.docCat) - }[compData.docType]; - } - eLog.checkLog2("dialog", "Component Data", { ...compData }); - return compData; - } - async _onItemOpenClick(event) { - event.preventDefault(); - const { doc } = this._getCompData(event); - if (!doc) { - return; - } - doc.sheet?.render(true); - } - async _onItemAddClick(event) { - event.preventDefault(); - const { docCat, docType, dialogDocs, docTags } = this._getCompData(event); - eLog.checkLog("_onItemAddClick", { docCat, dialogDocs }); - if (!dialogDocs || !docCat || !docType) { - return; - } - await BladesSelectorDialog.Display(this.actor, U.tCase(`Add ${docCat.replace(/_/g, " ")}`), docType, dialogDocs, docTags); - } - async _onItemRemoveClick(event) { - event.preventDefault(); - const { elem$, doc } = this._getCompData(event); - if (!doc) { - return; - } - G.effects.blurRemove(elem$).then(() => doc.addTag(Tag.System.Archived)); - } - async _onItemFullRemoveClick(event) { - event.preventDefault(); - const { elem$, doc } = this._getCompData(event); - if (!doc) { - return; - } - G.effects.blurRemove(elem$).then(() => doc.delete()); - } - async _onItemToggleClick(event) { - event.preventDefault(); - const target = $(event.currentTarget).data("target"); - this.actor.update({ - [target]: !getProperty(this.actor, target) - }); - } - - async _onRollAttributeDieClick(event) { - const attribute_name = $(event.currentTarget).data("rollAttribute"); - this.actor.rollAttributePopup(attribute_name); - } - - async _onActiveEffectControlClick(event) { - BladesActiveEffect.onManageActiveEffect(event, this.actor); - } -} -export default BladesSheet; \ No newline at end of file diff --git a/module/sheets/blades-tracker-sheet.js b/module/sheets/blades-tracker-sheet.js deleted file mode 100644 index 6447bae2..00000000 --- a/module/sheets/blades-tracker-sheet.js +++ /dev/null @@ -1,94 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesItemSheet from "./blades-item-sheet.js"; -import BladesItem from "../blades-item.js"; -export var BladesPhase; -(function (BladesPhase) { - BladesPhase["CharGen"] = "CharGen"; - BladesPhase["Freeplay"] = "Freeplay"; - BladesPhase["Score"] = "Score"; - BladesPhase["Downtime"] = "Downtime"; -})(BladesPhase || (BladesPhase = {})); -export var BladesTipContext; -(function (BladesTipContext) { - BladesTipContext["DiceRoll"] = "DiceRoll"; - BladesTipContext["Combat"] = "Combat"; - BladesTipContext["General"] = "General"; -})(BladesTipContext || (BladesTipContext = {})); -class BladesTipGenerator { - static get Tips() { - return { - [BladesTipContext.DiceRoll]: [], - [BladesTipContext.Combat]: [ - "Every combat encounter should advance the main plot, or else it's filler.", - "Inject dialogue into combat encounters, especially from important adversaries.", - "Combat encounters should be a challenge, but not a slog. Don't be afraid to end them early.", - "Infiltrate/Rescue/Destroy: Use these as additional/secondary goals in combat encounters.", - "Tell the next player in the initiative order that they're on deck.", - "Don't trigger combats automatically: Use alternate objectives to incite the players to fight, giving them agency.", - "Add another layer by drawing focus to collateral effects of the combat: a fire, a hostage, a collapsing building, innocents in danger" - ], - [BladesTipContext.General]: [ - "Rolling the dice always means SOMETHING happens.", - "Jump straight to the action; don't waste time on establishing scenes or filler.", - "Invoke elements of characters' backstories or beliefs to make any scene more personal." - ] - }; - } - tipContext; - constructor(tipContext) { - this.tipContext = tipContext; - } -} -class BladesTrackerSheet extends BladesItemSheet { - static async Get() { - return game.eunoblades.Tracker || (await BladesItem.create({ - name: "GM Tracker", - type: "gm_tracker", - img: "systems/eunos-blades/assets/icons/gm-tracker.svg" - })); - } - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "item", "gm-tracker"], - template: "systems/eunos-blades/templates/items/gm_tracker-sheet.hbs", - width: 700, - height: 970 - }); - } - static async Initialize() { - game.eunoblades ??= {}; - Items.registerSheet("blades", BladesTrackerSheet, { types: ["gm_tracker"], makeDefault: true }); - Hooks.once("ready", async () => { - let tracker = game.items.find((item) => item.type === "gm_tracker"); - if (!(tracker instanceof BladesItem)) { - tracker = (await BladesItem.create({ - name: "GM Tracker", - type: "gm_tracker", - img: "systems/eunos-blades/assets/icons/gm-tracker.svg" - })); - } - game.eunoblades.Tracker = tracker; - }); - return loadTemplates([ - "systems/eunos-blades/templates/items/gm_tracker-sheet.hbs" - ]); - } - get phase() { return this.item.system.game_phase; } - set phase(phase) { this.item.update({ system: { game_phase: phase } }); } - get actionMax() { return this.phase === BladesPhase.CharGen ? 2 : undefined; } - async getData() { - const context = await super.getData(); - context.system.phases = Object.values(BladesPhase); - return context; - } - async activateListeners(html) { - super.activateListeners(html); - } -} -export default BladesTrackerSheet; \ No newline at end of file diff --git a/module/sheets/item/BladesClockKeeperSheet.js b/module/sheets/item/BladesClockKeeperSheet.js index 79ad3445..63d05dbb 100644 --- a/module/sheets/item/BladesClockKeeperSheet.js +++ b/module/sheets/item/BladesClockKeeperSheet.js @@ -1,14 +1,11 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - +/*~ @@DOUBLE-BLANK@@ ~*/ import BladesItemSheet from "./BladesItemSheet.js"; import BladesClockKeeper from "../../documents/items/BladesClockKeeper.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesClockKeeperSheet extends BladesItemSheet { + /*~ @@DOUBLE-BLANK@@ ~*/ static Get() { return game.eunoblades.ClockKeeper; } + /*~ @@DOUBLE-BLANK@@ ~*/ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["eunos-blades", "sheet", "item", "clock-keeper"], @@ -17,6 +14,7 @@ class BladesClockKeeperSheet extends BladesItemSheet { height: 970 }); } + /*~ @@DOUBLE-BLANK@@ ~*/ static async Initialize() { game.eunoblades ??= {}; Items.registerSheet("blades", BladesClockKeeperSheet, { types: ["clock_keeper"], makeDefault: true }); @@ -38,6 +36,7 @@ class BladesClockKeeperSheet extends BladesItemSheet { "systems/eunos-blades/templates/parts/clock-sheet-row.hbs" ]); } + /*~ @@DOUBLE-BLANK@@ ~*/ static InitSockets() { if (game.eunoblades.ClockKeeper) { socketlib.system.register("renderOverlay", game.eunoblades.ClockKeeper.renderOverlay); @@ -45,18 +44,30 @@ class BladesClockKeeperSheet extends BladesItemSheet { } return false; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Override async _updateObject(event: unknown, formData: any) { + // const updateData = await`this.object.update(formData); + // socketlib.system.executeForEveryone("renderOverlay"); + // // this.item.renderOverlay(); + // return updateData; + // } + /*~ @@DOUBLE-BLANK@@ ~*/ getData() { const context = super.getData(); + /*~ @@DOUBLE-BLANK@@ ~*/ const sheetData = { clock_keys: Object.fromEntries((Object.entries(context.system.clock_keys ?? {}) .filter(([keyID, keyData]) => Boolean(keyData && keyData.scene === context.system.targetScene)))) }; + /*~ @@DOUBLE-BLANK@@ ~*/ return { ...context, ...sheetData }; } + /*~ @@DOUBLE-BLANK@@ ~*/ addKey(event) { event.preventDefault(); this.item.addClockKey(); } + /*~ @@DOUBLE-BLANK@@ ~*/ deleteKey(event) { event.preventDefault(); const keyID = event.currentTarget.dataset.id; @@ -64,6 +75,7 @@ class BladesClockKeeperSheet extends BladesItemSheet { this.item.deleteClockKey(keyID); } } + /*~ @@DOUBLE-BLANK@@ ~*/ setKeySize(event) { event.preventDefault(); const keyID = event.target.dataset.id; @@ -71,11 +83,18 @@ class BladesClockKeeperSheet extends BladesItemSheet { this.item.setKeySize(keyID, parseInt(event.target.value, 10)); } } + /*~ @@DOUBLE-BLANK@@ ~*/ async activateListeners(html) { super.activateListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ + // @ts-expect-error Fuck. html.find("[data-action=\"add-key\"").on("click", this.addKey.bind(this)); + // @ts-expect-error Fuck. html.find("[data-action=\"delete-key\"").on("click", this.deleteKey.bind(this)); + // @ts-expect-error Fuck. html.find(".key-clock-counter").on("change", this.setKeySize.bind(this)); } } -export default BladesClockKeeperSheet; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesClockKeeperSheet; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/sheets/item/BladesGMTrackerSheet.js b/module/sheets/item/BladesGMTrackerSheet.js index 9591ffab..0092c4d6 100644 --- a/module/sheets/item/BladesGMTrackerSheet.js +++ b/module/sheets/item/BladesGMTrackerSheet.js @@ -1,30 +1,30 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - +/*~ @@DOUBLE-BLANK@@ ~*/ import { BladesActorType, BladesItemType, BladesPhase } from "../../core/constants.js"; import BladesItemSheet from "./BladesItemSheet.js"; import BladesItem from "../../BladesItem.js"; import BladesGMTracker from "../../documents/items/BladesGMTracker.js"; import BladesActor from "../../BladesActor.js"; import BladesPC from "../../documents/actors/BladesPC.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ +// eslint-disable-next-line no-shadow export var BladesTipContext; (function (BladesTipContext) { BladesTipContext["DiceRoll"] = "DiceRoll"; BladesTipContext["Combat"] = "Combat"; BladesTipContext["General"] = "General"; })(BladesTipContext || (BladesTipContext = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesTipGenerator { + /*~ @@DOUBLE-BLANK@@ ~*/ static Test(pcActor) { if (BladesActor.IsType(pcActor, BladesActorType.pc)) { return pcActor; } return undefined; } + /*~ @@DOUBLE-BLANK@@ ~*/ testActor = new BladesPC({ name: "blah", type: "pc" }); + /*~ @@DOUBLE-BLANK@@ ~*/ static get Tips() { return { [BladesTipContext.DiceRoll]: [], @@ -41,16 +41,22 @@ class BladesTipGenerator { "Rolling the dice always means SOMETHING happens.", "Jump straight to the action; don't waste time on establishing scenes or filler.", "Invoke elements of characters' backstories or beliefs to make any scene more personal." + /*~ @@DOUBLE-BLANK@@ ~*/ ] }; } + /*~ @@DOUBLE-BLANK@@ ~*/ tipContext; + /*~ @@DOUBLE-BLANK@@ ~*/ constructor(tipContext) { this.tipContext = tipContext; } } +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesGMTrackerSheet extends BladesItemSheet { + /*~ @@DOUBLE-BLANK@@ ~*/ static Get() { return game.eunoblades.Tracker; } + /*~ @@DOUBLE-BLANK@@ ~*/ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["eunos-blades", "sheet", "item", "gm-tracker"], @@ -59,6 +65,7 @@ class BladesGMTrackerSheet extends BladesItemSheet { height: 970 }); } + /*~ @@DOUBLE-BLANK@@ ~*/ static async Initialize() { game.eunoblades ??= {}; Items.registerSheet("blades", BladesGMTrackerSheet, { types: ["gm_tracker"], makeDefault: true }); @@ -78,9 +85,12 @@ class BladesGMTrackerSheet extends BladesItemSheet { "systems/eunos-blades/templates/items/gm_tracker-sheet.hbs" ]); } + /*~ @@DOUBLE-BLANK@@ ~*/ async activateListeners(html) { super.activateListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onSubmit(event, params = {}) { const prevPhase = this.item.system.phase; const submitData = await super._onSubmit(event, params); @@ -89,9 +99,11 @@ class BladesGMTrackerSheet extends BladesItemSheet { if (prevPhase !== newPhase) { switch (prevPhase) { case BladesPhase.CharGen: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } case BladesPhase.Freeplay: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } case BladesPhase.Score: { @@ -101,21 +113,26 @@ class BladesGMTrackerSheet extends BladesItemSheet { break; } case BladesPhase.Downtime: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } default: break; } switch (newPhase) { case BladesPhase.CharGen: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } case BladesPhase.Freeplay: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } case BladesPhase.Score: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } case BladesPhase.Downtime: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } default: break; @@ -128,5 +145,7 @@ class BladesGMTrackerSheet extends BladesItemSheet { return submitData; } } +/*~ @@DOUBLE-BLANK@@ ~*/ export default BladesGMTrackerSheet; -export { BladesTipGenerator }; \ No newline at end of file +export { BladesTipGenerator }; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/sheets/item/BladesItemSheet.js b/module/sheets/item/BladesItemSheet.js index 64a0ca61..25db4276 100644 --- a/module/sheets/item/BladesItemSheet.js +++ b/module/sheets/item/BladesItemSheet.js @@ -1,17 +1,13 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import C, { BladesItemType, BladesPhase, Factor } from "../../core/constants.js"; import U from "../../core/utilities.js"; import G, { ApplyTooltipListeners } from "../../core/gsap.js"; import BladesItem from "../../BladesItem.js"; import BladesActiveEffect from "../../BladesActiveEffect.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ import Tags from "../../core/tags.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesItemSheet extends ItemSheet { + /*~ @@DOUBLE-BLANK@@ ~*/ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["eunos-blades", "sheet", "item"], @@ -20,10 +16,19 @@ class BladesItemSheet extends ItemSheet { tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }] }); } - - + /*~ @@DOUBLE-BLANK@@ ~*/ + /* -------------------------------------------- */ + /*~ @@DOUBLE-BLANK@@ ~*/ + // constructor(item: BladesItem, options: Partial = {}) { + // options.classes = [...options.classes ?? [], "eunos-blades", "sheet", "item", item.type]; + // super(item, options); + // } + /*~ @@DOUBLE-BLANK@@ ~*/ + // override async getData() { + /*~ @@DOUBLE-BLANK@@ ~*/ getData() { const context = super.getData(); + /*~ @@DOUBLE-BLANK@@ ~*/ const sheetData = { cssClass: this.item.type, editable: this.options.editable, @@ -34,8 +39,10 @@ class BladesItemSheet extends ItemSheet { tierTotal: this.item.getFactorTotal(Factor.tier) > 0 ? U.romanizeNum(this.item.getFactorTotal(Factor.tier)) : "0", activeEffects: Array.from(this.item.effects) }; + /*~ @@DOUBLE-BLANK@@ ~*/ return this._getTypedItemData[this.item.type]({ ...context, ...sheetData }); } + /*~ @@DOUBLE-BLANK@@ ~*/ _getTypedItemData = { [BladesItemType.ability]: (context) => { if (!BladesItem.IsType(this.item, BladesItemType.ability)) { @@ -89,12 +96,14 @@ class BladesItemSheet extends ItemSheet { } } }; + /*~ @@DOUBLE-BLANK@@ ~*/ sheetData.edgeData = Object.fromEntries(Object.values(context.system.edges ?? []) .filter((edge) => /[A-Za-z]/.test(edge)) .map((edge) => [edge.trim(), C.EdgeTooltips[edge]])); sheetData.flawData = Object.fromEntries(Object.values(context.system.flaws ?? []) .filter((flaw) => /[A-Za-z]/.test(flaw)) .map((flaw) => [flaw.trim(), C.FlawTooltips[flaw]])); + /*~ @@DOUBLE-BLANK@@ ~*/ return { ...context, ...sheetData @@ -195,6 +204,7 @@ class BladesItemSheet extends ItemSheet { } } }; + /*~ @@DOUBLE-BLANK@@ ~*/ return { ...context, ...sheetData @@ -295,6 +305,7 @@ class BladesItemSheet extends ItemSheet { return context; } }; + /*~ @@DOUBLE-BLANK@@ ~*/ get template() { const pathComps = [ "systems/eunos-blades/templates/items" @@ -307,22 +318,32 @@ class BladesItemSheet extends ItemSheet { } return pathComps.join("/"); } - + /*~ @@DOUBLE-BLANK@@ ~*/ + /* -------------------------------------------- */ + /*~ @@DOUBLE-BLANK@@ ~*/ activateListeners(html) { super.activateListeners(html); const self = this; + /*~ @@DOUBLE-BLANK@@ ~*/ Tags.InitListeners(html, this.item); ApplyTooltipListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Everything below here is only needed if the sheet is editable if (!this.options.editable) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Add dotline functionality html.find(".dotline").each((__, elem) => { if ($(elem).hasClass("locked")) { return; } + /*~ @@DOUBLE-BLANK@@ ~*/ const targetDoc = this.item; const targetField = $(elem).data("target"); + /*~ @@DOUBLE-BLANK@@ ~*/ const comp$ = $(elem).closest("comp"); + /*~ @@DOUBLE-BLANK@@ ~*/ const curValue = U.pInt($(elem).data("value")); $(elem) .find(".dot") @@ -351,6 +372,8 @@ class BladesItemSheet extends ItemSheet { }); }); }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Harm Bar Functionality for Cohorts if (BladesItem.IsType(this.item, BladesItemType.cohort_expert, BladesItemType.cohort_gang)) { html.find("[data-harm-click]").on({ click: (event) => { @@ -369,9 +392,12 @@ class BladesItemSheet extends ItemSheet { } }); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // This is a workaround until is being fixed in FoundryVTT. if (this.options.submitOnChange) { - html.on("change", "textarea", this._onChangeInput.bind(this)); + html.on("change", "textarea", this._onChangeInput.bind(this)); // Use delegated listener on the form } + /*~ @@DOUBLE-BLANK@@ ~*/ html.find(".effect-control").on("click", (ev) => { if (self.item.isOwned) { ui.notifications?.warn(game.i18n.localize("BITD.EffectWarning")); @@ -379,8 +405,10 @@ class BladesItemSheet extends ItemSheet { } BladesActiveEffect.onManageActiveEffect(ev, self.item); }); + /*~ @@DOUBLE-BLANK@@ ~*/ html.find("[data-action=\"toggle-turf-connection\"").on("click", this.toggleTurfConnection.bind(this)); } + /*~ @@DOUBLE-BLANK@@ ~*/ toggleTurfConnection(event) { const button$ = $(event.currentTarget); const connector$ = button$.parent(); @@ -401,4 +429,6 @@ class BladesItemSheet extends ItemSheet { this.item.update(updateData); } } -export default BladesItemSheet; \ No newline at end of file +/*~ @@DOUBLE-BLANK@@ ~*/ +export default BladesItemSheet; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/sheets/item/BladesScoreSheet.js b/module/sheets/item/BladesScoreSheet.js index df997456..9afa9cdf 100644 --- a/module/sheets/item/BladesScoreSheet.js +++ b/module/sheets/item/BladesScoreSheet.js @@ -1,23 +1,23 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - import U from "../../core/utilities.js"; import { BladesActorType, BladesPhase, Tag, Randomizers } from "../../core/constants.js"; import BladesItemSheet from "./BladesItemSheet.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ import { BladesActor } from "../../documents/BladesActorProxy.js"; import { BladesScore } from "../../documents/BladesItemProxy.js"; import BladesRoll, { BladesRollOpposition } from "../../BladesRoll.js"; +/*~ @@DOUBLE-BLANK@@ ~*/ +/* #region BladesTipGenerator */ +/*~ @@DOUBLE-BLANK@@ ~*/ +// eslint-disable-next-line no-shadow export var BladesTipContext; (function (BladesTipContext) { BladesTipContext["DiceRoll"] = "DiceRoll"; BladesTipContext["Combat"] = "Combat"; BladesTipContext["General"] = "General"; })(BladesTipContext || (BladesTipContext = {})); +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesTipGenerator { + /*~ @@DOUBLE-BLANK@@ ~*/ static get Tips() { return { [BladesTipContext.DiceRoll]: [], @@ -34,15 +34,21 @@ class BladesTipGenerator { "Rolling the dice always means SOMETHING happens.", "Jump straight to the action; don't waste time on establishing scenes or filler.", "Invoke elements of characters' backstories or beliefs to make any scene more personal." + /*~ @@DOUBLE-BLANK@@ ~*/ ] }; } + /*~ @@DOUBLE-BLANK@@ ~*/ tipContext; + /*~ @@DOUBLE-BLANK@@ ~*/ constructor(tipContext) { this.tipContext = tipContext; } } +/* #endregion */ +/*~ @@DOUBLE-BLANK@@ ~*/ class BladesScoreSheet extends BladesItemSheet { + /*~ @@DOUBLE-BLANK@@ ~*/ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["eunos-blades", "sheet", "item", "score-sheet"], @@ -52,7 +58,9 @@ class BladesScoreSheet extends BladesItemSheet { height: 970 }); } + /*~ @@DOUBLE-BLANK@@ ~*/ async generateRandomizerData(category) { + // Generate full set of random data. const randomData = { Bargains: Object.fromEntries(Object.entries(U.sample(Randomizers.GM.Bargains .filter((bData) => !Object.values(this.document.system.randomizers.Bargains) @@ -92,6 +100,8 @@ class BladesScoreSheet extends BladesItemSheet { return [k, v]; })) }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // If category specified, replace all other categories with stored data if (category) { Object.keys(randomData) .filter((cat) => cat !== category) @@ -100,12 +110,16 @@ class BladesScoreSheet extends BladesItemSheet { randomData[_cat] = this.document.system.randomizers[_cat]; }); } + /*~ @@DOUBLE-BLANK@@ ~*/ + // Combine locked data stored in system with randomly-generated data const finalRandomData = { Bargains: {}, Obstacles: {}, NPCs: {}, Scores: {} }; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Iterate through all randomizer categories. If system entry isLocked, use that, or use newly-generated data Object.keys(randomData).forEach((cat) => { const _cat = cat; Object.keys(randomData[_cat]).forEach((index) => { @@ -117,11 +131,17 @@ class BladesScoreSheet extends BladesItemSheet { } }); }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Overwrite stored data with newly generated & merged randomizer data await this.document.update({ "system.randomizers": finalRandomData }); } + /*~ @@DOUBLE-BLANK@@ ~*/ getData() { const context = super.getData(); + /*~ @@DOUBLE-BLANK@@ ~*/ const sheetData = {}; + /*~ @@DOUBLE-BLANK@@ ~*/ + // Get player characters, assign simplified actionData that I probably should have coded them with from the start sheetData.playerCharacters = BladesActor.GetTypeWithTags(BladesActorType.pc, Tag.PC.ActivePC) .map((pc) => { return Object.assign(pc, { @@ -140,6 +160,8 @@ class BladesScoreSheet extends BladesItemSheet { })) }); }); + /*~ @@DOUBLE-BLANK@@ ~*/ + // Prune system data for blank/empty opposition entries const validOppositions = {}; for (const [id, data] of Object.entries(context.system.oppositions)) { if (!data.rollOppName && !data.rollOppSubName) { @@ -148,11 +170,13 @@ class BladesScoreSheet extends BladesItemSheet { validOppositions[id] = data; } context.system.oppositions = validOppositions; + /*~ @@DOUBLE-BLANK@@ ~*/ return { ...context, ...sheetData }; } + /*~ @@DOUBLE-BLANK@@ ~*/ _toggleRandomizerLock(event) { const elem$ = $(event.currentTarget); const elemCat = elem$.data("category"); @@ -162,18 +186,22 @@ class BladesScoreSheet extends BladesItemSheet { this.document.update({ [`system.randomizers.${elemCat}.${elemIndex}.isLocked`]: false }); } else { + /*~ @@DOUBLE-BLANK@@ ~*/ this.document.update({ [`system.randomizers.${elemCat}.${elemIndex}.isLocked`]: true }); } } + /*~ @@DOUBLE-BLANK@@ ~*/ _selectImage(event) { const elem$ = $(event.currentTarget); const imageNum = elem$.data("imgNum"); this.document.update({ "system.imageSelected": imageNum }); } + /*~ @@DOUBLE-BLANK@@ ~*/ _deselectOrDeleteImage(event) { const elem$ = $(event.currentTarget); const imageNum = elem$.data("imgNum"); if (this.document.system.imageSelected === imageNum) { + /*~ @@DOUBLE-BLANK@@ ~*/ this.document.update({ "system.-=imageSelected": null }); return; } @@ -183,12 +211,14 @@ class BladesScoreSheet extends BladesItemSheet { .filter((_, i) => U.pInt(imageNum) !== i))) })); } + /*~ @@DOUBLE-BLANK@@ ~*/ _addImage() { U.displayImageSelector((path) => { const imgIndex = U.objSize(this.document.system.images); return this.document.update({ [`system.images.${imgIndex}`]: path }); }, "systems/eunos-blades/assets", this.position); } + /*~ @@DOUBLE-BLANK@@ ~*/ _selectRollOpposition(event) { eLog.checkLog3("Select Roll Opposition", { event }); const elem$ = $(event.currentTarget); @@ -198,6 +228,7 @@ class BladesScoreSheet extends BladesItemSheet { BladesRoll.Active.rollOpposition = new BladesRollOpposition(BladesRoll.Active, this.document.system.oppositions[oppId]); } } + /*~ @@DOUBLE-BLANK@@ ~*/ _triggerRandomize(event) { const elem$ = $(event.currentTarget); const category = elem$.data("category"); @@ -208,6 +239,7 @@ class BladesScoreSheet extends BladesItemSheet { this.generateRandomizerData(); } } + /*~ @@DOUBLE-BLANK@@ ~*/ async _updateGMNotesOnPC(event) { const elem$ = $(event.currentTarget); const actor = BladesActor.Get(elem$.data("id")); @@ -219,8 +251,10 @@ class BladesScoreSheet extends BladesItemSheet { await actor.update({ "system.gm_notes": updateText }); eLog.checkLog3("scoreSheet", "Updated!", { gm_notes: actor.system.gm_notes }); } + /*~ @@DOUBLE-BLANK@@ ~*/ activateListeners(html) { super.activateListeners(html); + /*~ @@DOUBLE-BLANK@@ ~*/ html.find("[data-action='select-image']").on({ click: this._selectImage.bind(this), contextmenu: this._deselectOrDeleteImage.bind(this) @@ -237,22 +271,28 @@ class BladesScoreSheet extends BladesItemSheet { html.find("[data-action='randomize'").on({ click: this._triggerRandomize.bind(this) }); + /*~ @@DOUBLE-BLANK@@ ~*/ html.find("textarea.pc-summary-notes-body").on({ change: this._updateGMNotesOnPC.bind(this) }); } + /*~ @@DOUBLE-BLANK@@ ~*/ async _onSubmit(event, params = {}) { eLog.checkLog3("scoreSheet", "_onSubmit()", { event, params, elemText: event.currentTarget.innerHTML }); let isForcingRender = true; + /*~ @@DOUBLE-BLANK@@ ~*/ const prevPhase = this.item.system.phase; const submitData = await super._onSubmit(event, params); + /*~ @@DOUBLE-BLANK@@ ~*/ const newPhase = this.item.system.phase; if (prevPhase !== newPhase) { switch (prevPhase) { case BladesPhase.CharGen: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } case BladesPhase.Freeplay: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } case BladesPhase.Score: { @@ -262,22 +302,29 @@ class BladesScoreSheet extends BladesItemSheet { break; } case BladesPhase.Downtime: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } + // No default } switch (newPhase) { case BladesPhase.CharGen: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } case BladesPhase.Freeplay: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } case BladesPhase.Score: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } case BladesPhase.Downtime: { + /*~ @@DOUBLE-BLANK@@ ~*/ break; } + // No default } } if (isForcingRender) { @@ -287,5 +334,7 @@ class BladesScoreSheet extends BladesItemSheet { return submitData; } } +/*~ @@DOUBLE-BLANK@@ ~*/ export { BladesTipGenerator }; -export default BladesScoreSheet; \ No newline at end of file +export default BladesScoreSheet; +/*~ @@DOUBLE-BLANK@@ ~*/ diff --git a/module/sheets/item/blades-clock-keeper-sheet.js b/module/sheets/item/blades-clock-keeper-sheet.js deleted file mode 100644 index 7f420165..00000000 --- a/module/sheets/item/blades-clock-keeper-sheet.js +++ /dev/null @@ -1,82 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import BladesItemSheet from "./blades-item-sheet.js"; -import BladesClockKeeper from "../../documents/items/blades-clock-keeper.js"; -class BladesClockKeeperSheet extends BladesItemSheet { - static Get() { return game.eunoblades.ClockKeeper; } - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "item", "clock-keeper"], - template: "systems/eunos-blades/templates/items/clock_keeper-sheet.hbs", - width: 700, - height: 970 - }); - } - static async Initialize() { - game.eunoblades ??= {}; - Items.registerSheet("blades", BladesClockKeeperSheet, { types: ["clock_keeper"], makeDefault: true }); - Hooks.once("ready", async () => { - let clockKeeper = game.items.find((item) => item.type === "clock_keeper"); - if (!clockKeeper) { - clockKeeper = (await BladesClockKeeper.create({ - name: "Clock Keeper", - type: "clock_keeper", - img: "systems/eunos-blades/assets/icons/misc-icons/clock-keeper.svg" - })); - } - game.eunoblades.ClockKeeper = clockKeeper; - game.eunoblades.ClockKeeper.renderOverlay(); - }); - Hooks.on("canvasReady", async () => { game.eunoblades.ClockKeeper?.renderOverlay(); }); - return loadTemplates([ - "systems/eunos-blades/templates/items/clock_keeper-sheet.hbs", - "systems/eunos-blades/templates/parts/clock-sheet-row.hbs" - ]); - } - static InitSockets() { - if (game.eunoblades.ClockKeeper) { - socketlib.system.register("renderOverlay", game.eunoblades.ClockKeeper.renderOverlay); - return true; - } - return false; - } - getData() { - const context = super.getData(); - const sheetData = { - clock_keys: Object.fromEntries((Object.entries(context.system.clock_keys ?? {}) - .filter(([keyID, keyData]) => Boolean(keyData && keyData.scene === context.system.targetScene)))) - }; - return { ...context, ...sheetData }; - } - addKey(event) { - event.preventDefault(); - this.item.addClockKey(); - } - deleteKey(event) { - event.preventDefault(); - const keyID = event.currentTarget.dataset.id; - if (keyID) { - this.item.deleteClockKey(keyID); - } - } - setKeySize(event) { - event.preventDefault(); - const keyID = event.target.dataset.id; - if (keyID) { - this.item.setKeySize(keyID, parseInt(event.target.value, 10)); - } - } - async activateListeners(html) { - super.activateListeners(html); - html.find("[data-action=\"add-key\"").on("click", this.addKey.bind(this)); - html.find("[data-action=\"delete-key\"").on("click", this.deleteKey.bind(this)); - html.find(".key-clock-counter").on("change", this.setKeySize.bind(this)); - } -} -export default BladesClockKeeperSheet; -//# sourceMappingURL=blades-clock-keeper-sheet.js.map \ No newline at end of file diff --git a/module/sheets/item/blades-item-sheet.js b/module/sheets/item/blades-item-sheet.js deleted file mode 100644 index c0e8a307..00000000 --- a/module/sheets/item/blades-item-sheet.js +++ /dev/null @@ -1,405 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import C, { BladesItemType, BladesPhase, Factor } from "../../core/constants.js"; -import U from "../../core/utilities.js"; -import G, { ApplyTooltipListeners } from "../../core/gsap.js"; -import BladesItem from "../../blades-item.js"; -import BladesActiveEffect from "../../blades-active-effect.js"; -import Tags from "../../core/tags.js"; -class BladesItemSheet extends ItemSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "item"], - width: 560, - height: 500, - tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }] - }); - } - - - getData() { - const context = super.getData(); - const sheetData = { - cssClass: this.item.type, - editable: this.options.editable, - isGM: (game.eunoblades.Tracker?.system.is_spoofing_player ? false : Boolean(game.user.isGM)), - isEmbeddedItem: Boolean(this.item.parent), - item: this.item, - system: this.item.system, - tierTotal: this.item.getFactorTotal(Factor.tier) > 0 ? U.romanizeNum(this.item.getFactorTotal(Factor.tier)) : "0", - activeEffects: Array.from(this.item.effects) - }; - return this._getTypedItemData[this.item.type]({ ...context, ...sheetData }); - } - _getTypedItemData = { - [BladesItemType.ability]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.ability)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.background]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.background)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.clock_keeper]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.clock_keeper)) { - return undefined; - } - const sheetData = { - phases: Object.values(BladesPhase) - }; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.cohort_gang]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.cohort_gang, BladesItemType.cohort_expert)) { - return undefined; - } - context.tierTotal = this.item.system.quality > 0 ? U.romanizeNum(this.item.system.quality) : "0"; - context.system.subtypes ??= {}; - context.system.elite_subtypes ??= {}; - const sheetData = { - tierData: { - "class": "comp-tier comp-vertical comp-teeth", - "dotline": { - data: this.item.system.tier, - target: "system.tier.value", - iconEmpty: "dot-empty.svg", - iconEmptyHover: "dot-empty-hover.svg", - iconFull: "dot-full.svg", - iconFullHover: "dot-full-hover.svg" - } - } - }; - sheetData.edgeData = Object.fromEntries(Object.values(context.system.edges ?? []) - .filter((edge) => /[A-Za-z]/.test(edge)) - .map((edge) => [edge.trim(), C.EdgeTooltips[edge]])); - sheetData.flawData = Object.fromEntries(Object.values(context.system.flaws ?? []) - .filter((flaw) => /[A-Za-z]/.test(flaw)) - .map((flaw) => [flaw.trim(), C.FlawTooltips[flaw]])); - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.cohort_expert]: (context) => this._getTypedItemData[BladesItemType.cohort_gang](context), - [BladesItemType.crew_ability]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.crew_ability)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.crew_reputation]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.crew_reputation)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.crew_playbook]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.crew_playbook)) { - return undefined; - } - if (context.isGM) { - const expClueData = {}; - [...Object.values(context.system.experience_clues ?? []).filter((clue) => /[A-Za-z]/.test(clue)), " "].forEach((clue, i) => { expClueData[(i + 1).toString()] = clue; }); - context.system.experience_clues = expClueData; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.crew_upgrade]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.crew_upgrade)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.feature]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.feature)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.gm_tracker]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.gm_tracker)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.heritage]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.heritage)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.gear]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.gear)) { - return undefined; - } - const sheetData = { - tierData: { - "class": "comp-tier comp-vertical comp-teeth", - "label": "Quality", - "labelClass": "filled-label full-width", - "dotline": { - data: this.item.system.tier, - target: "system.tier.value", - iconEmpty: "dot-empty.svg", - iconEmptyHover: "dot-empty-hover.svg", - iconFull: "dot-full.svg", - iconFullHover: "dot-full-hover.svg" - } - } - }; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.playbook]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.playbook)) { - return undefined; - } - if (context.isGM) { - const expClueData = {}; - [...Object.values(context.system.experience_clues ?? []).filter((clue) => /[A-Za-z]/.test(clue)), " "].forEach((clue, i) => { expClueData[(i + 1).toString()] = clue; }); - context.system.experience_clues = expClueData; - const gatherInfoData = {}; - [...Object.values(context.system.gather_info_questions ?? []).filter((question) => /[A-Za-z]/.test(question)), " "].forEach((question, i) => { gatherInfoData[(i + 1).toString()] = question; }); - context.system.gather_info_questions = gatherInfoData; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.preferred_op]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.preferred_op)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.stricture]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.stricture)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.vice]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.vice)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.project]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.project)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.ritual]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.ritual)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.design]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.design)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.location]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.location)) { - return undefined; - } - const sheetData = {}; - return { - ...context, - ...sheetData - }; - }, - [BladesItemType.score]: (context) => { - if (!BladesItem.IsType(this.item, BladesItemType.score)) { - return undefined; - } - return context; - } - }; - get template() { - const pathComps = [ - "systems/eunos-blades/templates/items" - ]; - if (C.SimpleItemTypes.includes(this.item.type)) { - pathComps.push("simple-sheet.hbs"); - } - else { - pathComps.push(`${this.item.type}-sheet.hbs`); - } - return pathComps.join("/"); - } - - activateListeners(html) { - super.activateListeners(html); - const self = this; - Tags.InitListeners(html, this.item); - ApplyTooltipListeners(html); - if (!this.options.editable) { - return; - } - html.find(".dotline").each((__, elem) => { - if ($(elem).hasClass("locked")) { - return; - } - const targetDoc = this.item; - const targetField = $(elem).data("target"); - const comp$ = $(elem).closest("comp"); - const curValue = U.pInt($(elem).data("value")); - $(elem) - .find(".dot") - .each((_, dot) => { - $(dot).on("click", (event) => { - event.preventDefault(); - const thisValue = U.pInt($(dot).data("value")); - if (thisValue !== curValue) { - if (comp$.hasClass("comp-coins") - || comp$.hasClass("comp-stash")) { - G.effects - .fillCoins($(dot).prevAll(".dot")) - .then(() => targetDoc.update({ [targetField]: thisValue })); - } - else { - targetDoc.update({ [targetField]: thisValue }); - } - } - }); - $(dot).on("contextmenu", (event) => { - event.preventDefault(); - const thisValue = U.pInt($(dot).data("value")) - 1; - if (thisValue !== curValue) { - targetDoc.update({ [targetField]: thisValue }); - } - }); - }); - }); - if (BladesItem.IsType(this.item, BladesItemType.cohort_expert, BladesItemType.cohort_gang)) { - html.find("[data-harm-click]").on({ - click: (event) => { - event.preventDefault(); - const harmLevel = U.pInt($(event.currentTarget).data("harmClick")); - if (this.item.system.harm?.value !== harmLevel) { - this.item.update({ "system.harm.value": harmLevel }); - } - }, - contextmenu: (event) => { - event.preventDefault(); - const harmLevel = Math.max(0, U.pInt($(event.currentTarget).data("harmClick")) - 1); - if (this.item.system.harm?.value !== harmLevel) { - this.item.update({ "system.harm.value": harmLevel }); - } - } - }); - } - if (this.options.submitOnChange) { - html.on("change", "textarea", this._onChangeInput.bind(this)); - } - html.find(".effect-control").on("click", (ev) => { - if (self.item.isOwned) { - ui.notifications?.warn(game.i18n.localize("BITD.EffectWarning")); - return; - } - BladesActiveEffect.onManageActiveEffect(ev, self.item); - }); - html.find("[data-action=\"toggle-turf-connection\"").on("click", this.toggleTurfConnection.bind(this)); - } - toggleTurfConnection(event) { - const button$ = $(event.currentTarget); - const connector$ = button$.parent(); - const turfNum = parseInt(connector$.data("index") ?? 0, 10); - const turfDir = connector$.data("dir"); - if (!turfNum || !turfDir) { - return; - } - const toggleState = connector$.hasClass("no-connect"); - const updateData = { - [`system.turfs.${turfNum}.connects.${turfDir}`]: toggleState - }; - const partner = connector$.data("partner"); - if (typeof partner === "string" && /-/.test(partner)) { - const [partnerNum, partnerDir] = partner.split("-"); - updateData[`system.turfs.${partnerNum}.connects.${partnerDir}`] = toggleState; - } - this.item.update(updateData); - } -} -export default BladesItemSheet; -//# sourceMappingURL=blades-item-sheet.js.map \ No newline at end of file diff --git a/module/sheets/item/blades-score-sheet.js b/module/sheets/item/blades-score-sheet.js deleted file mode 100644 index 8260383a..00000000 --- a/module/sheets/item/blades-score-sheet.js +++ /dev/null @@ -1,290 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import U from "../../core/utilities.js"; -import { BladesActorType, BladesPhase, Tag, Randomizers } from "../../core/constants.js"; -import BladesItemSheet from "./blades-item-sheet.js"; -import { BladesActor } from "../../documents/blades-actor-proxy.js"; -import { BladesScore } from "../../documents/blades-item-proxy.js"; -import BladesRollCollab from "../../blades-roll-collab.js"; -export var BladesTipContext; -(function (BladesTipContext) { - BladesTipContext["DiceRoll"] = "DiceRoll"; - BladesTipContext["Combat"] = "Combat"; - BladesTipContext["General"] = "General"; -})(BladesTipContext || (BladesTipContext = {})); -class BladesTipGenerator { - static get Tips() { - return { - [BladesTipContext.DiceRoll]: [], - [BladesTipContext.Combat]: [ - "Every combat encounter should advance the main plot, or else it's filler.", - "Inject dialogue into combat encounters, especially from important adversaries.", - "Combat encounters should be a challenge, but not a slog. Don't be afraid to end them early.", - "Infiltrate/Rescue/Destroy: Use these as additional/secondary goals in combat encounters.", - "Tell the next player in the initiative order that they're on deck.", - "Don't trigger combats automatically: Use alternate objectives to incite the players to fight, giving them agency.", - "Add another layer by drawing focus to collateral effects of the combat: a fire, a hostage, a collapsing building, innocents in danger" - ], - [BladesTipContext.General]: [ - "Rolling the dice always means SOMETHING happens.", - "Jump straight to the action; don't waste time on establishing scenes or filler.", - "Invoke elements of characters' backstories or beliefs to make any scene more personal." - ] - }; - } - tipContext; - constructor(tipContext) { - this.tipContext = tipContext; - } -} -class BladesScoreSheet extends BladesItemSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "item", "score-sheet"], - template: "systems/eunos-blades/templates/items/score-sheet.hbs", - width: 900, - submitOnChange: false, - height: 970 - }); - } - async generateRandomizerData(category) { - const randomData = { - Bargains: Object.fromEntries(Object.entries(U.sample(Randomizers.GM.Bargains - .filter((bData) => !Object.values(this.document.system.randomizers.Bargains) - .some((_bData) => _bData.name === bData.name || _bData.effect === bData.effect)), 3, true, (e, a) => a - .filter((_e) => e.category === _e.category).length === 0)) - .map(([k, v]) => { - k = `${k}`; - Object.assign(v, { notes: "" }); - return [k, v]; - })), - Obstacles: Object.fromEntries(Object.entries(U.sample(Randomizers.GM.Obstacles - .filter((bData) => !Object.values(this.document.system.randomizers.Obstacles) - .some((_bData) => _bData.name === bData.name || _bData.desc === bData.desc)), 3, true, (e, a) => a - .filter((_e) => e.category === _e.category).length === 0)) - .map(([k, v]) => { - k = `${k}`; - Object.assign(v, { notes: "" }); - return [k, v]; - })), - NPCs: Object.fromEntries(Object.entries(U.sample(Randomizers.GM.NPCs - .filter((bData) => !Object.values(this.document.system.randomizers.NPCs) - .some((_bData) => _bData.name === bData.name || _bData.description === bData.description)), 3, true, (e, a) => a - .filter((_e) => e.arena === _e.arena).length === 0)) - .map(([k, v]) => { - k = `${k}`; - Object.assign(v, { notes: "" }); - return [k, v]; - })), - Scores: Object.fromEntries(Object.entries(U.sample(Randomizers.GM.Scores - .filter((bData) => !Object.values(this.document.system.randomizers.Scores) - .some((_bData) => _bData.name === bData.name || _bData.desc === bData.desc)), 3, true, (e, a) => a - .filter((_e) => e.category === _e.category).length === 0)) - .map(([k, v]) => { - k = `${k}`; - Object.assign(v, { notes: "" }); - return [k, v]; - })) - }; - if (category) { - Object.keys(randomData) - .filter((cat) => cat !== category) - .forEach((cat) => { - const _cat = cat; - randomData[_cat] = this.document.system.randomizers[_cat]; - }); - } - const finalRandomData = { - Bargains: {}, - Obstacles: {}, - NPCs: {}, - Scores: {} - }; - Object.keys(randomData).forEach((cat) => { - const _cat = cat; - Object.entries(randomData[_cat]).forEach(([index, randData]) => { - if (this.document.system.randomizers?.[_cat][index].isLocked) { - finalRandomData[_cat][index] = this.document.system.randomizers[_cat][index]; - } - else { - finalRandomData[_cat][index] = randomData[_cat][index]; - } - }); - }); - this.document.update({ "system.randomizers": finalRandomData }); - } - getData() { - const context = super.getData(); - const sheetData = {}; - sheetData.playerCharacters = BladesActor.GetTypeWithTags(BladesActorType.pc, Tag.PC.ActivePC) - .map((pc) => { - return Object.assign(pc, { - actionData: Object.fromEntries(Object.entries(pc.system.attributes) - .map(([attrName, attrData]) => { - return [ - attrName, - Object.fromEntries(Object.entries(attrData) - .map(([actionName, actionData]) => { - return [ - U.uCase(actionName).slice(0, 3), - actionData - ]; - })) - ]; - })) - }); - }); - const validOppositions = {}; - for (const [id, data] of Object.entries(context.system.oppositions)) { - if (!data.rollOppName && !data.rollOppSubName) { - continue; - } - validOppositions[id] = data; - } - context.system.oppositions = validOppositions; - return { - ...context, - ...sheetData - }; - } - _toggleRandomizerLock(event) { - const elem$ = $(event.currentTarget); - const elemCat = elem$.data("category"); - const elemIndex = `${elem$.data("index")}`; - const elemValue = elem$.data("value"); - if (`${elemValue}` === "true") { - this.document.update({ [`system.randomizers.${elemCat}.${elemIndex}.isLocked`]: false }); - } - else { - this.document.update({ [`system.randomizers.${elemCat}.${elemIndex}.isLocked`]: true }); - } - } - _selectImage(event) { - const elem$ = $(event.currentTarget); - const imageNum = elem$.data("imgNum"); - this.document.update({ "system.imageSelected": imageNum }); - } - _deselectOrDeleteImage(event) { - const elem$ = $(event.currentTarget); - const imageNum = elem$.data("imgNum"); - if (this.document.system.imageSelected === imageNum) { - this.document.update({ "system.-=imageSelected": null }); - return; - } - const images = { ...this.document.system.images }; - this.document.update({ "system.-=images": null }).then(() => this.document.update({ - "system.images": Object.fromEntries(Object.entries(Object.values(images) - .filter((_, i) => U.pInt(imageNum) !== i))) - })); - } - _addImage() { - U.displayImageSelector(path => { - const imgIndex = U.objSize(this.document.system.images); - return this.document.update({ [`system.images.${imgIndex}`]: path }); - }, "systems/eunos-blades/assets", this.position); - } - _selectRollOpposition(event) { - eLog.checkLog3("Select Roll Opposition", { event }); - const elem$ = $(event.currentTarget); - const oppId = elem$.data("oppId"); - this.document.update({ "system.oppositionSelected": oppId }); - if (BladesScore.Active?.id === this.document.id && BladesRollCollab.Active) { - BladesRollCollab.Active.rollOpposition = this.document.system.oppositions[oppId]; - } - } - _triggerRandomize(event) { - const elem$ = $(event.currentTarget); - const category = elem$.data("category"); - if (category && category in Randomizers.GM) { - this.generateRandomizerData(category); - } - else { - this.generateRandomizerData(); - } - } - async _updateGMNotesOnPC(event) { - const elem$ = $(event.currentTarget); - const actor = BladesActor.Get(elem$.data("id")); - if (!actor) { - throw new Error(`Unable to retrieve actor with id '${elem$.data("id")}'`); - } - const updateText = event.currentTarget.innerHTML; - eLog.checkLog3("scoreSheet", "Retrieved Text, Updating ...", { updateText }); - await actor.update({ "system.gm_notes": updateText }); - eLog.checkLog3("scoreSheet", "Updated!", { gm_notes: actor.system.gm_notes }); - } - async activateListeners(html) { - super.activateListeners(html); - html.find("[data-action='select-image']").on({ - click: this._selectImage.bind(this), - contextmenu: this._deselectOrDeleteImage.bind(this) - }); - html.find("[data-action='add-image']").on({ - click: this._addImage.bind(this) - }); - html.find(".roll-opposition-name").on({ - dblclick: this._selectRollOpposition.bind(this) - }); - html.find(".toggle-lock").on({ - click: this._toggleRandomizerLock.bind(this) - }); - html.find("[data-action='randomize'").on({ - click: this._triggerRandomize.bind(this) - }); - html.find("textarea.pc-summary-notes-body").on({ - change: this._updateGMNotesOnPC.bind(this) - }); - } - async _onSubmit(event, params = {}) { - eLog.checkLog3("scoreSheet", "_onSubmit()", { event, params, elemText: event.currentTarget.innerHTML }); - let isForcingRender = true; - const prevPhase = this.item.system.phase; - const submitData = await super._onSubmit(event, params); - const newPhase = this.item.system.phase; - if (prevPhase !== newPhase) { - switch (prevPhase) { - case BladesPhase.CharGen: { - break; - } - case BladesPhase.Freeplay: { - break; - } - case BladesPhase.Score: { - isForcingRender = false; - game.actors.filter((actor) => BladesActor.IsType(actor, BladesActorType.pc)) - .forEach((actor) => actor.clearLoadout()); - break; - } - case BladesPhase.Downtime: { - break; - } - } - switch (newPhase) { - case BladesPhase.CharGen: { - break; - } - case BladesPhase.Freeplay: { - break; - } - case BladesPhase.Score: { - break; - } - case BladesPhase.Downtime: { - break; - } - } - } - if (isForcingRender) { - game.actors.filter((actor) => actor.type === BladesActorType.pc) - .forEach((actor) => actor.sheet?.render()); - } - return submitData; - } -} -export default BladesScoreSheet; -//# sourceMappingURL=blades-score-sheet.js.map \ No newline at end of file diff --git a/module/sheets/item/blades-tracker-sheet copy.js b/module/sheets/item/blades-tracker-sheet copy.js deleted file mode 100644 index 4564d00f..00000000 --- a/module/sheets/item/blades-tracker-sheet copy.js +++ /dev/null @@ -1,130 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import { BladesActorType, BladesItemType, BladesPhase } from "../../core/constants.js"; -import BladesItemSheet from "./blades-item-sheet.js"; -import BladesItem from "../../blades-item.js"; -import BladesGMTracker from "../../documents/items/blades-gm-tracker.js"; -import BladesActor from "../../blades-actor.js"; -import BladesPC from "../../documents/actors/blades-pc.js"; -export var BladesTipContext; -(function (BladesTipContext) { - BladesTipContext["DiceRoll"] = "DiceRoll"; - BladesTipContext["Combat"] = "Combat"; - BladesTipContext["General"] = "General"; -})(BladesTipContext || (BladesTipContext = {})); -class BladesTipGenerator { - static Test(pcActor) { - if (BladesActor.IsType(pcActor, BladesActorType.pc)) { - return pcActor; - } - return undefined; - } - testActor = new BladesPC({ name: "blah", type: "pc" }); - static get Tips() { - return { - [BladesTipContext.DiceRoll]: [], - [BladesTipContext.Combat]: [ - "Every combat encounter should advance the main plot, or else it's filler.", - "Inject dialogue into combat encounters, especially from important adversaries.", - "Combat encounters should be a challenge, but not a slog. Don't be afraid to end them early.", - "Infiltrate/Rescue/Destroy: Use these as additional/secondary goals in combat encounters.", - "Tell the next player in the initiative order that they're on deck.", - "Don't trigger combats automatically: Use alternate objectives to incite the players to fight, giving them agency.", - "Add another layer by drawing focus to collateral effects of the combat: a fire, a hostage, a collapsing building, innocents in danger" - ], - [BladesTipContext.General]: [ - "Rolling the dice always means SOMETHING happens.", - "Jump straight to the action; don't waste time on establishing scenes or filler.", - "Invoke elements of characters' backstories or beliefs to make any scene more personal." - ] - }; - } - tipContext; - constructor(tipContext) { - this.tipContext = tipContext; - } -} -class BladesTrackerSheet extends BladesItemSheet { - static Get() { return game.eunoblades.Tracker; } - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "item", "gm-tracker"], - template: "systems/eunos-blades/templates/items/gm_tracker-sheet.hbs", - width: 700, - height: 970 - }); - } - static async Initialize() { - game.eunoblades ??= {}; - Items.registerSheet("blades", BladesTrackerSheet, { types: ["gm_tracker"], makeDefault: true }); - Hooks.once("ready", async () => { - let tracker = game.items.find((item) => BladesItem.IsType(item, BladesItemType.gm_tracker)); - if (!tracker) { - tracker = (await BladesGMTracker.create({ - name: "GM Tracker", - type: "gm_tracker", - img: "systems/eunos-blades/assets/icons/misc-icons/gm-tracker.svg" - })); - } - game.eunoblades.Tracker = tracker; - }); - return loadTemplates([ - "systems/eunos-blades/templates/items/gm_tracker-sheet.hbs" - ]); - } - - - async activateListeners(html) { - super.activateListeners(html); - } - async _onSubmit(event, params = {}) { - const prevPhase = this.item.system.phase; - const submitData = await super._onSubmit(event, params); - const newPhase = this.item.system.phase; - let isForcingRender = true; - if (prevPhase !== newPhase) { - switch (prevPhase) { - case BladesPhase.CharGen: { - break; - } - case BladesPhase.Freeplay: { - break; - } - case BladesPhase.Score: { - isForcingRender = false; - game.actors.filter((actor) => BladesActor.IsType(actor, BladesActorType.pc)) - .forEach((actor) => actor.clearLoadout()); - break; - } - case BladesPhase.Downtime: { - break; - } - } - switch (newPhase) { - case BladesPhase.CharGen: { - break; - } - case BladesPhase.Freeplay: { - break; - } - case BladesPhase.Score: { - break; - } - case BladesPhase.Downtime: { - break; - } - } - } - if (isForcingRender) { - game.actors.filter((actor) => actor.type === BladesActorType.pc) - .forEach((actor) => actor.sheet?.render()); - } - return submitData; - } -} -export default BladesTrackerSheet; \ No newline at end of file diff --git a/module/sheets/item/blades-tracker-sheet.js b/module/sheets/item/blades-tracker-sheet.js deleted file mode 100644 index d8df0208..00000000 --- a/module/sheets/item/blades-tracker-sheet.js +++ /dev/null @@ -1,129 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import { BladesActorType, BladesItemType, BladesPhase } from "../../core/constants.js"; -import BladesItemSheet from "./blades-item-sheet.js"; -import BladesItem from "../../blades-item.js"; -import BladesGMTracker from "../../documents/items/blades-gm-tracker.js"; -import BladesActor from "../../blades-actor.js"; -import BladesPC from "../../documents/actors/blades-pc.js"; -export var BladesTipContext; -(function (BladesTipContext) { - BladesTipContext["DiceRoll"] = "DiceRoll"; - BladesTipContext["Combat"] = "Combat"; - BladesTipContext["General"] = "General"; -})(BladesTipContext || (BladesTipContext = {})); -class BladesTipGenerator { - static Test(pcActor) { - if (BladesActor.IsType(pcActor, BladesActorType.pc)) { - return pcActor; - } - return undefined; - } - testActor = new BladesPC({ name: "blah", type: "pc" }); - static get Tips() { - return { - [BladesTipContext.DiceRoll]: [], - [BladesTipContext.Combat]: [ - "Every combat encounter should advance the main plot, or else it's filler.", - "Inject dialogue into combat encounters, especially from important adversaries.", - "Combat encounters should be a challenge, but not a slog. Don't be afraid to end them early.", - "Infiltrate/Rescue/Destroy: Use these as additional/secondary goals in combat encounters.", - "Tell the next player in the initiative order that they're on deck.", - "Don't trigger combats automatically: Use alternate objectives to incite the players to fight, giving them agency.", - "Add another layer by drawing focus to collateral effects of the combat: a fire, a hostage, a collapsing building, innocents in danger" - ], - [BladesTipContext.General]: [ - "Rolling the dice always means SOMETHING happens.", - "Jump straight to the action; don't waste time on establishing scenes or filler.", - "Invoke elements of characters' backstories or beliefs to make any scene more personal." - ] - }; - } - tipContext; - constructor(tipContext) { - this.tipContext = tipContext; - } -} -class BladesTrackerSheet extends BladesItemSheet { - static Get() { return game.eunoblades.Tracker; } - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "item", "gm-tracker"], - template: "systems/eunos-blades/templates/items/gm_tracker-sheet.hbs", - width: 700, - height: 970 - }); - } - static async Initialize() { - game.eunoblades ??= {}; - Items.registerSheet("blades", BladesTrackerSheet, { types: ["gm_tracker"], makeDefault: true }); - Hooks.once("ready", async () => { - let tracker = game.items.find((item) => BladesItem.IsType(item, BladesItemType.gm_tracker)); - if (!tracker) { - tracker = (await BladesGMTracker.create({ - name: "GM Tracker", - type: "gm_tracker", - img: "systems/eunos-blades/assets/icons/misc-icons/gm-tracker.svg" - })); - } - game.eunoblades.Tracker = tracker; - }); - return loadTemplates([ - "systems/eunos-blades/templates/items/gm_tracker-sheet.hbs" - ]); - } - async activateListeners(html) { - super.activateListeners(html); - } - async _onSubmit(event, params = {}) { - const prevPhase = this.item.system.phase; - const submitData = await super._onSubmit(event, params); - const newPhase = this.item.system.phase; - let isForcingRender = true; - if (prevPhase !== newPhase) { - switch (prevPhase) { - case BladesPhase.CharGen: { - break; - } - case BladesPhase.Freeplay: { - break; - } - case BladesPhase.Score: { - isForcingRender = false; - game.actors.filter((actor) => BladesActor.IsType(actor, BladesActorType.pc)) - .forEach((actor) => actor.clearLoadout()); - break; - } - case BladesPhase.Downtime: { - break; - } - } - switch (newPhase) { - case BladesPhase.CharGen: { - break; - } - case BladesPhase.Freeplay: { - break; - } - case BladesPhase.Score: { - break; - } - case BladesPhase.Downtime: { - break; - } - } - } - if (isForcingRender) { - game.actors.filter((actor) => actor.type === BladesActorType.pc) - .forEach((actor) => actor.sheet?.render()); - } - return submitData; - } -} -export default BladesTrackerSheet; -//# sourceMappingURL=blades-tracker-sheet.js.map \ No newline at end of file diff --git a/module/sheets/roll/blades-roll-collab-sheet.js b/module/sheets/roll/blades-roll-collab-sheet.js deleted file mode 100644 index dd9eddc3..00000000 --- a/module/sheets/roll/blades-roll-collab-sheet.js +++ /dev/null @@ -1,641 +0,0 @@ -/* ****▌███████████████████████████████████████████████████████████████████████████▐**** *\ -|* ▌█░░░░░░░░░ Euno's Blades in the Dark for Foundry VTT ░░░░░░░░░░░█▐ *| -|* ▌██████████████████░░░░░░░░░░░░░ by Eunomiac ░░░░░░░░░░░░░██████████████████▐ *| -|* ▌█ License █ v0.1.0 ██▐ *| -|* ▌████░░░░ ░░░░█████▐ *| -\* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ - -import U from "../../core/utilities.js"; -import C, { BladesActorType, RollType, RollModStatus, RollModCategory, Action, Attribute, Position, Effect, Factor } from "../../core/constants.js"; -import BladesActor from "../../blades-actor.js"; -import BladesItem from "../../blades-item.js"; -import { ApplyTooltipListeners } from "../../core/gsap.js"; -function isAction(trait) { - return Boolean(trait && typeof trait === "string" && trait in Action); -} -function isAttribute(trait) { - return Boolean(trait && typeof trait === "string" && trait in Attribute); -} -function isTier(trait) { return U.lCase(trait) === "tier"; } -function isNumber(trait) { return U.isInt(trait); } -export const ModEffects = { - NegateTierPenalty: (mod, sheetData) => { - return sheetData; - }, - NegateQualityPenalty: (mod, sheetData) => { - return sheetData; - }, - IsPush: (mod, sheetData) => { - return sheetData; - } -}; -class BladesRollCollabSheet extends DocumentSheet { - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["eunos-blades", "sheet", "roll-collab"], - template: `systems/eunos-blades/templates/roll/roll-collab${game.user.isGM ? "-gm" : ""}.hbs`, - submitOnChange: true, - width: 500 - }); - } - static Initialize() { - return loadTemplates([ - "systems/eunos-blades/templates/roll/roll-collab.hbs", - "systems/eunos-blades/templates/roll/roll-collab-gm.hbs", - "systems/eunos-blades/templates/roll/partials/roll-collab-gm-number-line.hbs", - "systems/eunos-blades/templates/roll/partials/roll-collab-gm-select-doc.hbs", - "systems/eunos-blades/templates/roll/partials/roll-collab-action.hbs", - "systems/eunos-blades/templates/roll/partials/roll-collab-action-gm.hbs", - "systems/eunos-blades/templates/roll/partials/roll-collab-resistance.hbs", - "systems/eunos-blades/templates/roll/partials/roll-collab-resistance-gm.hbs", - "systems/eunos-blades/templates/roll/partials/roll-collab-downtime.hbs", - "systems/eunos-blades/templates/roll/partials/roll-collab-downtime-gm.hbs", - "systems/eunos-blades/templates/roll/partials/roll-collab-fortune.hbs", - "systems/eunos-blades/templates/roll/partials/roll-collab-fortune-gm.hbs", - "systems/eunos-blades/templates/roll/partials/roll-collab-incarceration.hbs", - "systems/eunos-blades/templates/roll/partials/roll-collab-incarceration-gm.hbs" - ]); - } - static InitSockets() { - socketlib.system.register("renderRollCollab", BladesRollCollabSheet.RenderRollCollab); - socketlib.system.register("closeRollCollab", BladesRollCollabSheet.CloseRollCollab); - } - static Current = {}; - static get DefaultFlagData() { - return { - rollID: randomID(), - rollType: RollType.Action, - rollPrimaryType: "Actor", - rollPrimaryID: "", - rollTrait: Factor.tier, - rollMods: { - [RollModCategory.roll]: { - positive: { - Push: { - name: "Push", - category: RollModCategory.roll, - status: RollModStatus.ToggledOff, - posNeg: "positive", - modType: "general", - stressCost: 2, - value: 1, - tooltip: "

Push for +1d

For 2 Stress, add 1 die to your pool.

(You cannot also accept a Devil's Bargain to increase your dice pool: It's one or the other.)

" - }, - Bargain: { - name: "Bargain", - category: RollModCategory.roll, - status: RollModStatus.Hidden, - posNeg: "positive", - modType: "general", - value: 1, - tooltip: "

Devil's Bargain

The GM has offered you a Devil's Bargain.

Accept the terms to add 1 die to your pool.

(You cannot also Push for +1d to increase your dice pool: It's one or the other.)

" - }, - Assist: { - name: "Assist", - category: RollModCategory.roll, - status: RollModStatus.Hidden, - posNeg: "positive", - modType: "teamwork", - value: 1, - sideString: "", - tooltip: "

@CHARACTER_NAME@ Assists

@CHARACTER_NAME@ is Assisting your efforts, adding 1 die to your pool.

" - } - }, - negative: {} - }, - [RollModCategory.position]: { - positive: { - Setup: { - name: "Setup", - category: RollModCategory.position, - status: RollModStatus.Hidden, - posNeg: "positive", - modType: "teamwork", - value: 1, - sideString: undefined, - tooltip: "

@CHARACTER_NAME@ Sets You Up

@CHARACTER_NAME@ has set you up for success with a preceding Setup action, increasing your Position by one level.

" - } - }, - negative: {} - }, - [RollModCategory.effect]: { - positive: { - Push: { - name: "Push", - category: RollModCategory.effect, - status: RollModStatus.ToggledOff, - posNeg: "positive", - modType: "general", - stressCost: 2, - value: 1, - tooltip: "

Push for Effect

For 2 Stress, increase your Effect by one level.

" - }, - Setup: { - name: "Setup", - category: RollModCategory.effect, - status: RollModStatus.Hidden, - posNeg: "positive", - modType: "teamwork", - value: 1, - sideString: undefined, - tooltip: "

@CHARACTER_NAME@ Sets You Up

@CHARACTER_NAME@ has set you up for success with a preceding Setup action, increasing your Effect by one level.

" - }, - Potency: { - name: "Potency", - category: RollModCategory.effect, - status: RollModStatus.Hidden, - posNeg: "positive", - modType: "general", - value: 1, - tooltip: "

Potency

By circumstance or advantage, you have Potency in this action, increasing your Effect by one level.

" - } - }, - negative: { - Potency: { - name: "Potency", - category: RollModCategory.effect, - status: RollModStatus.Hidden, - posNeg: "negative", - modType: "general", - value: 1, - tooltip: "

Potency

By circumstance or advantage, @OPPOSITION_NAME@ has Potency against you, reducing your Effect by one level." - } - } - }, - [RollModCategory.result]: { positive: {}, negative: {} }, - [RollModCategory.after]: { positive: {}, negative: {} } - }, - rollPositionInitial: Position.risky, - rollEffectInitial: Effect.standard, - rollPosEffectTrade: false, - rollFactors: { - [Factor.tier]: { name: "Tier", cssClasses: "roll-factor roll-factor-tier", value: 0, max: 0, isActive: false, isDominant: false, highFavorsPC: true } - }, - isGMReady: false, - GMBoosts: {}, - GMOppBoosts: {}, - docSelections: { - [RollModCategory.roll]: { - Assist: false, - Group_1: false, - Group_2: false, - Group_3: false, - Group_4: false, - Group_5: false, - Group_6: false - }, - [RollModCategory.position]: { - Setup: false - }, - [RollModCategory.effect]: { - Setup: false - } - } - }; - } - static async RenderRollCollab({ userID, rollID }) { - const user = game.users.get(userID); - if (!user) { - return; - } - BladesRollCollabSheet.Current[rollID] = new BladesRollCollabSheet(user, rollID); - BladesRollCollabSheet.Current[rollID].render(true); - } - static async CloseRollCollab(rollID) { - eLog.checkLog3("rollCollab", "CloseRollCollab()", { rollID }); - await BladesRollCollabSheet.Current[rollID]?.close({ rollID }); - delete BladesRollCollabSheet.Current[rollID]; - } - static async NewRoll(config) { - - const user = game.users.get(config.userID ?? game.user._id); - if (!(user instanceof User)) { - eLog.error("rollCollab", `[NewRoll()] Can't Find User '${config.userID}'`, config); - return; - } - await user.unsetFlag(C.SYSTEM_ID, "rollCollab"); - const flagUpdateData = { ...BladesRollCollabSheet.DefaultFlagData }; - flagUpdateData.rollType = config.rollType; - if (!(flagUpdateData.rollType in RollType)) { - eLog.error("rollCollab", `[RenderRollCollab()] Invalid rollType: ${flagUpdateData.rollType}`, config); - return; - } - const rollPrimary = config.rollPrimary ?? user.character; - if (!(rollPrimary instanceof BladesActor || rollPrimary instanceof BladesItem)) { - eLog.error("rollCollab", "[RenderRollCollab()] Invalid rollPrimary", { rollPrimary, config }); - return; - } - flagUpdateData.rollPrimaryID = rollPrimary.id; - flagUpdateData.rollPrimaryType = rollPrimary instanceof BladesActor ? "Actor" : "Item"; - if (U.isInt(config.rollTrait)) { - flagUpdateData.rollTrait = config.rollTrait; - } - else if (!config.rollTrait) { - eLog.error("rollCollab", "[RenderRollCollab()] No RollTrait in Config", config); - return; - } - else { - switch (flagUpdateData.rollType) { - case RollType.Action: { - if (!(U.lCase(config.rollTrait) in { ...Action, ...Factor })) { - eLog.error("rollCollab", `[RenderRollCollab()] Bad RollTrait for Action Roll: ${config.rollTrait}`, config); - return; - } - flagUpdateData.rollTrait = U.lCase(config.rollTrait); - break; - } - case RollType.Downtime: { - if (!(U.lCase(config.rollTrait) in { ...Action, ...Factor })) { - eLog.error("rollCollab", `[RenderRollCollab()] Bad RollTrait for Downtime Roll: ${config.rollTrait}`, config); - return; - } - flagUpdateData.rollTrait = U.lCase(config.rollTrait); - break; - } - case RollType.Fortune: { - if (!(U.lCase(config.rollTrait) in { ...Action, ...Attribute, ...Factor })) { - eLog.error("rollCollab", `[RenderRollCollab()] Bad RollTrait for Fortune Roll: ${config.rollTrait}`, config); - return; - } - flagUpdateData.rollTrait = U.lCase(config.rollTrait); - break; - } - case RollType.Resistance: { - if (!(U.lCase(config.rollTrait) in Attribute)) { - eLog.error("rollCollab", `[RenderRollCollab()] Bad RollTrait for Resistance Roll: ${config.rollTrait}`, config); - return; - } - break; - } - } - flagUpdateData.rollTrait = U.lCase(config.rollTrait); - } - await user.setFlag(C.SYSTEM_ID, "rollCollab", flagUpdateData); - BladesRollCollabSheet.RenderRollCollab({ userID: user._id, rollID: flagUpdateData.rollID }); - socketlib.system.executeForAllGMs("renderRollCollab", { userID: user._id, rollID: flagUpdateData.rollID }); - } - rollID; - constructor(user, rollID) { - super(user); - this.rollID = rollID; - } - get rData() { - if (!this.document.getFlag(C.SYSTEM_ID, "rollCollab")) { - eLog.error("rollCollab", "[get flags()] No RollCollab Flags Found on User", { user: this.document, flags: this.document.flags }); - return null; - } - return this.document.flags["eunos-blades"].rollCollab; - } - get rollPrimary() { - if (!this.rData) { - return undefined; - } - return this.rData.rollPrimaryType === "Actor" - ? game.actors.get(this.rData.rollPrimaryID) - : game.items.get(this.rData.rollPrimaryID); - } - getData() { - const context = super.getData(); - const { rData } = this; - if (!rData) { - return context; - } - const sheetData = { - cssClass: "roll-collab", - editable: this.options.editable, - isGM: game.eunoblades.Tracker.system.is_spoofing_player ? false : game.user.isGM, - rollPositions: Object.values(Position), - rollEffects: Object.values(Effect), - ...rData - }; - if (!this.rollPrimary) { - eLog.error("rollCollab", `[getData()] No '${sheetData.rollPrimaryType}' Found with ID '${sheetData.rollPrimaryID}'`, { user: this.document, rData: rData }); - return null; - } - sheetData.system = this.rollPrimary.system; - sheetData.rollPrimary = this.rollPrimary; - if (sheetData.rollOppositionID) { - const rollOpposition = BladesActor.Get(sheetData.rollOppositionID) ?? BladesItem.Get(sheetData.rollOppositionID); - if (!rollOpposition) { - throw new Error(`Cannot find Roll Opposition with ID '${sheetData.rollOppositionID}'`); - } - sheetData.rollOpposition = rollOpposition; - } - if (BladesActor.IsType(this.rollPrimary, BladesActorType.pc) && isAction(sheetData.rollTrait)) { - const { rollPrimary } = this; - sheetData.rollTraitData = { - name: sheetData.rollTrait, - value: rollPrimary.actions[sheetData.rollTrait], - max: rollPrimary.actions[sheetData.rollTrait] - }; - sheetData.rollTraitOptions = Object.values(Action) - .map((action) => ({ - name: U.uCase(action), - value: action - })); - } - else if (BladesActor.IsType(this.rollPrimary, BladesActorType.pc) && isAttribute(sheetData.rollTrait)) { - const { rollPrimary } = this; - sheetData.rollTraitData = { - name: sheetData.rollTrait, - value: rollPrimary.attributes[sheetData.rollTrait], - max: rollPrimary.attributes[sheetData.rollTrait] - }; - sheetData.rollTraitOptions = Object.values(Attribute) - .map((attribute) => ({ - name: U.uCase(attribute), - value: attribute - })); - } - else if (sheetData.rollTrait === "tier") { - const { rollPrimary } = this; - sheetData.rollTraitData = { - name: "Tier", - value: rollPrimary.getTierTotal(), - max: rollPrimary.getTierTotal() - }; - sheetData.rollTraitOptions = false; - } - else if (U.isInt(sheetData.rollTrait)) { - sheetData.rollTraitData = { - name: `+${sheetData.rollTrait}`, - value: sheetData.rollTrait, - max: sheetData.rollTrait - }; - sheetData.rollTraitOptions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - .map((num) => ({ - name: `+${num}`, - value: num - })); - } - sheetData.rollMods = mergeObject(sheetData.rollSource.rollMods, sheetData.rollMods); - function isModAutoActive(mod) { - const autoRollTypes = mod.autoRollTypes ?? []; - const autoRollTraits = mod.autoRollTraits ?? []; - return autoRollTypes.length + autoRollTraits.length > 0 - && (autoRollTypes.length === 0 || autoRollTypes.includes(sheetData.rollType)) - && (autoRollTraits.length === 0 || autoRollTraits.includes(sheetData.rollTrait)); - } - function isModConditional(mod) { - const conditionalRollTypes = mod.conditionalRollTypes ?? []; - const conditionalRollTraits = mod.conditionalRollTraits ?? []; - return conditionalRollTypes.length + conditionalRollTraits.length > 0 - && (conditionalRollTypes.length === 0 || conditionalRollTypes.includes(sheetData.rollType)) - && (conditionalRollTraits.length === 0 || conditionalRollTraits.includes(sheetData.rollTrait)); - } - Object.values(RollModCategory).forEach((modCat) => { - Object.values(sheetData.rollMods[modCat]?.positive ?? {}) - .filter((mod) => mod.isConditional && mod.status === RollModStatus.ToggledOff) - .forEach((mod) => { - if (isModAutoActive(mod)) { - sheetData.rollMods[modCat].positive[mod.name].status = RollModStatus.ForcedOn; - } - else if (!isModConditional(mod)) { - sheetData.rollMods[modCat].positive[mod.name].status = RollModStatus.Hidden; - } - }); - }); - function getModsDelta(cat) { - const activePosMods = Object.values(sheetData.rollMods?.[cat]?.positive ?? {}) - .filter((mod) => [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(mod.status)); - const posModVals = activePosMods.map((mod) => mod.value); - const posModSum = U.sum(posModVals); - const activeNegMods = Object.values(sheetData.rollMods?.[cat]?.negative ?? {}) - .filter((mod) => [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(mod.status)); - const negModVals = activeNegMods.map((mod) => mod.value); - const negModSum = U.sum(negModVals); - eLog.checkLog3("rollMods", `getModsDelta(${cat})`, { activePosMods, posModVals, posModSum, activeNegMods, negModVals, negModSum, returnVal: U.sum(Object.values(sheetData.rollMods?.[cat]?.positive ?? {}) - .filter((mod) => [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(mod.status)) - .map((mod) => mod.value)) - - U.sum(Object.values(sheetData.rollMods?.[cat]?.negative ?? {}) - .filter((mod) => [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(mod.status)) - .map((mod) => mod.value)) }); - return U.sum(Object.values(sheetData.rollMods?.[cat]?.positive ?? {}) - .filter((mod) => [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(mod.status)) - .map((mod) => mod.value)) - - U.sum(Object.values(sheetData.rollMods?.[cat]?.negative ?? {}) - .filter((mod) => [RollModStatus.ToggledOn, RollModStatus.ForcedOn].includes(mod.status)) - .map((mod) => mod.value)); - } - sheetData.diceTotal = Math.max(0, (sheetData.rollTraitData?.value ?? 0) - + getModsDelta(RollModCategory.roll) - + (sheetData.GMBoosts.Dice ?? 0)); - let finalPosIndex = Object.values(Position).indexOf(sheetData.rollPositionInitial ?? Position.risky) - + getModsDelta(RollModCategory.position); - let finalEffectIndex = Object.values(Effect).indexOf(sheetData.rollEffectInitial ?? Effect.standard) - + getModsDelta(RollModCategory.effect); - sheetData.canTradePosition = sheetData.rollPosEffectTrade === "position" - || (sheetData.rollPosEffectTrade === false && (finalPosIndex > 0 && finalEffectIndex < 4)); - sheetData.canTradeEffect = sheetData.rollPosEffectTrade === "effect" - || (sheetData.rollPosEffectTrade === false && (finalPosIndex < 2 && finalEffectIndex > 1)); - if (sheetData.rollPosEffectTrade === "position") { - finalPosIndex++; - finalEffectIndex--; - } - if (sheetData.rollPosEffectTrade === "effect") { - finalPosIndex--; - finalEffectIndex++; - } - sheetData.rollPositionFinal = Object.values(Position)[U.clampNum(finalPosIndex, [0, 2])]; - sheetData.rollEffectFinal = Object.values(Effect)[U.clampNum(finalEffectIndex, [0, 4])]; - sheetData.isAffectingResult = getModsDelta(RollModCategory.result) !== 0 - || (sheetData.GMBoosts.Result ?? 0) !== 0 - || Object.values({ - ...(sheetData.rollMods.result?.negative ?? {}), - ...(sheetData.rollMods.result?.positive ?? {}) - }).filter((mod) => sheetData.isGM || mod.status !== RollModStatus.Hidden).length > 0; - if (sheetData.isAffectingResult) { - sheetData.rollResultFinal = getModsDelta(RollModCategory.result) - + (sheetData.GMBoosts.Result ?? 0); - } - if (sheetData.rollFactors) { - for (const [factorName] of Object.entries(sheetData.rollFactors)) { - if (sheetData.GMBoosts && factorName in sheetData.GMBoosts) { - sheetData.rollFactors[factorName].value += sheetData.GMBoosts[factorName] ?? 0; - } - if ([Factor.tier, Factor.quality].includes(factorName)) { - sheetData.rollFactors[factorName].display = U.romanizeNum(sheetData.rollFactors[factorName].value); - } - } - } - if (sheetData.rollOpposition) { - sheetData.rollOppositionFactors = sheetData.rollOpposition.rollFactors; - if (sheetData.rollOppositionFactors) { - for (const [factorName] of Object.entries(sheetData.rollOppositionFactors)) { - if (sheetData.GMOppBoosts && factorName in sheetData.GMOppBoosts) { - sheetData.rollOppositionFactors[factorName].value += sheetData.GMOppBoosts[factorName] ?? 0; - } - if ([Factor.tier, Factor.quality].includes(factorName)) { - sheetData.rollOppositionFactors[factorName].display = U.romanizeNum(sheetData.rollOppositionFactors[factorName].value); - } - } - } - } - sheetData.hasInactiveConditionals = { - [RollModCategory.roll]: Object.values(sheetData.rollMods?.roll?.positive ?? {}) - .filter((mod) => mod.isConditional && mod.status === RollModStatus.ToggledOff) - .length > 0, - [RollModCategory.position]: Object.values(sheetData.rollMods?.position?.positive ?? {}) - .filter((mod) => mod.isConditional && mod.status === RollModStatus.ToggledOff) - .length > 0, - [RollModCategory.effect]: Object.values(sheetData.rollMods?.effect?.positive ?? {}) - .filter((mod) => mod.isConditional && mod.status === RollModStatus.ToggledOff) - .length > 0, - [RollModCategory.result]: Object.values(sheetData.rollMods?.result?.positive ?? {}) - .filter((mod) => mod.isConditional && mod.status === RollModStatus.ToggledOff) - .length > 0, - [RollModCategory.after]: Object.values(sheetData.rollMods?.after?.positive ?? {}) - .filter((mod) => mod.isConditional && mod.status === RollModStatus.ToggledOff) - .length > 0 - }; - const { success, partial, fail } = C.DiceOdds[sheetData.diceTotal ?? 0]; - sheetData.oddsGradient = [ - "linear-gradient(to right", - `var(--blades-black-dark) ${fail}%`, - `var(--blades-grey) ${fail + partial}%`, - `var(--blades-white-bright) ${fail + partial + success}%`, - "var(--blades-gold-bright))" - ].join(", "); - const stressMods = Object.values(sheetData.rollMods ?? {}) - .map((catModData) => Object.values(catModData) - .map((posNegModData) => Object.values(posNegModData))) - .flat(3) - .filter((modData) => [RollModStatus.ForcedOn, RollModStatus.ToggledOn].includes(modData.status) && (modData.stressCost ?? 0) > 0); - const stressTotal = U.sum(stressMods.map((mod) => mod.stressCost)); - if (stressTotal > 0) { - sheetData.stressData = { - cost: stressTotal, - tooltip: [ - `

Stress Cost: ${stressTotal}

    `, - ...stressMods - .map((mod) => `
  • ${mod.name} (${mod.category}): ${mod.stressCost} Stress.
  • `), - "
" - ].join("") - }; - } - eLog.checkLog3("getData", "RollCollab.getData()", { ...context, ...sheetData }); - return { - ...context, - ...sheetData - }; - } - _toggleRollModClick(target, status) { - switch (status) { - case RollModStatus.Hidden: { - return this.document.setFlag(C.SYSTEM_ID, target, RollModStatus.ForcedOn); - } - case RollModStatus.ToggledOff: { - return this.document.setFlag(C.SYSTEM_ID, target, RollModStatus.ToggledOn); - } - case RollModStatus.ToggledOn: { - return this.document.setFlag(C.SYSTEM_ID, target, RollModStatus.ToggledOff); - } - case RollModStatus.ForcedOn: { - if (game.user.isGM) { - return this.document.setFlag(C.SYSTEM_ID, target, RollModStatus.ToggledOff); - } - return undefined; - } - } - return undefined; - } - async _toggleRollModContext(target, status) { - if (!game.user.isGM) { - return undefined; - } - switch (status) { - case RollModStatus.Hidden: { - return this.document.setFlag(C.SYSTEM_ID, target, RollModStatus.ToggledOff); - } - case RollModStatus.ToggledOff: { - return this.document.setFlag(C.SYSTEM_ID, target, RollModStatus.Hidden); - } - case RollModStatus.ToggledOn: { - return this.document.setFlag(C.SYSTEM_ID, target, RollModStatus.Hidden); - } - case RollModStatus.ForcedOn: { - if (game.user.isGM) { - return this.document.setFlag(C.SYSTEM_ID, target, RollModStatus.Hidden); - } - return undefined; - } - } - return undefined; - } - activateListeners(html) { - super.activateListeners(html); - ApplyTooltipListeners(html); - html.find("[data-action='toggle']").on({ - click: async (event) => { - event.preventDefault(); - const elem$ = $(event.currentTarget); - const status = elem$.data("status"); - const cat = elem$.data("cat"); - const posNeg = elem$.data("posNeg"); - const name = elem$.data("name"); - await this._toggleRollModClick(`rollCollab.rollMods.${cat}.${posNeg}.${name}.status`, status); - const bargainStatus = this.document.getFlag(C.SYSTEM_ID, "rollCollab.rollMods.roll.positive.Bargain.status"); - const pushStatus = this.document.getFlag(C.SYSTEM_ID, "rollCollab.rollMods.roll.positive.Push.status"); - if (name === "Bargain") { - if ([RollModStatus.ForcedOn, RollModStatus.ToggledOn].includes(bargainStatus ?? "") && pushStatus !== RollModStatus.Hidden) { - await this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollMods.roll.positive.Push.status", RollModStatus.Hidden); - } - else if ([RollModStatus.ToggledOff, RollModStatus.Hidden].includes(bargainStatus ?? "") && pushStatus === RollModStatus.Hidden) { - await this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollMods.roll.positive.Push.status", RollModStatus.ToggledOff); - } - } - }, - contextmenu: (event) => { - event.preventDefault(); - const elem$ = $(event.currentTarget); - const status = elem$.data("status"); - const cat = elem$.data("cat"); - const posNeg = elem$.data("posNeg"); - const name = elem$.data("name"); - this._toggleRollModContext(`rollCollab.rollMods.${cat}.${posNeg}.${name}.status`, status); - } - }); - html.find("[data-action='tradePosition']").on({ - click: (event) => { - const curVal = `${$(event.currentTarget).data("value")}`; - if (curVal === "false") { - this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", "effect"); - } - else { - this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", false); - } - } - }); - html.find("[data-action='tradeEffect']").on({ - click: (event) => { - const curVal = `${$(event.currentTarget).data("value")}`; - if (curVal === "false") { - this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", "position"); - } - else { - this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", false); - } - } - }); - } - async _onSubmit(event, { updateData } = {}) { - return super._onSubmit(event, { updateData, preventClose: true }) - .then((returnVal) => { this.render(); return returnVal; }); - } - async close(options = {}) { - eLog.checkLog3("rollCollab", "RollCollab.close()", { options }); - if (options.rollID) { - return super.close({}); - } - this.document.setFlag(C.SYSTEM_ID, "rollCollab", null); - socketlib.system.executeForEveryone("closeRollCollab", this.rollID); - return undefined; - } - render(force, options) { - if (!this.document.getFlag(C.SYSTEM_ID, "rollCollab")) { - return this; - } - return super.render(force, options); - } -} -export default BladesRollCollabSheet; \ No newline at end of file diff --git a/scss/chat/_chat.scss b/scss/chat/_chat.scss index 6906f88b..13efcec1 100644 --- a/scss/chat/_chat.scss +++ b/scss/chat/_chat.scss @@ -1,53 +1,144 @@ .chat-message { background: var(--blades-black); - // .message-header { + .message-header { + position: relative; + z-index: 1; - // } + .message-sender { + color: var(--blades-grey); + } + } .message-content { - .dice-roll-strip { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - height: 30px; + .chat-message-bg { + position: absolute; + top: 0; + left: 0; + height: 100%; width: 100%; - align-items: stretch; - justify-content: space-evenly; + z-index: 0; - .blades-die { - display: block; - height: 30px; + &.roll-position-risky { + background-image: url("../assets/animations/chat/roll-position-risky.webp"); + background-size: cover; + } + } - img { - height: 30px; - width: 30px; - display: block; - } + .blades-roll { + position: relative; + z-index: 2; + + .chat-header { + margin: 0; + padding: 0; + background: transparent; + box-shadow: none; + color: var(--blades-grey); + } + + h1.chat-header { + margin: 0; + padding: 0; + text-align: center; + background: transparent; + box-shadow: none; + font-size: 32px; + color: var(--blades-gold); + } - &.blades-die-critical { - outline: 2px solid var(--blades-gold-bright); + .roll-states { + justify-content: space-between; + padding: 0 20px; + .roll-state-container { + flex-basis: 30%; + flex-grow: 0; + flex-shrink: 0; } - &.blades-die-success { - outline: 2px solid var(--blades-white-bright); + + h4.roll-state-label { + font-family: var(--font-primary); + // border-bottom: 1px solid white; + font-size: 12px; + display: block; + width: 100%; + text-align: center; + white-space: nowrap; + color: var(--blades-grey); + margin-top: -4px; + } + + h3.roll-state { + white-space: nowrap; } - // &.blades-die-partial { + } - // } - // &.blades-die-fail { + h2.chat-header.roll-position { + text-align: right; + font-family: var(--font-primary); - // } - &.blades-die-ghost { - img { opacity: 0.5 } + .position-text { + margin: 0 15px; + transform-origin: 50% 50%; + scale: 1.5; + display: inline-block; + font-family: var(--font-emphasis); + + &.position-text-controlled {} + &.position-text-risky {} + &.position-text-desperate { } } - &.blades-die-resistance { - outline: 2px solid var(--blades-cyan-bright); + } + + .dice-roll-strip { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + height: 30px; + width: 100%; + align-items: stretch; + justify-content: center; + gap: 10px; + margin: 10px 0; + + .blades-die { + display: block; + height: 30px; + + img { + height: 30px; + width: 30px; + display: block; + } + + &.blades-die-critical { + outline: 2px solid var(--blades-gold-bright); + + } + &.blades-die-success { + outline: 2px solid var(--blades-white-bright); + } + // &.blades-die-partial { + + // } + // &.blades-die-fail { + + // } + &.blades-die-ghost { + img { opacity: 0.5 } + } + &.blades-die-resistance { + outline: 2px solid var(--blades-blue-bright); + } } } } + .dice-roll-strip { + } + .chat-label, .chat-trait-label { background-color: var(--blades-grey-bright); font-family: var(--font-emphasis); diff --git a/scss/components/_comps.scss b/scss/components/_comps.scss index be89bd5a..60c6f5b7 100644 --- a/scss/components/_comps.scss +++ b/scss/components/_comps.scss @@ -825,27 +825,63 @@ } } +// .consequence-container.flex-vertical.full-width { +// --container-height: 40px; +// height: calc(var(--container-height) * 0.75); +// max-height: calc(var(--container-height) * 0.75); +// justify-content: flex-start; +// } .comp.consequence-display-container { --container-height: 40px; - --container-left-shift: 50px; - - --csq-icon-dark: var(--blades-red-dark); - --csq-icon-med: var(--blades-red); - --csq-icon-bright: var(--blades-red-bright); + --container-left-shift: 70px; + --csq-icon-bg-color: var(--blades-black-dark); --csq-type-bg: var(--csq-icon-dark); - --csq-type-color: var(--blades-black-dark); - // --csq-name-color: var(--csq-icon-bright); + --csq-button-size-mult: 0.33; - --csq-icon-bg-color: var(--blades-black-dark); + @keyframes anim-glow { + 0% { + box-shadow: 0 0 0px var(--blades-red-bright); + background-color: var(--blades-red-darkest); + } - // --csq-icon-border-color: var(--blades-red); - // --csq-icon-stroke-color: var(--blades-grey-bright); + 10% { + background-color: var(--blades-red-bright) + } - --csq-button-size-mult: 0.33; - // --csq-button-bg-color: var(--blades-black); - // --csq-button-color: var(--blades-grey-bright); + 100% { + box-shadow: 0 0 10px 10px transparent; + background-color: var(--blades-red-darkest); + } + } + + @keyframes icon-glow { + 0% { + filter: brightness(1); + } + 10% { + filter: brightness(1); + } + 100% { + filter: brightness(1); + } + } + + @keyframes icon-red-pulse { + 0% { + scale: 1; + fill: var(--blades-grey); + } + 10% { + scale: 1.1; + fill: var(--blades-red-bright); + } + 100% { + scale: 1; + fill: var(--blades-grey); + } + } position: relative; display: block; @@ -853,9 +889,25 @@ max-height: var(--container-height); min-height: var(--container-height); - // .base-consequence { opacity: 0 !important } + .base-consequence { + --csq-icon-dark: var(--blades-black); + --csq-icon-med: var(--blades-grey); + --csq-icon-bright: var(--blades-white); + --csq-type-color: var(--blades-grey); + --csq-name-color: var(--blades-white); + } + + .accept-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-red-dark); + --csq-icon-med: var(--blades-red); + --csq-icon-bright: var(--blades-red-bright); + --csq-type-color: var(--blades-black-dark); + --csq-name-color: var(--blades-red); + } + .resist-consequence { - // opacity: 1 !important; + opacity: 0; --csq-icon-dark: var(--blades-gold-dark); --csq-icon-med: var(--blades-gold); --csq-icon-bright: var(--blades-gold-bright); @@ -863,24 +915,62 @@ --csq-name-color: var(--blades-gold-bright); } + .special-armor-consequence { + opacity: 0; + --csq-icon-dark: var(--blades-blue-dark); + --csq-icon-med: var(--blades-blue); + --csq-icon-bright: var(--blades-blue-bright); + --csq-type-color: var(--blades-blue-dark); + --csq-name-color: var(--blades-blue-bright); + } + + .consequence-interaction-pad { + display: none; + display: block; + position: absolute; + z-index: 2; + pointer-events: auto; + height: 100%; + top: 0; + // outline: 1px dotted cyan; + + &.accept-consequence-pad { + --pad-left-shift: calc(var(--container-left-shift) + (var(--container-height))); + left: var(--pad-left-shift); + width: calc(100% - var(--pad-left-shift)); + } + + &.resist-consequence-pad, &.special-armor-consequence-pad { + left: 0; + width: calc(var(--container-left-shift) - (var(--container-height) * 0)); + } + + &.special-armor-consequence-pad { + height: 40%; + z-index: 3; + } + } + .consequence-icon-container { position: relative; - height: var(--container-height); - width: var(--container-height); - min-width: var(--container-height); - max-width: var(--container-height); - min-height: var(--container-height); - max-height: var(--container-height); + height: calc(var(--container-height) * 0.75); + max-width: calc(var(--container-height) * 0.75); background: transparent; left: var(--container-left-shift); z-index: 1; + pointer-events: auto; + transition: 0.2s; + + &:hover { + filter: brightness(2); + } .consequence-icon-circle { position: absolute; translate: -50% -50%; - transform-origin: 50% 50%; - top: 50%; - left: 50%; + transform-origin: 100% 0%; + top: calc(var(--container-height) * 0.5); + left: calc(var(--container-height) * 0.5); border-radius: 50%; height: var(--container-height); width: var(--container-height); @@ -902,6 +992,27 @@ .fill-bright { fill: var(--csq-icon-bright) } .fill-radial { fill: var(--csq-icon-med) } .fill-linear { fill: var(--csq-icon-med) } + + path { transform-origin: 50% 50% } + } + + &.base-consequence { + scale: 0.75; + animation: icon-glow 2s ease infinite; + pointer-events: auto; + + svg path { + animation: icon-red-pulse 2s ease infinite; + } + + &:hover { + filter: brightness(2) !important; + animation: none; + } + } + + &.resist-consequence, &.special-armor-consequence { + outline-width: 2px; } .consequence-icon { @@ -915,16 +1026,22 @@ display: flex; flex-direction: row; flex-wrap: nowrap; - bottom: 0px; + bottom: -10px; .consequence-button-bg { position: absolute; z-index: -1; height: 100%; width: calc(100% + 24px); + transform-origin: 0% 50%; top: 0px; background: var(--csq-icon-bright); display: block; + + &.consequence-resist-button-bg, + &.consequence-special-armor-button-bg { + transform-origin: 100% 50%; + } } .consequence-button-label { @@ -934,6 +1051,12 @@ font-size: 10px; line-height: 14px; color: var(--blades-grey); + font-weight: 800; + text-shadow: + // 0px 0px 1px var(--blades-black-dark), + // 0px 0px 0.5px var(--blades-black-dark), + 0px 0px 1px var(--blades-black-dark); + letter-spacing: 1; // color: var(--blades-black-dark); text-transform: uppercase; // text-indent: 3px; @@ -955,7 +1078,7 @@ } &.consequence-resist-button-container { - right: calc(100% - 3px); + right: 100%; .consequence-button-bg { left: -7px; @@ -963,7 +1086,7 @@ } } &.consequence-accept-button-container { - left: 100%; + left: 140%; .consequence-button-bg { right: -7px; @@ -982,7 +1105,7 @@ height: calc(var(--container-height) * 0.33); transform-origin: 0% 50%; left: calc(var(--container-height) + var(--container-left-shift) - 10px); - top: 0px; + top: -2px; padding: 0 5px 0 15px; .consequence-type-bg { @@ -991,21 +1114,23 @@ left: -20px; height: 100%; width: 170px; + transform-origin: 0% 50%; transform: skewX(-45deg); - background: var(--csq-type-bg); + background: var(--csq-icon-dark); } .consequence-type { position: absolute; top: 0; - left: 10px; + // left: calc(var(--container-height) + var(--container-left-shift) - 75px); + transform-origin: 0% 50%; white-space: nowrap; font-family: Oswald, sans-serif; text-transform: uppercase; text-align: right; font-size: 10px; color: var(--csq-type-color); - font-weight: bold; + font-weight: normal; } } @@ -1023,9 +1148,10 @@ z-index: 1; padding: 0 5px 0 35px; font-size: 14px; - line-height: calc(var(--container-height) * 0.55); + line-height: 17px; font-family: Kirsty, serif; font-variant: small-caps; + transform-origin: 0% 50%; // text-shadow: var(--text-shadow-dark); color: var(--csq-icon-bright); font-style: italic; @@ -1040,7 +1166,7 @@ .consequence-footer-container { position: absolute; height: calc(var(--container-height) * var(--csq-button-size-mult)); - width: 120px; + width: auto; bottom: 0; top: unset; left: calc(var(--container-height) + var(--container-left-shift) - 20px); @@ -1054,9 +1180,20 @@ background: var(--csq-icon-bright); display: block; transform: skewX(45deg); + transform-origin: 0% 50%; + + &.resist-consequence { + width: 120px; + } + + &.special-armor-consequence { + width: 250px; + } } - .consequence-resist-attribute { + .consequence-footer-message { + position: absolute; + white-space: nowrap; font-family: Oswald, sans-serif; font-weight: bold; color: var(--blades-black-dark); @@ -1064,6 +1201,7 @@ line-height: 14px; padding-left: 25px; justify-content: flex-start; + transform-origin: 0% 50%; gap: 5px; } .dotline { diff --git a/scss/core/_globals.scss b/scss/core/_globals.scss index a06de769..49274237 100644 --- a/scss/core/_globals.scss +++ b/scss/core/_globals.scss @@ -137,7 +137,7 @@ .grey { color: var(--blades-grey) !important } .white { color: var(--blades-white) !important } .white-bright { color: var(--blades-white-bright) !important } - .cyan-bright { color: var(--blades-cyan-bright) !important } + .cyan-bright { color: var(--blades-blue-bright) !important } .uppercase { text-transform: uppercase !important } .inline-code { font-family: var(--font-mono) !important; diff --git a/scss/core/_vars.scss b/scss/core/_vars.scss index 2a530d6b..a72569c6 100644 --- a/scss/core/_vars.scss +++ b/scss/core/_vars.scss @@ -143,25 +143,70 @@ $isCompactLayout: true; --blades-black-nums: 32, 32, 32; --blades-black-dark-nums: 0, 0, 0; - --blades-gold-bright-nums: 255, 231, 92; // 253, 212, 112; - --blades-gold-nums: 255, 215, 0; // 171, 146, 84; - --blades-gold-dark-nums: 184, 156, 0; - --blades-gold-darkest-nums: 55, 53, 0; + --blades-gold-bright-nums: 206,180, 71; // 175,149, 37; // 255, 231, 92; + --blades-gold-nums: 143,118, 11; // 255, 215, 0; + --blades-gold-dark-nums: 105, 86, 0; // 184, 156, 0; + --blades-gold-darkest-nums: 64, 52, 0; // 55, 53, 0; - --blades-red-bright-nums: 220, 20, 60; - --blades-red-nums: 204, 0, 0; - --blades-red-dark-nums: 122, 0, 0; - --blades-red-darkest-nums: 50, 0, 0; + --blades-red-bright-nums: 255, 0, 0; + --blades-red-nums: 200, 0, 0; // 255, 0, 0; + --blades-red-dark-nums: 150, 0, 0; + --blades-red-darkest-nums: 50, 0, 0; --blades-green-bright-nums: 20, 220, 60; --blades-green-nums: 0, 204, 0; --blades-green-dark-nums: 0, 122, 0; --blades-green-darkest-nums: 0, 60, 0; - --blades-cyan-bright-nums: 198, 255, 255; - --blades-cyan-nums: 150, 255, 255; - --blades-cyan-dark-nums: 40, 120, 120; - --blades-cyan-darkest-nums: 25, 49, 49; + --blades-blue-bright-nums: 198, 255, 255; + --blades-blue-nums: 150, 255, 255; + --blades-blue-dark-nums: 40, 120, 120; + --blades-blue-darkest-nums: 25, 49, 49; + + /* + NEW COLOR PALETTE OVERRIDE + + == GOLD == + http://paletton.com/#uid=11n0u0kNTr2qtG1K2DKRbkEVqcT + + shade 0 = #D7AF00 = rgb(215,175, 0) = rgba(215,175, 0,1) = rgb0(0.843,0.686,0) + shade 1 = #FFD82C = rgb(255,216, 44) = rgba(255,216, 44,1) = rgb0(1,0.847,0.173) + shade 2 = #FFCF00 = rgb(255,207, 0) = rgba(255,207, 0,1) = rgb0(1,0.812,0) + shade 3 = #A58600 = rgb(165,134, 0) = rgba(165,134, 0,1) = rgb0(0.647,0.525,0) + shade 4 = #675300 = rgb(103, 83, 0) = rgba(103, 83, 0,1) = rgb0(0.404,0.325,0)' + + == RED == + http://paletton.com/#uid=1000u0kTixTijNOwGQpTXmEXg9Y + shade 0 = #FF0000 = rgb(255, 0, 0) = rgba(255, 0, 0,1) = rgb0(1,0,0) + shade 1 = #FF6D6D = rgb(255,109,109) = rgba(255,109,109,1) = rgb0(1,0.427,0.427) + shade 2 = #FF0000 = rgb(255, 0, 0) = rgba(255, 0, 0,1) = rgb0(1,0,0) + shade 3 = #B40000 = rgb(180, 0, 0) = rgba(180, 0, 0,1) = rgb0(0.706,0,0) + shade 4 = #4F0000 = rgb( 79, 0, 0) = rgba( 79, 0, 0,1) = rgb0(0.31,0,0) + + == BLUE == + http://paletton.com/#uid=13i0u0kTixTodNREARdTRoAV1g4 + shade 0 = #009F9F = rgb( 0,159,159) = rgba( 0,159,159,1) = rgb0(0,0.624,0.624) + shade 1 = #34D5D5 = rgb( 52,213,213) = rgba( 52,213,213,1) = rgb0(0.204,0.835,0.835) + shade 2 = #00E0E0 = rgb( 0,224,224) = rgba( 0,224,224,1) = rgb0(0,0.878,0.878) + shade 3 = #007676 = rgb( 0,118,118) = rgba( 0,118,118,1) = rgb0(0,0.463,0.463) + shade 4 = #004D4D = rgb( 0, 77, 77) = rgba( 0, 77, 77,1) = rgb0(0,0.302,0.302) + */ + + --blades-gold-bright-nums: 255,216, 44; + --blades-gold-nums: 215,175, 0; // 255,207, 0 + --blades-gold-dark-nums: 165,134, 0; + --blades-gold-darkest-nums: 103, 83, 0; + + /* --blades-red-bright-nums: 255,109,109; + --blades-red-nums: 255, 0, 0; + --blades-red-dark-nums: 180, 0, 0; + --blades-red-darkest-nums: 79, 0, 0; */ + + --blades-blue-bright-nums: 0,224,224; + --blades-blue-nums: 52,213,213; // 0,159,159 + --blades-blue-dark-nums: 0,118,118; + --blades-blue-darkest-nums: 0, 77, 77; + /* END OVERRIDE */ --blades-white-bright: rgba(var(--blades-white-bright-nums), 1); --blades-white: rgba(var(--blades-white-nums), 1); @@ -171,6 +216,7 @@ $isCompactLayout: true; --blades-black: rgba(var(--blades-black-nums), 1); --blades-black-dark: rgba(var(--blades-black-dark-nums), 1); + --blades-gold-brightest: rgba(var(--blades-gold-brightest-nums), 1); --blades-gold-bright: rgba(var(--blades-gold-bright-nums), 1); --blades-gold: rgba(var(--blades-gold-nums), 1); --blades-gold-dark: rgba(var(--blades-gold-dark-nums), 1); @@ -186,10 +232,10 @@ $isCompactLayout: true; --blades-green-dark: rgba(var(--blades-green-dark-nums), 1); --blades-green-darkest: rgba(var(--blades-green-darkest-nums), 1); - --blades-cyan-bright: rgba(var(--blades-cyan-bright-nums), 1); - --blades-cyan: rgba(var(--blades-cyan-nums), 1); - --blades-cyan-dark: rgba(var(--blades-cyan-dark-nums), 1); - --blades-cyan-darkest: rgba(var(--blades-cyan-darkest-nums), 1); + --blades-blue-bright: rgba(var(--blades-blue-bright-nums), 1); + --blades-blue: rgba(var(--blades-blue-nums), 1); + --blades-blue-dark: rgba(var(--blades-blue-dark-nums), 1); + --blades-blue-darkest: rgba(var(--blades-blue-darkest-nums), 1); --blades-white-fade: rgba(var(--blades-white-nums), 0.5); --blades-white-fade-strong: rgba(var(--blades-white-nums), 0.25); @@ -203,10 +249,10 @@ $isCompactLayout: true; --blades-red-dark-fade: rgba(var(--blades-red-dark-nums), 0.5); --blades-green-dark-fade: rgba(var(--blades-green-dark-nums), 0.5); - --blades-cyan-dark-fade: rgba(var(--blades-cyan-dark-nums), 0.5); + --blades-blue-dark-fade: rgba(var(--blades-blue-dark-nums), 0.5); --blades-red-dark-fade-strong: rgba(var(--blades-red-dark-nums), 0.25); --blades-green-dark-fade-strong: rgba(var(--blades-green-dark-nums), 0.25); - --blades-cyan-dark-fade-strong: rgba(var(--blades-cyan-dark-nums), 0.25); + --blades-blue-dark-fade-strong: rgba(var(--blades-blue-dark-nums), 0.25); // #endregion ░░░░[Blades Colors & Fades]░░░░ // #region ░░░░░░░ Numerically-Defined Colors (for Emu Stylesheet) ░░░░░░░ ~ --color-primary: var(--blades-white-nums); diff --git a/scss/dialog/_dialogs.scss b/scss/dialog/_dialogs.scss index 243fe99f..4d41d6b7 100644 --- a/scss/dialog/_dialogs.scss +++ b/scss/dialog/_dialogs.scss @@ -84,7 +84,7 @@ .comp.fine-quality { .comp-body { - .comp-title { color: var(--blades-cyan) } + .comp-title { color: var(--blades-blue) } } } @@ -185,7 +185,7 @@ width: 600px; background: var(--section-bg-color); - &.consequence-section-controlled { --section-bg-color: var(--blades-cyan-dark-fade) } + &.consequence-section-controlled { --section-bg-color: var(--blades-blue-dark-fade) } &.consequence-section-risky { --section-bg-color: transparent } &.consequence-section-desperate { --section-bg-color: var(--blades-red-dark-fade-strong) } @@ -201,7 +201,7 @@ text-shadow: none; box-shadow: none; - &.consequence-header-controlled { --h1-color: var(--blades-cyan) } + &.consequence-header-controlled { --h1-color: var(--blades-blue) } &.consequence-header-risky { --h1-color: var(--blades-grey-bright) } &.consequence-header-desperate { --h1-color: var(--blades-red) } } diff --git a/scss/sheets/_roll-collab-sheet.scss b/scss/sheets/_roll-collab-sheet.scss index 8febaa27..133d22f8 100644 --- a/scss/sheets/_roll-collab-sheet.scss +++ b/scss/sheets/_roll-collab-sheet.scss @@ -620,9 +620,9 @@ } &.position-controlled { - --final-block-text-color: var(--blades-cyan-bright); - --final-block-background-color: var(--blades-cyan-dark-fade); - --final-block-border-color: var(--blades-cyan); + --final-block-text-color: var(--blades-blue-bright); + --final-block-background-color: var(--blades-blue-dark-fade); + --final-block-border-color: var(--blades-blue); } } @@ -640,9 +640,9 @@ } &.effect-great { - --final-block-text-color: var(--blades-cyan-bright); - --final-block-background-color: var(--blades-cyan-dark-fade); - --final-block-border-color: var(--blades-cyan); + --final-block-text-color: var(--blades-blue-bright); + --final-block-background-color: var(--blades-blue-dark-fade); + --final-block-border-color: var(--blades-blue); } &.effect-extreme { @@ -815,8 +815,8 @@ &.gm-control-effect-great, &.gm-control-position-controlled { - --gm-control-border: var(--blades-cyan-bright); - --gm-control-background: var(--blades-cyan-dark); + --gm-control-border: var(--blades-blue-bright); + --gm-control-background: var(--blades-blue-dark); } &.gm-control-effect-extreme { @@ -1079,10 +1079,10 @@ } &.roll-type-fortune { - --roll-type-header-color: var(--blades-cyan-bright); - --roll-type-header-bg-color: var(--blades-cyan-dark); - --roll-type-header-underline-color: var(--blades-cyan-bright); - --roll-type-header-shadow-color: var(--blades-cyan-dark); + --roll-type-header-color: var(--blades-blue-bright); + --roll-type-header-bg-color: var(--blades-blue-dark); + --roll-type-header-underline-color: var(--blades-blue-bright); + --roll-type-header-shadow-color: var(--blades-blue-dark); } .roll-type-header { @@ -1779,7 +1779,7 @@ } .cyan-bright, .cyan-bright * { - color: var(--blades-cyan-bright) !important; + color: var(--blades-blue-bright) !important; } .tooltip.tooltip-roll-stress { diff --git a/scss/sheets/_score-sheet.scss b/scss/sheets/_score-sheet.scss index df6d43dc..a6eca24f 100644 --- a/scss/sheets/_score-sheet.scss +++ b/scss/sheets/_score-sheet.scss @@ -50,7 +50,7 @@ position: relative; &:last-child { grid-area: controls-right; - // background: var(--blades-cyan-dark); + // background: var(--blades-blue-dark); } .toggle-icon { diff --git a/scss/style.scss b/scss/style.scss index 2b9a7ac0..1c0bb192 100644 --- a/scss/style.scss +++ b/scss/style.scss @@ -89,6 +89,7 @@ #chat { @import "./core/reset"; + @import './core/globals'; &, * { diff --git a/templates/chat/roll-result-action-roll.hbs b/templates/chat/roll-result-action-roll.hbs new file mode 100644 index 00000000..7c515888 --- /dev/null +++ b/templates/chat/roll-result-action-roll.hbs @@ -0,0 +1,65 @@ +
+ +
+ + {{!-- Roll Trait --}} +

+ {{case "upper" rollTrait}} +

+ + {{!-- Opposition --}} + {{#if rollOpposition}} +

+ vs. + {{case "title" rollOpposition.rollOppName}} +

+ {{/if}} + + {{!-- Position & Effect --}} +
+
+

{{case "title" finalPosition}}

+

Position

+
+
+

{{case "title" finalEffect}}

+

Effect

+
+
+ + {{!-- Dice Rolls --}} +
{{{dieValsHTML}}}
+ + {{!-- Roll Result --}} +

{{case "upper" rollResult}}

+ + {{!-- Roll Result Description --}} +

{{rollResultDescription}}

+ + {{!-- Consequences --}} + {{#if (test rollResult "==" "partial")}} +
+ {{log "ConsequenceContainer" this}} + {{#with (lookup flagData.consequenceData finalPosition) as |csqData|}} + {{#each csqData.partial as |cData cIndex|}} + {{#if cData.resistedTo}} + {{> "systems/eunos-blades/templates/components/consequence.hbs" cData + isResistanceVisible=true }} + {{/if}} + {{/each}} + {{/with}} +
+ {{/if}} + {{#if (test rollResult "==" "fail")}} +
+ {{#with (lookup flagData.consequenceData finalPosition) as |csqData|}} + {{#each csqData.fail as |cData cIndex|}} + {{#if cData.resistedTo}} + {{> "systems/eunos-blades/templates/components/consequence.hbs" cData + isResistanceVisible=true }} + {{/if}} + {{/each}} + {{/with}} +
+ {{/if}} +
diff --git a/templates/chat/action-roll.hbs b/templates/chat/roll-result-fortune-roll.hbs similarity index 100% rename from templates/chat/action-roll.hbs rename to templates/chat/roll-result-fortune-roll.hbs diff --git a/templates/chat/resistance-roll.hbs b/templates/chat/roll-result-indulgevice-roll.hbs similarity index 100% rename from templates/chat/resistance-roll.hbs rename to templates/chat/roll-result-indulgevice-roll.hbs diff --git a/templates/chat/roll-result-resistance-roll.hbs b/templates/chat/roll-result-resistance-roll.hbs new file mode 100644 index 00000000..733c89ed --- /dev/null +++ b/templates/chat/roll-result-resistance-roll.hbs @@ -0,0 +1,26 @@ +
+ {{#if attribute_label}}
{{localize attribute_label}}
{{/if}} + + {{#if (eq roll_status "critical-success")}} +
{{localize "BITD.RollCriticalSuccess"}}
+
+

{{{localize "BITD.RollResistanceCritical"}}}

+
+ {{else}} +
{{localize "BITD.RollSuccess"}}
+

{{{localize "BITD.RollResistance" stress=stress}}}

+ {{/if}} + {{#if note}} + {{note}} + {{/if}} + +
    + {{#each this.rolls}} + {{#if this.result}} +
  1. {{{this.result}}}
  2. + {{else}} +
  3. {{{this.roll}}}
  4. + {{/if}} + {{/each}} +
+
diff --git a/templates/components/consequence.hbs b/templates/components/consequence.hbs index dac8af43..6a71a9e6 100644 --- a/templates/components/consequence.hbs +++ b/templates/components/consequence.hbs @@ -7,22 +7,31 @@ data-resist-name="{{resistedTo.name}}" data-resist-type="{{resistedTo.type}}" > +
+
+ {{#if specialArmorTo}} +
+ {{/if}} +
{{{compileSvg type icon}}}
-
+
+ {{{compileSvg type icon}}} +
+
{{{compileSvg resistedTo.type resistedTo.icon}}}
{{#if specialArmorTo}} -
+
{{{compileSvg specialArmorTo.type specialArmorTo.icon}}}
{{/if}} -
-
+
+
{{> "systems/eunos-blades/templates/components/button-icon.hbs" blockClass="consequence-resist-button" @@ -30,18 +39,18 @@ action="resist-consequence" }}
-
-
+
+
{{> "systems/eunos-blades/templates/components/button-icon.hbs" blockClass="consequence-accept-button" - buttonClass="fa-regular fa-square-check" + buttonClass="fa-solid fa-square-check" action="accept-consequence" }}
{{#if specialArmorTo}} -
-
+
+
{{> "systems/eunos-blades/templates/components/button-icon.hbs" blockClass="consequence-special-armor-button" @@ -55,16 +64,18 @@
-
+
+ + {{#unless (test resistedTo.type "==" "None")}} - + {{/unless}} {{#if specialArmorTo}} {{#unless (test specialArmorTo.type "==" "None")}} - + {{/unless}} {{/if}} @@ -73,26 +84,27 @@
+ {{#if (test resistedTo.type "==" "None")}} - + {{else}} - + {{/if}} {{#if specialArmorTo}} {{#if (test specialArmorTo.type "==" "None")}} - + {{else}} - + {{/if}} {{/if}}
{{!-- AI Options --}}
diff --git a/templates/roll/partials/roll-collab-action-gm.hbs b/templates/roll/partials/roll-collab-action-gm.hbs index f61a7f33..c6f24bb9 100644 --- a/templates/roll/partials/roll-collab-action-gm.hbs +++ b/templates/roll/partials/roll-collab-action-gm.hbs @@ -239,8 +239,6 @@ {{#if consequenceData}} {{#with (lookup consequenceData rollPositionFinal) as |csqData|}}

On Partial Success:

-
-
{{#each csqData.partial as |cData cIndex|}} {{eLog "Partial cData" cData}} @@ -250,11 +248,7 @@ {{/if}} {{/each}}
-
-

On Failure:

-
-
{{#each csqData.fail as |cData cIndex|}} {{#if cData.resistedTo}} @@ -263,8 +257,6 @@ {{/if}} {{/each}}
-
-
{{/with}} {{/if}} {{> "systems/eunos-blades/templates/components/button-icon.hbs" @@ -323,10 +315,9 @@ {{{oddsHTMLStart}}}{{{oddsHTMLStop}}}
-
+
{{#if (test rollPhase "==" "Collaboration")}} - {{/if}} diff --git a/templates/roll/partials/roll-collab-resistance-gm.hbs b/templates/roll/partials/roll-collab-resistance-gm.hbs index 8925a05a..ecc967d7 100644 --- a/templates/roll/partials/roll-collab-resistance-gm.hbs +++ b/templates/roll/partials/roll-collab-resistance-gm.hbs @@ -152,7 +152,7 @@
-
+
{{#if (test rollPhase "==" "Collaboration")}} , resistedTo?: ConsequenceResistOption|false, + canArmorA?: boolean, + armorToAOptions?: Record< + string, // stringified index + ConsequenceResistOption // ai + >, + armorToA?: ConsequenceResistOption|false, + canArmorB?: boolean, + armorToBOptions?: Record< + string, // stringified index + ConsequenceResistOption // ai + >, + armorToB?: ConsequenceResistOption|false, specialArmorTo?: ConsequenceResistOption } @@ -209,7 +222,8 @@ declare global { |BladesActorOfType |BladesItemOfType |BladesItemOfType - |BladesItemOfType; + |BladesItemOfType + |BladesItemOfType; export interface PrimaryDocData { rollPrimaryID?: string, diff --git a/ts/BladesChat.ts b/ts/BladesChat.ts new file mode 100644 index 00000000..2b7388ac --- /dev/null +++ b/ts/BladesChat.ts @@ -0,0 +1,90 @@ +// #region IMPORTS ~ +/* import U from "./core/utilities"; +import C, { + BladesActorType, BladesItemType, RollPermissions, RollType, RollSubType, + RollModStatus, RollModSection, ActionTrait, DowntimeAction, AttributeTrait, + Position, Effect, Factor, RollResult, RollPhase, ConsequenceType, Tag +} from "./core/constants"; +import {BladesActor, BladesPC, BladesCrew} from "./documents/BladesActorProxy"; +import {BladesItem, BladesGMTracker} from "./documents/BladesItemProxy"; +import {ApplyTooltipListeners, ApplyConsequenceListeners} from "./core/gsap"; +import BladesDialog from "./BladesDialog"; */ + +import U from "./core/utilities"; +import C, {BladesActorType, BladesItemType, RollType} from "./core/constants"; +import {BladesPC, BladesCrew} from "./documents/BladesActorProxy"; +import {BladesItem} from "./documents/BladesItemProxy"; +import {ApplyTooltipListeners, ApplyConsequenceListeners} from "./core/gsap"; + +import BladesRoll from "./BladesRoll"; +// #endregion + +class BladesChat extends ChatMessage { + + static Initialize() { + Hooks.on("renderChatMessage", (_msg: ChatMessage, html: JQuery) => { + ApplyTooltipListeners(html); + ApplyConsequenceListeners(html); + }); + return loadTemplates([ + "systems/eunos-blades/templates/chat/roll-result-action-roll.hbs", + "systems/eunos-blades/templates/chat/roll-result-resistance-roll.hbs", + "systems/eunos-blades/templates/chat/roll-result-fortune-roll.hbs", + "systems/eunos-blades/templates/chat/roll-result-indulgevice-roll.hbs" + ]); + } + + static GetRollSpeaker(rollInst: BladesRoll) { + + // Get initial speaker data + const speaker = BladesChat.getSpeaker(); + + // Compare against rollInst.rollPrimary and modify accordingly. + const {rollPrimaryID, rollPrimaryName, rollPrimaryType, rollPrimaryDoc} = rollInst.rollPrimary; + + speaker.alias = rollPrimaryName; + + if (BladesItem.IsType(BladesItemType.cohort_gang, BladesItemType.cohort_expert)) { + speaker.actor = rollPrimaryDoc?.parent?.id ?? speaker.actor; + if (rollPrimaryDoc?.parent instanceof BladesPC) { + speaker.alias = `${speaker.alias} (${rollPrimaryDoc.parent.name})`; + } + } else if (BladesItem.IsType(BladesItemType.gm_tracker, BladesItemType.score)) { + speaker.actor = null; + speaker.alias = "The Gamemaster"; + } else if (rollPrimaryID) { + speaker.actor = rollPrimaryID; + } + + speaker.alias = `${speaker.alias} Rolls ...`; + + return speaker; + } + + static async ConstructRollOutput(rollInst: BladesRoll) { + const speaker = BladesChat.GetRollSpeaker(rollInst); + + const template = `systems/eunos-blades/templates/chat/roll-result-${U.lCase(rollInst.rollType)}-roll.hbs`; + + const templateData: BladesRoll & {rollResultDescription?: string} = rollInst; + if (rollInst.rollResult) { + templateData.rollResultDescription = C.RollResultDescriptions[rollInst.finalPosition][rollInst.rollResult]; + } + + const renderedHTML = await renderTemplate(template, rollInst); + + const messageData = { + speaker, + content: renderedHTML + }; + + BladesChat.create(messageData, {}); + } +} + + +interface BladesChat { + +} + +export default BladesChat; diff --git a/ts/BladesPushAlert.ts b/ts/BladesPushAlert.ts index 482e0ec8..9806413f 100644 --- a/ts/BladesPushAlert.ts +++ b/ts/BladesPushAlert.ts @@ -1,4 +1,5 @@ import U from "./core/utilities"; +import C from "./core/constants"; export default class BladesPushAlert { @@ -75,8 +76,8 @@ export default class BladesPushAlert { U.gsap.from( pushElem$[0], { - background: "rgb(255, 231, 92)", - borderColor: "rgb(255, 255, 255)", + background: C.Colors.bGOLD, + borderColor: C.Colors.WHITE, duration: 10, ease: "power2" } diff --git a/ts/BladesRoll.ts b/ts/BladesRoll.ts index cbf8c868..129ec789 100644 --- a/ts/BladesRoll.ts +++ b/ts/BladesRoll.ts @@ -5,6 +5,7 @@ import {BladesActor, BladesPC, BladesCrew} from "./documents/BladesActorProxy"; import {BladesItem, BladesGMTracker} from "./documents/BladesItemProxy"; import {ApplyTooltipListeners, ApplyConsequenceListeners} from "./core/gsap"; import BladesDialog from "./BladesDialog"; +import BladesChat from "./BladesChat"; // #endregion // #region Types & Type Checking ~ @@ -3139,60 +3140,7 @@ class BladesRoll extends DocumentSheet { } async outputRollToChat() { - const speaker = ChatMessage.getSpeaker(); - - // Const renderedHTML = await this.getChatHTML(); - let renderedHTML = ""; - this.rollPhase = RollPhase.AwaitingChatInput; - - switch (this.rollType) { - case RollType.Action: { - renderedHTML = - - - await renderTemplate("systems/eunos-blades/templates/chat/action-roll.hbs", { - sourceName: this.rollPrimary.rollPrimaryName, - oppName: this.rollOpposition?.rollOppName, - type: U.lCase(this.rollType), - subType: U.lCase(this.rollSubType), - downtimeAction: U.lCase(this.rollDowntimeAction), - position: this.finalPosition, - effect: this.finalEffect, - result: this.rollResult, - trait_label: typeof this.rollTrait === "number" ? `${this.rollTrait} Dice` : U.tCase(this.rollTrait), - dieVals: this.dieValsHTML - }); - break; - } - case RollType.Resistance: { - renderedHTML = await renderTemplate("systems/eunos-blades/templates/chat/resistance-roll.hbs", { - dieVals: this.dieValsHTML, - result: this.rollResult, - trait_label: typeof this.rollTrait === "number" ? `${this.rollTrait} Dice` : U.tCase(this.rollTrait), - stress: this.resistanceStressCost - }); - - break; - } - case RollType.Fortune: { - - break; - } - case RollType.IndulgeVice: { - - break; - } - default: throw new Error(`Unrecognized RollType: ${this.rollType}`); - } - - const messageData = { - speaker, - content: renderedHTML, - type: CONST.CHAT_MESSAGE_TYPES.ROLL, - roll: this.roll - }; - - CONFIG.ChatMessage.documentClass.create(messageData, {}); + BladesChat.ConstructRollOutput(this); } async resolveRoll() { @@ -3260,6 +3208,29 @@ class BladesRoll extends DocumentSheet { await this.document.setFlag(C.SYSTEM_ID, target, value).then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID)); } + async _gmControlCycleTarget(event: ClickEvent) { + event.preventDefault(); + if (!game.user.isGM) { return; } + const elem$ = $(event.currentTarget); + const flagTarget = elem$.data("flagTarget"); + const curVal = elem$.data("curVal"); + const cycleVals = elem$.data("vals")?.split(/\|/); + if (!cycleVals) { + throw new Error(`Unable to parse cycle values from data-vals = ${elem$.data("vals")}`); + } + const curValIndex = cycleVals.indexOf(curVal); + if (curValIndex === -1) { + throw new Error(`Unable to find current value '${curVal}' in cycle values '${elem$.data("vals")}'`); + } + let newValIndex = curValIndex + 1; + if (newValIndex >= cycleVals.length) { + newValIndex = 0; + } + const newVal = cycleVals[newValIndex]; + eLog.checkLog3("gmControlCycleTarget", "gmControlCycleTarget", {flagTarget, curVal, cycleVals, curValIndex, newValIndex, newVal}); + await this.setFlagVal(flagTarget, newVal); + } + /** * Handles resetting value associated with GM number line on a right-click. * @param {ClickEvent} event JQuery context menu event sent to listener. @@ -3487,6 +3458,13 @@ class BladesRoll extends DocumentSheet { click: this._gmControlSetTargetToValue.bind(this), contextmenu: this._gmControlResetTarget.bind(this) }); + /** + * Handles setting values via GM number line (e.g. roll factor boosts/modifications). + * Handles resetting value associated with GM number line on a right-click. + */ + html.find("[data-action=\"gm-cycle-target\"]").on({ + click: this._gmControlCycleTarget.bind(this) + }); /** * Handles setting of Factor toggles: isActive, isPrimary, highFavorsPC, isDominant */ diff --git a/ts/blades.ts b/ts/blades.ts index f2d85df6..6e5a717f 100644 --- a/ts/blades.ts +++ b/ts/blades.ts @@ -3,6 +3,7 @@ import C, {ActionTrait, AttributeTrait, RollType, ConsequenceType, Position, Rol import registerSettings, {initTinyMCEStyles, initCanvasStyles} from "./core/settings"; import {registerHandlebarHelpers, preloadHandlebarsTemplates} from "./core/helpers"; import BladesPushAlert from "./BladesPushAlert"; +import BladesChat from "./BladesChat"; import U from "./core/utilities"; import logger from "./core/logger"; import G, {Initialize as GsapInitialize} from "./core/gsap"; @@ -73,6 +74,7 @@ class GlobalGetter { 0: { type: ConsequenceType.ProwessHarm2, attribute: AttributeTrait.prowess, + attributeVal: 3, name: "Broken Leg", resistOptions: { 0: { @@ -107,47 +109,179 @@ class GlobalGetter { isSelected: true, type: ConsequenceType.None, icon: C.ConsequenceIcons[ConsequenceType.None], + footerMsg: "If vs. Physical Harm", typeDisplay: "" }, icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm2], typeDisplay: C.ConsequenceDisplay[ConsequenceType.ProwessHarm2] + }, + 1: { + type: ConsequenceType.ReducedEffect, + 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", + 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", + typeDisplay: "Level 1 Harm (Lesser)", + attributeVal: 4 } }, [RollResult.fail]: { 0: { - type: ConsequenceType.ProwessHarm2, + type: ConsequenceType.WorsePosition, + 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", + typeDisplay: "Worse Position" + }, + 1: { + type: ConsequenceType.ComplicationMajor, attribute: AttributeTrait.prowess, - name: "Broken Leg", + name: "Your pick snaps off inside the lock.", resistOptions: { 0: { - name: "Sprained Ankle", - isSelected: true, - type: ConsequenceType.ProwessHarm1, - icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm1], - typeDisplay: C.ConsequenceDisplay[ConsequenceType.ProwessHarm1] + name: "Lock remains intact but jammed", + isSelected: false, + type: ConsequenceType.ComplicationMinor, + icon: "main" }, 1: { - name: "Bruised Leg", + 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.ProwessHarm1, - icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm1] + type: ConsequenceType.ComplicationMinor, + icon: "main" + } + }, + resistedTo: { + name: "Pick breaks, but lock is still pickable", + isSelected: true, + type: ConsequenceType.ComplicationMinor, + icon: "main", + typeDisplay: "Minor Complication" + }, + attributeVal: 3, + icon: "main", + 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: "Fractured Foot", + name: "Given Partially Incorrect Information", isSelected: false, - type: ConsequenceType.ProwessHarm1, - icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm1] + type: ConsequenceType.InsightHarm1, + icon: "eye|iris" } }, resistedTo: { + name: "Confused by Deception", + isSelected: true, + type: ConsequenceType.InsightHarm1, + typeDisplay: "Level 1 Harm (Lesser)", + icon: "eye|iris" + }, + specialArmorTo: { name: "Sprained Ankle", isSelected: true, - type: ConsequenceType.ProwessHarm1, - typeDisplay: C.ConsequenceDisplay[ConsequenceType.ProwessHarm1], - icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm1] + type: ConsequenceType.InsightHarm1, + footerMsg: "If vs. Supernatural, Arcane Forces", + icon: C.ConsequenceIcons[ConsequenceType.InsightHarm1], + typeDisplay: "Level 1 Harm (Lesser)" }, - icon: C.ConsequenceIcons[ConsequenceType.ProwessHarm2], - typeDisplay: C.ConsequenceDisplay[ConsequenceType.ProwessHarm2] + icon: "eye|iris", + typeDisplay: "Level 2 Harm (Moderate)", + attributeVal: 4 } } } @@ -220,6 +354,7 @@ class GlobalGetter { BladesRollPrimary, BladesRollOpposition, BladesRollParticipant, + BladesChat, G, U, C, @@ -247,6 +382,7 @@ Hooks.once("init", async () => { CONFIG.Item.documentClass = BladesItemProxy as unknown as typeof Item; CONFIG.Actor.documentClass = BladesActorProxy as unknown as typeof Actor; + CONFIG.ChatMessage.documentClass = BladesChat; // Register sheet application classes Actors.unregisterSheet("core", ActorSheet); diff --git a/ts/core/constants.ts b/ts/core/constants.ts index c07b9d61..a97a52b5 100644 --- a/ts/core/constants.ts +++ b/ts/core/constants.ts @@ -504,30 +504,50 @@ const C = { [ConsequenceType.ResolveHarm4]: "spikes|eyeball|iris", [ConsequenceType.None]: "" }, + RollResultDescriptions: { + [Position.controlled]: { + [RollResult.critical]: "You critically succeed from a controlled position!", + [RollResult.success]: "You fully succeed from a controlled position!", + [RollResult.partial]: "You partially succeed from a controlled position!", + [RollResult.fail]: "You fail from a controlled position!" + }, + [Position.risky]: { + [RollResult.critical]: "You critically succeed from a risky position!", + [RollResult.success]: "You fully succeed from a risky position!", + [RollResult.partial]: "You partially succeed from a risky position!", + [RollResult.fail]: "You fail from a risky position!" + }, + [Position.desperate]: { + [RollResult.critical]: "You critically succeed from a desperate position!", + [RollResult.success]: "You fully succeed from a desperate position!", + [RollResult.partial]: "You partially succeed from a desperate position!", + [RollResult.fail]: "You fail from a desperate position!" + } + }, // Colors: { bWHITE: "rgba(255, 255, 255, 1)", WHITE: "rgba(200, 200, 200, 1)", bGREY: "rgba(170, 170, 170, 1)", - GREY: "rgba(128, 128, 128, 1)", - dGREY: "rgba(78, 78, 78, 1)", - BLACK: "rgba(16, 16, 16, 1)", + GREY: "rgba(119, 119, 119, 1)", + dGREY: "rgba(68, 68, 68, 1)", + BLACK: "rgba(32, 32, 32, 1)", dBLACK: "rgba(0, 0, 0, 1)", - gGOLD: "rgba(255, 254, 200, 1)", - bGOLD: "rgba(171, 146, 84, 1)", - GOLD: "rgba(253, 212, 112, 1)", - dGOLD: "rgba(65, 61, 46, 1)", + bGOLD: "rgba(255,216, 44, 1)", + GOLD: "rgba(215,175, 0, 1)", + dGOLD: "rgba(165,134, 0, 1)", + ddGOLD: "rgba(103, 83, 0, 1)", - RED: "rgba(155, 32, 32, 1)", - dRED: "rgba(70, 14, 14, 1)", - bRED: "rgba(240, 50, 50, 1)", - gRED: "rgba(255, 0, 0, 1)", + bRED: "rgba(255, 0, 0, 1)", + RED: "rgba(200, 0, 0, 1)", + dRED: "rgba(150, 0, 0, 1)", + ddRED: "rgba(50, 0, 0, 1)", - BLUE: "rgba(43, 85, 139, 1)", - dBLUE: "rgba(17, 33, 54, 1)", - bBLUE: "rgba(69, 137, 224, 1)", - gBLUE: "rgba(128, 185, 255, 1)" + bBLUE: "rgba( 0,224,224, 1)", + BLUE: "rgba(52,213,213, 1)", + dBLUE: "rgba(0,118,118, 1)", + ddBLUE: "rgba(0, 77, 77, 1)" }, Loadout: { selections: [ diff --git a/ts/core/gsap.ts b/ts/core/gsap.ts index dfccf7be..b8e4fe76 100644 --- a/ts/core/gsap.ts +++ b/ts/core/gsap.ts @@ -1,4 +1,5 @@ import U from "./utilities"; +import C from "./constants"; // eslint-disable-next-line import/no-unresolved import {TextPlugin} from "gsap/all"; @@ -18,6 +19,515 @@ type gsapEffect = { } const gsapEffects: Record = { + csqEnter: { + effect: (csqContainer: HTMLElement, config) => { + const csqRoot = U.gsap.utils.selector(csqContainer); + // ELog.checkLog3("gsap", "gsapEffects.consequenceEnter -> THIS", {this: this, csqRoot}); + const csqIconCircle = csqRoot(".consequence-icon-circle.base-consequence"); + 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}); + + // Fade out base-consequence components + 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); + + // Brighten the entire container slightly + 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); + + return tl; + }, + defaults: { + brightness: 1.5, + duration: 0.5, + scale: 1.5, + stagger: 0.05, + ease: "sine", + easeStrength: 1.5 + } + }, + csqClickIcon: { + effect: (csqIconContainer: HTMLElement, config) => { + const csqRoot = U.gsap.utils.selector(csqIconContainer); + 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}); + + // Initialize interaction pads to display: none + if (csqInteractionPads.length) { + tl.set(csqInteractionPads, {display: "none"}); + } + + // Fade out the base consequence icon circle + 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); + + // 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); + + // Finally, toggle on interaction pads + if (csqInteractionPads.length) { + tl.set(csqInteractionPads, {display: "block"}); + } + + return tl; + }, + defaults: { + duration: 0.5, + scale: 1.5, + stagger: 0.05, + ease: "sine", + easeStrength: 1.5 + } + }, + csqEnterAccept: { + effect: (csqContainer: HTMLElement) => { + const csqRoot = U.gsap.utils.selector(csqContainer); + const typeLine = csqRoot(".consequence-type-container .consequence-type.accept-consequence"); + const typeLineBg = csqRoot(".consequence-type-container .consequence-type-bg.accept-consequence"); + const buttonRoot = U.gsap.utils.selector(csqRoot(".consequence-button-container.consequence-accept-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}); + + // Turn type line white + 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); + + // 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); + + // 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); + + // 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); + + return tl; + }, + defaults: {} + }, + csqEnterResist: { + effect: (csqContainer: HTMLElement) => { + const csqRoot = U.gsap.utils.selector(csqContainer); + + const typeLine = csqRoot(".consequence-type-container .consequence-type.resist-consequence"); + + const acceptElems = csqRoot(".accept-consequence"); + const specialArmorElems = csqRoot(".special-armor-consequence"); + + const footerBg = csqRoot(".consequence-footer-container .consequence-footer-bg.resist-consequence"); + const attrElem = csqRoot(".consequence-footer-container .consequence-resist-attribute"); + const resistCsqName = csqRoot(".consequence-name.resist-consequence"); + const iconCircle = csqRoot(".consequence-icon-circle.resist-consequence"); + + const buttonRoot = U.gsap.utils.selector(csqRoot(".consequence-button-container.consequence-resist-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}); + + // Fade out all accept elems and special armor elems + tl.to([...acceptElems, ...specialArmorElems], { + opacity: 0, + duration: 0.25, + ease: "sine.out" + }); + + if (typeLine.length) { + // Slide out .consequence-type.resist-consequence from left + tl.fromTo(typeLine, { + x: -15, + scaleX: 0, + opacity: 1, + color: C.Colors.dGOLD + }, { + x: 0, + scaleX: 1, + opacity: 1, + color: C.Colors.dGOLD, + duration: 0.5, + ease: "back.out" + }, 0); + } + + // 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); + + // 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); + + // 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); + + // 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); + + // 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, + 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); + + return tl; + }, + defaults: {} + }, + csqEnterSpecialArmor: { + effect: (csqContainer: HTMLElement) => { + const csqRoot = U.gsap.utils.selector(csqContainer); + + const typeLine = csqRoot(".consequence-type-container .consequence-type.special-armor-consequence"); + + const acceptElems = csqRoot(".accept-consequence"); + const resistElems = csqRoot(".resist-consequence"); + + const footerBg = csqRoot(".consequence-footer-container .consequence-footer-bg.special-armor-consequence"); + const footerMsg = csqRoot(".consequence-footer-container .consequence-special-armor-message"); + 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}); + + // Fade out all accept elems and resist elems + tl.to([...acceptElems, ...resistElems], { + opacity: 0, + duration: 0.25, + ease: "sine.out" + }); + + if (typeLine) { + // Slide out .consequence-type.special-armor-consequence from left + tl.fromTo(typeLine, { + x: -15, + scaleX: 0, + opacity: 1, + color: C.Colors.dBLUE + }, { + x: 0, + scaleX: 1, + opacity: 1, + color: C.Colors.dBLUE, + duration: 0.5, + ease: "back.out" + }, 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 + 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-special-armor-message from left + if (footerMsg) { + tl.fromTo(footerMsg, { + scaleX: 0, + opacity: 1 + }, { + scaleX: 1, + opacity: 1, + duration: 0.5, + ease: "back.inOut" + }, 0); + } + + // Slide out .consequence-name.special-armor-consequence from left + if (specialArmorCsqName) { + tl.fromTo(specialArmorCsqName, { + scaleX: 0, + opacity: 1 + }, { + scaleX: 1, + opacity: 1, + duration: 0.5, + ease: "back.inOut" + }, 0); + } + + // 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, + 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); + + return tl; + }, + defaults: {} + }, blurRemove: { effect: (targets, config) => U.gsap.timeline() .to( @@ -119,11 +629,11 @@ const gsapEffects: Record = { extendTimeline: true }, pulseClockWedges: { - effect: (targets, config) => U.gsap.timeline({duration: 0}), + effect: () => U.gsap.timeline({duration: 0}), defaults: {} }, reversePulseClockWedges: { - effect: (targets, config) => U.gsap.timeline({duration: 0}), + effect: () => U.gsap.timeline({duration: 0}), defaults: {} }, fillCoins: { @@ -155,7 +665,7 @@ const gsapEffects: Record = { { scale: "+=0.2", filter: "none", - color: "rgba(255, 255, 255, 1)", + color: C.Colors.WHITE, opacity: 1, duration: 0.125, ease: "back" @@ -194,7 +704,7 @@ const gsapEffects: Record = { }; /** - * + * Registers relevant GSAP plugins and effects. */ export function Initialize() { if (gsapPlugins.length) { @@ -206,8 +716,8 @@ export function Initialize() { } /** - * - * @param html + * Applies listeners to '.tooltip-trigger' elements in the document. + * @param {JQuery} html The document to be searched. */ export function ApplyTooltipListeners(html: JQuery) { html.find(".tooltip-trigger").each((_, el) => { @@ -236,8 +746,8 @@ export function ApplyTooltipListeners(html: JQuery) { } /** - * - * @param html + * Applies listeners to .consequence-display-container and children found in document. + * @param {JQuery} html The document to be searched. */ export function ApplyConsequenceListeners(html: JQuery) { /** @@ -267,135 +777,113 @@ export function ApplyConsequenceListeners(html: JQuery) { * and don't slide the resistance ones out at all. * */ - html .find(".comp.consequence-display-container") .each((_i, csqContainer) => { - const resistButton = $(csqContainer).find(".consequence-resist-button-container")[0]; - const acceptButton = $(csqContainer).find(".consequence-accept-button-container")[0]; - const specialArmorButton = $(csqContainer).find(".consequence-special-armor-button-container")[0]; - const baseElems = Array.from($(csqContainer).find(".base-consequence")); - const resistElems = Array.from($(csqContainer).find(".resist-consequence")); - const specialArmorElems = Array.from($(csqContainer).find(".special-armor-consequence")); - if (resistButton) { - $(resistButton) - .on({ - mouseenter: function() { - $(resistButton).css("z-index", 10); - U.gsap.to(resistButton, { - scale: 1.25, - filter: "brightness(1.25)", - // xPercent: -50, - // yPercent: -50, - duration: 0.25, - ease: "sine.out" - }); - U.gsap.to(acceptButton, { - scale: 0.8, - filter: "greyscale(1)", - // xPercent: -50, - // yPercent: -50, - duration: 0.5, - ease: "sine.inOut" - }); - U.gsap.to(specialArmorButton, { - scale: 0.8, - filter: "greyscale(1)", - // xPercent: -50, - // yPercent: -50, - duration: 0.5, - ease: "sine.inOut" - }); - U.gsap.to(baseElems, { - opacity: 0, - duration: 0.5, - ease: "sine.out" - }); - U.gsap.to(resistElems, { - opacity: 1, - duration: 0.5, - ease: "sine.out" - }); - }, - mouseleave: function() { - U.gsap.to(resistButton, { - scale: 1, - filter: "brightness(1)", - duration: 0.25, - // xPercent: -50, - // yPercent: -50, - ease: "sine.out" - }).then(() => { - $(resistButton).css("z-index", ""); - }); - U.gsap.to(acceptButton, { - scale: 1, - filter: "", - // xPercent: -50, - // yPercent: -50, - duration: 0.5, - ease: "sine.inOut" - }); - U.gsap.to(specialArmorButton, { - scale: 1, - filter: "", - // xPercent: -50, - // yPercent: -50, - duration: 0.5, - ease: "sine.inOut" - }); - U.gsap.to(baseElems, { - opacity: 1, - duration: 0.5, - ease: "sine.out" - }); - U.gsap.to(resistElems, { - opacity: 0, - duration: 0.5, - ease: "sine.out" - }); - } - }); - } - if (acceptButton) { - $(acceptButton) - .on({ - mouseenter: function() { - $(acceptButton).css("z-index", 10); - U.gsap.to(acceptButton, { - scale: 1.25, - // xPercent: -50, - // yPercent: -50, - filter: "brightness(1.25)", - duration: 0.25, - ease: "sine.out" - }); - U.gsap.to(baseElems, { - filter: "brightness(1.25)", - duration: 0.5, - ease: "sine.out" - }); - }, - mouseleave: function() { - U.gsap.to(acceptButton, { - scale: 1, - filter: "brightness(1)", - // xPercent: -50, - // yPercent: -50, - duration: 0.25, - ease: "sine.out" - }).then(() => { - $(acceptButton).css("z-index", ""); - }); - U.gsap.to(baseElems, { - filter: "brightness(1)", - duration: 0.5, - ease: "sine.out" + + const iconContainer$ = $(csqContainer).find(".consequence-icon-container"); + + const acceptInteractionPad$ = $(csqContainer).find(".accept-consequence-pad"); + const resistInteractionPad$ = $(csqContainer).find(".resist-consequence-pad"); + const specialArmorInteractionPad$ = $(csqContainer).find(".special-armor-consequence-pad"); + + $(csqContainer).data("hoverTimeline", U.gsap.effects.csqEnter(csqContainer)); + $(csqContainer).on({ + mouseenter: function() { + $(csqContainer).css("z-index", 10); + $(csqContainer).data("hoverTimeline").play(); + }, + mouseleave: function() { + if (!(iconContainer$.data("isToggled") || iconContainer$.data("isTogglingOn")) || iconContainer$.data("isTogglingOff")) { + $(csqContainer).data("hoverTimeline").reverse().then(() => { + $(csqContainer).css("z-index", ""); + }); + } + } + }); + + iconContainer$.data("clickTimeline", U.gsap.effects.csqClickIcon(iconContainer$[0])); + iconContainer$.on({ + click: function() { + if (iconContainer$.data("isToggled") || iconContainer$.data("isTogglingOn")) { + iconContainer$.data("isTogglingOn", false); + iconContainer$.data("isTogglingOff", true); + iconContainer$.data("clickTimeline").reverse().then(() => { + iconContainer$.data("isTogglingOff", false); + iconContainer$.data("isToggled", false); + }); + } else { + iconContainer$.data("isTogglingOn", true); + iconContainer$.data("isTogglingOff", false); + + // Find any siblings with toggled-on iconContainers, and toggle them off + Array.from($(csqContainer).siblings(".consequence-display-container")) + .forEach((containerElem) => { + const iContainer$ = $(containerElem).find(".consequence-icon-container"); + if (iContainer$?.data("isToggled") || iContainer$?.data("isTogglingOn")) { + iContainer$.data("isTogglingOn", false); + iContainer$.data("isTogglingOff", true); + iContainer$.data("clickTimeline").reverse().then(() => { + iContainer$.data("isTogglingOff", false); + iContainer$.data("isToggled", false); + $(containerElem).data("hoverTimeline").reverse().then(() => { + $(containerElem).css("z-index", ""); + }); + }); + } }); - } - }); - } + iconContainer$.data("clickTimeline").play().then(() => { + iconContainer$.data("isTogglingOn", false); + iconContainer$.data("isToggled", true); + }); + } + } + }); + + acceptInteractionPad$.data("hoverTimeline", U.gsap.effects.csqEnterAccept(csqContainer)); + acceptInteractionPad$.on({ + mouseenter: function() { + if (iconContainer$.data("isToggled")) { + acceptInteractionPad$.data("hoverTimeline").play(); + } + }, + mouseleave: function() { + acceptInteractionPad$.data("hoverTimeline").reverse(); + } + }); + + resistInteractionPad$.data("hoverTimeline", U.gsap.effects.csqEnterResist(csqContainer)); + resistInteractionPad$.on({ + mouseenter: function() { + if (iconContainer$.data("isToggled")) { + resistInteractionPad$.data("hoverTimeline").play(); + } + }, + mouseleave: function() { + if (iconContainer$.data("isToggled")) { + resistInteractionPad$.data("hoverTimeline").reverse(); + } + } + }); + + specialArmorInteractionPad$.data("hoverTimeline", U.gsap.effects.csqEnterSpecialArmor(csqContainer)); + specialArmorInteractionPad$.on({ + mouseenter: function() { + if (iconContainer$.data("isToggled")) { + specialArmorInteractionPad$.data("hoverTimeline").play(); + } + }, + mouseleave: function() { + if (iconContainer$.data("isToggled")) { + specialArmorInteractionPad$.data("hoverTimeline").reverse(); + } + } + }); + + }); + + } export default U.gsap; diff --git a/ts/core/logger.ts b/ts/core/logger.ts index b8bf9783..cf878c70 100644 --- a/ts/core/logger.ts +++ b/ts/core/logger.ts @@ -13,24 +13,24 @@ const LOGGERCONFIG = { const STYLES = { base: { background: C.Colors.BLACK, - color: C.Colors.bGOLD, + color: C.Colors.dGOLD, "font-family": "Pragmata Pro", padding: "0 25px", "margin-right": "25px" }, log0: { - background: C.Colors.bGOLD, + background: C.Colors.dGOLD, color: C.Colors.dBLACK, "font-size": "16px" }, log1: { background: C.Colors.dBLACK, - color: C.Colors.gGOLD, + color: C.Colors.bGOLD, "font-size": "16px" }, log2: { background: C.Colors.dBLACK, - color: C.Colors.bGOLD, + color: C.Colors.dGOLD, "font-size": "16px" }, log3: { @@ -45,15 +45,15 @@ const STYLES = { "font-size": "10px" }, display: { - color: C.Colors.gGOLD, + color: C.Colors.bGOLD, "font-family": "Kirsty Rg", "font-size": "16px", "margin-left": "-100px", padding: "0 100px" }, error: { - color: C.Colors.gRED, - background: C.Colors.dRED, + color: C.Colors.bRED, + background: C.Colors.ddRED, "font-weight": 500 }, handlebars: { @@ -136,7 +136,7 @@ const eLogger = (type: "checkLog"|"log"|KeyOf = "base", ...conten }); } if (stackTrace) { - console.group("%cSTACK TRACE", `color: ${C.Colors.bGOLD}; font-family: "Pragmata Pro"; font-size: 12px; background: ${C.Colors.BLACK}; font-weight: bold; padding: 0 10px;`); + console.group("%cSTACK TRACE", `color: ${C.Colors.dGOLD}; font-family: "Pragmata Pro"; font-size: 12px; background: ${C.Colors.BLACK}; font-weight: bold; padding: 0 10px;`); console.log(`%c${stackTrace}`, Object.entries(STYLES.stack).map(([prop, val]) => `${prop}: ${val};`).join(" ")); console.groupEnd(); }