diff --git a/assets/dice/image/1.webp b/assets/dice/image/1.webp
new file mode 100644
index 00000000..7cac6498
Binary files /dev/null and b/assets/dice/image/1.webp differ
diff --git a/assets/dice/image/2.webp b/assets/dice/image/2.webp
new file mode 100644
index 00000000..28108406
Binary files /dev/null and b/assets/dice/image/2.webp differ
diff --git a/assets/dice/image/3.webp b/assets/dice/image/3.webp
new file mode 100644
index 00000000..cae5a7c3
Binary files /dev/null and b/assets/dice/image/3.webp differ
diff --git a/assets/dice/image/4.webp b/assets/dice/image/4.webp
new file mode 100644
index 00000000..b689cbd2
Binary files /dev/null and b/assets/dice/image/4.webp differ
diff --git a/assets/dice/image/5.webp b/assets/dice/image/5.webp
new file mode 100644
index 00000000..e6043fcf
Binary files /dev/null and b/assets/dice/image/5.webp differ
diff --git a/assets/dice/image/6-crit.webp b/assets/dice/image/6-crit.webp
new file mode 100644
index 00000000..f00f54a3
Binary files /dev/null and b/assets/dice/image/6-crit.webp differ
diff --git a/assets/dice/image/6.webp b/assets/dice/image/6.webp
new file mode 100644
index 00000000..88264398
Binary files /dev/null and b/assets/dice/image/6.webp differ
diff --git a/assets/dice/image/ghost-1.webp b/assets/dice/image/ghost-1.webp
new file mode 100644
index 00000000..d5b88468
Binary files /dev/null and b/assets/dice/image/ghost-1.webp differ
diff --git a/assets/dice/image/ghost-2.webp b/assets/dice/image/ghost-2.webp
new file mode 100644
index 00000000..1c4ba8f6
Binary files /dev/null and b/assets/dice/image/ghost-2.webp differ
diff --git a/assets/dice/image/ghost-3.webp b/assets/dice/image/ghost-3.webp
new file mode 100644
index 00000000..f5c14950
Binary files /dev/null and b/assets/dice/image/ghost-3.webp differ
diff --git a/assets/dice/image/ghost-4.webp b/assets/dice/image/ghost-4.webp
new file mode 100644
index 00000000..6789e92d
Binary files /dev/null and b/assets/dice/image/ghost-4.webp differ
diff --git a/assets/dice/image/ghost-5.webp b/assets/dice/image/ghost-5.webp
new file mode 100644
index 00000000..144b5e84
Binary files /dev/null and b/assets/dice/image/ghost-5.webp differ
diff --git a/assets/dice/image/ghost-6.webp b/assets/dice/image/ghost-6.webp
new file mode 100644
index 00000000..c3c151b2
Binary files /dev/null and b/assets/dice/image/ghost-6.webp differ
diff --git a/assets/dice/image/grad-1.webp b/assets/dice/image/grad-1.webp
new file mode 100644
index 00000000..ae122615
Binary files /dev/null and b/assets/dice/image/grad-1.webp differ
diff --git a/assets/dice/image/grad-2.webp b/assets/dice/image/grad-2.webp
new file mode 100644
index 00000000..c743b40e
Binary files /dev/null and b/assets/dice/image/grad-2.webp differ
diff --git a/assets/dice/image/grad-3.webp b/assets/dice/image/grad-3.webp
new file mode 100644
index 00000000..32a132d3
Binary files /dev/null and b/assets/dice/image/grad-3.webp differ
diff --git a/assets/dice/image/grad-4.webp b/assets/dice/image/grad-4.webp
new file mode 100644
index 00000000..ab763735
Binary files /dev/null and b/assets/dice/image/grad-4.webp differ
diff --git a/assets/dice/image/grad-5.webp b/assets/dice/image/grad-5.webp
new file mode 100644
index 00000000..c3328cbe
Binary files /dev/null and b/assets/dice/image/grad-5.webp differ
diff --git a/assets/dice/image/grad-6-crit.webp b/assets/dice/image/grad-6-crit.webp
new file mode 100644
index 00000000..792e595d
Binary files /dev/null and b/assets/dice/image/grad-6-crit.webp differ
diff --git a/assets/dice/image/grad-6.webp b/assets/dice/image/grad-6.webp
new file mode 100644
index 00000000..88264398
Binary files /dev/null and b/assets/dice/image/grad-6.webp differ
diff --git a/css/style.min.css b/css/style.min.css
index 084fabb3..3ac73553 100644
--- a/css/style.min.css
+++ b/css/style.min.css
@@ -2219,21 +2219,30 @@ template {
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 .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-button-bg,
+:root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-accept-button-bg,
+:root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-accept-button-bg {
+ width: calc(100% + 30px);
+ right: -7px;
+ transform: skewX(-45deg);
+}
+: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-armor-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-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-armor-button-bg,
:root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg {
+ width: calc(100% + 30px);
transform-origin: 100% 50%;
+ right: calc(-0.5 * var(--container-height));
+ transform: skewX(45deg);
}
-:root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg,
+:root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-armor-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg,
+:root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-armor-button-bg,
:root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg {
- width: calc(100% + 40px);
- margin-left: -10px;
+ width: calc(100% + 35px);
}
: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 {
@@ -2269,24 +2278,21 @@ template {
:root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container {
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 {
- left: -7px;
- transform: skewX(45deg);
-}
: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: 105%;
}
-: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 {
- right: -7px;
- transform: skewX(-45deg);
+:root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container,
+:root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container {
+ right: 100%;
+ transform: translate(0%, 0%) !important;
+ top: 0%;
}
:root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container,
:root * .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container {
right: 100%;
- bottom: 12px;
+ transform: translate(0%, -50%) !important;
+ top: 50%;
}
:root .comp.consequence-display-container .consequence-type-container,
:root * .comp.consequence-display-container .consequence-type-container {
@@ -3981,18 +3987,24 @@ template {
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-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-accept-button-bg {
+ width: calc(100% + 30px);
+ right: -7px;
+ transform: skewX(-45deg);
+}
+: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-armor-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-button-bg {
+ width: calc(100% + 30px);
transform-origin: 100% 50%;
+ right: calc(-0.5 * var(--container-height));
+ transform: skewX(45deg);
}
-:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg {
- width: calc(100% + 40px);
- margin-left: -10px;
+:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-armor-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-button-bg {
+ width: calc(100% + 35px);
}
:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-label {
position: relative;
@@ -4024,20 +4036,18 @@ template {
:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container {
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: 105%;
}
-: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;
- transform: skewX(-45deg);
+:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container {
+ right: 100%;
+ transform: translate(0%, 0%) !important;
+ top: 0%;
}
:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container {
right: 100%;
- bottom: 12px;
+ transform: translate(0%, -50%) !important;
+ top: 50%;
}
:root body.vtt.game.system-eunos-blades #clocks-overlay .comp.consequence-display-container .consequence-type-container {
position: absolute;
@@ -7443,30 +7453,48 @@ template {
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 #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-button-bg,
+:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-accept-button-bg,
+:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-accept-button-bg,
+:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-accept-button-bg,
+:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-accept-button-bg,
+:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-accept-button-bg {
+ width: calc(100% + 30px);
+ right: -7px;
+ transform: skewX(-45deg);
+}
+: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-armor-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-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-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-special-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-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-special-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-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-special-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-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-special-button-bg {
+ width: calc(100% + 30px);
transform-origin: 100% 50%;
+ right: calc(-0.5 * var(--container-height));
+ transform: skewX(45deg);
}
-:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg,
+:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-armor-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-button-bg,
+:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-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-special-button-bg,
+:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-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-special-button-bg,
+:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-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-special-button-bg,
+:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-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-special-button-bg {
- width: calc(100% + 40px);
- margin-left: -10px;
+ width: calc(100% + 35px);
}
: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,
@@ -7514,14 +7542,6 @@ template {
:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container {
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,
-:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg,
-:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container .consequence-button-bg,
-:root body.vtt.game.system-eunos-blades #players .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 #interface .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container,
:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container,
:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container,
@@ -7529,13 +7549,14 @@ template {
:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container {
left: 105%;
}
-: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,
-:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg,
-:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg,
-:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg {
- right: -7px;
- transform: skewX(-45deg);
+:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container,
+:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container,
+:root body.vtt.game.system-eunos-blades #navigation .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container,
+:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container,
+:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container {
+ right: 100%;
+ transform: translate(0%, 0%) !important;
+ top: 0%;
}
:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container,
:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container,
@@ -7543,7 +7564,8 @@ template {
:root body.vtt.game.system-eunos-blades #hotbar .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container,
:root body.vtt.game.system-eunos-blades #players .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container {
right: 100%;
- bottom: 12px;
+ transform: translate(0%, -50%) !important;
+ top: 50%;
}
:root body.vtt.game.system-eunos-blades #interface .comp.consequence-display-container .consequence-type-container,
:root body.vtt.game.system-eunos-blades #controls .comp.consequence-display-container .consequence-type-container,
@@ -10522,30 +10544,48 @@ template {
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 #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-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-accept-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-accept-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-accept-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-accept-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-accept-button-bg {
+ width: calc(100% + 30px);
+ right: -7px;
+ transform: skewX(-45deg);
+}
+: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-armor-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-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-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-special-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-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-special-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-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-special-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-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-special-button-bg {
+ width: calc(100% + 30px);
transform-origin: 100% 50%;
+ right: calc(-0.5 * var(--container-height));
+ transform: skewX(45deg);
}
-:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-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-armor-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-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-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-special-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-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-special-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-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-special-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-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-special-button-bg {
- width: calc(100% + 40px);
- margin-left: -10px;
+ width: calc(100% + 35px);
}
: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,
@@ -10593,14 +10633,6 @@ template {
:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-resist-button-container {
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,
-:root body.vtt.game.system-eunos-blades #navigation #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 #hotbar #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 #players #chat .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 #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container,
:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container,
:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container,
@@ -10608,13 +10640,14 @@ template {
:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container {
left: 105%;
}
-: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,
-:root body.vtt.game.system-eunos-blades #navigation #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 #hotbar #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 #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-accept-button-container .consequence-button-bg {
- right: -7px;
- transform: skewX(-45deg);
+:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container,
+:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container,
+:root body.vtt.game.system-eunos-blades #navigation #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container,
+:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container,
+:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container {
+ right: 100%;
+ transform: translate(0%, 0%) !important;
+ top: 0%;
}
:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container,
:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container,
@@ -10622,7 +10655,8 @@ template {
:root body.vtt.game.system-eunos-blades #hotbar #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container,
:root body.vtt.game.system-eunos-blades #players #chat .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container {
right: 100%;
- bottom: 12px;
+ transform: translate(0%, -50%) !important;
+ top: 50%;
}
:root body.vtt.game.system-eunos-blades #interface #chat .comp.consequence-display-container .consequence-type-container,
:root body.vtt.game.system-eunos-blades #controls #chat .comp.consequence-display-container .consequence-type-container,
@@ -12805,6 +12839,17 @@ template {
pointer-events: auto;
z-index: 20;
}
+:root body.vtt.game.system-eunos-blades #interface #chat #chat-controls, :root body.vtt.game.system-eunos-blades #interface #chat #chat-form,
+:root body.vtt.game.system-eunos-blades #controls #chat #chat-controls,
+:root body.vtt.game.system-eunos-blades #controls #chat #chat-form,
+:root body.vtt.game.system-eunos-blades #navigation #chat #chat-controls,
+:root body.vtt.game.system-eunos-blades #navigation #chat #chat-form,
+:root body.vtt.game.system-eunos-blades #hotbar #chat #chat-controls,
+:root body.vtt.game.system-eunos-blades #hotbar #chat #chat-form,
+:root body.vtt.game.system-eunos-blades #players #chat #chat-controls,
+:root body.vtt.game.system-eunos-blades #players #chat #chat-form {
+ display: none;
+}
: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,
@@ -12851,7 +12896,7 @@ template {
:root body.vtt.game.system-eunos-blades #navigation #chat .flexrow.jump-to-bottom,
:root body.vtt.game.system-eunos-blades #hotbar #chat .flexrow.jump-to-bottom,
:root body.vtt.game.system-eunos-blades #players #chat .flexrow.jump-to-bottom {
- bottom: 154px;
+ bottom: 25px;
z-index: 5;
}
:root body.vtt.game.system-eunos-blades #interface #chat #chat-controls, :root body.vtt.game.system-eunos-blades #interface #chat #chat-form,
@@ -12869,6 +12914,13 @@ template {
place-self: stretch flex-end;
z-index: 1;
}
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message:not(.display-ok),
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message:not(.display-ok),
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message:not(.display-ok),
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message:not(.display-ok),
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message:not(.display-ok) {
+ opacity: 0 !important;
+}
:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll],
:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll],
:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll],
@@ -12894,32 +12946,60 @@ template {
background: transparent;
border: 2px ouset var(--blades-white);
}
-:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll]:not(.active-chat-roll) .trait-label.trait-verb,
-:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll]:not(.active-chat-roll) .trait-label.trait-verb,
-:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll]:not(.active-chat-roll) .trait-label.trait-verb,
-:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll]:not(.active-chat-roll) .trait-label.trait-verb,
-:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll]:not(.active-chat-roll) .trait-label.trait-verb {
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .blades-roll > *:not(.chat-message-speaker-portrait-wrapper),
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .blades-roll > *:not(.chat-message-speaker-portrait-wrapper),
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .blades-roll > *:not(.chat-message-speaker-portrait-wrapper),
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .blades-roll > *:not(.chat-message-speaker-portrait-wrapper),
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .blades-roll > *:not(.chat-message-speaker-portrait-wrapper) {
+ filter: sepia(1) grayscale(0.5) brightness(0.6);
+}
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .trait-label.trait-verb,
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .trait-label.trait-verb,
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .trait-label.trait-verb,
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .trait-label.trait-verb,
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .trait-label.trait-verb {
display: none;
}
-:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll]:not(.active-chat-roll) .consequence-icon-container .consequence-icon-circle.base-consequence,
-:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll]:not(.active-chat-roll) .consequence-icon-container .consequence-icon-circle.base-consequence,
-:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll]:not(.active-chat-roll) .consequence-icon-container .consequence-icon-circle.base-consequence,
-:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll]:not(.active-chat-roll) .consequence-icon-container .consequence-icon-circle.base-consequence,
-:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll]:not(.active-chat-roll) .consequence-icon-container .consequence-icon-circle.base-consequence {
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .consequence-icon-container .consequence-icon-circle.base-consequence,
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .consequence-icon-container .consequence-icon-circle.base-consequence,
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .consequence-icon-container .consequence-icon-circle.base-consequence,
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .consequence-icon-container .consequence-icon-circle.base-consequence,
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .consequence-icon-container .consequence-icon-circle.base-consequence {
animation: none !important;
}
-:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll]:not(.active-chat-roll) .consequence-icon-container .consequence-icon-circle.base-consequence img,
-:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll]:not(.active-chat-roll) .consequence-icon-container .consequence-icon-circle.base-consequence img,
-:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll]:not(.active-chat-roll) .consequence-icon-container .consequence-icon-circle.base-consequence img,
-:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll]:not(.active-chat-roll) .consequence-icon-container .consequence-icon-circle.base-consequence img,
-:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll]:not(.active-chat-roll) .consequence-icon-container .consequence-icon-circle.base-consequence img {
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .consequence-icon-container .consequence-icon-circle.base-consequence img,
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .consequence-icon-container .consequence-icon-circle.base-consequence img,
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .consequence-icon-container .consequence-icon-circle.base-consequence img,
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .consequence-icon-container .consequence-icon-circle.base-consequence img,
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .consequence-icon-container .consequence-icon-circle.base-consequence img {
animation: none !important;
}
-:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll].active-chat-roll,
-:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll].active-chat-roll,
-:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll].active-chat-roll,
-:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll].active-chat-roll,
-:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll].active-chat-roll {
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .chat-message-speaker-portrait-wrapper,
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .chat-message-speaker-portrait-wrapper,
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .chat-message-speaker-portrait-wrapper,
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .chat-message-speaker-portrait-wrapper,
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll]:not(.unresolved-action-roll) .chat-message-speaker-portrait-wrapper {
+ --border-gradient: linear-gradient(-72deg,
+ #565656,
+ #9b9b9b 16%,
+ #565656 21%,
+ #9b9b9b 24%,
+ #383838 27%,
+ #565656 36%,
+ #9b9b9b 45%,
+ #9b9b9b 60%,
+ #565656 72%,
+ #9b9b9b 80%,
+ #565656 84%,
+ #232323);
+ transform-origin: 50% 0%;
+ scale: 0.6 0.75;
+}
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll].unresolved-action-roll,
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll].unresolved-action-roll,
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll].unresolved-action-roll,
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll].unresolved-action-roll,
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll].unresolved-action-roll {
--bg-controlled: url("../assets/animations/chat/roll-position-controlled.webp");
--bg-risky: url("../assets/animations/chat/roll-position-risky.webp");
--bg-desperate: url("../assets/animations/chat/roll-position-desperate.webp");
@@ -12934,18 +13014,18 @@ template {
background: transparent;
outline: none;
}
-:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll].active-chat-roll .trait-label.trait-past-verb,
-:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll].active-chat-roll .trait-label.trait-past-verb,
-:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll].active-chat-roll .trait-label.trait-past-verb,
-:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll].active-chat-roll .trait-label.trait-past-verb,
-:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll].active-chat-roll .trait-label.trait-past-verb {
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll].unresolved-action-roll .trait-label.trait-past-verb,
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll].unresolved-action-roll .trait-label.trait-past-verb,
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll].unresolved-action-roll .trait-label.trait-past-verb,
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll].unresolved-action-roll .trait-label.trait-past-verb,
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll].unresolved-action-roll .trait-label.trait-past-verb {
display: none;
}
-:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll].active-chat-roll .message-content .blades-roll .dice-roll-strip,
-:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll].active-chat-roll .message-content .blades-roll .dice-roll-strip,
-:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll].active-chat-roll .message-content .blades-roll .dice-roll-strip,
-:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll].active-chat-roll .message-content .blades-roll .dice-roll-strip,
-:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll].active-chat-roll .message-content .blades-roll .dice-roll-strip {
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll].unresolved-action-roll .message-content .blades-roll .dice-roll-strip,
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll].unresolved-action-roll .message-content .blades-roll .dice-roll-strip,
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll].unresolved-action-roll .message-content .blades-roll .dice-roll-strip,
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll].unresolved-action-roll .message-content .blades-roll .dice-roll-strip,
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll].unresolved-action-roll .message-content .blades-roll .dice-roll-strip {
margin-top: 5px;
}
:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll],
@@ -13000,6 +13080,19 @@ template {
:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll] .chat-message-speaker-portrait-wrapper,
:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll] .chat-message-speaker-portrait-wrapper,
:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll] .chat-message-speaker-portrait-wrapper {
+ --border-gradient: linear-gradient(-72deg,
+ #6b5630,
+ #9e7f46 16%,
+ #6b5630 21%,
+ #9e7f46 24%,
+ #46381f 27%,
+ #6b5630 36%,
+ #9e7f46 45%,
+ #9e7f46 60%,
+ #6b5630 72%,
+ #9e7f46 80%,
+ #6b5630 84%,
+ #2c2315);
height: var(--speaker-portrait-height);
width: calc(var(--speaker-portrait-height));
position: absolute;
@@ -13009,7 +13102,7 @@ template {
border-radius: calc(0.5 * var(--speaker-portrait-height));
scale: 0.8 1;
transform-origin: 50% 50%;
- background: linear-gradient(-72deg, #6b5630, #9e7f46 16%, #6b5630 21%, #9e7f46 24%, #46381f 27%, #6b5630 36%, #9e7f46 45%, #9e7f46 60%, #6b5630 72%, #9e7f46 80%, #6b5630 84%, #2c2315);
+ background: var(--border-gradient);
background-repeat: no-repeat;
outline: 3px solid black;
box-shadow: 0 0 5px 5px black;
@@ -13072,11 +13165,11 @@ template {
:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll].action-roll + .chat-message.resistance-roll {
margin-top: calc(-1 * var(--chat-vertical-gap));
}
-:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll].action-roll.active-chat-roll + .chat-message.resistance-roll,
-:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll].action-roll.active-chat-roll + .chat-message.resistance-roll,
-:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll].action-roll.active-chat-roll + .chat-message.resistance-roll,
-:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll].action-roll.active-chat-roll + .chat-message.resistance-roll,
-:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll].action-roll.active-chat-roll + .chat-message.resistance-roll {
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll].action-roll.unresolved-action-roll + .chat-message.resistance-roll,
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll].action-roll.unresolved-action-roll + .chat-message.resistance-roll,
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll].action-roll.unresolved-action-roll + .chat-message.resistance-roll,
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll].action-roll.unresolved-action-roll + .chat-message.resistance-roll,
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll].action-roll.unresolved-action-roll + .chat-message.resistance-roll {
margin-top: 0;
}
:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll] .message-content .blades-roll,
@@ -13566,6 +13659,40 @@ template {
padding: 4px;
gap: 0;
}
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label,
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label,
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label,
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label,
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label {
+ font-family: var(--font-emphasis-narrow);
+ font-size: 12px;
+ line-height: 12px;
+ display: block;
+ transform: translate(0%, -50%);
+ top: 50%;
+ text-align: center;
+ white-space: nowrap;
+ color: var(--side-color-secondary);
+}
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label strong,
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label strong,
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label strong,
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label strong,
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label strong {
+ color: var(--side-color-secondary) !important;
+ font-weight: 900 !important;
+}
+:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h3.roll-state,
+:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h3.roll-state,
+:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h3.roll-state,
+:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h3.roll-state,
+:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h3.roll-state {
+ white-space: nowrap;
+ font-size: 18px;
+ line-height: 18px;
+ text-shadow: var(--text-shadow-dark-strong);
+ color: var(--side-color-main);
+}
:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container.roll-state-container-left h4.roll-state-label,
:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container.roll-state-container-left h4.roll-state-label,
:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container.roll-state-container-left h4.roll-state-label,
@@ -13580,6 +13707,7 @@ template {
:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container.roll-state-container-left h3.roll-state,
:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container.roll-state-container-left h3.roll-state {
position: absolute;
+ transform-origin: 100% 0%;
top: 5px;
right: 5px;
scale: var(--side-left-x-scale) 1;
@@ -13601,46 +13729,12 @@ template {
:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container.roll-state-container-right h3.roll-state {
width: 100%;
position: absolute;
+ transform-origin: 0% 0%;
bottom: 5px;
left: 5px;
text-align: left;
scale: var(--side-right-x-scale) 1;
}
-:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label,
-:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label,
-:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label,
-:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label,
-:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label {
- font-family: var(--font-emphasis-narrow);
- font-size: 12px;
- line-height: 12px;
- display: block;
- transform: translate(0%, -50%);
- top: 50%;
- text-align: center;
- white-space: nowrap;
- color: var(--side-color-secondary);
-}
-:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label strong,
-:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label strong,
-:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label strong,
-:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label strong,
-:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h4.roll-state-label strong {
- color: var(--side-color-secondary) !important;
- font-weight: 900 !important;
-}
-:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h3.roll-state,
-:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h3.roll-state,
-:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h3.roll-state,
-:root body.vtt.game.system-eunos-blades #hotbar #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h3.roll-state,
-:root body.vtt.game.system-eunos-blades #players #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-state-container h3.roll-state {
- white-space: nowrap;
- font-size: 18px;
- line-height: 18px;
- transform-origin: 0% 0%;
- text-shadow: var(--text-shadow-dark-strong);
- color: var(--side-color-main);
-}
:root body.vtt.game.system-eunos-blades #interface #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-result-container,
:root body.vtt.game.system-eunos-blades #controls #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-result-container,
:root body.vtt.game.system-eunos-blades #navigation #chat .chat-message[class*=-roll] .message-content .blades-roll .roll-outcome-container .roll-result-container,
@@ -15136,18 +15230,24 @@ template {
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-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-accept-button-bg {
+ width: calc(100% + 30px);
+ right: -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-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-armor-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-button-bg {
+ width: calc(100% + 30px);
transform-origin: 100% 50%;
+ right: calc(-0.5 * var(--container-height));
+ transform: skewX(45deg);
}
-: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-button-bg {
- width: calc(100% + 40px);
- margin-left: -10px;
+:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-armor-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-button-bg {
+ width: calc(100% + 35px);
}
: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;
@@ -15179,20 +15279,18 @@ template {
: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: 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: 105%;
}
-: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;
- transform: skewX(-45deg);
+:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container {
+ right: 100%;
+ transform: translate(0%, 0%) !important;
+ top: 0%;
}
:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container {
right: 100%;
- bottom: 12px;
+ transform: translate(0%, -50%) !important;
+ top: 50%;
}
:root body.vtt.game.system-eunos-blades .app.window-app .comp.consequence-display-container .consequence-type-container {
position: absolute;
@@ -20875,6 +20973,11 @@ template {
margin-right: 5px;
flex-grow: 2;
}
+:root body.vtt.game.system-eunos-blades .app.window-app.sheet.dialog .window-content .consequence-section .roll-consequence-row input.consequence-name.consequence-name-none {
+ box-shadow: none;
+ background: none;
+ cursor: default;
+}
:root body.vtt.game.system-eunos-blades .app.window-app.sheet.dialog .window-content .consequence-section .roll-consequence-row .roll-consequence-type-select {
min-width: 130px;
}
@@ -21583,6 +21686,16 @@ template {
right: 6px;
flex-basis: auto;
}
+:root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab.gm-roll-collab .window-content form .sheet-root .split-root .split-root-right .sheet-main.roll-consequences {
+ min-height: 330px;
+}
+:root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab.gm-roll-collab .window-content form .sheet-root .split-root .split-root-right .sheet-main.roll-consequences h3 {
+ margin-bottom: 10px;
+}
+:root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab.gm-roll-collab .window-content form .sheet-root .split-root .split-root-right .sheet-main.factor-controls {
+ min-height: 112px;
+ place-self: stretch flex-end;
+}
:root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-header {
opacity: 0;
top: -45px;
@@ -22000,8 +22113,8 @@ template {
:root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root .full-root .split-root-left.split-root-left,
:root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root .full-root .split-root-right.split-root-left,
:root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root .full-root .full-root-container.split-root-left {
- flex-basis: 85%;
- max-width: 85%;
+ flex-basis: 80%;
+ max-width: 80%;
}
:root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root .split-root .split-root-left.split-root-right,
:root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root .split-root .split-root-right.split-root-right,
diff --git a/css/tinymce/content.min.css b/css/tinymce/content.min.css
index f4f2135f..2527a44e 100644
--- a/css/tinymce/content.min.css
+++ b/css/tinymce/content.min.css
@@ -1315,18 +1315,24 @@ html .comp.consequence-display-container .consequence-icon-container .consequenc
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-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-button-bg {
+html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-accept-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-accept-button-bg {
+ width: calc(100% + 30px);
+ right: -7px;
+ transform: skewX(-45deg);
+}
+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-armor-button-bg, html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-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-armor-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg {
+ width: calc(100% + 30px);
transform-origin: 100% 50%;
+ right: calc(-0.5 * var(--container-height));
+ transform: skewX(45deg);
}
-html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg {
- width: calc(100% + 40px);
- margin-left: -10px;
+html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-armor-button-bg, html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-armor-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg {
+ width: calc(100% + 35px);
}
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;
@@ -1358,20 +1364,18 @@ html .comp.consequence-display-container .consequence-icon-container .consequenc
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: 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: 105%;
}
-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;
- transform: skewX(-45deg);
+html .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container {
+ right: 100%;
+ transform: translate(0%, 0%) !important;
+ top: 0%;
}
html .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container {
right: 100%;
- bottom: 12px;
+ transform: translate(0%, -50%) !important;
+ top: 50%;
}
html .comp.consequence-display-container .consequence-type-container, :root .comp.consequence-display-container .consequence-type-container {
position: absolute;
@@ -2995,18 +2999,24 @@ html .comp.consequence-display-container .consequence-icon-container .consequenc
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-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-button-bg {
+html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-accept-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-accept-button-bg {
+ width: calc(100% + 30px);
+ right: -7px;
+ transform: skewX(-45deg);
+}
+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-armor-button-bg, html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-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-armor-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg {
+ width: calc(100% + 30px);
transform-origin: 100% 50%;
+ right: calc(-0.5 * var(--container-height));
+ transform: skewX(45deg);
}
-html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg {
- width: calc(100% + 40px);
- margin-left: -10px;
+html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-armor-button-bg, html .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-armor-button-bg, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container .consequence-button-bg.consequence-special-button-bg {
+ width: calc(100% + 35px);
}
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;
@@ -3038,20 +3048,18 @@ html .comp.consequence-display-container .consequence-icon-container .consequenc
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: 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: 105%;
}
-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;
- transform: skewX(-45deg);
+html .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-armor-button-container {
+ right: 100%;
+ transform: translate(0%, 0%) !important;
+ top: 0%;
}
html .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container, :root .comp.consequence-display-container .consequence-icon-container .consequence-button-container.consequence-special-button-container {
right: 100%;
- bottom: 12px;
+ transform: translate(0%, -50%) !important;
+ top: 50%;
}
html .comp.consequence-display-container .consequence-type-container, :root .comp.consequence-display-container .consequence-type-container {
position: absolute;
diff --git a/module/BladesActor.js b/module/BladesActor.js
index de4722fa..76f020c8 100644
--- a/module/BladesActor.js
+++ b/module/BladesActor.js
@@ -837,7 +837,12 @@ class BladesActor extends Actor {
get rollPrimaryID() { return this.id; }
get rollPrimaryDoc() { return this; }
get rollPrimaryName() { return this.name; }
- get rollPrimaryType() { return this.type; }
+ get rollPrimaryType() {
+ if (![BladesActorType.pc, BladesActorType.crew].includes(this.type)) {
+ throw new Error(`BladesActor of type '${this.type}' ("${this.name}") cannot be RollPrimary.`);
+ }
+ return this.type;
+ }
get rollPrimaryImg() { return this.img; }
// #region BladesCrew Implementation ~
get members() {
diff --git a/module/BladesChat.js b/module/BladesChat.js
index 94f18850..56c4dc90 100644
--- a/module/BladesChat.js
+++ b/module/BladesChat.js
@@ -8,6 +8,7 @@ class BladesChat extends ChatMessage {
ApplyTooltipAnimations(html);
ApplyConsequenceAnimations(html);
BladesConsequence.ApplyChatListeners(html);
+ setTimeout(() => { html.addClass("display-ok"); }, 2000);
});
return loadTemplates([
"systems/eunos-blades/templates/chat/roll-result-action-roll.hbs",
diff --git a/module/BladesDialog.js b/module/BladesDialog.js
index b6b5d70b..c0310c9a 100644
--- a/module/BladesDialog.js
+++ b/module/BladesDialog.js
@@ -1,7 +1,9 @@
import { ApplyTooltipAnimations } from "./core/gsap.js";
import U from "./core/utilities.js";
import { BladesActor, BladesPC } from "./documents/BladesActorProxy.js";
+import BladesItem from "./BladesItem.js";
import BladesRoll from "./BladesRoll.js";
+import BladesConsequence from "./sheets/roll/BladesConsequence.js";
import C, { RollResult, ConsequenceType, AttributeTrait, Position } from "./core/constants.js";
import BladesAI, { AGENTS } from "./core/ai.js";
export var SelectionCategory;
@@ -30,6 +32,7 @@ export var SelectionCategory;
})(SelectionCategory || (SelectionCategory = {}));
export var BladesDialogType;
(function (BladesDialogType) {
+ BladesDialogType["Input"] = "Input";
BladesDialogType["Selection"] = "Selection";
BladesDialogType["Consequence"] = "Consequence";
})(BladesDialogType || (BladesDialogType = {}));
@@ -46,9 +49,39 @@ class BladesDialog extends Dialog {
return loadTemplates([
"systems/eunos-blades/templates/dialog-selection.hbs",
"systems/eunos-blades/templates/dialog-consequence.hbs",
+ "systems/eunos-blades/templates/dialog-input.hbs",
"systems/eunos-blades/templates/parts/dialog-consequence-block.hbs"
]);
}
+ static async DisplaySimpleInputDialog(parent, prompt, target, flagTarget) {
+ const app = new BladesDialog({
+ parent,
+ title: parent instanceof BladesRoll ? "Roll Input" : `${parent.name}: Input`,
+ dialogType: BladesDialogType.Input,
+ content: "",
+ prompt,
+ target,
+ flagTarget,
+ buttons: {
+ apply: {
+ icon: '',
+ label: "Apply",
+ callback: (html) => app
+ .writeToRollInstance(html)
+ },
+ cancel: {
+ icon: '',
+ label: game.i18n.localize("Cancel"),
+ callback: (html) => {
+ eLog.checkLog3("dialog", "Callback Scope", { this: app, html });
+ return false;
+ }
+ }
+ },
+ default: "apply"
+ }, { classes: ["eunos-blades", "sheet", "dialog", "simple-input-dialog"] });
+ return app._render(true, { width: app.width }).then(() => eLog.checkLog3("dialog", "Input Dialog Instance", { this: app }));
+ }
static async DisplaySelectionDialog(parent, title, docType, tabs, tags) {
const app = new BladesDialog({
parent,
@@ -97,12 +130,7 @@ class BladesDialog extends Dialog {
}, { classes: ["eunos-blades", "sheet", "dialog", "consequence-dialog"] });
return app._render(true, { width: app.width }).then(() => eLog.checkLog3("dialog", "Dialog Instance", { this: app }));
}
- get template() {
- if (this.dialogType === BladesDialogType.Selection) {
- return "systems/eunos-blades/templates/dialog-selection.hbs";
- }
- return "systems/eunos-blades/templates/dialog-consequence.hbs";
- }
+ get template() { return `systems/eunos-blades/templates/dialog-${U.lCase(this.dialogType)}.hbs`; }
get hasItems() {
return Object.values(this.tabs ?? []).some((tabItems) => tabItems.length > 0);
}
@@ -113,12 +141,19 @@ class BladesDialog extends Dialog {
width;
docType;
csqData;
+ prompt;
+ target;
+ flagTarget;
constructor(data, options) {
super(data, options);
this.dialogType = data.dialogType ?? BladesDialogType.Selection;
this.parent = data.parent;
- this.width = 500;
+ this.width = options?.width ?? 500;
+ this.prompt = data.prompt;
+ this.target = data.target;
+ this.flagTarget = data.flagTarget;
switch (this.dialogType) {
+ case BladesDialogType.Input: return;
case BladesDialogType.Selection:
this.constructSelectionData(data /* , options */);
return;
@@ -183,11 +218,18 @@ class BladesDialog extends Dialog {
getData() {
const data = super.getData();
switch (this.dialogType) {
+ case BladesDialogType.Input: return this.prepareInputData(data);
case BladesDialogType.Selection: return this.prepareSelectionData(data);
case BladesDialogType.Consequence: return this.prepareConsequenceData(data);
default: return null;
}
}
+ prepareInputData(data) {
+ data.prompt = this.prompt;
+ data.target = this.target;
+ data.flagTarget = this.flagTarget;
+ return data;
+ }
prepareSelectionData(data) {
data.title = this.title;
data.tabs = this.tabs;
@@ -225,9 +267,18 @@ class BladesDialog extends Dialog {
}
return {};
}
- updateConsequenceData(html, cData) {
- const csqElem$ = html.find(`.roll-consequence-row[data-csq-id='${cData.id}']`); // flag-target="rollCollab.consequenceData.${rollPos}.${rollResult}.${i}.attribute"]`)
- // Update Type
+ updateInputText(inputElem$) {
+ const value = inputElem$.val();
+ if (this.parent instanceof BladesRoll) {
+ const flagTarget = inputElem$.data("flagTarget");
+ eLog.checkLog3("dialog", "updateInputText", { value, flagTarget });
+ this.parent.setFlagVal(flagTarget, value, true);
+ }
+ else if (this.parent instanceof BladesItem || this.parent instanceof BladesActor) {
+ this.parent.update({ [inputElem$.data("target")]: inputElem$.val() });
+ }
+ }
+ updateConsequenceType(csqElem$, cData) {
const type$ = csqElem$.find(".roll-consequence-type-select");
const typeVal = type$.val();
if (typeVal && typeVal in ConsequenceType) {
@@ -235,61 +286,142 @@ class BladesDialog extends Dialog {
cData.icon = C.ConsequenceIcons[cData.type];
cData.typeDisplay = C.ConsequenceDisplay[cData.type];
}
- // Update Resistance Attribute
- if (/Resolve/.exec(cData.type)) {
- cData.attribute = AttributeTrait.resolve;
- }
- else if (/Insight/.exec(cData.type)) {
+ }
+ updateConsequenceAttribute(csqElem$, cData) {
+ if (/Insight/.exec(cData.type)) {
cData.attribute = AttributeTrait.insight;
}
else if (/Prowess/.exec(cData.type)) {
cData.attribute = AttributeTrait.prowess;
}
+ else if (/Resolve/.exec(cData.type)) {
+ cData.attribute = AttributeTrait.resolve;
+ }
else {
const attribute$ = csqElem$.find(".roll-consequence-attribute-select");
const attrVal = attribute$.val();
- if (attrVal && attrVal in AttributeTrait) {
+ if (attrVal) {
cData.attribute = attrVal;
- if (this.parent.rollPrimaryDoc instanceof BladesPC) {
- cData.attributeVal = this.parent.rollPrimaryDoc.attributes[cData.attribute];
- }
- else if (this.parent.rollPrimaryDoc?.parent instanceof BladesPC) {
- cData.attributeVal = this.parent.rollPrimaryDoc.parent.attributes[cData.attribute];
+ }
+ }
+ }
+ updateConsequenceAttributeVal(cData) {
+ if (this.parent.rollPrimaryDoc instanceof BladesPC) {
+ cData.attributeVal = this.parent.rollPrimaryDoc.attributes[cData.attribute];
+ }
+ else if (this.parent.rollPrimaryDoc?.parent instanceof BladesPC) {
+ cData.attributeVal = this.parent.rollPrimaryDoc.parent.attributes[cData.attribute];
+ }
+ else {
+ eLog.error(`Unable to get attribute from rollPrimaryDoc '${this.parent.rollPrimaryDoc?.name}' of type '${this.parent.rollPrimaryDoc?.rollPrimaryType}' (may need to log via flags if either of the previous show 'undefined'.`);
+ }
+ }
+ getSelectedResistOption(cData) {
+ return Object.values(cData?.resistOptions ?? {}).find((rCsq) => rCsq.isSelected) ?? false;
+ }
+ updateConsequenceResist(csqElem$, cData) {
+ const resistOptions = cData.resistOptions ?? {};
+ // If consequence is already minimal, toggle resistNegates to true and set 'resistTo' to None-type
+ const minimalCsqTypes = Object.entries(C.ResistedConsequenceTypes)
+ .filter(([_, rCsqType]) => rCsqType === ConsequenceType.None)
+ .map(([csqType]) => csqType);
+ if (minimalCsqTypes.includes(cData.type)) {
+ cData.resistNegates = true;
+ const noneCsq = BladesConsequence.None;
+ cData.resistOptions = { [noneCsq.id]: noneCsq };
+ cData.resistTo = noneCsq;
+ return;
+ }
+ else {
+ // Clear 'resistTo' (will be redetermined below)
+ delete cData.resistTo;
+ delete cData.resistNegates;
+ csqElem$.find(".consequence-resist-option").each((_, elem) => {
+ const resCsqID = $(elem).data("csq-id");
+ resistOptions[resCsqID] ??= { id: resCsqID, name: "", type: undefined, isSelected: false };
+ // Update Resistance Option Type
+ const resType$ = $(elem).find(".roll-consequence-type-select");
+ const resTypeVal = resType$.val();
+ if (resTypeVal && resTypeVal in ConsequenceType) {
+ resistOptions[resCsqID].type = resTypeVal;
+ resistOptions[resCsqID].icon = C.ConsequenceIcons[resistOptions[resCsqID].type];
+ resistOptions[resCsqID].typeDisplay = C.ConsequenceDisplay[resistOptions[resCsqID].type];
}
- else {
- eLog.error(`Unable to get attribute from rollPrimaryDoc '${this.parent.rollPrimaryDoc?.name}' of type '${this.parent.rollPrimaryDoc?.rollPrimaryType}' (may need to log via flags if either of the previous show 'undefined'.`);
+ // Update Resistance Option Name
+ const resName$ = $(elem).find(".consequence-name");
+ const resNameVal = resName$.val();
+ resistOptions[resCsqID].name = resNameVal ?? "";
+ // If this is selected, update 'resistTo' data as well
+ if (resistOptions[resCsqID].isSelected) {
+ cData.resistTo = resistOptions[resCsqID];
}
- }
+ });
+ }
+ cData.resistOptions = resistOptions;
+ }
+ updateConsequenceArmorResist(csqElem$, cData) {
+ // If consequence is already minimal, toggle armorNegates to true and set 'armorTo' to None-type
+ const minimalCsqTypes = Object.entries(C.ResistedConsequenceTypes)
+ .filter(([_, rCsqType]) => rCsqType === ConsequenceType.None)
+ .map(([csqType]) => csqType);
+ if (minimalCsqTypes.includes(cData.type)) {
+ cData.armorNegates = true;
+ cData.armorTo = BladesConsequence.None;
+ }
+ else {
+ delete cData.armorNegates;
+ cData.armorTo = this.getSelectedResistOption(cData);
+ }
+ }
+ updateConsequenceSpecialArmorResist(csqElem$, cData) {
+ // If consequence is already minimal, toggle specialArmorNegates to true and set 'specialArmorTo' to None-type
+ const minimalCsqTypes = Object.entries(C.ResistedConsequenceTypes)
+ .filter(([_, rCsqType]) => rCsqType === ConsequenceType.None)
+ .map(([csqType]) => csqType);
+ if (minimalCsqTypes.includes(cData.type)) {
+ cData.specialArmorNegates = true;
+ cData.specialArmorTo = BladesConsequence.None;
}
+ else {
+ delete cData.specialArmorNegates;
+ cData.specialArmorNegates ??= false;
+ cData.specialArmorTo = this.getSelectedResistOption(cData);
+ }
+ }
+ updateConsequenceData(html, cData) {
+ const csqElem$ = html.find(`.roll-consequence-row[data-csq-id='${cData.id}']`);
+ // Update Type
+ this.updateConsequenceType(csqElem$, cData);
// Update Name
- const name$ = csqElem$.find(".consequence-name");
- const nameVal = name$.val();
- cData.name = nameVal ?? "";
+ if (cData.type === ConsequenceType.None) {
+ cData.name = "";
+ }
+ else {
+ const name$ = csqElem$.find(".consequence-name");
+ const nameVal = name$.val();
+ cData.name = nameVal ?? "";
+ }
+ // Update Resistance Attribute
+ this.updateConsequenceAttribute(csqElem$, cData);
+ this.updateConsequenceAttributeVal(cData);
// Update Resistance Options
- const resistOptions = cData.resistOptions ?? {};
- // Clear 'resistTo' (will be redetermined below)
- delete cData.resistTo;
- csqElem$.find(".consequence-resist-option").each((_, elem) => {
- const resCsqID = $(elem).data("csq-id");
- resistOptions[resCsqID] ??= { id: resCsqID, name: "", type: undefined, isSelected: false };
- // Update Resistance Option Type
- const resType$ = $(elem).find(".roll-consequence-type-select");
- const resTypeVal = resType$.val();
- if (resTypeVal && resTypeVal in ConsequenceType) {
- resistOptions[resCsqID].type = resTypeVal;
- resistOptions[resCsqID].icon = C.ConsequenceIcons[resistOptions[resCsqID].type];
- resistOptions[resCsqID].typeDisplay = C.ConsequenceDisplay[resistOptions[resCsqID].type];
- }
- // Update Resistance Option Name
- const resName$ = $(elem).find(".consequence-name");
- const resNameVal = resName$.val();
- resistOptions[resCsqID].name = resNameVal ?? "";
- // If this is selected, update 'resistTo' data as well
- if (resistOptions[resCsqID].isSelected) {
- cData.resistTo = resistOptions[resCsqID];
- }
- });
- cData.resistOptions = resistOptions;
+ this.updateConsequenceResist(csqElem$, cData);
+ // Update Armor Options
+ if (this.parent.canResistWithArmor(cData)) {
+ cData.isDisplayingArmorToggle = true;
+ this.updateConsequenceArmorResist(csqElem$, cData);
+ }
+ else {
+ cData.isDisplayingArmorToggle = false;
+ }
+ // Update Special Armor Options
+ if (this.parent.canResistWithSpecialArmor(cData)) {
+ cData.isDisplayingSpecialArmorToggle = true;
+ this.updateConsequenceSpecialArmorResist(csqElem$, cData);
+ }
+ else {
+ cData.isDisplayingSpecialArmorToggle = false;
+ }
return cData;
}
updateConsequenceDialog(html, isRendering = true) {
@@ -325,6 +457,14 @@ class BladesDialog extends Dialog {
}
}
_consequenceAI;
+ getCsqDataFromElem(elem, paramCount = 3) {
+ const dataAction = elem.dataset.action;
+ if (dataAction) {
+ const params = dataAction.split(/-/).reverse().slice(0, paramCount);
+ return params.reverse();
+ }
+ return [];
+ }
async queryAI(event) {
if (!this.csqData) {
return;
@@ -333,19 +473,32 @@ class BladesDialog extends Dialog {
if (!this._consequenceAI) {
this._consequenceAI = new BladesAI(AGENTS.ConsequenceAdjuster);
}
- // Get the name of the consequence.
- const dataAction = event.currentTarget.dataset.action;
- if (dataAction && dataAction.startsWith("ai-query")) {
- const [rollPosition, rollResult, csqID] = dataAction.split(/-/).slice(2);
- const csqName = this.csqData[rollPosition][rollResult][csqID]?.name;
- if (csqName) {
- const response = await this._consequenceAI?.query(csqName, csqName);
- if (response) {
- this.refreshResistanceOptions(rollPosition, rollResult, csqID, response.split("|"));
- }
+ const [rollPosition, rollResult, csqID] = this.getCsqDataFromElem(event.currentTarget);
+ const csqName = this.csqData[rollPosition][rollResult][csqID]?.name;
+ if (csqName) {
+ const response = await this._consequenceAI?.query(csqName, csqName);
+ if (response) {
+ this.refreshResistanceOptions(rollPosition, rollResult, csqID, response.split("|"));
}
}
}
+ async spawnBlankResistOption(event) {
+ if (!this.csqData) {
+ return;
+ }
+ const [rollPosition, rollResult, csqID] = this.getCsqDataFromElem(event.currentTarget);
+ const rCsqID = randomID();
+ this.csqData[rollPosition][rollResult][csqID]
+ .resistOptions = {
+ [rCsqID]: {
+ id: rCsqID,
+ name: "",
+ type: undefined,
+ isSelected: true
+ }
+ };
+ this.render();
+ }
async setFlagVal(target, value) {
if (this.parent instanceof BladesRoll) {
return this.parent.setFlagVal(target, value, false);
@@ -383,49 +536,85 @@ class BladesDialog extends Dialog {
if (!this.csqData) {
return;
}
- 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 ??= {};
- // Toggle clicked resistance option
- cData.resistOptions[resIndex].isSelected = !cData.resistOptions[resIndex].isSelected;
- // 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;
- });
- // ... and set 'resistTo' to this consequence.
- cData.resistTo = cData.resistOptions[resIndex];
- }
- else {
- // Otherwise, set 'resistTo' to false.
- cData.resistTo = false;
- }
- // Assign new cData instance.
- this.csqData[rollPosition][rollResult][csqIndex] = cData;
- this.render();
+ const [rollPosition, rollResult, csqID, resID] = this.getCsqDataFromElem(event.currentTarget, 4);
+ eLog.checkLog3("dialog", "... Action Passed", { rollResult, csqIndex: csqID, resIndex: resID });
+ // Get consequence data
+ const cData = this.csqData[rollPosition][rollResult][csqID];
+ cData.resistOptions ??= {};
+ // Toggle clicked resistance option
+ cData.resistOptions[resID].isSelected = !cData.resistOptions[resID].isSelected;
+ // If resistance option is now selected...
+ if (cData.resistOptions[resID].isSelected) {
+ // ... deselect & hide other options
+ Object.keys(cData.resistOptions)
+ .filter((key) => key !== resID)
+ .forEach((key) => {
+ Object.assign(cData.resistOptions?.[key] ?? {}, { isSelected: false, isVisible: false });
+ });
+ // ... and set 'resistTo' to this consequence.
+ cData.resistTo = cData.resistOptions[resID];
}
+ else {
+ // Otherwise, set 'resistTo' to false...
+ cData.resistTo = false;
+ // ... and unhide other options.
+ Object.keys(cData.resistOptions)
+ .filter((key) => key !== resID)
+ .forEach((key) => {
+ Object.assign(cData.resistOptions?.[key] ?? {}, { isVisible: true });
+ });
+ }
+ // Assign new cData instance.
+ this.csqData[rollPosition][rollResult][csqID] = cData;
+ this.render();
+ }
+ async clearResistOptions(event) {
+ if (!this.csqData) {
+ return;
+ }
+ const [rollPosition, rollResult, csqID] = this.getCsqDataFromElem(event.currentTarget);
+ this.csqData[rollPosition][rollResult][csqID].resistOptions = {};
+ this.render();
+ }
+ async toggleArmor(event) {
+ if (!this.csqData) {
+ return;
+ }
+ const [rollPosition, rollResult, csqID] = this.getCsqDataFromElem(event.currentTarget);
+ const cData = this.csqData[rollPosition][rollResult][csqID];
+ cData.canArmor = !cData.canArmor;
+ this.render();
+ }
+ async toggleSpecialArmor(event) {
+ if (!this.csqData) {
+ return;
+ }
+ const [rollPosition, rollResult, csqID] = this.getCsqDataFromElem(event.currentTarget);
+ const cData = this.csqData[rollPosition][rollResult][csqID];
+ cData.canSpecialArmor = !cData.canSpecialArmor;
+ this.render();
}
activateListeners(html) {
super.activateListeners(html);
// ~ Tooltips
ApplyTooltipAnimations(html);
switch (this.dialogType) {
+ case BladesDialogType.Input:
+ this.activateInputListeners(html);
+ break;
case BladesDialogType.Selection:
this.activateSelectionListeners(html);
break;
- case BladesDialogType.Consequence:
+ case BladesDialogType.Consequence: {
this.activateConsequenceListeners(html);
+ // Select --> updateConsequenceDialog -> updateConsequenceData(each csq)
break;
+ }
}
}
+ activateInputListeners(html) {
+ html.find("textarea").on({ change: (event) => this.updateInputText($(event.currentTarget)) });
+ }
activateSelectionListeners(html) {
const self = this;
// ~ Changing Width on Tab Change Depending on Number of Items
@@ -458,8 +647,17 @@ class BladesDialog extends Dialog {
activateConsequenceListeners(html) {
html.find("input").on({ change: () => this.updateConsequenceDialog(html) });
html.find("select").on({ change: () => this.updateConsequenceDialog(html) });
- html.find('[data-action^="ai-query"]').on({ click: (event) => this.queryAI(event) });
+ html.find('[data-action^="ai-query"]').on({
+ click: (event) => this.queryAI(event),
+ contextmenu: (event) => this.clearResistOptions(event)
+ });
+ html.find('[data-action^="blank-option"]').on({
+ click: (event) => this.spawnBlankResistOption(event),
+ contextmenu: (event) => this.clearResistOptions(event)
+ });
html.find('[data-action^="gm-select-toggle"]').on({ click: (event) => this.selectResistOption(event) });
+ html.find('[data-action^="toggle-armor"]').on({ click: (event) => this.toggleArmor(event) });
+ html.find('[data-action^="toggle-special"]').on({ click: (event) => this.toggleSpecialArmor(event) });
}
}
export default BladesDialog;
diff --git a/module/BladesItem.js b/module/BladesItem.js
index e13d5731..df11b533 100644
--- a/module/BladesItem.js
+++ b/module/BladesItem.js
@@ -171,7 +171,17 @@ class BladesItem extends Item {
get rollPrimaryID() { return this.id; }
get rollPrimaryDoc() { return this; }
get rollPrimaryName() { return this.name; }
- get rollPrimaryType() { return this.type; }
+ get rollPrimaryType() {
+ if (![
+ BladesItemType.cohort_gang,
+ BladesItemType.cohort_expert,
+ BladesItemType.gm_tracker,
+ BladesItemType.score
+ ].includes(this.type)) {
+ throw new Error(`BladesItem of type '${this.type}' ("${this.name}") cannot be RollPrimary.`);
+ }
+ return this.type;
+ }
get rollPrimaryImg() { return this.img; }
get rollModsData() {
// Const rollModData = BladesRollMod.ParseDocRollMods(this);
@@ -211,7 +221,21 @@ class BladesItem extends Item {
get rollOppImg() { return this.img; }
get rollOppName() { return this.name; }
get rollOppSubName() { return ""; }
- get rollOppType() { return this.type; }
+ get rollOppType() {
+ if (![
+ BladesItemType.cohort_gang,
+ BladesItemType.cohort_expert,
+ BladesItemType.gm_tracker,
+ BladesItemType.score,
+ BladesItemType.location,
+ BladesItemType.project,
+ BladesItemType.design,
+ BladesItemType.ritual
+ ].includes(this.type)) {
+ throw new Error(`BladesItem of type '${this.type}' ("${this.name}") cannot be RollOpposition.`);
+ }
+ return this.type;
+ }
get rollOppModsData() { return []; }
// #endregion
// #region BladesRoll.ParticipantDoc Implementation
@@ -219,7 +243,16 @@ class BladesItem extends Item {
get rollParticipantDoc() { return this; }
get rollParticipantIcon() { return this.img; }
get rollParticipantName() { return this.name; }
- get rollParticipantType() { return this.type; }
+ get rollParticipantType() {
+ if (![
+ BladesItemType.cohort_gang,
+ BladesItemType.cohort_expert,
+ BladesItemType.gm_tracker
+ ].includes(this.type)) {
+ throw new Error(`BladesItem of type '${this.type}' ("${this.name}") cannot be RollParticipant.`);
+ }
+ return this.type;
+ }
get rollParticipantModsData() { return []; }
// #endregion
// #endregion
@@ -309,12 +342,12 @@ class BladesItem extends Item {
const expClueData = {};
[...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 });
+ // eLog.checkLog3("experienceClues", {expClueData})
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 });
+ // eLog.checkLog3("gatherInfoQuestions", {gatherInfoData});
}
}
}
diff --git a/module/BladesRoll.js b/module/BladesRoll.js
index 03a8387b..85598b0b 100644
--- a/module/BladesRoll.js
+++ b/module/BladesRoll.js
@@ -1,6 +1,6 @@
// #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 C, { BladesActorType, BladesItemType, BladesPhase, 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 { ApplyTooltipAnimations, ApplyConsequenceAnimations } from "./core/gsap.js";
@@ -522,6 +522,10 @@ class BladesRollMod {
return BladesActor.IsType(rollPrimaryDoc, BladesActorType.pc)
&& rollPrimaryDoc.system.stress.max - rollPrimaryDoc.system.stress.value >= val;
}
+ case "Heat": {
+ return (BladesPC.IsType(rollPrimaryDoc) && BladesCrew.IsType(rollPrimaryDoc.crew))
+ || BladesCrew.IsType(rollPrimaryDoc);
+ }
default: throw new Error(`Unrecognize Payable Key: ${traitStr}`);
}
});
@@ -758,7 +762,6 @@ class BladesRollPrimary {
|| BladesItem.IsType(doc, BladesItemType.cohort_expert, BladesItemType.cohort_gang, BladesItemType.gm_tracker);
}
// #endregion
- rollInstance;
rollPrimaryID;
_rollPrimaryDoc;
get rollPrimaryDoc() {
@@ -812,18 +815,78 @@ class BladesRollPrimary {
return this.rollPrimaryDoc.applyWorsePosition();
}
}
+ get hasArmor() {
+ if (!this.rollPrimaryDoc) {
+ return false;
+ }
+ if (this.rollPrimaryType === BladesActorType.pc) {
+ const rollPrimaryDoc = this.rollPrimaryDoc;
+ // Can PC spend normal armor?
+ if (!rollPrimaryDoc.system.armor.checked.light
+ && (rollPrimaryDoc.system.armor.active.light
+ || rollPrimaryDoc.remainingLoad >= 2)) {
+ return true;
+ }
+ // Otherwise, can PC spend heavy armor?
+ if (!rollPrimaryDoc.system.armor.checked.heavy
+ && (rollPrimaryDoc.system.armor.active.heavy
+ || rollPrimaryDoc.remainingLoad >= 3)) {
+ return true;
+ }
+ }
+ if (BladesItem.IsType(this.rollPrimaryDoc, BladesItemType.cohort_gang, BladesItemType.cohort_expert)) {
+ const { value, max } = this.rollPrimaryDoc.system.armor;
+ return max - value > 1;
+ }
+ return false;
+ }
+ get hasSpecialArmor() {
+ if (!this.rollPrimaryDoc) {
+ return false;
+ }
+ if (!BladesPC.IsType(this.rollPrimaryDoc)) {
+ return false;
+ }
+ if (!this.rollPrimaryDoc.system.armor.active.special) {
+ return false;
+ }
+ if (this.rollPrimaryDoc.system.armor.checked.special) {
+ return false;
+ }
+ return true;
+ }
+ async spendArmor() {
+ if (this.hasArmor) {
+ if (BladesPC.IsType(this.rollPrimaryDoc)) {
+ if (this.rollPrimaryDoc.system.armor.checked.light) {
+ await this.rollPrimaryDoc.update({ "system.armor.checked.heavy": true });
+ }
+ else {
+ await this.rollPrimaryDoc.update({ "system.armor.checked.light": true });
+ }
+ }
+ else if (BladesItem.IsType(this.rollPrimaryDoc, BladesItemType.cohort_gang, BladesItemType.cohort_expert)) {
+ await this.rollPrimaryDoc.update({ "system.armor.value": this.rollPrimaryDoc.system.armor.value + 1 });
+ }
+ }
+ }
+ async spendSpecialArmor() {
+ if (this.hasSpecialArmor) {
+ await this.rollPrimaryDoc.update({ "system.armor.checked.special": true });
+ }
+ }
// #region Constructor ~
- constructor(rollInstance, { rollPrimaryID, rollPrimaryName, rollPrimaryType, rollPrimaryImg, rollModsData, rollFactors }) {
+ constructor(rollInstance, { rollPrimaryID, rollPrimaryName, rollPrimaryType, rollPrimaryImg, rollModsData, rollFactors } = {}) {
// Identify ID, Doc, Name, SubName, Type & Image, to best of ability
- this.rollInstance = rollInstance;
+ // this.rollInstance = rollInstance;
this.rollPrimaryID = rollPrimaryID
- ?? this.rollInstance.rollPrimary.rollPrimaryID
- ?? this.rollInstance.rollPrimary.rollPrimaryDoc?.rollPrimaryID;
- 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;
+ ?? rollInstance?.rollPrimary.rollPrimaryID
+ ?? rollInstance?.rollPrimary.rollPrimaryDoc?.rollPrimaryID;
+ rollPrimaryName ??= rollInstance?.rollPrimary.rollPrimaryName;
+ rollPrimaryType ??= rollInstance?.rollPrimary.rollPrimaryType;
+ rollPrimaryImg ??= rollInstance?.rollPrimary.rollPrimaryImg;
+ rollModsData ??= rollInstance?.rollPrimary.rollModsData;
+ rollFactors ??= rollInstance?.rollPrimary.rollFactors;
if (BladesRollPrimary.IsDoc(this.rollPrimaryDoc)) {
this.rollPrimaryName = rollPrimaryName ?? this.rollPrimaryDoc.rollPrimaryName;
this.rollPrimaryType = this.rollPrimaryDoc.rollPrimaryType;
@@ -914,8 +977,21 @@ class BladesRollOpposition {
rollOppImg;
rollOppModsData;
rollFactors;
+ _clockData;
+ get clockData() { return this._clockData ?? {}; }
+ set clockData(val) {
+ val.id ??= randomID();
+ val.display ??= "";
+ val.value ??= 0;
+ val.max ??= 8;
+ val.color ??= "white";
+ val.isActive = true;
+ val.isNameVisible = false;
+ val.isVisible = true;
+ this._clockData = val;
+ }
// #region Constructor ~
- constructor(rollInstance, { rollOppID, rollOppDoc, rollOppName, rollOppSubName, rollOppType, rollOppImg, rollOppModsData, rollFactors } = {}) {
+ constructor(rollInstance, { rollOppID, rollOppDoc, rollOppName, rollOppSubName, rollOppType, rollOppImg, rollOppModsData, rollFactors, rollOppClock } = {}) {
this.rollInstance = rollInstance;
// Attempt to fetch an associated BladesActor or BladesItem document
const doc = BladesRollOpposition.GetDoc(rollOppDoc ?? rollOppID ?? rollOppName);
@@ -940,9 +1016,6 @@ class BladesRollOpposition {
if (!rollOppName) {
throw new Error("Must include a rollOppName when constructing a BladesRollOpposition object.");
}
- if (!rollOppSubName) {
- throw new Error("Must include a rollOppSubName when constructing a BladesRollOpposition object.");
- }
if (!rollOppType) {
throw new Error("Must include a rollOppType when constructing a BladesRollOpposition object.");
}
@@ -957,6 +1030,9 @@ class BladesRollOpposition {
this.rollOppImg = rollOppImg ?? "";
this.rollOppModsData = rollOppModsData ?? [];
this.rollFactors = rollFactors;
+ if (rollOppClock) {
+ this.clockData = rollOppClock;
+ }
}
// #endregion
get flagParams() {
@@ -970,7 +1046,8 @@ class BladesRollOpposition {
rollOppType: this.rollOppType,
rollOppImg: this.rollOppImg,
rollOppModsData: this.rollOppModsData,
- rollFactors: this.rollFactors
+ rollFactors: this.rollFactors,
+ rollOppClock: this.clockData
};
}
async updateRollFlags() {
@@ -1463,7 +1540,7 @@ class BladesRoll extends DocumentSheet {
}
static async PrepareActionRoll(rollID, config) {
// Validate the rollTrait
- if (!(U.isInt(config.rollTrait) || U.lCase(config.rollTrait) in { ...ActionTrait, ...Factor })) {
+ if (!(config.rollTrait === "" || U.isInt(config.rollTrait) || U.lCase(config.rollTrait) in { ...ActionTrait, ...Factor })) {
throw new Error(`[PrepareActionRoll()] Bad RollTrait for Action Roll: ${config.rollTrait}`);
}
// Retrieve the roll users
@@ -1699,6 +1776,7 @@ class BladesRoll extends DocumentSheet {
_rollPrimary;
_rollOpposition;
_rollParticipants;
+ projectSelectOptions;
constructor(userID, rollID, rollPermission) {
const rollUser = game.users.get(userID);
if (!rollUser) {
@@ -1712,6 +1790,11 @@ class BladesRoll extends DocumentSheet {
if (rollFlagData.rollOppData) {
this._rollOpposition = new BladesRollOpposition(this, rollFlagData.rollOppData);
}
+ else if (rollFlagData.rollDowntimeAction === DowntimeAction.LongTermProject) {
+ this.projectSelectOptions = Array.from(game.items)
+ .filter((item) => BladesItem.IsType(item, BladesItemType.project))
+ .map((project) => ({ value: project.id ?? "", display: project.name }));
+ }
if (rollFlagData.rollParticipantData) {
this._rollParticipants = {};
for (const [rollSection, rollParticipantList] of Object.entries(rollFlagData.rollParticipantData)) {
@@ -1952,18 +2035,18 @@ class BladesRoll extends DocumentSheet {
}
getFlagVal(flagKey) {
if (flagKey) {
- return this.document.getFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`);
+ return this.document.getFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`.replace(/(rollCollab\.)+/g, "rollCollab."));
}
return this.document.getFlag(C.SYSTEM_ID, "rollCollab");
}
async setFlagVal(flagKey, flagVal, isRerendering = true) {
- await this.document.setFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`, flagVal);
+ await this.document.setFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`.replace(/(rollCollab\.)+/g, "rollCollab."), flagVal);
if (isRerendering) {
socketlib.system.executeForEveryone("renderRollCollab", this.rollID);
}
}
async clearFlagVal(flagKey, isRerendering = true) {
- await this.document.unsetFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`);
+ await this.document.unsetFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`.replace(/(rollCollab\.)+/g, "rollCollab."));
if (isRerendering) {
socketlib.system.executeForEveryone("renderRollCollab", this.rollID);
}
@@ -2370,6 +2453,18 @@ class BladesRoll extends DocumentSheet {
return [...this._rollMods].sort((modA, modB) => this.compareMods(modA, modB));
}
set rollMods(val) { this._rollMods = val; }
+ canResistWithArmor(csqData) {
+ if (!this.rollPrimary.hasArmor) {
+ return false;
+ }
+ return csqData.attribute === AttributeTrait.prowess;
+ }
+ canResistWithSpecialArmor(_csqData) {
+ if (!BladesPC.IsType(this.rollPrimary.rollPrimaryDoc)) {
+ return false;
+ }
+ return this.rollPrimary.rollPrimaryDoc.armorStatus.special;
+ }
// #endregion
// #region CONSEQUENCES: Getting, Accepting, Resisting
get _csqData() {
@@ -2407,6 +2502,147 @@ class BladesRoll extends DocumentSheet {
const sheetData = this.getSheetData(this.getIsGM(), this.getRollCosts());
return { ...context, ...sheetData };
}
+ getFortuneRollModsData() {
+ const modsData = [];
+ if (this.rollSubType === RollSubType.Engagement) {
+ modsData.push({
+ id: "BoldPlan-positive-roll",
+ name: "Bold Plan",
+ section: RollModSection.roll,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "positive",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: "
"
+ });
+ modsData.push({
+ id: "ComplexPlan-negative-roll",
+ name: "Complex Plan",
+ section: RollModSection.roll,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "negative",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: ""
+ });
+ modsData.push({
+ id: "ExploitWeakness-positive-roll",
+ name: "Exploiting a Weakness",
+ section: RollModSection.roll,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "positive",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: ""
+ });
+ modsData.push({
+ id: "WellDefended-negative-roll",
+ name: "Well-Defended",
+ section: RollModSection.roll,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "negative",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: ""
+ });
+ modsData.push({
+ id: "HelpFromFriend-positive-roll",
+ name: "Help From a Friend",
+ section: RollModSection.position,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "positive",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: "Help From a Friend
Add +1d if you enlist the help of a friend or contact.
"
+ });
+ modsData.push({
+ id: "EnemyInterference-negative-roll",
+ name: "Enemy Interference",
+ section: RollModSection.roll,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "negative",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: ""
+ });
+ }
+ return modsData;
+ }
+ getDowntimeActionRollModsData() {
+ const modsData = [];
+ modsData.push({
+ id: "HelpFromFriend-positive-roll",
+ name: "Help From a Friend",
+ section: RollModSection.position,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "positive",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: "Help From a Friend
Add +1d if you enlist the help of a friend or contact.
"
+ });
+ if (this.rollDowntimeAction !== DowntimeAction.IndulgeVice) {
+ modsData.push({
+ id: "CanBuyResultLevel-positive-after",
+ name: "Buying Result Level",
+ section: RollModSection.after,
+ base_status: RollModStatus.ForcedOn,
+ posNeg: "positive",
+ modType: "general",
+ value: 0,
+ effectKeys: [],
+ tooltip: "Buying Result Level
After your roll, you can increase the result level by one for each Coin you spend.
"
+ });
+ }
+ switch (this.rollDowntimeAction) {
+ case DowntimeAction.AcquireAsset: {
+ modsData.push({
+ id: "RepeatPurchase-positive-roll",
+ name: "Repeat Purchase",
+ section: RollModSection.roll,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "positive",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: "Repeat Purchase Bonus
Add +1d if you have previously acquired this asset or service with a Acquire Asset Downtime activity.
"
+ });
+ modsData.push({
+ id: "RestrictedItem-negative-after",
+ name: "Restricted",
+ section: RollModSection.after,
+ base_status: RollModStatus.Hidden,
+ posNeg: "negative",
+ modType: "general",
+ value: 0,
+ effectKeys: ["Cost-Heat2"],
+ tooltip: "Restricted
Whether contraband goods or dangerous materials, this Acquire Asset Downtime activity will add +2 Heat to your crew.
"
+ });
+ break;
+ }
+ default: break;
+ }
+ /*
+ modsData.push({
+ id: "--",
+ name: "",
+ section: RollModSection,
+ base_status: RollModStatus,
+ posNeg: "",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: ""
+ })
+ */
+ return modsData;
+ }
/**
* Gets the roll modifications data.
* @returns {BladesRoll.RollModData[]} The roll modifications data.
@@ -2416,18 +2652,30 @@ class BladesRoll extends DocumentSheet {
...BladesRoll.DefaultRollMods,
...this.rollPrimary.rollModsData
];
- if (this.rollType === RollType.Action && this.rollPrimary.isWorsePosition) {
- defaultMods.push({
- id: "WorsePosition-negative-position",
- name: "Worse Position",
- section: RollModSection.position,
- base_status: RollModStatus.ForcedOn,
- posNeg: "negative",
- modType: "general",
- value: 1,
- effectKeys: [],
- tooltip: "Worse Position
A Consequence on a previous roll has worsened your Position.
"
- });
+ if (this.rollDowntimeAction) {
+ defaultMods.push(...this.getDowntimeActionRollModsData());
+ }
+ if (this.rollType === RollType.Action) {
+ if (this.rollPrimary.isWorsePosition) {
+ defaultMods.push({
+ id: "WorsePosition-negative-position",
+ name: "Worse Position",
+ section: RollModSection.position,
+ base_status: RollModStatus.ForcedOn,
+ posNeg: "negative",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: "Worse Position
A Consequence on a previous roll has worsened your Position.
"
+ });
+ }
+ switch (this.rollDowntimeAction) {
+ case DowntimeAction.AcquireAsset: {
+ defaultMods.push();
+ break;
+ }
+ default: break;
+ }
}
if (this.rollType === RollType.Action
&& this.acceptedConsequences.some((csq) => csq.type === ConsequenceType.ReducedEffect)) {
@@ -2535,6 +2783,7 @@ class BladesRoll extends DocumentSheet {
const GMBoostsData = this.calculateGMBoostsData(rData);
const positionEffectTradeData = this.calculatePositionEffectTradeData();
const userPermission = U.objFindKey(baseData.userPermissions, (v) => v.includes(game.user.id ?? ""));
+ // const downtimeData = this.processDowntimeActions();
return {
...baseData,
...(this.rollPrimary.rollPrimaryDoc ? { rollPrimary: this.rollPrimary.rollPrimaryDoc } : {}),
@@ -2543,9 +2792,36 @@ class BladesRoll extends DocumentSheet {
...rollResultData,
...GMBoostsData,
...positionEffectTradeData,
- userPermission
+ // ...downtimeData,
+ userPermission,
+ gamePhase: game.eunoblades.Tracker?.phase || BladesPhase.Freeplay
};
}
+ // type BladesSelectOption = {
+ // value: valueType,
+ // display: displayType
+ // };
+ // private processDowntimeActions() {
+ // const downtimeData: Record;
+ // if (BladesActor.IsType(this.rollPrimary.rollPrimaryDoc, BladesActorType.pc)) {
+ // downtimeData.canDoDowntimeActions = true;
+ // downtimeData.downtimeActionsRemaining = this.rollPrimary.rollPrimaryDoc.remainingDowntimeActions;
+ // const availableDowntimeActions: DowntimeAction[] = [];
+ // if (this.rollType === RollType.Action) {
+ // availableDowntimeActions.push(...[
+ // DowntimeAction.AcquireAsset,
+ // DowntimeAction.LongTermProject,
+ // DowntimeAction.Recover,
+ // DowntimeAction.ReduceHeat
+ // ]);
+ // } else if (this.rollType === RollType.Fortune) {
+ // availableDowntimeActions.push(...[
+ // DowntimeAction.
+ // ])
+ // }
+ // downtimeData.downtimeActionOptions =
+ // downtimeActionOptions?: Array
+ // }
calculatePositionData(finalPosition) {
return {
rollPositions: Object.values(Position),
@@ -2798,7 +3074,24 @@ class BladesRoll extends DocumentSheet {
return this.isRollingZero ? this.dieVals.slice(1) : this.dieVals;
}
getDieClass(val, i) {
- eLog.checkLog3("rollCollab", `getDieClass(${val}, ${i})`, { inst: this });
+ switch (this.rollType) {
+ case RollType.Resistance: {
+ if (val === 6 && i <= 1 && this.rollResult === -1) {
+ return "blades-die-critical";
+ }
+ if (i === 0) {
+ return "blades-die-resistance";
+ }
+ return "blades-die-fail";
+ }
+ case RollType.IndulgeVice: {
+ if (i === 0) {
+ return "blades-die-indulge-vice";
+ }
+ return "blades-die-fail";
+ }
+ default: break;
+ }
if (val === 6 && i <= 1 && this.rollResult === RollResult.critical) {
val++;
}
@@ -2813,28 +3106,32 @@ class BladesRoll extends DocumentSheet {
"blades-die-critical"
][val];
}
+ getDieImage(val, i, isGhost = false) {
+ let imgPath = "systems/eunos-blades/assets/dice/image/";
+ if (isGhost) {
+ imgPath += "ghost-";
+ }
+ else if ([RollType.Resistance, RollType.IndulgeVice].includes(this.rollType)
+ || this.rollDowntimeAction) {
+ imgPath += "grad-";
+ }
+ imgPath += val;
+ if (!isGhost && val === 6 && i <= 1 && this.isCritical) {
+ imgPath += "-crit";
+ }
+ imgPath += ".webp";
+ return imgPath;
+ }
get dieValsHTML() {
eLog.checkLog3("rollCollab", "[get dieValsHTML()]", { roll: this, dieVals: this.dieVals });
const dieVals = [...this.dieVals];
const ghostNum = this.isRollingZero ? dieVals.shift() : null;
- if (this.rollType === RollType.Resistance) {
- const numHighlightedDice = this.rollResult === RollResult.critical ? 2 : 1;
- const highlightClass = this.rollResult === RollResult.critical ? "blades-die-critical" : "blades-die-resistance";
- return [
- ...dieVals.map((val, i) => ``),
- ghostNum ? `` : null
- ]
- .filter((val) => typeof val === "string")
- .join("");
- }
- else {
- return [
- ...dieVals.map((val, i) => ``),
- ghostNum ? `` : null
- ]
- .filter((val) => typeof val === "string")
- .join("");
- }
+ return [
+ ...dieVals.map((val, i) => ``),
+ ghostNum ? `` : null
+ ]
+ .filter((val) => typeof val === "string")
+ .join("");
}
// #endregion
// #region RESULT GETTERS ~
@@ -2897,6 +3194,7 @@ class BladesRoll extends DocumentSheet {
await this.setRollPhase(RollPhase.Complete);
}
// Apply Stress & Special Armor Costs
+ eLog.checkLog2("bladesRoll", "Costs", this.getRollCosts());
if (BladesPC.IsType(this.rollPrimaryDoc)) {
const rollCostData = this.getRollCosts();
const stressCost = this.getTotalStressCost(this.getStressCosts(rollCostData));
@@ -2921,7 +3219,7 @@ class BladesRoll extends DocumentSheet {
if (csqID && chatID) {
const resistedCsq = await BladesConsequence.GetFromID(chatID, csqID);
if (resistedCsq) {
- await resistedCsq.applyResistedConsequence();
+ await resistedCsq.applyResistedConsequence("resist");
}
}
if (BladesPC.IsType(this.rollPrimaryDoc)) {
@@ -3328,7 +3626,8 @@ class BladesRoll extends DocumentSheet {
.on({ change: this._onSelectChange.bind(this) });
html
.find("[data-action=\"gm-edit-consequences\"]")
- .on({ click: () => BladesDialog.DisplayRollConsequenceDialog(this) });
+ // .on({click: () => BladesDialog.DisplayRollConsequenceDialog(this)});
+ .on({ click: () => BladesDialog.DisplaySimpleInputDialog(this, "What consequence would you add?", undefined, "rollCollab.inputDialogTest") });
html
.find("[data-action='gm-text-input']")
.on({ blur: this._onTextInputBlur.bind(this) });
diff --git a/module/core/ai.js b/module/core/ai.js
index b2f0aac4..12f27c21 100644
--- a/module/core/ai.js
+++ b/module/core/ai.js
@@ -413,7 +413,7 @@ export const AGENTS = {
{ human: "Soul Destroyed", ai: "Fully Corrupted|Lost In Darkness|Spirit Broken" },
{ human: "Humiliated", ai: "Embarrassed|Momentarily Off-Balance|Enraged" },
{ human: "She Escapes!", ai: "She Spots a Means of Escape|She Puts More Distance Between You|She Stops to Gloat" },
- { human: "The fire spreads to the hostages.", ai: "The fire approaches the hostages|The hostages must be evacuated|The fire billows choking black smoke." }
+ { human: "The fire spreads to the hostages.", ai: "The fire approaches the hostages.|The hostages must be evacuated.|The fire billows choking black smoke." }
]
}
};
diff --git a/module/core/constants.js b/module/core/constants.js
index 6a4256e2..2c318c84 100644
--- a/module/core/constants.js
+++ b/module/core/constants.js
@@ -399,6 +399,14 @@ const C = {
AI_FILE_IDS: {
BladesPDF: "file-n72HTTNwt051piPbswQ8isUa"
},
+ DowntimeActionDisplay: {
+ [DowntimeAction.AcquireAsset]: "Acquire an Asset",
+ [DowntimeAction.IndulgeVice]: "Indulge Your Vice",
+ [DowntimeAction.LongTermProject]: "Work on a Project",
+ [DowntimeAction.Recover]: "Heal",
+ [DowntimeAction.ReduceHeat]: "Reduce the Crew's Heat",
+ [DowntimeAction.Train]: "Train"
+ },
Consequences: {
[Position.controlled]: {
[RollResult.partial]: [
diff --git a/module/core/gsapBROKEN.js b/module/core/gsapBROKEN.js
new file mode 100644
index 00000000..b07afc37
--- /dev/null
+++ b/module/core/gsapBROKEN.js
@@ -0,0 +1,582 @@
+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";
+const gsapPlugins = [
+ TextPlugin
+];
+const gsapEffects = {
+ /* Basic Element Effects */
+ fadeOut: {
+ effect: (targets, config) => {
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .to(targets, {
+ autoAlpha: 0,
+ duration: config.duration,
+ ease: config.ease
+ });
+ // if (config.reversed) {
+ // tl.seek(config.duration);
+ // }
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ duration: 0.5,
+ ease: "power4.out"
+ }
+ },
+ blurOut: {
+ effect: (targets, config) => {
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .to(targets, {
+ skewX: config.skewX,
+ duration: 0.5 * config.duration,
+ ease: "power4.out"
+ })
+ .to(targets, {
+ x: `+=${config.rangeX}`,
+ marginBottom(i, target) {
+ return U.get(target, "height") * -1;
+ },
+ marginRight(i, target) {
+ return U.get(target, "width") * -1;
+ },
+ scale: config.scale,
+ filter: `blur(${config.blurStrength}px)`,
+ duration: 0.75 * config.duration
+ }, 0.25 * config.duration)
+ .to(targets, {
+ autoAlpha: 0,
+ duration: 0.5 * config.duration,
+ ease: "power3.in"
+ }, 0.5 * config.duration);
+ // if (config.reversed) {
+ // tl.seek(config.duration);
+ // }
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ duration: 0.5,
+ skewX: -20,
+ rangeX: 300,
+ scale: 1.5,
+ blurStrength: 10
+ }
+ },
+ slideOut: {
+ effect: (targets, config) => {
+ const scaleKey = ["up", "down"].includes(config.dir) ? "scaleY" : "scaleX";
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .to(targets, {
+ [scaleKey]: 0,
+ duration: config.duration,
+ ease: config.ease
+ });
+ // if (config.reversed) {
+ // tl.seek(config.duration);
+ // }
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ duration: 0.5,
+ ease: "back.out(3)"
+ }
+ },
+ brighten: {
+ effect: (targets, config) => {
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .fromTo(targets, {
+ filter: `brightness(${config.startStrength})`
+ }, {
+ filter: `brightness(${config.strength})`,
+ duration: config.duration,
+ ease: config.ease
+ });
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ initialStrength: 1,
+ strength: 1.5,
+ duration: 0.5,
+ ease: "none"
+ }
+ },
+ enlarge: {
+ effect: (targets, config) => {
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .fromTo(targets, {
+ scale: config.startScale
+ }, {
+ scale: config.scale,
+ duration: config.duration,
+ ease: config.ease
+ });
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ startScale: 1,
+ scale: 1.5,
+ duration: 0.5,
+ ease: "sine.out"
+ }
+ },
+ changeColor: {
+ effect: (targets, config) => {
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .to(targets, {
+ color: config.color,
+ duration: config.duration,
+ ease: config.ease
+ });
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ color: C.Colors.bWHITE,
+ duration: 0.5,
+ ease: "sine"
+ }
+ },
+ skewX: {
+ effect: (targets, config) => {
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .to(targets, {
+ skewX: config.angle,
+ duration: config.duration,
+ ease: config.ease
+ });
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ angle: -45,
+ duration: 0.5,
+ ease: "sine"
+ }
+ },
+ blurRemove: {
+ effect: (targets, config) => U.gsap.timeline()
+ .to(targets, {
+ skewX: config.skewX,
+ duration: config.duration / 2,
+ ease: "power4.out"
+ })
+ .to(targets, {
+ x: config.x,
+ marginBottom(i, target) {
+ return U.get(target, "height") * -1;
+ },
+ marginRight(i, target) {
+ return U.get(target, "width") * -1;
+ },
+ scale: config.scale,
+ filter: config.filter,
+ duration: (3 / 4) * config.duration
+ }, config.duration / 4)
+ .to(targets, {
+ opacity: 0,
+ duration: config.duration / 2,
+ ease: "power3.in"
+ }, config.duration / 2),
+ defaults: {
+ skewX: -20,
+ duration: 0.5,
+ x: "+=300",
+ scale: 1.5,
+ filter: "blur(10px)"
+ }
+ },
+ slideUp: {
+ effect: (targets) => U.gsap.to(targets, {
+ height: 0,
+ // PaddingTop: 0,
+ // paddingBottom: 0,
+ duration: 0.5,
+ ease: "power3"
+ }),
+ defaults: {}
+ },
+ pulse: {
+ effect: (targets, config) => U.gsap.to(targets, {
+ repeat: config.repCount,
+ yoyo: true,
+ duration: config.duration / config.repCount,
+ ease: config.ease,
+ opacity: 0.25
+ }),
+ defaults: {
+ repCount: 3,
+ duration: 5,
+ ease: "sine.inOut"
+ }
+ },
+ throb: {
+ effect: (targets, config) => U.gsap.to(targets, {
+ repeat: config.stagger ? undefined : 1,
+ yoyo: config.stagger ? undefined : true,
+ duration: config.duration / 2,
+ scale: config.scale,
+ filter: config.filter,
+ ease: config.ease,
+ stagger: config.stagger
+ ? {
+ ...config.stagger,
+ repeat: 1,
+ yoyo: true
+ }
+ : {}
+ }),
+ defaults: {
+ duration: 1,
+ scale: 1,
+ filter: "saturate(1) brightness(2)",
+ ease: "power2.in"
+ },
+ extendTimeline: true
+ },
+ pulseClockWedges: {
+ effect: () => U.gsap.timeline({ duration: 0 }),
+ defaults: {}
+ },
+ reversePulseClockWedges: {
+ 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 ?
+ return U.gsap.effects.throb(targets, { stagger: {
+ amount: 0.25,
+ from: "start",
+ repeat: 1,
+ yoyo: true
+ }, ...config ?? {} });
+ },
+ defaults: {}
+ },
+ hoverTooltip: {
+ effect: (tooltip, config) => {
+ const tl = U.gsap.timeline({ paused: true, defaults: {} });
+ if (!tooltip) {
+ return tl;
+ }
+ // Tooltip = $(tooltip);
+ if (config.scalingElems.length > 0) {
+ tl.to(config.scalingElems, {
+ scale: "+=0.2",
+ filter: "none",
+ color: C.Colors.WHITE,
+ opacity: 1,
+ duration: 0.125,
+ ease: "back"
+ }, 0.5);
+ }
+ if (tooltip) {
+ tl.fromTo(tooltip, {
+ filter: "blur(50px)",
+ opacity: 0,
+ scale: 2 * config.tooltipScale
+ }, {
+ filter: "none",
+ opacity: 1,
+ scale: config.tooltipScale,
+ x: config.xMotion,
+ duration: 0.25,
+ ease: "power2"
+ }, 1);
+ }
+ return tl;
+ },
+ defaults: {
+ xMotion: "+=200",
+ tooltipScale: 1.25
+ }
+ }
+};
+/**
+ * Registers relevant GSAP plugins and effects.
+ */
+export function Initialize() {
+ if (gsapPlugins.length) {
+ U.gsap.registerPlugin(...gsapPlugins);
+ }
+ Object.entries(gsapEffects).forEach(([name, effect]) => {
+ U.gsap.registerEffect(Object.assign(effect, { name, extendTimeline: true }));
+ });
+}
+/**
+ * Applies listeners to '.tooltip-trigger' elements in the document.
+ * @param {JQuery} html The document to be searched.
+ */
+export function ApplyTooltipAnimations(html) {
+ html.find(".tooltip-trigger").each((_, el) => {
+ const tooltipElem = $(el).find(".tooltip")[0] ?? $(el).next(".tooltip")[0];
+ if (!tooltipElem) {
+ return;
+ }
+ $(el).data("hoverTimeline", U.gsap.effects.hoverTooltip(tooltipElem, {
+ scalingElems: [...$(el).find(".tooltip-scaling-elem")].filter((elem) => Boolean(elem)),
+ xMotion: $(tooltipElem).hasClass("tooltip-left") ? "-=250" : "+=200",
+ tooltipScale: $(tooltipElem).hasClass("tooltip-small") ? 1 : 1.2
+ }));
+ $(el).on({
+ mouseenter: function () {
+ $(el).css("z-index", 10);
+ $(el).data("hoverTimeline").play();
+ },
+ mouseleave: function () {
+ $(el).data("hoverTimeline").reverse().then(() => {
+ $(el).css("z-index", "");
+ });
+ }
+ });
+ });
+}
+/**
+ * Applies listeners to .consequence-display-container and children found in document.
+ * @param {JQuery} html The document to be searched.
+ */
+export function ApplyConsequenceAnimations(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 resistTo.type === "None", blurRemove the base_consequence name and type instead of sliding them in,
+ * and don't slide the resistance ones out at all.
+ * */
+ html
+ .find(".comp.consequence-display-container")
+ .each((_i, csqContainer) => {
+ if (!$(csqContainer).hasClass("consequence-accepted")) {
+ const csqRoot = U.gsap.utils.selector(csqContainer);
+ const csqBgImg = csqRoot(".consequence-bg-image");
+ const [iconContainer] = csqRoot(".consequence-icon-container");
+ const iconRoot = U.gsap.utils.selector(iconContainer);
+ const typeRoot = U.gsap.utils.selector(csqRoot(".consequence-type-container")[0]);
+ const nameRoot = U.gsap.utils.selector(csqRoot(".consequence-name-container")[0]);
+ const footerRoot = U.gsap.utils.selector(csqRoot(".consequence-footer-container")[0]);
+ const csqOptions = ["base", "accept"];
+ ["resist", "armor", "special"].forEach((csqOpt) => {
+ if (iconRoot(`.${csqOpt}-consequence`).length) {
+ csqOptions.push(csqOpt);
+ }
+ });
+ const interactionPads = {};
+ const iconCircles = {};
+ const buttonContainers = {};
+ const buttonBGs = {};
+ const buttonIcons = {};
+ const buttonLabels = {};
+ const typeLabels = {};
+ const typeBGs = {};
+ const nameLabels = {};
+ const nameBGs = {};
+ const footerLabels = {};
+ const footerBGs = {};
+ ["right", "left", "left-resist", "left-armor", "left-special"].forEach((iPadOpt) => {
+ [interactionPads[iPadOpt]] = csqRoot(`.consequence-interaction-pad.interaction-pad-${iPadOpt}`);
+ });
+ ["base", "accept", "resist", "armor", "special"].forEach((csqOpt) => {
+ [iconCircles[csqOpt]] = iconRoot(`.consequence-icon-circle.${csqOpt}-consequence`);
+ [buttonContainers[csqOpt]] = iconRoot(`.consequence-button-container.${csqOpt}-consequence`);
+ [buttonBGs[csqOpt]] = iconRoot(`.consequence-button-container.${csqOpt}-consequence .consequence-button-bg`);
+ [buttonIcons[csqOpt]] = iconRoot(`.consequence-button-container.${csqOpt}-consequence .button-icon i`);
+ [buttonLabels[csqOpt]] = iconRoot(`.consequence-button-container.${csqOpt}-consequence .consequence-button-label`);
+ [typeLabels[csqOpt]] = typeRoot(`.consequence-type.${csqOpt}-consequence`);
+ if (csqOpt === "accept") {
+ [typeBGs[csqOpt]] = typeRoot(`.consequence-type-bg.${csqOpt}-consequence`);
+ }
+ [nameLabels[csqOpt]] = nameRoot(`.consequence-name.${csqOpt}-consequence`);
+ [nameBGs[csqOpt]] = nameRoot(`.consequence-name-bg.${csqOpt}-consequence`);
+ [footerLabels[csqOpt]] = footerRoot(`.consequence-footer-message.${csqOpt}-consequence`);
+ [footerBGs[csqOpt]] = footerRoot(`.consequence-footer-bg.${csqOpt}-consequence`);
+ });
+ [
+ interactionPads,
+ iconCircles,
+ buttonContainers, buttonBGs, buttonIcons, buttonLabels,
+ typeLabels, typeBGs,
+ nameLabels, nameBGs,
+ footerLabels, footerBGs
+ ]
+ .forEach((elemRecord) => U.objCompact(elemRecord, [undefined, null, false], true));
+ // Apply master on-enter hover timeline to consequence container.
+ $(csqContainer).data("hoverTimelines", [
+ U.gsap.effects.fadeOut([typeLabels.base, nameLabels.base], { paused: true, duration: 0.5 }),
+ U.gsap.effects.fadeOut([typeLabels.accept, nameLabels.accept], { paused: true, reversed: true, duration: 0.25 }),
+ U.gsap.effects.brighten([csqContainer], { paused: true, duration: 0.5 }),
+ U.gsap.effects.enlarge([iconCircles.base], { paused: true, duration: 0.75, startScale: 0.75, scale: 0.85 })
+ ]);
+ $(csqContainer).on({
+ mouseenter: function () {
+ $(csqContainer).css("z-index", 10);
+ $(csqContainer).data("hoverTimelines").forEach((tl) => tl.play());
+ },
+ mouseleave: function () {
+ if (!($(iconContainer).data("isToggled"))) {
+ $(csqContainer).css("z-index", "");
+ $(csqContainer).data("hoverTimelines").forEach((tl) => tl.reverse());
+ }
+ }
+ });
+ // Apply click timeline to icon circle
+ $(iconContainer).data("clickTimelines", [
+ U.gsap.timeline({ paused: true })
+ .fromTo([csqBgImg], {
+ xPercent: 110,
+ yPercent: -50
+ }, {
+ xPercent: -60,
+ yPercent: -50,
+ duration: 0.5,
+ ease: "back"
+ }),
+ U.gsap.effects.fadeOut([iconCircles.base], { paused: true }),
+ U.gsap.effects.fadeOut([iconCircles.accept], { paused: true, reversed: true }),
+ U.gsap.effects.blurOut([buttonContainers], { paused: true, reversed: true })
+ ]);
+ $(iconContainer).on({
+ click: function () {
+ if ($(iconContainer).data("isToggled")) {
+ $(iconContainer).data("isToggled", false);
+ $(iconContainer).data("clickTimelines").forEach((tl) => tl.reverse());
+ }
+ else {
+ $(iconContainer).data("isToggled", true);
+ $(iconContainer).data("clickTimelines").forEach((tl) => tl.play());
+ // 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("isToggled", false);
+ $(containerElem).css("z-index", "");
+ [
+ ...iContainer$.data("clickTimelines"),
+ ...$(containerElem).data("hoverTimelines")
+ ].forEach((tl) => tl.reverse());
+ }
+ });
+ }
+ }
+ });
+ // Apply hover timelines to right (accept) interaction pad
+ $(interactionPads.right).data("hoverTimelines", [
+ U.gsap.effects.changeColor([typeLabels.accept], { paused: true, color: C.Colors.WHITE }),
+ U.gsap.effects.slideOut([typeBGs.accept, buttonBGs.accept], { paused: true, reversed: true, dir: "left" }),
+ U.gsap.effects.skewX([typeBGs.accept, buttonBGs.accept], { paused: true, angle: -45 }),
+ U.gsap.effects.changeColor([buttonIcons.accept, buttonLabels.accept], { paused: true, color: C.Colors.dBLACK }),
+ U.gsap.effects.enlarge([buttonIcons.accept], { paused: true, scale: 1.25 })
+ ]);
+ $(interactionPads.right).on({
+ mouseenter: function () {
+ if ($(iconContainer).data("isToggled")) {
+ $(interactionPads.right).data("hoverTimelines").forEach((tl) => tl.play());
+ }
+ },
+ mouseleave: function () {
+ if ($(iconContainer).data("isToggled")) {
+ $(interactionPads.right).data("hoverTimelines").forEach((tl) => tl.reverse());
+ }
+ }
+ });
+ // Apply hover timeline to left (resist/armor/special) interaction pad
+ $(interactionPads.left).data("hoverTimelines", [
+ U.gsap.effects.fadeOut([typeLabels.accept, nameLabels.accept, iconCircles.accept, buttonContainers.accept], { paused: true, duration: 0.25 })
+ ]);
+ $(interactionPads.left).on({
+ mouseenter: function () {
+ if ($(iconContainer).data("isToggled")) {
+ $(interactionPads.left).data("hoverTimelines").forEach((tl) => tl.play());
+ }
+ },
+ mouseleave: function () {
+ if ($(iconContainer).data("isToggled")) {
+ $(interactionPads.left).data("hoverTimelines").forEach((tl) => tl.reverse());
+ }
+ }
+ });
+ // Apply hover timelines to specific left interaction pads where they exist
+ ["resist", "armor", "special"].forEach((csqOpt) => {
+ if (interactionPads[`left-${csqOpt}`]) {
+ $(interactionPads[`left-${csqOpt}`]).data("hoverTimelines", [
+ U.gsap.effects.fadeOut([iconCircles[csqOpt], typeLabels[csqOpt]], { paused: true, reversed: true }),
+ U.gsap.effects.slideOut([buttonBGs[csqOpt], nameLabels[csqOpt], footerBGs[csqOpt], footerLabels[csqOpt]], { paused: true, reversed: true, dir: "left" }),
+ U.gsap.effects.skewX([buttonBGs[csqOpt], nameLabels[csqOpt], footerBGs[csqOpt], footerLabels[csqOpt]], { paused: true, angle: -45 }),
+ U.gsap.effects.changeColor([buttonIcons[csqOpt], buttonLabels[csqOpt]], { paused: true, color: C.Colors.dBLACK }),
+ U.gsap.effects.enlarge([buttonIcons[csqOpt]], { paused: true, scale: 1.25 })
+ ]);
+ $(interactionPads[`left-${csqOpt}`]).on({
+ mouseenter: function () {
+ if ($(iconContainer).data("isToggled")) {
+ $(interactionPads[`left-${csqOpt}`]).data("hoverTimelines").forEach((tl) => tl.play());
+ }
+ },
+ mouseleave: function () {
+ if ($(iconContainer).data("isToggled")) {
+ $(interactionPads[`left-${csqOpt}`]).data("hoverTimelines").forEach((tl) => tl.reverse());
+ }
+ }
+ });
+ }
+ });
+ }
+ });
+}
+export default U.gsap;
diff --git a/module/core/utilities.js b/module/core/utilities.js
index c90ac9ee..e462458b 100644
--- a/module/core/utilities.js
+++ b/module/core/utilities.js
@@ -1021,7 +1021,7 @@ function objFindKey(obj, keyFunc, valFunc) {
* @param {testFunc} [valFunc] The testing function for values.
* @returns {Type} The filtered object.
*/
-const objFilter = (obj, keyFunc, valFunc) => {
+const objFilter = (obj, keyFunc, valFunc, isMutating = false) => {
//
if (!valFunc) {
valFunc = keyFunc;
@@ -1031,10 +1031,23 @@ const objFilter = (obj, keyFunc, valFunc) => {
keyFunc = ((k) => k);
}
if (isArray(obj)) {
- return obj.filter(valFunc);
+ const keptValues = obj.filter(valFunc);
+ if (isMutating) {
+ obj.splice(0, obj.length, ...keptValues);
+ return obj;
+ }
+ return keptValues;
}
const kFunc = keyFunc || (() => true);
const vFunc = valFunc || (() => true);
+ if (isMutating) {
+ const entriesToRemove = Object.entries(obj)
+ .filter(([key, val]) => !(kFunc(key, val) && vFunc(val, key)));
+ for (const [key] of entriesToRemove) {
+ delete obj[key];
+ }
+ return obj;
+ }
return Object.fromEntries(Object.entries(obj)
.filter(([key, val]) => kFunc(key, val) && vFunc(val, key)));
};
@@ -1048,7 +1061,7 @@ const objForEach = (obj, func) => {
}
};
// Prunes an object of given set of values, [undefined, null] default
-const objCompact = (obj, removeWhiteList = [undefined, null]) => objFilter(obj, (val) => !removeWhiteList.includes(val));
+const objCompact = (obj, removeWhiteList = [undefined, null], isMutating = false) => objFilter(obj, (val) => !removeWhiteList.includes(val), undefined, isMutating);
const objClone = (obj, isStrictlySafe = false) => {
const cloneArray = (arr) => [...arr];
const cloneObject = (o) => ({ ...o });
diff --git a/module/documents/actors/BladesPC.js b/module/documents/actors/BladesPC.js
index 60219e6a..adc906cf 100644
--- a/module/documents/actors/BladesPC.js
+++ b/module/documents/actors/BladesPC.js
@@ -8,6 +8,23 @@ class BladesPC extends BladesActor {
static IsType(doc) {
return super.IsType(doc, BladesActorType.pc);
}
+ static GetFromUser(userRef) {
+ let user;
+ if (typeof userRef === "string") {
+ user = game.users.get(userRef) ?? game.users.getName(userRef);
+ }
+ else if (userRef instanceof User) {
+ user = userRef;
+ }
+ if (!user) {
+ throw new Error(`Unable to find user '${userRef}'`);
+ }
+ const actor = game.actors.get(user.character?.id ?? "");
+ if (BladesPC.IsType(actor)) {
+ return actor;
+ }
+ return undefined;
+ }
static async create(data, options = {}) {
data.token = data.token || {};
data.system = data.system ?? {};
@@ -209,6 +226,12 @@ class BladesPC extends BladesActor {
}
await this.update({ "system.stash.value": Math.min(this.system.stash.value + amount, this.system.stash.max) });
}
+ get remainingDowntimeActions() {
+ if (!BladesActor.IsType(this, BladesActorType.pc)) {
+ return 0;
+ }
+ return this.system.downtime_actions.max + this.system.downtime_action_bonus - this.system.downtime_actions.value;
+ }
// #endregion
// #region BladesRoll.PrimaryDoc Implementation
get rollModsData() {
diff --git a/module/documents/items/BladesClock.js b/module/documents/items/BladesClock.js
new file mode 100644
index 00000000..adb3762c
--- /dev/null
+++ b/module/documents/items/BladesClock.js
@@ -0,0 +1,45 @@
+import BladesItem from "../../BladesItem.js";
+import { BladesActorType, Factor } from "../../core/constants.js";
+import U from "../../core/utilities.js";
+import BladesActor from "../../BladesActor.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;
+ // no default
+ }
+ return 0;
+ }
+ get rollOppImg() { return this.img ?? ""; }
+ // #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;
diff --git a/module/sheets/actor/BladesActorSheet.js b/module/sheets/actor/BladesActorSheet.js
index fe0b4478..f92b8c87 100644
--- a/module/sheets/actor/BladesActorSheet.js
+++ b/module/sheets/actor/BladesActorSheet.js
@@ -1,9 +1,9 @@
// #region IMPORTS~
import U from "../../core/utilities.js";
import G, { ApplyTooltipAnimations } from "../../core/gsap.js";
-import C, { BladesActorType, BladesItemType, AttributeTrait, ActionTrait, Factor, RollType } from "../../core/constants.js";
+import C, { BladesActorType, BladesItemType, DowntimeAction, AttributeTrait, ActionTrait, Factor, RollType } from "../../core/constants.js";
import Tags from "../../core/tags.js";
-import BladesActor from "../../BladesActor.js";
+import { BladesActor, BladesPC } from "../../documents/BladesActorProxy.js";
import BladesItem from "../../BladesItem.js";
import BladesDialog from "../../BladesDialog.js";
import BladesActiveEffect from "../../BladesActiveEffect.js";
@@ -352,7 +352,7 @@ class BladesActorSheet extends ActorSheet {
if (!doc) {
return;
}
- await G.effects.blurRemove(elem$).then(async () => {
+ await G.effects.blurOut(elem$).then(async () => {
if (doc instanceof BladesItem) {
await this.actor.remSubItem(doc);
}
@@ -367,7 +367,7 @@ class BladesActorSheet extends ActorSheet {
if (!doc) {
return;
}
- await G.effects.blurRemove(elem$).then(async () => await doc.delete());
+ await G.effects.blurOut(elem$).then(async () => await doc.delete());
}
async _onItemToggleClick(event) {
event.preventDefault();
@@ -419,6 +419,77 @@ class BladesActorSheet extends ActorSheet {
}
await BladesRoll.NewRoll(rollData);
}
+ async _onDowntimeActionClick(event) {
+ const elem$ = $(event.currentTarget);
+ // Check whether character has downtime actions remaining.
+ // If not, prompt for whether spending Coin or Rep for extra
+ // If so, increase character's downtime count by one
+ const downtimeAction = elem$.data("downtimeAction");
+ const rollConfig = {
+ rollType: RollType.Action,
+ rollDowntimeAction: downtimeAction
+ };
+ // Determine Trait from action type
+ switch (downtimeAction) {
+ case DowntimeAction.AcquireAsset: {
+ rollConfig.rollTrait = Factor.tier;
+ break;
+ }
+ case DowntimeAction.IndulgeVice: {
+ if (!BladesPC.IsType(this.actor)) {
+ return;
+ }
+ rollConfig.rollType = RollType.IndulgeVice;
+ rollConfig.rollTrait = Object.values(AttributeTrait)
+ .reduce((minAttr, curAttr) => this.actor.attributes[curAttr]
+ < this.actor.attributes[minAttr]
+ ? curAttr
+ : minAttr, AttributeTrait.insight);
+ // GM needs to be able to set the desired asset as the rollOpposition, so can set minimum quality
+ break;
+ }
+ case DowntimeAction.LongTermProject: {
+ rollConfig.rollTrait = "";
+ // BladesRoll can search actor subitems for project/rituals and set up their clocks as the 'opposition'
+ break;
+ }
+ case DowntimeAction.Recover: {
+ // If clicked on by player from an NPC sheet -> rollPrimary is the NPC, trait is quality
+ // Otherwise -> Search 'ActivePC' characters for 'Physicker' Ability; if more than one will have to prompt user
+ // ... OR ...
+ // ActiveEffect added to any BladesActor that can heal, along with reference to the trait they roll.
+ rollConfig.rollTrait = ActionTrait.tinker || Factor.quality;
+ break;
+ }
+ case DowntimeAction.ReduceHeat: {
+ rollConfig.rollTrait = "";
+ break;
+ }
+ case DowntimeAction.Train: {
+ // Element will have target: Attribute or Playbook.
+ // Will have to check for crew upgrades that increase XP gained.
+ // If too much XP gained, will have to store excess so it can roll over after the player advances.
+ // Then, because this doesn't take a roll, we just return.
+ return;
+ }
+ }
+ // ... Pretty much everything else should be done over in BladesRoll.
+ BladesRoll.NewRoll(rollConfig);
+ }
+ async _onGatherInfoClick(event) {
+ const elem$ = $(event.currentTarget);
+ if (elem$.data("isFortuneRoll")) {
+ BladesRoll.NewRoll({
+ rollType: RollType.Fortune
+ });
+ }
+ else {
+ BladesRoll.NewRoll({
+ rollType: RollType.Action,
+ rollTrait: ""
+ });
+ }
+ }
// #endregion
// #region Active Effect Handlers
_onActiveEffectControlClick(event) {
diff --git a/module/sheets/roll/BladesConsequence.js b/module/sheets/roll/BladesConsequence.js
index 6e300cc4..29e1f062 100644
--- a/module/sheets/roll/BladesConsequence.js
+++ b/module/sheets/roll/BladesConsequence.js
@@ -3,6 +3,15 @@ import C, { BladesActorType, AttributeTrait, ConsequenceType, RollResult, RollTy
import U from "../../core/utilities.js";
import BladesRoll, { BladesRollPrimary } from "../../BladesRoll.js";
class BladesConsequence {
+ static get None() {
+ return {
+ id: randomID(),
+ name: "",
+ type: ConsequenceType.None,
+ isSelected: true,
+ isVisible: true
+ };
+ }
static GetActiveRollChatID() {
return Array.from(game.messages).filter((msg) => $(msg.content ?? "").data("chat-id")).pop()?.id ?? undefined;
}
@@ -25,6 +34,14 @@ class BladesConsequence {
else if (roll$.hasClass("roll-type-indulgevice")) {
roll$.closest(".chat-message").addClass("indulgevice-roll");
}
+ // If this message is an action roll result AND there are unresolved consequences, add 'unresolved-action-roll' class.
+ if (roll$.hasClass("roll-type-action")
+ && Array.from(roll$.find(".comp.consequence-display-container:not(.consequence-accepted)")).length >= 1) {
+ roll$.closest(".chat-message").addClass("unresolved-action-roll");
+ }
+ else {
+ roll$.closest(".chat-message").removeClass("unresolved-action-roll");
+ }
// If this message is the last one, add 'active-chat-roll' class and remove it from all others
if (BladesConsequence.GetActiveRollChatID() === roll$.data("chatId")) {
$(document).find(".chat-message").removeClass("active-chat-roll");
@@ -34,7 +51,7 @@ class BladesConsequence {
roll$.closest(".chat-message").removeClass("active-chat-roll");
}
const rollPhase = roll$.data("rollPhase");
- eLog.checkLog3("rollCollab", "ApplyChatListeners", { html, roll$, rollPhase });
+ // eLog.checkLog3("rollCollab", "ApplyChatListeners", {html, roll$, rollPhase});
if (rollPhase !== RollPhase.AwaitingConsequences) {
return;
}
@@ -151,7 +168,7 @@ class BladesConsequence {
static async GetFromChatMessage(message, csqID) {
const html$ = $(await message.getHTML());
const bCsqs = [];
- html$.find(".blades-roll .consequence-container .comp.consequence-display-container").each((_, elem) => {
+ html$.find(".blades-roll .consequence-container .comp.consequence-display-container:not(.consequence-accepted)").each((_, elem) => {
bCsqs.push(BladesConsequence.GetFromCsqElem(elem));
});
if (csqID) {
@@ -269,7 +286,7 @@ class BladesConsequence {
this._name = name;
this._primaryID = primaryID;
this._primaryType = primaryType;
- this._primaryDoc = primaryDoc;
+ this._primaryDoc = new BladesRollPrimary(undefined, primaryDoc);
this._type = type;
this._position = position;
this._effect = effect;
@@ -436,11 +453,7 @@ class BladesConsequence {
this._isAccepted = true;
await this._chatMessage.update({ content: message$[0].outerHTML });
}
- async resistConsequence() {
- eLog.checkLog3("rollCollab", `Resisting Consequence id ${this._id}`);
- if (!this._resistTo || !this.resistToData) {
- throw new Error(`Cannot find resistTo for resistance roll for csq id '${this._id}' in message '${this._chatMessage.id}'`);
- }
+ get rollFlagData() {
// Get rollPrimaryData from archived roll flags on user document.
let rollFlagData = this._user.getFlag(C.SYSTEM_ID, "rollCollab");
if (rollFlagData.rollID !== this._rollID) {
@@ -449,6 +462,15 @@ class BladesConsequence {
if (!rollFlagData) {
throw new Error(`Unable to locate flag data for roll id '${this._rollID}'`);
}
+ return rollFlagData;
+ }
+ async resistConsequence() {
+ eLog.checkLog3("rollCollab", `Resisting Consequence id ${this._id}`);
+ if (!this._resistTo || !this.resistToData) {
+ throw new Error(`Cannot find resistTo for resistance roll for csq id '${this._id}' in message '${this._chatMessage.id}'`);
+ }
+ // Get rollPrimaryData from archived roll flags on user document.
+ const rollFlagData = this.rollFlagData;
const resistConfig = {
rollType: RollType.Resistance,
rollUserID: this._user.id,
@@ -468,17 +490,24 @@ class BladesConsequence {
};
BladesRoll.NewRoll(resistConfig);
}
- async applyResistedConsequence() {
- if (this._resistTo) {
- await this._resistTo.applyConsequenceToPrimary();
- await this.transformToConsequence("resist");
- }
+ async applyResistedConsequence(resistType) {
+ const rCsq = {
+ resist: this._resistTo,
+ armor: this._armorTo,
+ special: this._specialTo
+ }[resistType];
+ if (rCsq) {
+ await rCsq.applyConsequenceToPrimary();
+ }
+ await this.transformToConsequence(resistType);
}
async resistArmorConsequence() {
- /* ... */
+ this._primaryDoc.spendArmor();
+ this.applyResistedConsequence("armor");
}
async resistSpecialArmorConsequence() {
- /* ... */
+ await this._primaryDoc.spendSpecialArmor();
+ this.applyResistedConsequence("special");
}
}
export default BladesConsequence;
diff --git a/scss/chat/_chat.scss b/scss/chat/_chat.scss
index 9f5dc9bb..f2be44d5 100644
--- a/scss/chat/_chat.scss
+++ b/scss/chat/_chat.scss
@@ -17,7 +17,7 @@
.flexrow.jump-to-bottom {
- bottom: 154px;
+ bottom: 25px;
z-index: 5;
}
@@ -29,7 +29,7 @@
z-index: 1;
}
}
-
+.chat-message:not(.display-ok) { opacity: 0 !important; }
.chat-message[class*=-roll] {
--font-primary: Beaufort !important;
@@ -58,7 +58,12 @@
background: transparent;
border: 2px ouset var(--blades-white);
- &:not(.active-chat-roll) {
+
+
+ &:not(.unresolved-action-roll) {
+ .blades-roll > *:not(.chat-message-speaker-portrait-wrapper) {
+ filter: sepia(1) grayscale(0.5) brightness(0.6);
+ }
.trait-label.trait-verb { display: none; }
.consequence-icon-container {
.consequence-icon-circle.base-consequence {
@@ -66,10 +71,33 @@
img { animation: none !important; }
}
}
+ .chat-message-speaker-portrait-wrapper {
+ $border-color-bright: #9b9b9b; // #aa5a29;
+ $border-color-med: #565656; // #5a2f17;
+ $border-color-dark: #383838; // #531c06;
+ $border-color-darkest: #232323; // #3b1201; // 46381f 6b5630 9e7f46
+
+ --border-gradient: linear-gradient(-72deg,
+ #{$border-color-med},
+ #{$border-color-bright} 16%,
+ #{$border-color-med} 21%,
+ #{$border-color-bright} 24%,
+ #{$border-color-dark} 27%,
+ #{$border-color-med} 36%,
+ #{$border-color-bright} 45%,
+ #{$border-color-bright} 60%,
+ #{$border-color-med} 72%,
+ #{$border-color-bright} 80%,
+ #{$border-color-med} 84%,
+ #{$border-color-darkest});
+
+ transform-origin: 50% 0%;
+ scale: 0.6 0.75;
+ }
}
}
-.chat-message[class*=-roll].active-chat-roll {
+.chat-message[class*=-roll].unresolved-action-roll {
--bg-controlled: url("../assets/animations/chat/roll-position-controlled.webp");
--bg-risky: url("../assets/animations/chat/roll-position-risky.webp");
--bg-desperate: url("../assets/animations/chat/roll-position-desperate.webp");
@@ -150,7 +178,7 @@
$border-color-dark: #46381f; // #531c06;
$border-color-darkest: #2c2315; // #3b1201; // 46381f 6b5630 9e7f46
- $border-gradient: linear-gradient(-72deg,
+ --border-gradient: linear-gradient(-72deg,
#{$border-color-med},
#{$border-color-bright} 16%,
#{$border-color-med} 21%,
@@ -175,7 +203,7 @@
scale: 0.8 1;
transform-origin: 50% 50%;
- background: $border-gradient;
+ background: var(--border-gradient);
background-repeat: no-repeat;
outline: 3px solid black;
box-shadow: 0 0 5px 5px black;
@@ -223,7 +251,7 @@
&.action-roll + .chat-message.resistance-roll {
margin-top: calc(-1 * var(--chat-vertical-gap));
}
- &.action-roll.active-chat-roll + .chat-message.resistance-roll {
+ &.action-roll.unresolved-action-roll + .chat-message.resistance-roll {
margin-top: 0;
}
@@ -578,6 +606,28 @@
padding: 4px;
gap: 0;
+ h4.roll-state-label {
+ font-family: var(--font-emphasis-narrow);
+ font-size: 12px;
+ line-height: 12px;
+ display: block;
+ transform: translate(0%, -50%);
+ top: 50%;
+ text-align: center;
+ white-space: nowrap;
+ color: var(--side-color-secondary);
+
+ strong { color: var(--side-color-secondary) !important; font-weight: 900 !important; }
+ }
+
+ h3.roll-state {
+ white-space: nowrap;
+ font-size: 18px;
+ line-height: 18px;
+ text-shadow: var(--text-shadow-dark-strong);
+ color: var(--side-color-main);
+ }
+
&.roll-state-container-left {
h4.roll-state-label {
position: absolute;
@@ -585,6 +635,7 @@
}
h3.roll-state {
position: absolute;
+ transform-origin: 100% 0%;
top: 5px;
right: 5px;
scale: var(--side-left-x-scale) 1;
@@ -601,6 +652,7 @@
h3.roll-state {
width: 100%;
position: absolute;
+ transform-origin: 0% 0%;
bottom: 5px;
left: 5px;
text-align: left;
@@ -608,29 +660,6 @@
scale: var(--side-right-x-scale) 1;
}
}
-
- h4.roll-state-label {
- font-family: var(--font-emphasis-narrow);
- font-size: 12px;
- line-height: 12px;
- display: block;
- transform: translate(0%, -50%);
- top: 50%;
- text-align: center;
- white-space: nowrap;
- color: var(--side-color-secondary);
-
- strong { color: var(--side-color-secondary) !important; font-weight: 900 !important; }
- }
-
- h3.roll-state {
- white-space: nowrap;
- font-size: 18px;
- line-height: 18px;
- transform-origin: 0% 0%;
- text-shadow: var(--text-shadow-dark-strong);
- color: var(--side-color-main);
- }
}
diff --git a/scss/components/_comps.scss b/scss/components/_comps.scss
index b8d3a9bc..bda4b08e 100644
--- a/scss/components/_comps.scss
+++ b/scss/components/_comps.scss
@@ -1082,20 +1082,29 @@
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-accept-button-bg {
+ width: calc(100% + 30px);
+ right: -7px;
+ transform: skewX(-45deg);
+ }
+
&.consequence-resist-button-bg,
+ &.consequence-armor-button-bg,
&.consequence-special-button-bg {
+ width: calc(100% + 30px);
transform-origin: 100% 50%;
+ right: calc(-0.5 * var(--container-height));
+ transform: skewX(45deg);
}
+ &.consequence-armor-button-bg,
&.consequence-special-button-bg {
- width: calc(100% + 40px);
- margin-left: -10px;
+ width: calc(100% + 35px);
}
}
@@ -1136,23 +1145,20 @@
&.consequence-resist-button-container {
right: 100%;
-
- .consequence-button-bg {
- left: -7px;
- transform: skewX(45deg);
- }
}
&.consequence-accept-button-container {
left: 105%;
- .consequence-button-bg {
- right: -7px;
- transform: skewX(-45deg);
- }
+ }
+ &.consequence-armor-button-container {
+ right: 100%;
+ transform: translate(0%, 0%) !important;
+ top: 0%;
}
&.consequence-special-button-container {
right: 100%;
- bottom: 12px;
+ transform: translate(0%, -50%) !important;
+ top: 50%;
}
}
}
diff --git a/scss/dialog/_dialogs.scss b/scss/dialog/_dialogs.scss
index a6e12065..e6be6f58 100644
--- a/scss/dialog/_dialogs.scss
+++ b/scss/dialog/_dialogs.scss
@@ -244,6 +244,12 @@
font-family: var(--font-emphasis-narrow);
margin-right: 5px;
flex-grow: 2;
+
+ &.consequence-name-none {
+ box-shadow: none;
+ background: none;
+ cursor: default;
+ }
}
.roll-consequence-type-select {
diff --git a/scss/sheets/_roll-collab-sheet.scss b/scss/sheets/_roll-collab-sheet.scss
index 530f58ef..ef604460 100644
--- a/scss/sheets/_roll-collab-sheet.scss
+++ b/scss/sheets/_roll-collab-sheet.scss
@@ -872,6 +872,20 @@
.sheet-main {
right: 6px;
flex-basis: auto;
+
+ &.roll-opp-block {
+
+ }
+
+ &.roll-consequences {
+ min-height: 330px;
+ h3 { margin-bottom: 10px; }
+ }
+
+ &.factor-controls {
+ min-height: 112px;
+ place-self: stretch flex-end;
+ }
}
}
}
@@ -1444,8 +1458,8 @@
z-index: 5;
&.split-root-left {
- flex-basis: 85%;
- max-width: 85%;
+ flex-basis: 80%;
+ max-width: 80%;
// max-width: 370px;
}
diff --git a/scss/style.scss b/scss/style.scss
index b15601b9..750be5e4 100644
--- a/scss/style.scss
+++ b/scss/style.scss
@@ -103,6 +103,7 @@
@import "./core/reset";
@import './core/globals';
+ #chat-controls, #chat-form { display: none }
&,
* {
--font-heading: var(--font-emphasis);
diff --git a/template.json b/template.json
index ed11ec70..cf6eddb2 100644
--- a/template.json
+++ b/template.json
@@ -45,7 +45,12 @@
"Ability": 0,
"Cohort": 0,
"CohortType": 0
- }
+ },
+ "downtime_actions": {
+ "value": 0,
+ "max": 2
+ },
+ "downtime_action_bonus": 0
},
"hold": {
"hold": "strong"
@@ -420,6 +425,10 @@
"value": 0,
"max": 4
},
+ "armor": {
+ "value": 0,
+ "max": 0
+ },
"scale_bonus": 0,
"quality_bonus": 0
},
diff --git a/templates/actor-sheet.hbs b/templates/actor-sheet.hbs
index a609450a..4f3a59b0 100644
--- a/templates/actor-sheet.hbs
+++ b/templates/actor-sheet.hbs
@@ -246,8 +246,8 @@
dataFlagTarget=false
dataDType="String"
dataDocType=false
- selected=loadData.selLoadLevel
- options=loadData.selectOptions }}
+ selected=loadData.selected
+ options=loadData.options }}
{{#if loadData.selLoadCount}}(Max
{{loadData.selLoadCount}} Load){{/if}}
diff --git a/templates/components/consequence.hbs b/templates/components/consequence.hbs
index de39b79d..277b28b3 100644
--- a/templates/components/consequence.hbs
+++ b/templates/components/consequence.hbs
@@ -23,12 +23,12 @@
- {{#if armorTo}}
-
- {{/if}}
{{#if specialArmorTo}}
{{/if}}
+ {{#if armorTo}}
+
+ {{/if}}
{{!-- Icon --}}
@@ -175,4 +175,6 @@
+
+
diff --git a/templates/dialog-input.hbs b/templates/dialog-input.hbs
new file mode 100644
index 00000000..b3d1eda6
--- /dev/null
+++ b/templates/dialog-input.hbs
@@ -0,0 +1,14 @@
+
+
+
+
+ {{#each buttons as |bData bName|}}
+
+ {{/each}}
+
\ No newline at end of file
diff --git a/templates/parts/dialog-consequence-block.hbs b/templates/parts/dialog-consequence-block.hbs
index 3c11b474..dba37ab4 100644
--- a/templates/parts/dialog-consequence-block.hbs
+++ b/templates/parts/dialog-consequence-block.hbs
@@ -38,38 +38,48 @@
selected=cData.attribute
options=../consequenceAttributeOptions
}}
+ {{!-- Blank Options Trigger --}}
+ {{> "systems/eunos-blades/templates/components/button-icon.hbs"
+ blockClass="blank-option-button"
+ buttonClass="fa-duotone fa-credit-card-blank"
+ action=(concat "blank-option-" ../rollPosition "-" ../rollResult "-" cID)
+ }}
{{!-- AI Trigger --}}
+ {{#unless cData.resistNegates}}
{{> "systems/eunos-blades/templates/components/button-icon.hbs"
blockClass="ai-query-button"
buttonClass="fa-solid fa-brain-circuit"
tooltip="Query AI
Query OpenAI for suggestions on resisted versions of this consequence."
action=(concat "ai-query-" ../rollPosition "-" ../rollResult "-" cID)
}}
- {{!-- Armor Toggles --}}
- {{#if (test cData.attribute "==" "prowess")}}
+ {{/unless}}
+ {{!-- Armor Toggle --}}
+ {{#if cData.isDisplayingArmorToggle}}
{{> "systems/eunos-blades/templates/components/toggle-icon.hbs"
- isToggled=cData.canArmorA
- blockClass="armor-toggle armor-toggle-a"
- targetFlagKey=(concat "rollCollab.consequenceData." ../rollPosition "." ../rollResult "." cID ".canArmorA")
+ isToggled=cData.canArmor
+ blockClass="armor-toggle"
+ dataAction=(concat "toggle-armor-" ../rollPosition "-" ../rollResult "-" cID)
activeClass="fa-sharp fa-solid fa-shield"
inactiveClass="fa-sharp fa-regular fa-shield"
}}
- {{#if cData.canArmorA}}
- {{> "systems/eunos-blades/templates/components/toggle-icon.hbs"
- isToggled=cData.canArmorB
- blockClass="armor-toggle armor-toggle-b"
- targetFlagKey=(concat "rollCollab.consequenceData." ../rollPosition "." ../rollResult "." cID ".canArmorB")
- activeClass="fa-sharp fa-solid fa-shield"
- inactiveClass="fa-sharp fa-regular fa-shield"
- }}
- {{/if}}
{{/if}}
+ {{!-- Special Armor Toggle --}}
+ {{#if cData.isDisplayingSpecialArmorToggle}}
+ {{> "systems/eunos-blades/templates/components/toggle-icon.hbs"
+ isToggled=cData.canSpecialArmor
+ blockClass="special-armor-toggle"
+ dataAction=(concat "toggle-special-" ../rollPosition "-" ../rollResult "-" cID)
+ activeClass="fa-sharp fa-duotone fa-shield-quartered"
+ inactiveClass="fa-sharp fa-regular fa-shield-quartered"
+ }}
+ {{/if}}
{{!-- Name --}}
{{!-- AI Options --}}
diff --git a/templates/roll/partials/roll-collab-action-gm.hbs b/templates/roll/partials/roll-collab-action-gm.hbs
index d4d2416a..fd2a66a0 100644
--- a/templates/roll/partials/roll-collab-action-gm.hbs
+++ b/templates/roll/partials/roll-collab-action-gm.hbs
@@ -242,7 +242,7 @@
{{#each csqData.partial as |cData cIndex|}}
{{#if cData.resistTo}}
- {{> "systems/eunos-blades/templates/components/consequence.hbs" cData
+ {{> "systems/eunos-blades/templates/components/consequence-accepted.hbs" cData
isResistanceVisible=true }}
{{/if}}
{{/each}}
@@ -251,7 +251,7 @@
{{#each csqData.fail as |cData cIndex|}}
{{#if cData.resistTo}}
- {{> "systems/eunos-blades/templates/components/consequence.hbs" cData
+ {{> "systems/eunos-blades/templates/components/consequence-accepted.hbs" cData
isResistanceVisible=true }}
{{/if}}
{{/each}}
diff --git a/templates/roll/partials/roll-collab-action.hbs b/templates/roll/partials/roll-collab-action.hbs
index b9021e59..8b79464d 100644
--- a/templates/roll/partials/roll-collab-action.hbs
+++ b/templates/roll/partials/roll-collab-action.hbs
@@ -42,6 +42,12 @@
+{{#if (test gamePhase "==" "Downtime")}}{{#if (test rollPrimary.rollPrimaryType "==" "pc")}}
+
+{{/if}}{{/if}}
+
@@ -242,9 +248,17 @@
- {{#if rollOpposition}}
{{/if}}
+ {{#if rollOpposition}}
+ {{#if (test rollOpposition.rollOppType "==" "clock")}}
+ {{> "systems/eunos-blades/templates/components/clock.hbs" rollOpposition.clockData}}
+ {{else}}
+
+ {{/if}}
+ {{/if}}
+ {{#unless (test rollOpposition.rollOppType "==" "clock")}}
+ {{/unless}}
{{> "systems/eunos-blades/templates/components/roll-collab-opposition.hbs" rollOpposition=rollOpposition factorData=rollFactors.opposition}}
diff --git a/ts/@types/blades-actor.d.ts b/ts/@types/blades-actor.d.ts
index d70d88f9..3b016df9 100644
--- a/ts/@types/blades-actor.d.ts
+++ b/ts/@types/blades-actor.d.ts
@@ -44,8 +44,10 @@ declare global {
experience: {
playbook: ValueMax,
clues: string[]
- }
- advancement_points: Record
+ },
+ advancement_points: Record,
+ downtime_actions: ValueMax,
+ downtime_action_bonus: number
}
export interface hold {
diff --git a/ts/@types/blades-clock.d.ts b/ts/@types/blades-clock.d.ts
index 879e7802..1386315f 100644
--- a/ts/@types/blades-clock.d.ts
+++ b/ts/@types/blades-clock.d.ts
@@ -11,6 +11,7 @@
isVisible: boolean,
scene?: string,
target?: string,
+ flagTarget?: string,
gm_notes?: string
}
diff --git a/ts/@types/blades-dialog.d.ts b/ts/@types/blades-dialog.d.ts
index ce4654b0..e13bb9b8 100644
--- a/ts/@types/blades-dialog.d.ts
+++ b/ts/@types/blades-dialog.d.ts
@@ -8,7 +8,7 @@ declare global {
namespace BladesDialog {
interface Options extends DialogOptions {}
interface Data extends Dialog.Data {
- parent: BladesActor|BladesRoll;
+ parent: BladesActor|BladesItem|BladesRoll;
dialogType?: BladesDialogType;
docType?: "Actor"|"Item";
tabs?: Record;
@@ -31,7 +31,10 @@ declare global {
>
>,
consequenceTypeOptionsAll?: Array>,
- consequenceAttributeOptions?: Array>
+ consequenceAttributeOptions?: Array>,
+ prompt?: string,
+ target?: string,
+ flagTarget?: string
}
}
}
\ No newline at end of file
diff --git a/ts/@types/blades-item.d.ts b/ts/@types/blades-item.d.ts
index 53718f50..4203ef9e 100644
--- a/ts/@types/blades-item.d.ts
+++ b/ts/@types/blades-item.d.ts
@@ -39,6 +39,7 @@ declare global {
edges: Record,
flaws: Record,
harm: ValueMax,
+ armor: ValueMax,
scale_bonus: number,
quality_bonus: number,
diff --git a/ts/@types/blades-roll.d.ts b/ts/@types/blades-roll.d.ts
index bbb1d381..4a60811a 100644
--- a/ts/@types/blades-roll.d.ts
+++ b/ts/@types/blades-roll.d.ts
@@ -1,4 +1,4 @@
-import {BladesActorType, BladesItemType, RollType, RollSubType, ConsequenceType, RollModStatus, RollModSection, ActionTrait, DowntimeAction, AttributeTrait, Position, Effect, Factor, RollPhase, RollResult} from "../core/constants";
+import {BladesActorType, BladesItemType, BladesPhase, RollType, RollSubType, ConsequenceType, RollModStatus, RollModSection, ActionTrait, DowntimeAction, AttributeTrait, Position, Effect, Factor, RollPhase, RollResult} from "../core/constants";
import BladesActor from "../BladesActor";
import BladesItem from "../BladesItem";
import {BladesRollMod, BladesRollPrimary, BladesRollOpposition, BladesRollParticipant} from "../BladesRoll";
@@ -24,7 +24,8 @@ declare global {
icon?: string,
footerMsg?: string,
isAccepted?: boolean,
- isSelected: boolean // Whether GM has selected this as the resisted consequence
+ isSelected: boolean, // Whether GM has selected this as the resisted consequence
+ isVisible?: boolean // Set to false when a different option is chosen, to hide
}
export type AcceptedConsequenceData = {
@@ -52,20 +53,16 @@ declare global {
string, // stringified index
ConsequenceResistOption // ai
>,
+ resistNegates?: boolean,
resistTo?: 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
+ isDisplayingArmorToggle?: boolean,
+ canArmor?: boolean,
+ armorNegates?: boolean,
+ armorTo?: ConsequenceResistOption|false,
+ isDisplayingSpecialArmorToggle?: boolean,
+ canSpecialArmor?: boolean,
+ specialArmorNegates?: boolean,
+ specialArmorTo?: ConsequenceResistOption|false
}
export interface ResistanceRollConsequenceData extends FreezeProps> {
@@ -186,13 +183,18 @@ declare global {
oddsHTMLStop: string,
costData?: Record<"footerLabel"|"tooltip",string>,
- userPermission: RollPermissions
+ userPermission: RollPermissions,
+ gamePhase: BladesPhase,
+ canDoDowntimeActions?: boolean,
+ downtimeActionsRemaining?: number,
+ downtimeActionOptions?: Array>,
+ projectSelectOptions?: Array>
}
export type PartialSheetData = Partial & FlagData;
export type AnyRollType = RollType|RollSubType|DowntimeAction;
- export type RollTrait = ActionTrait|AttributeTrait|Factor|number;
+ export type RollTrait = ActionTrait|AttributeTrait|Factor|number|""|"Heat"|"Coin"|"Lifestyle";
export type FactorToggle = "isActive"|"isPrimary"|"isDominant"|"highFavorsPC";
@@ -240,7 +242,8 @@ declare global {
participantRollTraits?: RollTrait[]
}
- export type PrimaryDocType = BladesActorType.pc
+ export type PrimaryDocType =
+ BladesActorType.pc
|BladesActorType.crew
|BladesItemType.cohort_gang
|BladesItemType.cohort_expert
@@ -259,7 +262,7 @@ declare global {
rollPrimaryID?: string,
rollPrimaryDoc?: PrimaryDoc,
rollPrimaryName: string,
- rollPrimaryType: string,
+ rollPrimaryType: PrimaryDocType,
rollPrimaryImg: string,
rollModsData: RollModData[],
@@ -267,6 +270,8 @@ declare global {
applyHarm?(amount: num, name: num)
applyWorsePosition?()
+ spendArmor?()
+ spendSpecialArmor?()
}
@@ -279,6 +284,18 @@ declare global {
// [BladesItemType.location]: BladesItemSchema.Location,
// [BladesItemType.score]: BladesItemSchema.Score,
+ export type OppositionDocType =
+ BladesActorType.npc
+ |BladesActorType.faction
+ |BladesItemType.cohort_gang
+ |BladesItemType.cohort_expert
+ |BladesItemType.gm_tracker
+ |BladesItemType.score
+ |BladesItemType.location
+ |BladesItemType.project
+ |BladesItemType.design
+ |BladesItemType.ritual
+ |"clock";
export type OppositionDoc =
BladesActorOfType
@@ -286,6 +303,8 @@ declare global {
|BladesItemOfType
|BladesItemOfType
|BladesItemOfType
+ |BladesItemOfType
+ |BladesItemOfType
|BladesItemOfType
|BladesItemOfType
|BladesItemOfType;
@@ -294,14 +313,24 @@ declare global {
rollOppID?: string,
rollOppDoc?: OppositionDoc,
rollOppName: string,
- rollOppType: string,
+ rollOppType: OppositionDocType,
rollOppImg: string,
rollOppSubName?: string,
rollOppModsData?: RollModData[],
- rollFactors: Partial>
+ rollFactors: Partial>,
+
+ rollOppClock?: Partial
}
+ export type ParticipantDocType =
+ BladesActorType.pc
+ |BladesActorType.crew
+ |BladesActorType.npc
+ |BladesItemType.cohort_gang
+ |BladesItemType.cohort_expert
+ |BladesItemType.gm_tracker
+
export type ParticipantDoc =
BladesActorOfType
|BladesActorOfType
@@ -314,7 +343,7 @@ declare global {
rollParticipantID?: string,
rollParticipantDoc?: ParticipantDoc,
rollParticipantName: string,
- rollParticipantType: string,
+ rollParticipantType: ParticipantDocType,
rollParticipantIcon: string,
rollParticipantModsData?: RollModData[], // As applied to MAIN roll when this participant involved
diff --git a/ts/BladesActor.ts b/ts/BladesActor.ts
index 7d48f07b..5d9c81d2 100644
--- a/ts/BladesActor.ts
+++ b/ts/BladesActor.ts
@@ -894,7 +894,11 @@ class BladesActor extends Actor implements BladesDocument {
get rollPrimaryName() { return this.name; }
- get rollPrimaryType() { return this.type; }
+ get rollPrimaryType() {
+ if (![BladesActorType.pc, BladesActorType.crew].includes(this.type)) {
+ throw new Error(`BladesActor of type '${this.type}' ("${this.name}") cannot be RollPrimary.`);
+ }
+ return this.type as BladesRoll.PrimaryDocType; }
get rollPrimaryImg() { return this.img; }
diff --git a/ts/BladesChat.ts b/ts/BladesChat.ts
index a7612248..fab1567f 100644
--- a/ts/BladesChat.ts
+++ b/ts/BladesChat.ts
@@ -12,6 +12,7 @@ class BladesChat extends ChatMessage {
ApplyTooltipAnimations(html);
ApplyConsequenceAnimations(html);
BladesConsequence.ApplyChatListeners(html);
+ setTimeout(() => { html.addClass("display-ok"); }, 2000);
});
return loadTemplates([
"systems/eunos-blades/templates/chat/roll-result-action-roll.hbs",
diff --git a/ts/BladesDialog.ts b/ts/BladesDialog.ts
index cc043613..c1e7072b 100644
--- a/ts/BladesDialog.ts
+++ b/ts/BladesDialog.ts
@@ -3,6 +3,7 @@ import U from "./core/utilities";
import {BladesActor, BladesPC} from "./documents/BladesActorProxy";
import BladesItem from "./BladesItem";
import BladesRoll from "./BladesRoll";
+import BladesConsequence from "./sheets/roll/BladesConsequence";
import C, {RollResult, ConsequenceType, AttributeTrait, Position} from "./core/constants";
import BladesAI, {AGENTS} from "./core/ai";
@@ -31,6 +32,7 @@ export enum SelectionCategory {
}
export enum BladesDialogType {
+ Input = "Input",
Selection = "Selection",
Consequence = "Consequence"
}
@@ -50,10 +52,47 @@ class BladesDialog extends Dialog {
return loadTemplates([
"systems/eunos-blades/templates/dialog-selection.hbs",
"systems/eunos-blades/templates/dialog-consequence.hbs",
+ "systems/eunos-blades/templates/dialog-input.hbs",
"systems/eunos-blades/templates/parts/dialog-consequence-block.hbs"
]);
}
+ static async DisplaySimpleInputDialog(
+ parent: BladesActor|BladesItem|BladesRoll,
+ prompt: string,
+ target?: string,
+ flagTarget?: string
+ ) {
+ const app: BladesDialog = new BladesDialog({
+ parent,
+ title: parent instanceof BladesRoll ? "Roll Input" : `${parent.name}: Input`,
+ dialogType: BladesDialogType.Input,
+ content: "",
+ prompt,
+ target,
+ flagTarget,
+ buttons: {
+ apply: {
+ icon: '',
+ label: "Apply",
+ callback: (html: HTMLElement|JQuery) => (app as BladesDialog)
+ .writeToRollInstance(html as JQuery)
+ },
+ cancel: {
+ icon: '',
+ label: game.i18n.localize("Cancel"),
+ callback: (html: JQuery|HTMLElement) => {
+ eLog.checkLog3("dialog", "Callback Scope", {this: app, html});
+ return false;
+ }
+ }
+ },
+ default: "apply"
+ }, {classes: ["eunos-blades", "sheet", "dialog", "simple-input-dialog"]});
+
+ return app._render(true, {width: app.width}).then(() => eLog.checkLog3("dialog", "Input Dialog Instance", {this: app}));
+ }
+
static async DisplaySelectionDialog(
parent: BladesActor,
title: string,
@@ -62,7 +101,7 @@ class BladesDialog extends Dialog {
tags?: string[]
) {
- const app = new BladesDialog({
+ const app: BladesDialog = new BladesDialog({
parent,
title,
docType,
@@ -114,18 +153,13 @@ class BladesDialog extends Dialog {
return app._render(true, {width: app.width}).then(() => eLog.checkLog3("dialog", "Dialog Instance", {this: app}));
}
- override get template() {
- if (this.dialogType === BladesDialogType.Selection) {
- return "systems/eunos-blades/templates/dialog-selection.hbs";
- }
- return "systems/eunos-blades/templates/dialog-consequence.hbs";
- }
+ override get template() { return `systems/eunos-blades/templates/dialog-${U.lCase(this.dialogType)}.hbs`; }
get hasItems() {
return Object.values(this.tabs ?? []).some((tabItems) => tabItems.length > 0);
}
- parent: BladesActor|BladesRoll;
+ parent: BladesActor|BladesItem|BladesRoll;
tabs?: Record;
@@ -148,14 +182,25 @@ class BladesDialog extends Dialog {
>
>;
+ prompt?: string;
+
+ target?: string;
+
+ flagTarget?: string;
+
constructor(data: BladesDialog.Data, options?: Partial) {
super(data, options);
this.dialogType = data.dialogType ?? BladesDialogType.Selection;
this.parent = data.parent;
- this.width = 500;
+ this.width = options?.width ?? 500;
+
+ this.prompt = data.prompt;
+ this.target = data.target;
+ this.flagTarget = data.flagTarget;
switch (this.dialogType) {
+ case BladesDialogType.Input: return;
case BladesDialogType.Selection: this.constructSelectionData(data/* , options */); return;
case BladesDialogType.Consequence: this.csqData = this.constructConsequenceData(data/* , options */); return;
default: throw new Error(`Unrecognized type for BladesDialog constructor: '${this.dialogType}'`);
@@ -230,12 +275,20 @@ class BladesDialog extends Dialog {
const data = super.getData() as BladesDialog.Data;
switch (this.dialogType) {
+ case BladesDialogType.Input: return this.prepareInputData(data);
case BladesDialogType.Selection: return this.prepareSelectionData(data);
case BladesDialogType.Consequence: return this.prepareConsequenceData(data);
default: return null as never;
}
}
+ prepareInputData(data: BladesDialog.Data) {
+ data.prompt = this.prompt;
+ data.target = this.target;
+ data.flagTarget = this.flagTarget;
+ return data;
+ }
+
prepareSelectionData(data: BladesDialog.Data) {
data.title = this.title;
data.tabs = this.tabs;
@@ -295,13 +348,18 @@ class BladesDialog extends Dialog {
return {} as never;
}
- updateConsequenceData(
- html: JQuery,
- cData: BladesRoll.ConsequenceData
- ) {
- const csqElem$ = html.find(`.roll-consequence-row[data-csq-id='${cData.id}']`); // flag-target="rollCollab.consequenceData.${rollPos}.${rollResult}.${i}.attribute"]`)
+ updateInputText(inputElem$: JQuery) {
+ const value = inputElem$.val();
+ if (this.parent instanceof BladesRoll) {
+ const flagTarget = inputElem$.data("flagTarget");
+ eLog.checkLog3("dialog", "updateInputText", {value, flagTarget});
+ this.parent.setFlagVal(flagTarget, value, true);
+ } else if (this.parent instanceof BladesItem || this.parent instanceof BladesActor) {
+ this.parent.update({[inputElem$.data("target")]: inputElem$.val()});
+ }
+ }
- // Update Type
+ updateConsequenceType(csqElem$: JQuery, cData: BladesRoll.ConsequenceData) {
const type$ = csqElem$.find(".roll-consequence-type-select") as JQuery;
const typeVal = type$.val() as string|undefined;
if (typeVal && typeVal in ConsequenceType) {
@@ -309,61 +367,150 @@ class BladesDialog extends Dialog {
cData.icon = C.ConsequenceIcons[cData.type];
cData.typeDisplay = C.ConsequenceDisplay[cData.type];
}
+ }
- // Update Resistance Attribute
- if (/Resolve/.exec(cData.type)) { cData.attribute = AttributeTrait.resolve; }
- else if (/Insight/.exec(cData.type)) { cData.attribute = AttributeTrait.insight; }
+ updateConsequenceAttribute(csqElem$: JQuery, cData: BladesRoll.ConsequenceData) {
+ if (/Insight/.exec(cData.type)) { cData.attribute = AttributeTrait.insight; }
else if (/Prowess/.exec(cData.type)) { cData.attribute = AttributeTrait.prowess; }
+ else if (/Resolve/.exec(cData.type)) { cData.attribute = AttributeTrait.resolve; }
else {
const attribute$ = csqElem$.find(".roll-consequence-attribute-select") as JQuery;
- const attrVal = attribute$.val() as string|undefined;
- if (attrVal && attrVal in AttributeTrait) {
- cData.attribute = attrVal as AttributeTrait;
- if (this.parent.rollPrimaryDoc instanceof BladesPC) {
- cData.attributeVal = this.parent.rollPrimaryDoc.attributes[cData.attribute];
- } else if (this.parent.rollPrimaryDoc?.parent instanceof BladesPC) {
- cData.attributeVal = this.parent.rollPrimaryDoc.parent.attributes[cData.attribute];
- } else {
- eLog.error(`Unable to get attribute from rollPrimaryDoc '${this.parent.rollPrimaryDoc?.name}' of type '${this.parent.rollPrimaryDoc?.rollPrimaryType}' (may need to log via flags if either of the previous show 'undefined'.`);
- }
+ const attrVal = attribute$.val() as AttributeTrait|undefined;
+ if (attrVal) {
+ cData.attribute = attrVal;
}
}
+ }
- // Update Name
- const name$ = csqElem$.find(".consequence-name") as JQuery;
- const nameVal = name$.val();
- cData.name = nameVal ?? "";
+ updateConsequenceAttributeVal(cData: BladesRoll.ConsequenceData) {
+ if (this.parent.rollPrimaryDoc instanceof BladesPC) {
+ cData.attributeVal = this.parent.rollPrimaryDoc.attributes[cData.attribute as AttributeTrait];
+ } else if (this.parent.rollPrimaryDoc?.parent instanceof BladesPC) {
+ cData.attributeVal = this.parent.rollPrimaryDoc.parent.attributes[cData.attribute as AttributeTrait];
+ } else {
+ eLog.error(`Unable to get attribute from rollPrimaryDoc '${this.parent.rollPrimaryDoc?.name}' of type '${this.parent.rollPrimaryDoc?.rollPrimaryType}' (may need to log via flags if either of the previous show 'undefined'.`);
+ }
+ }
+
+ getSelectedResistOption(cData: BladesRoll.ConsequenceData): BladesRoll.ConsequenceResistOption|false {
+ return Object.values(cData?.resistOptions ?? {}).find((rCsq) => rCsq.isSelected) ?? false;
+ }
+
+ updateConsequenceResist(csqElem$: JQuery, cData: BladesRoll.ConsequenceData) {
- // Update Resistance Options
const resistOptions: Record = cData.resistOptions ?? {};
- // Clear 'resistTo' (will be redetermined below)
- delete cData.resistTo;
- csqElem$.find(".consequence-resist-option").each((_, elem) => {
- const resCsqID = $(elem).data("csq-id");
- resistOptions[resCsqID] ??= {id: resCsqID, name: "", type: undefined, isSelected: false};
-
- // Update Resistance Option Type
- const resType$ = $(elem).find(".roll-consequence-type-select") as JQuery;
- const resTypeVal = resType$.val() as string|undefined;
- if (resTypeVal && resTypeVal in ConsequenceType) {
- resistOptions[resCsqID].type = resTypeVal as ConsequenceType;
- resistOptions[resCsqID].icon = C.ConsequenceIcons[resistOptions[resCsqID].type as ConsequenceType];
- resistOptions[resCsqID].typeDisplay = C.ConsequenceDisplay[resistOptions[resCsqID].type as ConsequenceType];
- }
+ // If consequence is already minimal, toggle resistNegates to true and set 'resistTo' to None-type
+ const minimalCsqTypes = Object.entries(C.ResistedConsequenceTypes)
+ .filter(([_, rCsqType]) => rCsqType === ConsequenceType.None)
+ .map(([csqType]) => csqType as ConsequenceType);
+ if (minimalCsqTypes.includes(cData.type as ConsequenceType)) {
+ cData.resistNegates = true;
+ const noneCsq = BladesConsequence.None;
+ cData.resistOptions = {[noneCsq.id]: noneCsq};
+ cData.resistTo = noneCsq;
+ return;
+ } else {
+ // Clear 'resistTo' (will be redetermined below)
+ delete cData.resistTo;
+ delete cData.resistNegates;
+ csqElem$.find(".consequence-resist-option").each((_, elem) => {
+ const resCsqID = $(elem).data("csq-id");
+ resistOptions[resCsqID] ??= {id: resCsqID, name: "", type: undefined, isSelected: false};
+
+ // Update Resistance Option Type
+ const resType$ = $(elem).find(".roll-consequence-type-select") as JQuery;
+ const resTypeVal = resType$.val() as string|undefined;
+ if (resTypeVal && resTypeVal in ConsequenceType) {
+ resistOptions[resCsqID].type = resTypeVal as ConsequenceType;
+ resistOptions[resCsqID].icon = C.ConsequenceIcons[resistOptions[resCsqID].type as ConsequenceType];
+ resistOptions[resCsqID].typeDisplay = C.ConsequenceDisplay[resistOptions[resCsqID].type as ConsequenceType];
+ }
- // Update Resistance Option Name
- const resName$ = $(elem).find(".consequence-name") as JQuery;
- const resNameVal = resName$.val();
- resistOptions[resCsqID].name = resNameVal ?? "";
+ // Update Resistance Option Name
+ const resName$ = $(elem).find(".consequence-name") as JQuery;
+ const resNameVal = resName$.val();
+ resistOptions[resCsqID].name = resNameVal ?? "";
- // If this is selected, update 'resistTo' data as well
- if (resistOptions[resCsqID].isSelected) {
- cData.resistTo = resistOptions[resCsqID];
- }
- });
+ // If this is selected, update 'resistTo' data as well
+ if (resistOptions[resCsqID].isSelected) {
+ cData.resistTo = resistOptions[resCsqID];
+ }
+ });
+ }
cData.resistOptions = resistOptions;
+ }
+
+ updateConsequenceArmorResist(csqElem$: JQuery, cData: BladesRoll.ConsequenceData) {
+ // If consequence is already minimal, toggle armorNegates to true and set 'armorTo' to None-type
+ const minimalCsqTypes = Object.entries(C.ResistedConsequenceTypes)
+ .filter(([_, rCsqType]) => rCsqType === ConsequenceType.None)
+ .map(([csqType]) => csqType as ConsequenceType);
+ if (minimalCsqTypes.includes(cData.type as ConsequenceType)) {
+ cData.armorNegates = true;
+ cData.armorTo = BladesConsequence.None;
+ } else {
+ delete cData.armorNegates;
+ cData.armorTo = this.getSelectedResistOption(cData);
+ }
+ }
+
+ updateConsequenceSpecialArmorResist(csqElem$: JQuery, cData: BladesRoll.ConsequenceData) {
+ // If consequence is already minimal, toggle specialArmorNegates to true and set 'specialArmorTo' to None-type
+ const minimalCsqTypes = Object.entries(C.ResistedConsequenceTypes)
+ .filter(([_, rCsqType]) => rCsqType === ConsequenceType.None)
+ .map(([csqType]) => csqType as ConsequenceType);
+ if (minimalCsqTypes.includes(cData.type as ConsequenceType)) {
+ cData.specialArmorNegates = true;
+ cData.specialArmorTo = BladesConsequence.None;
+ } else {
+ delete cData.specialArmorNegates;
+ cData.specialArmorNegates ??= false;
+ cData.specialArmorTo = this.getSelectedResistOption(cData);
+ }
+ }
+
+ updateConsequenceData(
+ html: JQuery,
+ cData: BladesRoll.ConsequenceData
+ ) {
+ const csqElem$ = html.find(`.roll-consequence-row[data-csq-id='${cData.id}']`);
+
+ // Update Type
+ this.updateConsequenceType(csqElem$, cData);
+
+ // Update Name
+ if (cData.type === ConsequenceType.None) {
+ cData.name = "";
+ } else {
+ const name$ = csqElem$.find(".consequence-name") as JQuery;
+ const nameVal = name$.val();
+ cData.name = nameVal ?? "";
+ }
+
+ // Update Resistance Attribute
+ this.updateConsequenceAttribute(csqElem$, cData);
+ this.updateConsequenceAttributeVal(cData);
+
+ // Update Resistance Options
+ this.updateConsequenceResist(csqElem$, cData);
+
+ // Update Armor Options
+ if (( this.parent).canResistWithArmor(cData)) {
+ cData.isDisplayingArmorToggle = true;
+ this.updateConsequenceArmorResist(csqElem$, cData);
+ } else {
+ cData.isDisplayingArmorToggle = false;
+ }
+
+ // Update Special Armor Options
+ if (( this.parent).canResistWithSpecialArmor(cData)) {
+ cData.isDisplayingSpecialArmorToggle = true;
+ this.updateConsequenceSpecialArmorResist(csqElem$, cData);
+ } else {
+ cData.isDisplayingSpecialArmorToggle = false;
+ }
return cData;
}
@@ -403,6 +550,15 @@ class BladesDialog extends Dialog {
_consequenceAI?: BladesAI;
+ getCsqDataFromElem(elem: HTMLElement, paramCount = 3): string[] {
+ const dataAction = elem.dataset.action;
+ if (dataAction) {
+ const params = dataAction.split(/-/).reverse().slice(0, paramCount);
+ return params.reverse();
+ }
+ return [];
+ }
+
async queryAI(event: ClickEvent) {
if (!this.csqData) { return; }
// If the AI generator has not been initialized, do so.
@@ -410,21 +566,33 @@ class BladesDialog extends Dialog {
this._consequenceAI = new BladesAI(AGENTS.ConsequenceAdjuster);
}
- // Get the name of the consequence.
- const dataAction = event.currentTarget.dataset.action;
- if (dataAction && dataAction.startsWith("ai-query")) {
- const [rollPosition, rollResult, csqID] = dataAction.split(/-/).slice(2);
- const csqName: string|undefined =
- this.csqData[rollPosition as Position][rollResult as RollResult.partial|RollResult.fail][csqID]?.name;
- if (csqName) {
- const response = await this._consequenceAI?.query(csqName, csqName);
- if (response) {
- this.refreshResistanceOptions(rollPosition as Position, rollResult as RollResult.partial|RollResult.fail, csqID, response.split("|"));
- }
+ const [rollPosition, rollResult, csqID] = this.getCsqDataFromElem(event.currentTarget);
+ const csqName: string|undefined =
+ this.csqData[rollPosition as Position][rollResult as RollResult.partial|RollResult.fail][csqID]?.name;
+ if (csqName) {
+ const response = await this._consequenceAI?.query(csqName, csqName);
+ if (response) {
+ this.refreshResistanceOptions(rollPosition as Position, rollResult as RollResult.partial|RollResult.fail, csqID, response.split("|"));
}
}
}
+ async spawnBlankResistOption(event: ClickEvent) {
+ if (!this.csqData) { return; }
+ const [rollPosition, rollResult, csqID] = this.getCsqDataFromElem(event.currentTarget);
+ const rCsqID = randomID();
+ this.csqData[rollPosition as Position][rollResult as RollResult.partial|RollResult.fail][csqID]
+ .resistOptions = {
+ [rCsqID]: {
+ id: rCsqID,
+ name: "",
+ type: undefined,
+ isSelected: true
+ }
+ };
+ this.render();
+ }
+
async setFlagVal(target: string, value: unknown) {
if (this.parent instanceof BladesRoll) {
return this.parent.setFlagVal(target, value, false);
@@ -458,39 +626,63 @@ class BladesDialog extends Dialog {
async selectResistOption(event: ClickEvent) {
if (!this.csqData) { return; }
- 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 as Position][rollResult as RollResult.partial|RollResult.fail][csqIndex];
- cData.resistOptions ??= {};
-
- // Toggle clicked resistance option
- cData.resistOptions[resIndex].isSelected = !cData.resistOptions[resIndex].isSelected;
-
- // 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 as Record<
- string,
- BladesRoll.ConsequenceResistOption
- >)[key].isSelected = false; });
-
- // ... and set 'resistTo' to this consequence.
- cData.resistTo = cData.resistOptions[resIndex];
- } else {
- // Otherwise, set 'resistTo' to false.
- cData.resistTo = false;
- }
-
- // Assign new cData instance.
- this.csqData[rollPosition as Position][rollResult as RollResult.partial|RollResult.fail][csqIndex] = cData;
- this.render();
+ const [rollPosition, rollResult, csqID, resID] = this.getCsqDataFromElem(event.currentTarget, 4);
+ eLog.checkLog3("dialog", "... Action Passed", {rollResult, csqIndex: csqID, resIndex: resID});
+ // Get consequence data
+ const cData = this.csqData[rollPosition as Position][rollResult as RollResult.partial|RollResult.fail][csqID];
+ cData.resistOptions ??= {};
+
+ // Toggle clicked resistance option
+ cData.resistOptions[resID].isSelected = !cData.resistOptions[resID].isSelected;
+
+ // If resistance option is now selected...
+ if (cData.resistOptions[resID].isSelected) {
+ // ... deselect & hide other options
+ Object.keys(cData.resistOptions)
+ .filter((key) => key !== resID)
+ .forEach((key) => {
+ Object.assign(cData.resistOptions?.[key] ?? {}, {isSelected: false, isVisible: false});
+ });
+
+ // ... and set 'resistTo' to this consequence.
+ cData.resistTo = cData.resistOptions[resID];
+ } else {
+ // Otherwise, set 'resistTo' to false...
+ cData.resistTo = false;
+ // ... and unhide other options.
+ Object.keys(cData.resistOptions)
+ .filter((key) => key !== resID)
+ .forEach((key) => {
+ Object.assign(cData.resistOptions?.[key] ?? {}, {isVisible: true});
+ });
}
+
+ // Assign new cData instance.
+ this.csqData[rollPosition as Position][rollResult as RollResult.partial|RollResult.fail][csqID] = cData;
+ this.render();
+ }
+
+ async clearResistOptions(event: ContextMenuEvent) {
+ if (!this.csqData) { return; }
+ const [rollPosition, rollResult, csqID] = this.getCsqDataFromElem(event.currentTarget);
+ this.csqData[rollPosition as Position][rollResult as RollResult.partial|RollResult.fail][csqID].resistOptions = {};
+ this.render();
+ }
+
+ async toggleArmor(event: ClickEvent) {
+ if (!this.csqData) { return; }
+ const [rollPosition, rollResult, csqID] = this.getCsqDataFromElem(event.currentTarget);
+ const cData = this.csqData[rollPosition as Position][rollResult as RollResult.partial|RollResult.fail][csqID];
+ cData.canArmor = !cData.canArmor;
+ this.render();
+ }
+
+ async toggleSpecialArmor(event: ClickEvent) {
+ if (!this.csqData) { return; }
+ const [rollPosition, rollResult, csqID] = this.getCsqDataFromElem(event.currentTarget);
+ const cData = this.csqData[rollPosition as Position][rollResult as RollResult.partial|RollResult.fail][csqID];
+ cData.canSpecialArmor = !cData.canSpecialArmor;
+ this.render();
}
override activateListeners(html: JQuery) {
@@ -500,11 +692,20 @@ class BladesDialog extends Dialog {
ApplyTooltipAnimations(html);
switch (this.dialogType) {
+ case BladesDialogType.Input: this.activateInputListeners(html); break;
case BladesDialogType.Selection: this.activateSelectionListeners(html); break;
- case BladesDialogType.Consequence: this.activateConsequenceListeners(html); break;
+ case BladesDialogType.Consequence: {
+ this.activateConsequenceListeners(html);
+ // Select --> updateConsequenceDialog -> updateConsequenceData(each csq)
+ break;
+ }
}
}
+ activateInputListeners(html: JQuery) {
+ html.find("textarea").on({change: (event) => this.updateInputText($(event.currentTarget))});
+ }
+
activateSelectionListeners(html: JQuery) {
const self = this;
@@ -539,8 +740,17 @@ class BladesDialog extends Dialog {
activateConsequenceListeners(html: JQuery) {
html.find("input").on({change: () => this.updateConsequenceDialog(html)});
html.find("select").on({change: () => this.updateConsequenceDialog(html)});
- html.find('[data-action^="ai-query"]').on({click: (event) => this.queryAI(event) });
+ html.find('[data-action^="ai-query"]').on({
+ click: (event) => this.queryAI(event),
+ contextmenu: (event) => this.clearResistOptions(event)
+ });
+ html.find('[data-action^="blank-option"]').on({
+ click: (event) => this.spawnBlankResistOption(event),
+ contextmenu: (event) => this.clearResistOptions(event)
+ });
html.find('[data-action^="gm-select-toggle"]').on({click: (event) => this.selectResistOption(event) });
+ html.find('[data-action^="toggle-armor"]').on({click: (event) => this.toggleArmor(event) });
+ html.find('[data-action^="toggle-special"]').on({click: (event) => this.toggleSpecialArmor(event) });
}
}
diff --git a/ts/BladesItem.ts b/ts/BladesItem.ts
index c496fd65..0a1c226e 100644
--- a/ts/BladesItem.ts
+++ b/ts/BladesItem.ts
@@ -209,7 +209,16 @@ class BladesItem extends Item implements BladesDocument- ,
get rollPrimaryName() { return this.name; }
- get rollPrimaryType() { return this.type; }
+ get rollPrimaryType() {
+ if (![
+ BladesItemType.cohort_gang,
+ BladesItemType.cohort_expert,
+ BladesItemType.gm_tracker,
+ BladesItemType.score
+ ].includes(this.type)) {
+ throw new Error(`BladesItem of type '${this.type}' ("${this.name}") cannot be RollPrimary.`);
+ }
+ return this.type as BladesRoll.PrimaryDocType; }
get rollPrimaryImg() { return this.img; }
@@ -260,7 +269,20 @@ class BladesItem extends Item implements BladesDocument
- ,
get rollOppSubName() { return ""; }
- get rollOppType() { return this.type; }
+ get rollOppType() {
+ if (![
+ BladesItemType.cohort_gang,
+ BladesItemType.cohort_expert,
+ BladesItemType.gm_tracker,
+ BladesItemType.score,
+ BladesItemType.location,
+ BladesItemType.project,
+ BladesItemType.design,
+ BladesItemType.ritual
+ ].includes(this.type)) {
+ throw new Error(`BladesItem of type '${this.type}' ("${this.name}") cannot be RollOpposition.`);
+ }
+ return this.type as BladesRoll.OppositionDocType; }
get rollOppModsData(): BladesRoll.RollModData[] { return []; }
// #endregion
@@ -274,7 +296,15 @@ class BladesItem extends Item implements BladesDocument
- ,
get rollParticipantName() { return this.name; }
- get rollParticipantType() { return this.type; }
+ get rollParticipantType() {
+ if (![
+ BladesItemType.cohort_gang,
+ BladesItemType.cohort_expert,
+ BladesItemType.gm_tracker
+ ].includes(this.type)) {
+ throw new Error(`BladesItem of type '${this.type}' ("${this.name}") cannot be RollParticipant.`);
+ }
+ return this.type as BladesRoll.ParticipantDocType; }
get rollParticipantModsData(): BladesRoll.RollModData[] { return []; }
// #endregion
@@ -368,13 +398,13 @@ class BladesItem extends Item implements BladesDocument
- ,
const expClueData: Record = {};
[...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});
+ // eLog.checkLog3("experienceClues", {expClueData})
if (BladesItem.IsType(this, BladesItemType.playbook)) {
const gatherInfoData: Record = {};
[...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});
+ // eLog.checkLog3("gatherInfoQuestions", {gatherInfoData});
}
}
diff --git a/ts/BladesRoll.ts b/ts/BladesRoll.ts
index fdfd1451..3cc30ca4 100644
--- a/ts/BladesRoll.ts
+++ b/ts/BladesRoll.ts
@@ -1,6 +1,6 @@
// #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 C, {BladesActorType, BladesItemType, BladesPhase, 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 {ApplyTooltipAnimations, ApplyConsequenceAnimations} from "./core/gsap";
@@ -515,6 +515,12 @@ class BladesRollMod {
return BladesActor.IsType(rollPrimaryDoc, BladesActorType.pc)
&& rollPrimaryDoc.system.stress.max - rollPrimaryDoc.system.stress.value >= val;
}
+ case "Heat": {
+ return (
+ BladesPC.IsType(rollPrimaryDoc) && BladesCrew.IsType(rollPrimaryDoc.crew))
+ || BladesCrew.IsType(rollPrimaryDoc
+ );
+ }
default: throw new Error(`Unrecognize Payable Key: ${traitStr}`);
}
});
@@ -777,8 +783,6 @@ class BladesRollPrimary implements BladesRoll.PrimaryDocData {
}
// #endregion
- rollInstance: BladesRoll;
-
rollPrimaryID: string | undefined;
_rollPrimaryDoc: BladesRoll.PrimaryDoc | undefined;
@@ -814,7 +818,7 @@ class BladesRollPrimary implements BladesRoll.PrimaryDocData {
rollPrimaryName: string;
- rollPrimaryType: string;
+ rollPrimaryType: BladesRoll.PrimaryDocType;
rollPrimaryImg: string;
@@ -845,9 +849,67 @@ class BladesRollPrimary implements BladesRoll.PrimaryDocData {
}
}
+ get hasArmor() {
+ if (!this.rollPrimaryDoc) { return false; }
+ if (this.rollPrimaryType === BladesActorType.pc) {
+ const rollPrimaryDoc = this.rollPrimaryDoc as BladesPC;
+
+ // Can PC spend normal armor?
+ if (
+ !rollPrimaryDoc.system.armor.checked.light
+ && (
+ rollPrimaryDoc.system.armor.active.light
+ || rollPrimaryDoc.remainingLoad >= 2
+ )
+ ) { return true; }
+
+ // Otherwise, can PC spend heavy armor?
+ if (
+ !rollPrimaryDoc.system.armor.checked.heavy
+ && (
+ rollPrimaryDoc.system.armor.active.heavy
+ || rollPrimaryDoc.remainingLoad >= 3
+ )
+ ) { return true; }
+ }
+ if (BladesItem.IsType(this.rollPrimaryDoc, BladesItemType.cohort_gang, BladesItemType.cohort_expert)) {
+ const {value, max} = this.rollPrimaryDoc.system.armor;
+ return max - value > 1;
+ }
+ return false;
+ }
+
+ get hasSpecialArmor() {
+ if (!this.rollPrimaryDoc) { return false; }
+ if (!BladesPC.IsType(this.rollPrimaryDoc)) { return false; }
+ if (!this.rollPrimaryDoc.system.armor.active.special) { return false; }
+ if (this.rollPrimaryDoc.system.armor.checked.special) { return false; }
+ return true;
+ }
+
+ async spendArmor() {
+ if (this.hasArmor) {
+ if (BladesPC.IsType(this.rollPrimaryDoc)) {
+ if (this.rollPrimaryDoc.system.armor.checked.light) {
+ await this.rollPrimaryDoc.update({"system.armor.checked.heavy": true});
+ } else {
+ await this.rollPrimaryDoc.update({"system.armor.checked.light": true});
+ }
+ } else if (BladesItem.IsType(this.rollPrimaryDoc, BladesItemType.cohort_gang, BladesItemType.cohort_expert)) {
+ await this.rollPrimaryDoc.update({"system.armor.value": this.rollPrimaryDoc.system.armor.value + 1});
+ }
+ }
+ }
+
+ async spendSpecialArmor() {
+ if (this.hasSpecialArmor) {
+ await (this.rollPrimaryDoc as BladesPC).update({"system.armor.checked.special": true});
+ }
+ }
+
// #region Constructor ~
constructor(
- rollInstance: BladesRoll,
+ rollInstance?: BladesRoll,
{
rollPrimaryID,
rollPrimaryName,
@@ -855,19 +917,19 @@ class BladesRollPrimary implements BladesRoll.PrimaryDocData {
rollPrimaryImg,
rollModsData,
rollFactors
- }: BladesRoll.PrimaryDocData) {
+ }: Partial = {}) {
// Identify ID, Doc, Name, SubName, Type & Image, to best of ability
- this.rollInstance = rollInstance;
+ // this.rollInstance = rollInstance;
this.rollPrimaryID = rollPrimaryID
- ?? this.rollInstance.rollPrimary.rollPrimaryID
- ?? this.rollInstance.rollPrimary.rollPrimaryDoc?.rollPrimaryID;
+ ?? rollInstance?.rollPrimary.rollPrimaryID
+ ?? rollInstance?.rollPrimary.rollPrimaryDoc?.rollPrimaryID;
- 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;
+ rollPrimaryName ??= rollInstance?.rollPrimary.rollPrimaryName;
+ rollPrimaryType ??= rollInstance?.rollPrimary.rollPrimaryType;
+ rollPrimaryImg ??= rollInstance?.rollPrimary.rollPrimaryImg;
+ rollModsData ??= rollInstance?.rollPrimary.rollModsData;
+ rollFactors ??= rollInstance?.rollPrimary.rollFactors;
if (BladesRollPrimary.IsDoc(this.rollPrimaryDoc)) {
this.rollPrimaryName = rollPrimaryName ?? this.rollPrimaryDoc.rollPrimaryName;
@@ -958,7 +1020,7 @@ class BladesRollOpposition implements BladesRoll.OppositionDocData {
rollOppSubName?: string;
- rollOppType: string;
+ rollOppType: BladesRoll.OppositionDocType;
rollOppImg: string;
@@ -966,6 +1028,23 @@ class BladesRollOpposition implements BladesRoll.OppositionDocData {
rollFactors: Partial>;
+ _clockData?: BladesClockData;
+
+ get clockData() { return this._clockData ?? {}; }
+
+ set clockData(val: Partial) {
+ val.id ??= randomID();
+ val.display ??= "";
+ val.value ??= 0;
+ val.max ??= 8;
+ val.color ??= "white";
+ val.isActive = true;
+ val.isNameVisible = false;
+ val.isVisible = true;
+
+ this._clockData = val as BladesClockData;
+ }
+
// #region Constructor ~
constructor(rollInstance: BladesRoll,
{
@@ -976,7 +1055,8 @@ class BladesRollOpposition implements BladesRoll.OppositionDocData {
rollOppType,
rollOppImg,
rollOppModsData,
- rollFactors
+ rollFactors,
+ rollOppClock
}: Partial = {}) {
this.rollInstance = rollInstance;
@@ -1004,7 +1084,6 @@ class BladesRollOpposition implements BladesRoll.OppositionDocData {
// Confirm required settings
if (!rollOppName) {throw new Error("Must include a rollOppName when constructing a BladesRollOpposition object.");}
- if (!rollOppSubName) {throw new Error("Must include a rollOppSubName when constructing a BladesRollOpposition object.");}
if (!rollOppType) {throw new Error("Must include a rollOppType when constructing a BladesRollOpposition object.");}
if (!rollFactors) {throw new Error("Must include a rollFactors when constructing a BladesRollOpposition object.");}
@@ -1016,6 +1095,9 @@ class BladesRollOpposition implements BladesRoll.OppositionDocData {
this.rollOppImg = rollOppImg ?? "";
this.rollOppModsData = rollOppModsData ?? [];
this.rollFactors = rollFactors;
+ if (rollOppClock) {
+ this.clockData = rollOppClock;
+ }
}
// #endregion
@@ -1032,7 +1114,8 @@ class BladesRollOpposition implements BladesRoll.OppositionDocData {
rollOppImg: this.rollOppImg,
rollOppModsData: this.rollOppModsData,
- rollFactors: this.rollFactors
+ rollFactors: this.rollFactors,
+ rollOppClock: this.clockData
};
}
@@ -1113,7 +1196,7 @@ class BladesRollParticipant implements BladesRoll.ParticipantDocData {
rollParticipantName: string;
- rollParticipantType: string;
+ rollParticipantType: BladesRoll.ParticipantDocType;
rollParticipantIcon: string;
@@ -1602,7 +1685,7 @@ class BladesRoll extends DocumentSheet {
static async PrepareActionRoll(rollID: string, config: BladesRoll.Config) {
// Validate the rollTrait
- if (!(U.isInt(config.rollTrait) || U.lCase(config.rollTrait) in {...ActionTrait, ...Factor})) {
+ if (!(config.rollTrait === "" || U.isInt(config.rollTrait) || U.lCase(config.rollTrait) in {...ActionTrait, ...Factor})) {
throw new Error(`[PrepareActionRoll()] Bad RollTrait for Action Roll: ${config.rollTrait}`);
}
@@ -1908,6 +1991,8 @@ class BladesRoll extends DocumentSheet {
_rollParticipants?: BladesRoll.RollParticipantDocs;
+ projectSelectOptions?: Array>;
+
constructor(userID: string, rollID: string, rollPermission: RollPermissions) {
const rollUser = game.users.get(userID);
if (!rollUser) {
@@ -1920,6 +2005,10 @@ class BladesRoll extends DocumentSheet {
this._rollPrimary = new BladesRollPrimary(this, rollFlagData.rollPrimaryData);
if (rollFlagData.rollOppData) {
this._rollOpposition = new BladesRollOpposition(this, rollFlagData.rollOppData);
+ } else if (rollFlagData.rollDowntimeAction === DowntimeAction.LongTermProject) {
+ this.projectSelectOptions = Array.from(game.items)
+ .filter((item) => BladesItem.IsType(item, BladesItemType.project))
+ .map((project) => ({value: project.id ?? "", display: project.name}));
}
if (rollFlagData.rollParticipantData) {
this._rollParticipants = {};
@@ -2212,20 +2301,20 @@ class BladesRoll extends DocumentSheet {
getFlagVal(flagKey?: string): T | undefined {
if (flagKey) {
- return this.document.getFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`) as T | undefined;
+ return this.document.getFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`.replace(/(rollCollab\.)+/g, "rollCollab.")) as T | undefined;
}
return this.document.getFlag(C.SYSTEM_ID, "rollCollab") as T | undefined;
}
async setFlagVal(flagKey: string, flagVal: unknown, isRerendering = true) {
- await this.document.setFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`, flagVal);
+ await this.document.setFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`.replace(/(rollCollab\.)+/g, "rollCollab."), flagVal);
if (isRerendering) {
socketlib.system.executeForEveryone("renderRollCollab", this.rollID);
}
}
async clearFlagVal(flagKey: string, isRerendering = true) {
- await this.document.unsetFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`);
+ await this.document.unsetFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`.replace(/(rollCollab\.)+/g, "rollCollab."));
if (isRerendering) {
socketlib.system.executeForEveryone("renderRollCollab", this.rollID);
}
@@ -2699,6 +2788,18 @@ class BladesRoll extends DocumentSheet {
set rollMods(val: BladesRollMod[]) {this._rollMods = val;}
+ canResistWithArmor(csqData: BladesRoll.ConsequenceData) {
+ if (!this.rollPrimary.hasArmor) { return false; }
+ return csqData.attribute === AttributeTrait.prowess;
+ }
+
+ canResistWithSpecialArmor(_csqData: BladesRoll.ConsequenceData) {
+ if (!BladesPC.IsType(this.rollPrimary.rollPrimaryDoc)) {
+ return false;
+ }
+ return this.rollPrimary.rollPrimaryDoc.armorStatus.special;
+ }
+
// #endregion
// #region CONSEQUENCES: Getting, Accepting, Resisting
@@ -2755,6 +2856,152 @@ class BladesRoll extends DocumentSheet {
return {...context, ...sheetData};
}
+ getFortuneRollModsData(): BladesRoll.RollModData[] {
+ const modsData: BladesRoll.RollModData[] = [];
+
+ if (this.rollSubType === RollSubType.Engagement) {
+ modsData.push({
+ id: "BoldPlan-positive-roll",
+ name: "Bold Plan",
+ section: RollModSection.roll,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "positive",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: ""
+ });
+ modsData.push({
+ id: "ComplexPlan-negative-roll",
+ name: "Complex Plan",
+ section: RollModSection.roll,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "negative",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: ""
+ });
+ modsData.push({
+ id: "ExploitWeakness-positive-roll",
+ name: "Exploiting a Weakness",
+ section: RollModSection.roll,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "positive",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: ""
+ });
+ modsData.push({
+ id: "WellDefended-negative-roll",
+ name: "Well-Defended",
+ section: RollModSection.roll,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "negative",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: ""
+ });
+ modsData.push({
+ id: "HelpFromFriend-positive-roll",
+ name: "Help From a Friend",
+ section: RollModSection.position,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "positive",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: "
Help From a Friend
Add +1d if you enlist the help of a friend or contact.
"
+ });
+ modsData.push({
+ id: "EnemyInterference-negative-roll",
+ name: "Enemy Interference",
+ section: RollModSection.roll,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "negative",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: ""
+ });
+ }
+
+ return modsData;
+ }
+
+ getDowntimeActionRollModsData(): BladesRoll.RollModData[] {
+ const modsData: BladesRoll.RollModData[] = [];
+ modsData.push({
+ id: "HelpFromFriend-positive-roll",
+ name: "Help From a Friend",
+ section: RollModSection.position,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "positive",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: "Help From a Friend
Add +1d if you enlist the help of a friend or contact.
"
+ });
+ if (this.rollDowntimeAction !== DowntimeAction.IndulgeVice) {
+ modsData.push({
+ id: "CanBuyResultLevel-positive-after",
+ name: "Buying Result Level",
+ section: RollModSection.after,
+ base_status: RollModStatus.ForcedOn,
+ posNeg: "positive",
+ modType: "general",
+ value: 0,
+ effectKeys: [],
+ tooltip: "Buying Result Level
After your roll, you can increase the result level by one for each Coin you spend.
"
+ });
+ }
+ switch (this.rollDowntimeAction) {
+ case DowntimeAction.AcquireAsset: {
+ modsData.push({
+ id: "RepeatPurchase-positive-roll",
+ name: "Repeat Purchase",
+ section: RollModSection.roll,
+ base_status: RollModStatus.ToggledOff,
+ posNeg: "positive",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: "Repeat Purchase Bonus
Add +1d if you have previously acquired this asset or service with a Acquire Asset Downtime activity.
"
+ });
+ modsData.push({
+ id: "RestrictedItem-negative-after",
+ name: "Restricted",
+ section: RollModSection.after,
+ base_status: RollModStatus.Hidden,
+ posNeg: "negative",
+ modType: "general",
+ value: 0,
+ effectKeys: ["Cost-Heat2"],
+ tooltip: "Restricted
Whether contraband goods or dangerous materials, this Acquire Asset Downtime activity will add +2 Heat to your crew.
"
+ });
+ break;
+ }
+ default: break;
+ }
+
+ /*
+ modsData.push({
+ id: "--",
+ name: "",
+ section: RollModSection,
+ base_status: RollModStatus,
+ posNeg: "",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: ""
+ })
+*/
+ return modsData;
+ }
+
/**
* Gets the roll modifications data.
* @returns {BladesRoll.RollModData[]} The roll modifications data.
@@ -2764,18 +3011,31 @@ class BladesRoll extends DocumentSheet {
...BladesRoll.DefaultRollMods,
...this.rollPrimary.rollModsData
];
- if (this.rollType === RollType.Action && this.rollPrimary.isWorsePosition) {
- defaultMods.push({
- id: "WorsePosition-negative-position",
- name: "Worse Position",
- section: RollModSection.position,
- base_status: RollModStatus.ForcedOn,
- posNeg: "negative",
- modType: "general",
- value: 1,
- effectKeys: [],
- tooltip: "Worse Position
A Consequence on a previous roll has worsened your Position.
"
- });
+ if (this.rollDowntimeAction) {
+ defaultMods.push(...this.getDowntimeActionRollModsData());
+ }
+ if (this.rollType === RollType.Action) {
+ if (this.rollPrimary.isWorsePosition) {
+ defaultMods.push({
+ id: "WorsePosition-negative-position",
+ name: "Worse Position",
+ section: RollModSection.position,
+ base_status: RollModStatus.ForcedOn,
+ posNeg: "negative",
+ modType: "general",
+ value: 1,
+ effectKeys: [],
+ tooltip: "Worse Position
A Consequence on a previous roll has worsened your Position.
"
+ });
+ }
+ switch (this.rollDowntimeAction) {
+ case DowntimeAction.AcquireAsset: {
+ defaultMods.push();
+
+ break;
+ }
+ default: break;
+ }
}
if (
this.rollType === RollType.Action
@@ -2920,6 +3180,8 @@ class BladesRoll extends DocumentSheet {
(v: string[]) => v.includes(game.user.id ?? "")
);
+ // const downtimeData = this.processDowntimeActions();
+
return {
...baseData,
...(this.rollPrimary.rollPrimaryDoc ? {rollPrimary: this.rollPrimary.rollPrimaryDoc} : {}),
@@ -2928,9 +3190,36 @@ class BladesRoll extends DocumentSheet {
...rollResultData,
...GMBoostsData,
...positionEffectTradeData,
- userPermission
+ // ...downtimeData,
+ userPermission,
+ gamePhase: game.eunoblades.Tracker?.phase || BladesPhase.Freeplay
};
}
+ // type BladesSelectOption = {
+ // value: valueType,
+ // display: displayType
+ // };
+ // private processDowntimeActions() {
+ // const downtimeData: Record;
+ // if (BladesActor.IsType(this.rollPrimary.rollPrimaryDoc, BladesActorType.pc)) {
+ // downtimeData.canDoDowntimeActions = true;
+ // downtimeData.downtimeActionsRemaining = this.rollPrimary.rollPrimaryDoc.remainingDowntimeActions;
+ // const availableDowntimeActions: DowntimeAction[] = [];
+ // if (this.rollType === RollType.Action) {
+ // availableDowntimeActions.push(...[
+ // DowntimeAction.AcquireAsset,
+ // DowntimeAction.LongTermProject,
+ // DowntimeAction.Recover,
+ // DowntimeAction.ReduceHeat
+ // ]);
+ // } else if (this.rollType === RollType.Fortune) {
+ // availableDowntimeActions.push(...[
+ // DowntimeAction.
+ // ])
+ // }
+ // downtimeData.downtimeActionOptions =
+ // downtimeActionOptions?: Array
+ // }
private calculatePositionData(finalPosition: Position) {
return {
@@ -3232,7 +3521,25 @@ class BladesRoll extends DocumentSheet {
}
private getDieClass(val: number, i: number) {
- eLog.checkLog3("rollCollab", `getDieClass(${val}, ${i})`, {inst: this});
+ switch (this.rollType) {
+ case RollType.Resistance: {
+ if (val === 6 && i <= 1 && this.rollResult === -1) {
+ return "blades-die-critical";
+ }
+ if (i === 0) {
+ return "blades-die-resistance";
+ }
+ return "blades-die-fail";
+ }
+ case RollType.IndulgeVice: {
+ if (i === 0) {
+ return "blades-die-indulge-vice";
+ }
+ return "blades-die-fail";
+ }
+ default: break;
+ }
+
if (val === 6 && i <= 1 && this.rollResult === RollResult.critical) {
val++;
}
@@ -3248,29 +3555,34 @@ class BladesRoll extends DocumentSheet {
][val];
}
+ private getDieImage(val: number, i: number, isGhost = false) {
+ let imgPath = "systems/eunos-blades/assets/dice/image/";
+ if (isGhost) {
+ imgPath += "ghost-";
+ } else if ([RollType.Resistance, RollType.IndulgeVice].includes(this.rollType)
+ || this.rollDowntimeAction) {
+ imgPath += "grad-";
+ }
+ imgPath += val;
+ if (!isGhost && val === 6 && i <= 1 && this.isCritical) {
+ imgPath += "-crit";
+ }
+ imgPath += ".webp";
+
+ return imgPath;
+ }
+
get dieValsHTML(): string {
eLog.checkLog3("rollCollab", "[get dieValsHTML()]", {roll: this, dieVals: this.dieVals});
const dieVals = [...this.dieVals];
const ghostNum = this.isRollingZero ? dieVals.shift() : null;
-
- if (this.rollType === RollType.Resistance) {
- const numHighlightedDice = this.rollResult === RollResult.critical ? 2 : 1;
- const highlightClass = this.rollResult === RollResult.critical ? "blades-die-critical" : "blades-die-resistance";
- return [
- ...dieVals.map((val, i) => ``),
- ghostNum ? `` : null
- ]
- .filter((val): val is string => typeof val === "string")
- .join("");
- } else {
- return [
- ...dieVals.map((val, i) => ``),
- ghostNum ? `` : null
- ]
- .filter((val): val is string => typeof val === "string")
- .join("");
- }
+ return [
+ ...dieVals.map((val, i) => ``),
+ ghostNum ? `` : null
+ ]
+ .filter((val): val is string => typeof val === "string")
+ .join("");
}
// #endregion
@@ -3334,6 +3646,8 @@ class BladesRoll extends DocumentSheet {
await this.setRollPhase(RollPhase.Complete);
}
// Apply Stress & Special Armor Costs
+ eLog.checkLog2("bladesRoll", "Costs", this.getRollCosts());
+
if (BladesPC.IsType(this.rollPrimaryDoc)) {
const rollCostData = this.getRollCosts();
const stressCost = this.getTotalStressCost(this.getStressCosts(rollCostData));
@@ -3343,6 +3657,7 @@ class BladesRoll extends DocumentSheet {
if (this.getSpecArmorCost(rollCostData)) {
this.rollPrimaryDoc.spendSpecialArmor();
}
+
}
// const rollCosts = get.roll.getTotalStressCost(get.roll.getStressCosts(get.roll.getRollCosts()))
await this.outputRollToChat();
@@ -3358,7 +3673,7 @@ class BladesRoll extends DocumentSheet {
if (csqID && chatID) {
const resistedCsq = await BladesConsequence.GetFromID(chatID, csqID);
if (resistedCsq) {
- await resistedCsq.applyResistedConsequence();
+ await resistedCsq.applyResistedConsequence("resist");
}
}
if (BladesPC.IsType(this.rollPrimaryDoc)) {
@@ -3778,7 +4093,13 @@ class BladesRoll extends DocumentSheet {
html
.find("[data-action=\"gm-edit-consequences\"]")
- .on({click: () => BladesDialog.DisplayRollConsequenceDialog(this)});
+ // .on({click: () => BladesDialog.DisplayRollConsequenceDialog(this)});
+ .on({click: () => BladesDialog.DisplaySimpleInputDialog(
+ this,
+ "What consequence would you add?",
+ undefined,
+ "rollCollab.inputDialogTest"
+ )});
html
.find("[data-action='gm-text-input']")
diff --git a/ts/core/ai.ts b/ts/core/ai.ts
index f9428d6f..0e1da350 100644
--- a/ts/core/ai.ts
+++ b/ts/core/ai.ts
@@ -512,7 +512,7 @@ export const AGENTS: Record<
{human: "Soul Destroyed", ai: "Fully Corrupted|Lost In Darkness|Spirit Broken"},
{human: "Humiliated", ai: "Embarrassed|Momentarily Off-Balance|Enraged"},
{human: "She Escapes!", ai: "She Spots a Means of Escape|She Puts More Distance Between You|She Stops to Gloat"},
- {human: "The fire spreads to the hostages.", ai: "The fire approaches the hostages|The hostages must be evacuated|The fire billows choking black smoke."}
+ {human: "The fire spreads to the hostages.", ai: "The fire approaches the hostages.|The hostages must be evacuated.|The fire billows choking black smoke."}
]
}
};
diff --git a/ts/core/constants.ts b/ts/core/constants.ts
index 4f1afebc..4e522cb9 100644
--- a/ts/core/constants.ts
+++ b/ts/core/constants.ts
@@ -384,6 +384,14 @@ const C = {
AI_FILE_IDS: {
BladesPDF: "file-n72HTTNwt051piPbswQ8isUa"
},
+ DowntimeActionDisplay: {
+ [DowntimeAction.AcquireAsset]: "Acquire an Asset",
+ [DowntimeAction.IndulgeVice]: "Indulge Your Vice",
+ [DowntimeAction.LongTermProject]: "Work on a Project",
+ [DowntimeAction.Recover]: "Heal",
+ [DowntimeAction.ReduceHeat]: "Reduce the Crew's Heat",
+ [DowntimeAction.Train]: "Train"
+ },
Consequences: {
[Position.controlled]: {
[RollResult.partial]: [
diff --git a/ts/core/gsapBROKEN.ts b/ts/core/gsapBROKEN.ts
new file mode 100644
index 00000000..ac54750e
--- /dev/null
+++ b/ts/core/gsapBROKEN.ts
@@ -0,0 +1,726 @@
+import U from "./utilities";
+import C from "./constants";
+// eslint-disable-next-line import/no-unresolved
+import {TextPlugin} from "gsap/all";
+
+const gsapPlugins: gsap.RegisterablePlugins[] = [
+ TextPlugin
+];
+
+type gsapConfig = gsap.TweenVars & {
+ duration: number,
+ targets: Record|Array>>
+}
+
+type gsapEffect = {
+ effect: (targets: BladesTweenTarget, config: gsapConfig) => gsap.core.Timeline|gsap.core.Tween,
+ defaults: gsap.TweenVars,
+ extendTimeline?: boolean
+}
+
+const gsapEffects: Record = {
+ /* Basic Element Effects */
+
+ fadeOut: {
+ effect: (targets, config) => {
+
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .to(targets, {
+ autoAlpha: 0,
+ duration: config.duration,
+ ease: config.ease
+ });
+
+ // if (config.reversed) {
+ // tl.seek(config.duration);
+ // }
+
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ duration: 0.5,
+ ease: "power4.out"
+ }
+ },
+
+ blurOut: {
+ effect: (targets, config) => {
+
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .to(targets, {
+ skewX: config.skewX,
+ duration: 0.5 * config.duration,
+ ease: "power4.out"
+ })
+ .to(targets, {
+ x: `+=${config.rangeX}`,
+ marginBottom(i, target) {
+ return U.get(target, "height") as number * -1;
+ },
+ marginRight(i, target) {
+ return U.get(target, "width") as number * -1;
+ },
+ scale: config.scale,
+ filter: `blur(${config.blurStrength}px)`,
+ duration: 0.75 * config.duration
+ },
+ 0.25 * config.duration
+ )
+ .to(targets, {
+ autoAlpha: 0,
+ duration: 0.5 * config.duration,
+ ease: "power3.in"
+ },
+ 0.5 * config.duration
+ );
+
+ // if (config.reversed) {
+ // tl.seek(config.duration);
+ // }
+
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ duration: 0.5,
+ skewX: -20,
+ rangeX: 300,
+ scale: 1.5,
+ blurStrength: 10
+ }
+ },
+
+ slideOut: {
+ effect: (targets, config) => {
+ const scaleKey = ["up", "down"].includes(config.dir) ? "scaleY" : "scaleX";
+
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .to(targets, {
+ [scaleKey]: 0,
+ duration: config.duration,
+ ease: config.ease
+ });
+
+ // if (config.reversed) {
+ // tl.seek(config.duration);
+ // }
+
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ duration: 0.5,
+ ease: "back.out(3)"
+ }
+ },
+
+ brighten: {
+ effect: (targets, config) => {
+
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .fromTo(targets, {
+ filter: `brightness(${config.startStrength})`
+ }, {
+ filter: `brightness(${config.strength})`,
+ duration: config.duration,
+ ease: config.ease
+ });
+
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ initialStrength: 1,
+ strength: 1.5,
+ duration: 0.5,
+ ease: "none"
+ }
+ },
+
+ enlarge: {
+ effect: (targets, config) => {
+
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .fromTo(targets, {
+ scale: config.startScale
+ }, {
+ scale: config.scale,
+ duration: config.duration,
+ ease: config.ease
+ });
+
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ startScale: 1,
+ scale: 1.5,
+ duration: 0.5,
+ ease: "sine.out"
+ }
+ },
+
+ changeColor: {
+ effect: (targets, config) => {
+
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .to(targets, {
+ color: config.color,
+ duration: config.duration,
+ ease: config.ease
+ });
+
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ color: C.Colors.bWHITE,
+ duration: 0.5,
+ ease: "sine"
+ }
+ },
+
+ skewX: {
+ effect: (targets, config) => {
+
+ const tl = U.gsap.timeline({
+ paused: config.paused,
+ runBackwards: config.reversed
+ })
+ .to(targets, {
+ skewX: config.angle,
+ duration: config.duration,
+ ease: config.ease
+ });
+
+ return tl;
+ },
+ defaults: {
+ paused: false,
+ reversed: false,
+ angle: -45,
+ duration: 0.5,
+ ease: "sine"
+ }
+ },
+
+ blurRemove: {
+ effect: (targets, config) => U.gsap.timeline()
+ .to(
+ targets,
+ {
+ skewX: config.skewX,
+ duration: config.duration / 2,
+ ease: "power4.out"
+ }
+ )
+ .to(
+ targets,
+ {
+ x: config.x,
+ marginBottom(i, target) {
+ return U.get(target, "height") as number * -1;
+ },
+ marginRight(i, target) {
+ return U.get(target, "width") as number * -1;
+ },
+ scale: config.scale,
+ filter: config.filter,
+ duration: (3 / 4) * config.duration
+ },
+ config.duration / 4
+ )
+ .to(
+ targets,
+ {
+ opacity: 0,
+ duration: config.duration / 2,
+ ease: "power3.in"
+ },
+ config.duration / 2
+ ),
+ defaults: {
+ skewX: -20,
+ duration: 0.5,
+ x: "+=300",
+ scale: 1.5,
+ filter: "blur(10px)"
+ }
+ },
+ slideUp: {
+ effect: (targets) => U.gsap.to(
+ targets,
+ {
+ height: 0,
+ // PaddingTop: 0,
+ // paddingBottom: 0,
+ duration: 0.5,
+ ease: "power3"
+ }
+ ),
+ defaults: {}
+ },
+ pulse: {
+ effect: (targets, config) => U.gsap.to(
+ targets,
+ {
+ repeat: config.repCount,
+ yoyo: true,
+ duration: config.duration / config.repCount,
+ ease: config.ease,
+ opacity: 0.25
+ }
+ ),
+ defaults: {
+ repCount: 3,
+ duration: 5,
+ ease: "sine.inOut"
+ }
+ },
+ throb: {
+ effect: (targets, config) => U.gsap.to(
+ targets,
+ {
+ repeat: config.stagger ? undefined : 1,
+ yoyo: config.stagger ? undefined : true,
+ duration: config.duration / 2,
+ scale: config.scale,
+ filter: config.filter,
+ ease: config.ease,
+ stagger: config.stagger
+ ? {
+ ...config.stagger as gsap.StaggerVars,
+ repeat: 1,
+ yoyo: true
+ }
+ : {}
+ }
+ ),
+ defaults: {
+ duration: 1,
+ scale: 1,
+ filter: "saturate(1) brightness(2)",
+ ease: "power2.in"
+ },
+ extendTimeline: true
+ },
+ pulseClockWedges: {
+ effect: () => U.gsap.timeline({duration: 0}),
+ defaults: {}
+ },
+ reversePulseClockWedges: {
+ 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 ?
+
+ return U.gsap.effects.throb(targets, {stagger: {
+ amount: 0.25,
+ from: "start",
+ repeat: 1,
+ yoyo: true
+ }, ...config ?? {}});
+ },
+ defaults: { }
+ },
+ hoverTooltip: {
+ effect: (tooltip, config) => {
+ const tl = U.gsap.timeline({paused: true, defaults: { }});
+ if (!tooltip) { return tl; }
+ // Tooltip = $(tooltip);
+
+ if (config.scalingElems.length > 0) {
+ tl.to(
+ config.scalingElems,
+ {
+ scale: "+=0.2",
+ filter: "none",
+ color: C.Colors.WHITE,
+ opacity: 1,
+ duration: 0.125,
+ ease: "back"
+ },
+ 0.5
+ );
+ }
+
+ if (tooltip) {
+ tl.fromTo(
+ tooltip,
+ {
+ filter: "blur(50px)",
+ opacity: 0,
+ scale: 2 * config.tooltipScale
+ },
+ {
+ filter: "none",
+ opacity: 1,
+ scale: config.tooltipScale,
+ x: config.xMotion,
+ duration: 0.25,
+ ease: "power2"
+ },
+ 1
+ );
+ }
+
+ return tl;
+ },
+ defaults: {
+ xMotion: "+=200",
+ tooltipScale: 1.25
+ }
+ }
+};
+
+/**
+ * Registers relevant GSAP plugins and effects.
+ */
+export function Initialize() {
+ if (gsapPlugins.length) {
+ U.gsap.registerPlugin(...gsapPlugins);
+ }
+ Object.entries(gsapEffects).forEach(([name, effect]) => {
+ U.gsap.registerEffect(Object.assign(effect, {name, extendTimeline: true}));
+ });
+}
+
+/**
+ * Applies listeners to '.tooltip-trigger' elements in the document.
+ * @param {JQuery} html The document to be searched.
+ */
+export function ApplyTooltipAnimations(html: JQuery) {
+ html.find(".tooltip-trigger").each((_, el) => {
+ const tooltipElem = $(el).find(".tooltip")[0] ?? $(el).next(".tooltip")[0];
+ if (!tooltipElem) { return; }
+ $(el).data("hoverTimeline", U.gsap.effects.hoverTooltip(
+ tooltipElem,
+ {
+ scalingElems: [...$(el).find(".tooltip-scaling-elem")].filter((elem) => Boolean(elem)),
+ xMotion: $(tooltipElem).hasClass("tooltip-left") ? "-=250" : "+=200",
+ tooltipScale: $(tooltipElem).hasClass("tooltip-small") ? 1 : 1.2
+ }
+ ));
+ $(el).on({
+ mouseenter: function() {
+ $(el).css("z-index", 10);
+ $(el).data("hoverTimeline").play();
+ },
+ mouseleave: function() {
+ $(el).data("hoverTimeline").reverse().then(() => {
+ $(el).css("z-index", "");
+ });
+ }
+ });
+ });
+}
+
+/**
+ * Applies listeners to .consequence-display-container and children found in document.
+ * @param {JQuery} html The document to be searched.
+ */
+export function ApplyConsequenceAnimations(html: JQuery) {
+ /**
+ * 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 resistTo.type === "None", blurRemove the base_consequence name and type instead of sliding them in,
+ * and don't slide the resistance ones out at all.
+ * */
+
+ html
+ .find(".comp.consequence-display-container")
+ .each((_i, csqContainer) => {
+
+ if (!$(csqContainer).hasClass("consequence-accepted")) {
+
+ const csqRoot = U.gsap.utils.selector(csqContainer);
+ const csqBgImg = csqRoot(".consequence-bg-image");
+ const [iconContainer] = csqRoot(".consequence-icon-container");
+ const iconRoot = U.gsap.utils.selector(iconContainer);
+ const typeRoot = U.gsap.utils.selector(csqRoot(".consequence-type-container")[0]);
+ const nameRoot = U.gsap.utils.selector(csqRoot(".consequence-name-container")[0]);
+ const footerRoot = U.gsap.utils.selector(csqRoot(".consequence-footer-container")[0]);
+
+ const csqOptions: string[] = ["base", "accept"];
+ ["resist", "armor", "special"].forEach((csqOpt) => {
+ if (iconRoot(`.${csqOpt}-consequence`).length) {
+ csqOptions.push(csqOpt);
+ }
+ });
+
+ const interactionPads: Record = {};
+ const iconCircles: Record = {};
+ const buttonContainers: Record = {};
+ const buttonBGs: Record = {};
+ const buttonIcons: Record = {};
+ const buttonLabels: Record = {};
+ const typeLabels: Record = {};
+ const typeBGs: Record = {};
+ const nameLabels: Record = {};
+ const nameBGs: Record = {};
+ const footerLabels: Record = {};
+ const footerBGs: Record = {};
+
+ ["right", "left", "left-resist", "left-armor", "left-special"].forEach((iPadOpt) => {
+ [interactionPads[iPadOpt]] = csqRoot(`.consequence-interaction-pad.interaction-pad-${iPadOpt}`);
+ });
+
+ ["base", "accept", "resist", "armor", "special"].forEach((csqOpt) => {
+ [iconCircles[csqOpt]] = iconRoot(`.consequence-icon-circle.${csqOpt}-consequence`);
+ [buttonContainers[csqOpt]] = iconRoot(`.consequence-button-container.${csqOpt}-consequence`);
+ [buttonBGs[csqOpt]] = iconRoot(`.consequence-button-container.${csqOpt}-consequence .consequence-button-bg`);
+ [buttonIcons[csqOpt]] = iconRoot(`.consequence-button-container.${csqOpt}-consequence .button-icon i`);
+ [buttonLabels[csqOpt]] = iconRoot(`.consequence-button-container.${csqOpt}-consequence .consequence-button-label`);
+ [typeLabels[csqOpt]] = typeRoot(`.consequence-type.${csqOpt}-consequence`);
+ if (csqOpt === "accept") {
+ [typeBGs[csqOpt]] = typeRoot(`.consequence-type-bg.${csqOpt}-consequence`);
+ }
+ [nameLabels[csqOpt]] = nameRoot(`.consequence-name.${csqOpt}-consequence`);
+ [nameBGs[csqOpt]] = nameRoot(`.consequence-name-bg.${csqOpt}-consequence`);
+ [footerLabels[csqOpt]] = footerRoot(`.consequence-footer-message.${csqOpt}-consequence`);
+ [footerBGs[csqOpt]] = footerRoot(`.consequence-footer-bg.${csqOpt}-consequence`);
+ });
+
+ [
+ interactionPads,
+ iconCircles,
+ buttonContainers, buttonBGs, buttonIcons, buttonLabels,
+ typeLabels, typeBGs,
+ nameLabels, nameBGs,
+ footerLabels, footerBGs
+ ]
+ .forEach((elemRecord) => U.objCompact(elemRecord, [undefined, null, false], true));
+
+ // Apply master on-enter hover timeline to consequence container.
+ $(csqContainer).data("hoverTimelines",
+ [
+ U.gsap.effects.fadeOut(
+ [typeLabels.base, nameLabels.base],
+ {paused: true, duration: 0.5}
+ ),
+ U.gsap.effects.fadeOut(
+ [typeLabels.accept, nameLabels.accept],
+ {paused: true, reversed: true, duration: 0.25}
+ ),
+ U.gsap.effects.brighten(
+ [csqContainer],
+ {paused: true, duration: 0.5}
+ ),
+ U.gsap.effects.enlarge(
+ [iconCircles.base],
+ {paused: true, duration: 0.75, startScale: 0.75, scale: 0.85}
+ )
+ ]
+ );
+ $(csqContainer).on({
+ mouseenter: function() {
+ $(csqContainer).css("z-index", 10);
+ $(csqContainer).data("hoverTimelines").forEach((tl: gsap.core.Timeline) => tl.play());
+ },
+ mouseleave: function() {
+ if (!($(iconContainer).data("isToggled"))) {
+ $(csqContainer).css("z-index", "");
+ $(csqContainer).data("hoverTimelines").forEach((tl: gsap.core.Timeline) => tl.reverse());
+ }
+ }
+ });
+
+ // Apply click timeline to icon circle
+ $(iconContainer).data("clickTimelines",
+ [
+ U.gsap.timeline({paused: true})
+ .fromTo([csqBgImg], {
+ xPercent: 110,
+ yPercent: -50
+ }, {
+ xPercent: -60,
+ yPercent: -50,
+ duration: 0.5,
+ ease: "back"
+ }
+ ),
+ U.gsap.effects.fadeOut([iconCircles.base], {paused: true}),
+ U.gsap.effects.fadeOut([iconCircles.accept], {paused: true, reversed: true}),
+ U.gsap.effects.blurOut([buttonContainers], {paused: true, reversed: true})
+ ]
+ );
+ $(iconContainer).on({
+ click: function() {
+ if ($(iconContainer).data("isToggled")) {
+ $(iconContainer).data("isToggled", false);
+ $(iconContainer).data("clickTimelines").forEach((tl: gsap.core.Timeline) => tl.reverse());
+ } else {
+ $(iconContainer).data("isToggled", true);
+ $(iconContainer).data("clickTimelines").forEach((tl: gsap.core.Timeline) => tl.play());
+
+ // 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("isToggled", false);
+ $(containerElem).css("z-index", "");
+ [
+ ...iContainer$.data("clickTimelines"),
+ ...$(containerElem).data("hoverTimelines")
+ ].forEach((tl: gsap.core.Timeline) => tl.reverse());
+ }
+ });
+ }
+ }
+ });
+
+ // Apply hover timelines to right (accept) interaction pad
+ $(interactionPads.right).data("hoverTimelines",
+ [
+ U.gsap.effects.changeColor(
+ [typeLabels.accept],
+ {paused: true, color: C.Colors.WHITE}
+ ),
+ U.gsap.effects.slideOut(
+ [typeBGs.accept, buttonBGs.accept],
+ {paused: true, reversed: true, dir: "left"}
+ ),
+ U.gsap.effects.skewX(
+ [typeBGs.accept, buttonBGs.accept],
+ {paused: true, angle: -45}
+ ),
+ U.gsap.effects.changeColor(
+ [buttonIcons.accept, buttonLabels.accept],
+ {paused: true, color: C.Colors.dBLACK}
+ ),
+ U.gsap.effects.enlarge(
+ [buttonIcons.accept],
+ {paused: true, scale: 1.25}
+ )
+ ]
+ );
+ $(interactionPads.right).on({
+ mouseenter: function() {
+ if ($(iconContainer).data("isToggled")) {
+ $(interactionPads.right).data("hoverTimelines").forEach((tl: gsap.core.Timeline) => tl.play());
+ }
+ },
+ mouseleave: function() {
+ if ($(iconContainer).data("isToggled")) {
+ $(interactionPads.right).data("hoverTimelines").forEach((tl: gsap.core.Timeline) => tl.reverse());
+ }
+ }
+ });
+
+ // Apply hover timeline to left (resist/armor/special) interaction pad
+ $(interactionPads.left).data("hoverTimelines",
+ [
+ U.gsap.effects.fadeOut(
+ [typeLabels.accept, nameLabels.accept, iconCircles.accept, buttonContainers.accept],
+ {paused: true, duration: 0.25}
+ )
+ ]
+ );
+ $(interactionPads.left).on({
+ mouseenter: function() {
+ if ($(iconContainer).data("isToggled")) {
+ $(interactionPads.left).data("hoverTimelines").forEach((tl: gsap.core.Timeline) => tl.play());
+ }
+ },
+ mouseleave: function() {
+ if ($(iconContainer).data("isToggled")) {
+ $(interactionPads.left).data("hoverTimelines").forEach((tl: gsap.core.Timeline) => tl.reverse());
+ }
+ }
+ });
+
+ // Apply hover timelines to specific left interaction pads where they exist
+ ["resist", "armor", "special"].forEach((csqOpt) => {
+ if (interactionPads[`left-${csqOpt}`]) {
+ $(interactionPads[`left-${csqOpt}`]).data("hoverTimelines",
+ [
+ U.gsap.effects.fadeOut([iconCircles[csqOpt], typeLabels[csqOpt]], {paused: true, reversed: true}),
+ U.gsap.effects.slideOut(
+ [buttonBGs[csqOpt], nameLabels[csqOpt], footerBGs[csqOpt], footerLabels[csqOpt]],
+ {paused: true, reversed: true, dir: "left"}
+ ),
+ U.gsap.effects.skewX(
+ [buttonBGs[csqOpt], nameLabels[csqOpt], footerBGs[csqOpt], footerLabels[csqOpt]],
+ {paused: true, angle: -45}
+ ),
+ U.gsap.effects.changeColor(
+ [buttonIcons[csqOpt], buttonLabels[csqOpt]],
+ {paused: true, color: C.Colors.dBLACK}
+ ),
+ U.gsap.effects.enlarge(
+ [buttonIcons[csqOpt]],
+ {paused: true, scale: 1.25}
+ )
+ ]
+ );
+ $(interactionPads[`left-${csqOpt}`]).on({
+ mouseenter: function() {
+ if ($(iconContainer).data("isToggled")) {
+ $(interactionPads[`left-${csqOpt}`]).data("hoverTimelines").forEach((tl: gsap.core.Timeline) => tl.play());
+ }
+ },
+ mouseleave: function() {
+ if ($(iconContainer).data("isToggled")) {
+ $(interactionPads[`left-${csqOpt}`]).data("hoverTimelines").forEach((tl: gsap.core.Timeline) => tl.reverse());
+ }
+ }
+ });
+ }
+ });
+ }
+ });
+}
+
+export default U.gsap;
diff --git a/ts/core/utilities.ts b/ts/core/utilities.ts
index 568e80b2..215c70aa 100644
--- a/ts/core/utilities.ts
+++ b/ts/core/utilities.ts
@@ -1048,7 +1048,8 @@ function objFindKey>(
const objFilter = >(
obj: Type,
keyFunc: testFunc | testFunc | false,
- valFunc?: testFunc
+ valFunc?: testFunc,
+ isMutating = false
): Type => {
//
if (!valFunc) {
@@ -1058,9 +1059,25 @@ const objFilter = >(
if (!keyFunc) {
keyFunc = ((k: unknown) => k) as testFunc;
}
- if (isArray(obj)) {return obj.filter(valFunc) as Type;}
+ if (isArray(obj)) {
+ const keptValues = obj.filter(valFunc);
+ if (isMutating) {
+ obj.splice(0, obj.length, ...keptValues);
+ return obj;
+ }
+ return keptValues as Type;
+ }
+
const kFunc = keyFunc || (() => true);
const vFunc = valFunc || (() => true);
+ if (isMutating) {
+ const entriesToRemove = Object.entries(obj)
+ .filter(([key, val]: [string, unknown]) => !(kFunc(key, val) && vFunc(val, key)));
+ for (const [key] of entriesToRemove) {
+ delete obj[key];
+ }
+ return obj;
+ }
return Object.fromEntries(
Object.entries(obj)
.filter(([key, val]: [string, unknown]) => kFunc(key, val) && vFunc(val, key))
@@ -1077,8 +1094,9 @@ const objForEach = (obj: Index, func: valFunc): void => {
// Prunes an object of given set of values, [undefined, null] default
const objCompact = )>(
obj: Type,
- removeWhiteList: unknown[] = [undefined, null]
-): Type => objFilter(obj, (val: unknown) => !removeWhiteList.includes(val));
+ removeWhiteList: unknown[] = [undefined, null],
+ isMutating = false
+): Type => objFilter(obj, (val: unknown) => !removeWhiteList.includes(val), undefined, isMutating);
const objClone = (obj: T, isStrictlySafe = false): T => {
const cloneArray = (arr: aT): aT => [...arr] as aT;
diff --git a/ts/documents/actors/BladesPC.ts b/ts/documents/actors/BladesPC.ts
index ba3ff593..00a55bef 100644
--- a/ts/documents/actors/BladesPC.ts
+++ b/ts/documents/actors/BladesPC.ts
@@ -16,6 +16,23 @@ class BladesPC extends BladesActor implements BladesActorSubClass.Scoundrel,
return super.IsType(doc, BladesActorType.pc);
}
+ static GetFromUser(userRef: unknown): BladesPC|undefined {
+ let user: User|undefined;
+ if (typeof userRef === "string") {
+ user = game.users.get(userRef) ?? game.users.getName(userRef);
+ } else if (userRef instanceof User) {
+ user = userRef;
+ }
+ if (!user) { throw new Error(`Unable to find user '${userRef}'`); }
+
+ const actor = game.actors.get(user.character?.id ?? "");
+ if (BladesPC.IsType(actor)) {
+ return actor;
+ }
+
+ return undefined;
+ }
+
static override async create(
data: ActorDataConstructorData & {
system?: Partial
@@ -227,6 +244,12 @@ class BladesPC extends BladesActor implements BladesActorSubClass.Scoundrel,
if (!BladesActor.IsType(this, BladesActorType.pc)) { return; }
await this.update({"system.stash.value": Math.min(this.system.stash.value + amount, this.system.stash.max)});
}
+
+ get remainingDowntimeActions(): number {
+ if (!BladesActor.IsType(this, BladesActorType.pc)) { return 0; }
+ return this.system.downtime_actions.max + this.system.downtime_action_bonus - this.system.downtime_actions.value;
+
+ }
// #endregion
// #region BladesRoll.PrimaryDoc Implementation
diff --git a/ts/documents/items/BladesClock.ts b/ts/documents/items/BladesClock.ts
new file mode 100644
index 00000000..b68752dc
--- /dev/null
+++ b/ts/documents/items/BladesClock.ts
@@ -0,0 +1,60 @@
+import BladesItem from "../../BladesItem";
+import {BladesActorType, BladesItemType, Factor} from "../../core/constants";
+import U from "../../core/utilities";
+import BladesActor from "../../BladesActor";
+import BladesRoll from "../../BladesRoll";
+
+class BladesLocation extends BladesItem implements BladesRoll.OppositionDocData {
+
+
+ override get rollFactors(): Partial> {
+
+ const factorData: Partial> = {};
+ [
+ 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;
+ }
+
+ override getFactorTotal(factor: Factor): number {
+ 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;
+ }
+
+ override get rollOppImg() { return this.img ?? ""; }
+
+ // #region OVERRIDES: _onUpdate
+ override async _onUpdate(changed: any, options: any, userId: string) {
+ await super._onUpdate(changed, options, userId);
+ BladesActor.GetTypeWithTags(BladesActorType.pc).forEach((actor) => actor.render());
+ }
+ // #endregion
+}
+
+declare interface BladesLocation {
+ type: BladesItemType.location,
+ system: BladesItemSchema.Location
+}
+
+export default BladesLocation;
diff --git a/ts/sheets/actor/BladesActorSheet.ts b/ts/sheets/actor/BladesActorSheet.ts
index 65a2de40..4c1dea66 100644
--- a/ts/sheets/actor/BladesActorSheet.ts
+++ b/ts/sheets/actor/BladesActorSheet.ts
@@ -2,9 +2,9 @@
import U from "../../core/utilities";
import G, {ApplyTooltipAnimations} from "../../core/gsap";
-import C, {BladesActorType, BladesItemType, AttributeTrait, Tag, ActionTrait, Factor, RollType} from "../../core/constants";
+import C, {BladesActorType, BladesItemType, DowntimeAction, AttributeTrait, Tag, ActionTrait, Factor, RollType, RollSubType} from "../../core/constants";
import Tags from "../../core/tags";
-import BladesActor from "../../BladesActor";
+import {BladesActor, BladesPC, BladesCrew} from "../../documents/BladesActorProxy";
import BladesItem from "../../BladesItem";
import BladesDialog, {SelectionCategory} from "../../BladesDialog";
import BladesActiveEffect from "../../BladesActiveEffect";
@@ -424,7 +424,7 @@ class BladesActorSheet extends ActorSheet {
if (!doc) {
return;
}
- await G.effects.blurRemove(elem$).then(async () => {
+ await G.effects.blurOut(elem$).then(async () => {
if (doc instanceof BladesItem) {
await this.actor.remSubItem(doc);
} else {
@@ -439,7 +439,7 @@ class BladesActorSheet extends ActorSheet {
if (!doc) {
return;
}
- await G.effects.blurRemove(elem$).then(async () => await doc.delete());
+ await G.effects.blurOut(elem$).then(async () => await doc.delete());
}
async _onItemToggleClick(event: ClickEvent) {
@@ -494,6 +494,82 @@ class BladesActorSheet extends ActorSheet {
await BladesRoll.NewRoll(rollData as BladesRoll.ConstructorConfig);
}
+ async _onDowntimeActionClick(event: ClickEvent) {
+ const elem$ = $(event.currentTarget);
+
+ // Check whether character has downtime actions remaining.
+ // If not, prompt for whether spending Coin or Rep for extra
+ // If so, increase character's downtime count by one
+
+ const downtimeAction = elem$.data("downtimeAction") as DowntimeAction;
+
+ const rollConfig: BladesRoll.ConstructorConfig = {
+ rollType: RollType.Action,
+ rollDowntimeAction: downtimeAction
+ };
+
+ // Determine Trait from action type
+ switch (downtimeAction) {
+ case DowntimeAction.AcquireAsset: {
+ rollConfig.rollTrait = Factor.tier;
+ break;
+ }
+ case DowntimeAction.IndulgeVice: {
+ if (!BladesPC.IsType(this.actor)) { return; }
+ rollConfig.rollType = RollType.IndulgeVice;
+ rollConfig.rollTrait = Object.values(AttributeTrait)
+ .reduce(
+ (minAttr: AttributeTrait, curAttr: AttributeTrait) => (this.actor as BladesPC).attributes[curAttr]
+ < (this.actor as BladesPC).attributes[minAttr]
+ ? curAttr
+ : minAttr,
+ AttributeTrait.insight);
+ // GM needs to be able to set the desired asset as the rollOpposition, so can set minimum quality
+ break;
+ }
+ case DowntimeAction.LongTermProject: {
+ rollConfig.rollTrait = "";
+ // BladesRoll can search actor subitems for project/rituals and set up their clocks as the 'opposition'
+ break;
+ }
+ case DowntimeAction.Recover: {
+ // If clicked on by player from an NPC sheet -> rollPrimary is the NPC, trait is quality
+ // Otherwise -> Search 'ActivePC' characters for 'Physicker' Ability; if more than one will have to prompt user
+ // ... OR ...
+ // ActiveEffect added to any BladesActor that can heal, along with reference to the trait they roll.
+ rollConfig.rollTrait = ActionTrait.tinker || Factor.quality;
+ break;
+ }
+ case DowntimeAction.ReduceHeat: {
+ rollConfig.rollTrait = "";
+ break;
+ }
+ case DowntimeAction.Train: {
+ // Element will have target: Attribute or Playbook.
+ // Will have to check for crew upgrades that increase XP gained.
+ // If too much XP gained, will have to store excess so it can roll over after the player advances.
+ // Then, because this doesn't take a roll, we just return.
+ return;
+ }
+ }
+
+ // ... Pretty much everything else should be done over in BladesRoll.
+ BladesRoll.NewRoll(rollConfig);
+ }
+ async _onGatherInfoClick(event: ClickEvent) {
+ const elem$ = $(event.currentTarget);
+
+ if (elem$.data("isFortuneRoll")) {
+ BladesRoll.NewRoll({
+ rollType: RollType.Fortune
+ });
+ } else {
+ BladesRoll.NewRoll({
+ rollType: RollType.Action,
+ rollTrait: ""
+ });
+ }
+ }
// #endregion
// #region Active Effect Handlers
diff --git a/ts/sheets/roll/BladesConsequence.ts b/ts/sheets/roll/BladesConsequence.ts
index 29250630..0c5d6df4 100644
--- a/ts/sheets/roll/BladesConsequence.ts
+++ b/ts/sheets/roll/BladesConsequence.ts
@@ -6,6 +6,16 @@ import BladesRoll, {BladesRollPrimary} from "../../BladesRoll";
class BladesConsequence {
+ static get None() {
+ return {
+ id: randomID(),
+ name: "",
+ type: ConsequenceType.None,
+ isSelected: true,
+ isVisible: true
+ };
+ }
+
static GetActiveRollChatID(): string | undefined {
return Array.from(game.messages).filter((msg) => $(msg.content ?? "").data("chat-id")).pop()?.id ?? undefined;
}
@@ -26,6 +36,16 @@ class BladesConsequence {
roll$.closest(".chat-message").addClass("indulgevice-roll");
}
+ // If this message is an action roll result AND there are unresolved consequences, add 'unresolved-action-roll' class.
+ if (
+ roll$.hasClass("roll-type-action")
+ && Array.from(roll$.find(".comp.consequence-display-container:not(.consequence-accepted)")).length >= 1
+ ) {
+ roll$.closest(".chat-message").addClass("unresolved-action-roll");
+ } else {
+ roll$.closest(".chat-message").removeClass("unresolved-action-roll");
+ }
+
// If this message is the last one, add 'active-chat-roll' class and remove it from all others
if (BladesConsequence.GetActiveRollChatID() === roll$.data("chatId")) {
$(document).find(".chat-message").removeClass("active-chat-roll");
@@ -35,7 +55,7 @@ class BladesConsequence {
}
const rollPhase = roll$.data("rollPhase") as RollPhase;
- eLog.checkLog3("rollCollab", "ApplyChatListeners", {html, roll$, rollPhase});
+ // eLog.checkLog3("rollCollab", "ApplyChatListeners", {html, roll$, rollPhase});
if (rollPhase !== RollPhase.AwaitingConsequences) {return;}
html$.find("[data-action*='-consequence']").on({
@@ -178,7 +198,7 @@ class BladesConsequence {
const html$ = $(await message.getHTML());
const bCsqs: BladesConsequence[] = [];
- html$.find(".blades-roll .consequence-container .comp.consequence-display-container").each((_, elem) => {
+ html$.find(".blades-roll .consequence-container .comp.consequence-display-container:not(.consequence-accepted)").each((_, elem) => {
bCsqs.push(BladesConsequence.GetFromCsqElem(elem));
});
@@ -199,7 +219,7 @@ class BladesConsequence {
_primaryType: BladesRoll.PrimaryDocType;
- _primaryDoc: BladesRoll.PrimaryDoc;
+ _primaryDoc: BladesRollPrimary;
_chatMessage: ChatMessage;
@@ -316,7 +336,7 @@ class BladesConsequence {
this._name = name;
this._primaryID = primaryID;
this._primaryType = primaryType;
- this._primaryDoc = primaryDoc;
+ this._primaryDoc = new BladesRollPrimary(undefined, primaryDoc);
this._type = type as ConsequenceType;
this._position = position as Position;
this._effect = effect as Effect;
@@ -473,17 +493,23 @@ class BladesConsequence {
await this._chatMessage.update({content: message$[0].outerHTML});
}
- async resistConsequence() {
- eLog.checkLog3("rollCollab", `Resisting Consequence id ${this._id}`);
-
- if (!this._resistTo || !this.resistToData) { throw new Error(`Cannot find resistTo for resistance roll for csq id '${this._id}' in message '${this._chatMessage.id}'`); }
-
+ get rollFlagData(): BladesRoll.FlagData {
// Get rollPrimaryData from archived roll flags on user document.
let rollFlagData = this._user.getFlag(C.SYSTEM_ID, "rollCollab") as BladesRoll.FlagData;
if (rollFlagData.rollID !== this._rollID) {
rollFlagData = this._user.getFlag(C.SYSTEM_ID, `rollCollabArchive.${this._rollID}`) as BladesRoll.FlagData;
}
if (!rollFlagData) { throw new Error(`Unable to locate flag data for roll id '${this._rollID}'`); }
+ return rollFlagData;
+ }
+
+ async resistConsequence() {
+ eLog.checkLog3("rollCollab", `Resisting Consequence id ${this._id}`);
+
+ if (!this._resistTo || !this.resistToData) { throw new Error(`Cannot find resistTo for resistance roll for csq id '${this._id}' in message '${this._chatMessage.id}'`); }
+
+ // Get rollPrimaryData from archived roll flags on user document.
+ const rollFlagData = this.rollFlagData;
const resistConfig = {
rollType: RollType.Resistance,
@@ -505,19 +531,26 @@ class BladesConsequence {
BladesRoll.NewRoll(resistConfig);
}
- async applyResistedConsequence() {
- if (this._resistTo) {
- await this._resistTo.applyConsequenceToPrimary();
- await this.transformToConsequence("resist");
+ async applyResistedConsequence(resistType: "resist"|"armor"|"special") {
+ const rCsq = {
+ resist: this._resistTo,
+ armor: this._armorTo,
+ special: this._specialTo
+ }[resistType];
+ if (rCsq) {
+ await rCsq.applyConsequenceToPrimary();
}
+ await this.transformToConsequence(resistType);
}
async resistArmorConsequence() {
- /* ... */
+ this._primaryDoc.spendArmor();
+ this.applyResistedConsequence("armor");
}
async resistSpecialArmorConsequence() {
- /* ... */
+ await this._primaryDoc.spendSpecialArmor();
+ this.applyResistedConsequence("special");
}
}