Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19b472de00 | ||
|
|
bf4e98a780 | ||
|
|
3d21b8e466 | ||
|
|
85fedf1e16 | ||
|
|
e7a18a735a | ||
|
|
eacdf8e94b | ||
|
|
971f6c4c49 |
@@ -63,6 +63,11 @@
|
||||
background: none;
|
||||
}
|
||||
|
||||
body.sla-pause-active #pause {
|
||||
opacity: 0 !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
#pause figcaption {
|
||||
font-family: "Roboto", sans-serif;
|
||||
color: #ffffff;
|
||||
@@ -70,6 +75,72 @@
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
body > .sla-pause-root {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
background:
|
||||
radial-gradient(circle at center, rgba(109, 0, 0, 0.08), transparent 28%),
|
||||
rgba(0, 0, 0, 0.16);
|
||||
}
|
||||
|
||||
body > .sla-pause-root .sla-pause-stage {
|
||||
width: min(34vw, 420px);
|
||||
height: min(42vw, 500px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
body > .sla-pause-root .sla-pause-svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
filter: drop-shadow(0 0 18px rgba(170, 24, 24, 0.26));
|
||||
}
|
||||
|
||||
body > .sla-pause-root .sla-pause-standby {
|
||||
animation: slaPausePulse 1.75s ease-in-out infinite;
|
||||
text-shadow: 0 0 10px rgba(255, 100, 100, 0.45);
|
||||
}
|
||||
|
||||
body > .sla-pause-root .sla-pause-scan {
|
||||
animation: slaPauseScan 2.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
body > .sla-pause-root .sla-pause-logo-core {
|
||||
animation: slaPauseLogoPulse 2.6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
body > .sla-pause-root .sla-pause-core-glow {
|
||||
animation: slaPauseCoreGlow 2.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes slaPausePulse {
|
||||
0%, 100% { opacity: 0.42; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slaPauseScan {
|
||||
0% { opacity: 0.08; }
|
||||
50% { opacity: 0.34; }
|
||||
100% { opacity: 0.08; }
|
||||
}
|
||||
|
||||
@keyframes slaPauseLogoPulse {
|
||||
0%, 100% { opacity: 0.86; filter: brightness(0.95); }
|
||||
50% { opacity: 1; filter: brightness(1.18); }
|
||||
}
|
||||
|
||||
@keyframes slaPauseCoreGlow {
|
||||
0%, 100% { opacity: 0.62; }
|
||||
50% { opacity: 0.92; }
|
||||
}
|
||||
|
||||
/* Paragraph */
|
||||
p {
|
||||
margin: 0em 0;
|
||||
@@ -1574,7 +1645,7 @@ p {
|
||||
|
||||
.mosh .headergrid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.2fr 1.2fr 1.1fr 0.95fr 1fr 1fr 1fr;
|
||||
grid-template-columns: 1.15fr 1.15fr 1fr 0.9fr 0.95fr 0.95fr 0.95fr 0.9fr;
|
||||
grid-template-rows: auto auto auto;
|
||||
gap: 0.35rem 0.5rem;
|
||||
padding: 1.45em 1em 1em;
|
||||
@@ -1646,7 +1717,7 @@ p {
|
||||
}
|
||||
|
||||
.mosh .header-field-lad {
|
||||
grid-column: 1 / span 3;
|
||||
grid-column: 1 / span 2;
|
||||
grid-row: 3;
|
||||
align-self: start;
|
||||
}
|
||||
@@ -1657,7 +1728,7 @@ p {
|
||||
}
|
||||
|
||||
.mosh .header-field-race {
|
||||
grid-column: 6 / span 2;
|
||||
grid-column: 6 / span 3;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
@@ -1676,12 +1747,21 @@ p {
|
||||
grid-row: 3;
|
||||
}
|
||||
|
||||
.mosh .header-field-rest {
|
||||
grid-column: 3;
|
||||
grid-row: 3;
|
||||
align-self: start;
|
||||
justify-self: start;
|
||||
width: 100%;
|
||||
max-width: 78px;
|
||||
}
|
||||
|
||||
.mosh .sla-header-checkbox {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
min-height: 2rem;
|
||||
padding: 0.25rem 0.9rem;
|
||||
padding: 0.2rem 0.7rem;
|
||||
border: 2px solid #111;
|
||||
border-radius: 999px;
|
||||
background: #fff;
|
||||
@@ -1703,6 +1783,21 @@ p {
|
||||
.mosh .sla-header-checkbox span {
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
font-size: 0.86rem;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.mosh .sla-header-rest-pill {
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-self: start;
|
||||
background: linear-gradient(180deg, #4aa8ff 0%, #1b64d8 100%) !important;
|
||||
color: #f4fbff !important;
|
||||
border: 1px solid rgba(134, 209, 255, 0.9) !important;
|
||||
box-shadow: 0 0 0 1px rgba(9, 29, 56, 0.45), 0 0 14px rgba(58, 152, 255, 0.28);
|
||||
padding: 0.28rem 0.55rem !important;
|
||||
font-size: 0.86rem !important;
|
||||
}
|
||||
|
||||
.mosh .header-field-compact .headerinputfield {
|
||||
@@ -1746,7 +1841,8 @@ p {
|
||||
height: auto;
|
||||
padding: 6px;
|
||||
border-radius: 10px;
|
||||
border: 0;
|
||||
border: 2px solid rgba(102, 209, 255, 0.95);
|
||||
box-shadow: 0 0 0 1px rgba(8, 18, 30, 0.9), 0 0 14px rgba(54, 176, 255, 0.35);
|
||||
}
|
||||
|
||||
.mosh .rollcontainer {
|
||||
@@ -2654,6 +2750,17 @@ p {
|
||||
border-color: rgba(42, 20, 15, 0.28) !important;
|
||||
}
|
||||
|
||||
.window-app.sla-window-theme-legacy .mosh .textarea-input,
|
||||
.window-app.sla-window-theme-legacy .mosh .trauma-response-box {
|
||||
background: #fffaf1 !important;
|
||||
color: #17120f !important;
|
||||
border: 1px solid rgba(42, 20, 15, 0.22) !important;
|
||||
}
|
||||
|
||||
.window-app.sla-window-theme-legacy .mosh .trauma-response-box::placeholder {
|
||||
color: rgba(31, 23, 18, 0.48) !important;
|
||||
}
|
||||
|
||||
.window-app.sla-window-theme-legacy .mosh .mainstatlabel,
|
||||
.window-app.sla-window-theme-legacy .mosh .stresslabel,
|
||||
.window-app.sla-window-theme-legacy .mosh .items-list .item-header {
|
||||
@@ -2915,12 +3022,61 @@ p {
|
||||
}
|
||||
|
||||
.mosh .trauma-response-box {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 92px;
|
||||
max-height: 132px;
|
||||
max-height: 150px;
|
||||
padding: 10px 12px;
|
||||
box-sizing: border-box;
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.25;
|
||||
overflow-y: auto;
|
||||
resize: vertical;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.mosh .sla-trauma-panel {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto minmax(112px, auto);
|
||||
row-gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.mosh .sla-trauma-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.mosh .sla-trauma-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
padding: 0.2rem 0.55rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||
background: rgba(14, 20, 28, 0.72);
|
||||
color: #e7eef6;
|
||||
font-size: 0.67rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mosh .sla-trauma-toggle input[type="checkbox"] {
|
||||
margin: 0;
|
||||
accent-color: #3f92ff;
|
||||
}
|
||||
|
||||
.mosh .sla-trauma-controls .sla-inline-mini-button {
|
||||
min-width: 0;
|
||||
padding: 0.2rem 0.7rem;
|
||||
font-size: 0.67rem;
|
||||
line-height: 1.1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mosh .sla-stat-heading {
|
||||
@@ -3159,6 +3315,17 @@ p {
|
||||
border-color: rgba(17, 22, 27, 0.24) !important;
|
||||
}
|
||||
|
||||
.window-app.sla-window-theme-modern .mosh .textarea-input,
|
||||
.window-app.sla-window-theme-modern .mosh .trauma-response-box {
|
||||
background: #11161d !important;
|
||||
color: #f4f7fa !important;
|
||||
border: 1px solid rgba(212, 190, 148, 0.24) !important;
|
||||
}
|
||||
|
||||
.window-app.sla-window-theme-modern .mosh .trauma-response-box::placeholder {
|
||||
color: rgba(244, 247, 250, 0.52) !important;
|
||||
}
|
||||
|
||||
.mosh.sla-sheet-theme-modern .textarea-input {
|
||||
background: #11161d;
|
||||
color: #f4f7fa;
|
||||
@@ -3317,7 +3484,7 @@ p {
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: -4px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.mosh .sla-health-label-row {
|
||||
@@ -3326,7 +3493,7 @@ p {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
min-height: 18px;
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
.mosh .sla-health-label-row .resource-label {
|
||||
@@ -3921,6 +4088,494 @@ p {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
/* SLA chat outcome redesign */
|
||||
.chat-message .mosh.sla-chat-shell,
|
||||
.mosh.sla-chat-shell {
|
||||
--sla-chat-bg: linear-gradient(180deg, #151318 0%, #0e0d11 100%);
|
||||
--sla-chat-surface: rgba(32, 25, 32, 0.92);
|
||||
--sla-chat-panel: rgba(17, 21, 28, 0.94);
|
||||
--sla-chat-panel-soft: rgba(27, 33, 43, 0.92);
|
||||
--sla-chat-ink: #f4efe7;
|
||||
--sla-chat-muted: #d2c5b8;
|
||||
--sla-chat-accent: #cf3148;
|
||||
--sla-chat-accent-soft: rgba(207, 49, 72, 0.18);
|
||||
--sla-chat-line: rgba(207, 49, 72, 0.48);
|
||||
--sla-chat-glow: rgba(207, 49, 72, 0.22);
|
||||
}
|
||||
|
||||
.chat-message .mosh.sla-chat-shell .rollcontainer,
|
||||
.mosh.sla-chat-shell .rollcontainer {
|
||||
background: var(--sla-chat-bg);
|
||||
border: 1px solid var(--sla-chat-line);
|
||||
border-radius: 18px;
|
||||
padding: 10px 10px 12px;
|
||||
color: var(--sla-chat-ink);
|
||||
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.22), inset 0 0 0 1px rgba(255, 255, 255, 0.03);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-message .mosh.sla-chat-shell .rollcontainer .body,
|
||||
.chat-message .mosh.sla-chat-shell .description,
|
||||
.chat-message .mosh.sla-chat-shell .description .body,
|
||||
.mosh.sla-chat-shell .rollcontainer .body,
|
||||
.mosh.sla-chat-shell .description,
|
||||
.mosh.sla-chat-shell .description .body {
|
||||
color: var(--sla-chat-ink);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-hero {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 62px;
|
||||
gap: 10px;
|
||||
align-items: stretch;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-hero-main {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-kicker {
|
||||
margin-bottom: 6px;
|
||||
color: #ffcf9a;
|
||||
font-family: "Futura PT Cond Bold", "Josefin Sans", "Roboto", sans-serif;
|
||||
font-size: 0.72rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.14em;
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-kicker-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-meta-row {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-deptline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-emblem {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 32px;
|
||||
height: 22px;
|
||||
padding: 0 8px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--sla-chat-line);
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0.02));
|
||||
color: var(--sla-chat-ink);
|
||||
font-size: 0.62rem;
|
||||
font-family: "Futura PT Cond Bold", "Josefin Sans", "Roboto", sans-serif;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
box-shadow: 0 0 0 1px rgba(255,255,255,0.03), 0 0 10px var(--sla-chat-glow);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-dept {
|
||||
color: var(--sla-chat-muted);
|
||||
font-size: 0.68rem;
|
||||
font-family: "Futura PT Cond Bold", "Josefin Sans", "Roboto", sans-serif;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-type-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 38px;
|
||||
height: 22px;
|
||||
padding: 0 8px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255,255,255,0.14);
|
||||
font-size: 0.62rem;
|
||||
font-family: "Futura PT Cond Bold", "Josefin Sans", "Roboto", sans-serif;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
color: #fff7ef;
|
||||
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.04), 0 0 12px rgba(0,0,0,0.18);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-type-normal {
|
||||
background: linear-gradient(180deg, rgba(49, 72, 99, 0.98), rgba(21, 34, 48, 0.98));
|
||||
border-color: rgba(128, 182, 232, 0.34);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-type-attack {
|
||||
background: linear-gradient(180deg, rgba(120, 34, 38, 0.98), rgba(61, 18, 22, 0.98));
|
||||
border-color: rgba(239, 112, 112, 0.36);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-type-panic {
|
||||
background: linear-gradient(180deg, rgba(132, 65, 24, 0.98), rgba(68, 35, 12, 0.98));
|
||||
border-color: rgba(255, 181, 110, 0.36);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-type-wound {
|
||||
background: linear-gradient(180deg, rgba(128, 18, 46, 0.98), rgba(67, 10, 25, 0.98));
|
||||
border-color: rgba(230, 88, 128, 0.38);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-type-ebb {
|
||||
background: linear-gradient(180deg, rgba(22, 98, 94, 0.98), rgba(11, 47, 45, 0.98));
|
||||
border-color: rgba(106, 208, 193, 0.38);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-type-heal {
|
||||
background: linear-gradient(180deg, rgba(22, 98, 58, 0.98), rgba(12, 48, 29, 0.98));
|
||||
border-color: rgba(118, 223, 159, 0.38);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-type-reload {
|
||||
background: linear-gradient(180deg, rgba(110, 87, 28, 0.98), rgba(58, 45, 13, 0.98));
|
||||
border-color: rgba(235, 205, 116, 0.38);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-type-table {
|
||||
background: linear-gradient(180deg, rgba(72, 57, 112, 0.98), rgba(36, 28, 58, 0.98));
|
||||
border-color: rgba(161, 135, 235, 0.34);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-type-item {
|
||||
background: linear-gradient(180deg, rgba(66, 66, 76, 0.98), rgba(28, 28, 36, 0.98));
|
||||
border-color: rgba(180, 180, 195, 0.3);
|
||||
}
|
||||
|
||||
.chat-message .mosh.sla-chat-shell .rollweaponh1,
|
||||
.mosh.sla-chat-shell .rollweaponh1 {
|
||||
min-height: 0;
|
||||
margin: 0;
|
||||
padding: 10px 12px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--sla-chat-line);
|
||||
background: linear-gradient(180deg, rgba(40, 18, 24, 0.98), rgba(22, 18, 28, 0.98));
|
||||
color: #fff7ef;
|
||||
font-size: 1.02rem;
|
||||
line-height: 1.08;
|
||||
white-space: normal;
|
||||
overflow-wrap: normal;
|
||||
word-break: normal;
|
||||
hyphens: none;
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-hero-title-tight {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.14;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-hero-media {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--sla-chat-line);
|
||||
background: linear-gradient(180deg, rgba(18, 25, 36, 0.96), rgba(9, 13, 18, 0.96));
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .roll-image {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
object-fit: cover;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-summary {
|
||||
margin: 0 0 10px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(180deg, rgba(245, 236, 226, 0.98), rgba(229, 217, 203, 0.98));
|
||||
color: #16120f;
|
||||
border: 1px solid rgba(255, 219, 176, 0.48);
|
||||
font-size: 0.98rem;
|
||||
line-height: 1.28;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-summary strong {
|
||||
color: #0f0c0a;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-rollframe {
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-roll-result,
|
||||
.mosh.sla-chat-shell .dice-roll,
|
||||
.mosh.sla-chat-shell .dice-result,
|
||||
.mosh.sla-chat-shell .roll-result,
|
||||
.mosh.sla-chat-shell .dice-total,
|
||||
.mosh.sla-chat-shell .dice-formula {
|
||||
color: #f4efe7;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .dice-formula {
|
||||
background: linear-gradient(180deg, rgba(24, 57, 77, 0.98), rgba(12, 33, 49, 0.98));
|
||||
border: 1px solid rgba(88, 181, 219, 0.52);
|
||||
border-radius: 10px;
|
||||
color: #d9f3ff;
|
||||
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.04);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .dice-total,
|
||||
.mosh.sla-chat-shell .roll-result {
|
||||
background: linear-gradient(180deg, rgba(23, 28, 37, 0.98), rgba(12, 15, 20, 0.98));
|
||||
border: 1px solid rgba(228, 195, 121, 0.48);
|
||||
border-radius: 12px;
|
||||
color: #fff5df;
|
||||
box-shadow: 0 0 0 1px rgba(255,255,255,0.03), 0 0 12px rgba(228, 195, 121, 0.12);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-roll-outcome {
|
||||
border-radius: 10px;
|
||||
padding: 6px 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-roll-outcome.sla-success,
|
||||
.mosh.sla-chat-shell .dice-total.sla-crit-success {
|
||||
background: linear-gradient(180deg, rgba(10, 84, 46, 0.95), rgba(10, 55, 31, 0.95));
|
||||
color: #dcffe7;
|
||||
border-color: rgba(119, 225, 154, 0.45);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-roll-outcome.sla-failure,
|
||||
.mosh.sla-chat-shell .dice-total.sla-crit-failure {
|
||||
background: linear-gradient(180deg, rgba(112, 20, 24, 0.95), rgba(67, 13, 18, 0.95));
|
||||
color: #ffe2e2;
|
||||
border-color: rgba(255, 133, 133, 0.45);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-section {
|
||||
margin: 0 0 10px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(207, 49, 72, 0.28);
|
||||
border-radius: 12px;
|
||||
background: var(--sla-chat-panel);
|
||||
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.03);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-section-title {
|
||||
margin-bottom: 7px;
|
||||
color: #ffd8a3;
|
||||
font-size: 0.73rem;
|
||||
letter-spacing: 0.16em;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-section-success {
|
||||
background: linear-gradient(180deg, rgba(18, 58, 36, 0.96), rgba(12, 35, 23, 0.96));
|
||||
border-color: rgba(96, 213, 141, 0.35);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-section-danger {
|
||||
background: linear-gradient(180deg, rgba(74, 20, 26, 0.96), rgba(45, 14, 17, 0.96));
|
||||
border-color: rgba(231, 104, 104, 0.38);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-section-heading {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 0.45rem;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-severity-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.18rem 0.62rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(255,255,255,0.24);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.14em;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 800;
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,0.12);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-section-danger.sla-severity-minor {
|
||||
background: linear-gradient(180deg, rgba(92, 57, 18, 0.96), rgba(55, 34, 12, 0.96));
|
||||
border-color: rgba(255, 190, 74, 0.5);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-section-danger.sla-severity-severe {
|
||||
background: linear-gradient(180deg, rgba(90, 18, 26, 0.97), rgba(52, 10, 15, 0.97));
|
||||
border-color: rgba(255, 91, 91, 0.52);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-section-danger.sla-severity-critical {
|
||||
background:
|
||||
radial-gradient(circle at top right, rgba(181, 237, 255, 0.12), transparent 42%),
|
||||
linear-gradient(180deg, rgba(102, 8, 28, 0.98), rgba(45, 8, 21, 0.98));
|
||||
border-color: rgba(204, 237, 255, 0.84);
|
||||
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.06), 0 0 14px rgba(162, 231, 255, 0.08);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-section-target {
|
||||
background: linear-gradient(180deg, rgba(28, 35, 48, 0.96), rgba(15, 20, 28, 0.96));
|
||||
border-color: rgba(96, 165, 214, 0.28);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-section-damage {
|
||||
background: linear-gradient(180deg, rgba(58, 21, 26, 0.98), rgba(33, 13, 16, 0.98));
|
||||
border-color: rgba(237, 122, 122, 0.34);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-section-heal {
|
||||
background: linear-gradient(180deg, rgba(18, 59, 42, 0.98), rgba(11, 34, 25, 0.98));
|
||||
border-color: rgba(96, 210, 160, 0.34);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-section-ebb-meta {
|
||||
background: linear-gradient(180deg, rgba(24, 53, 51, 0.98), rgba(13, 29, 28, 0.98));
|
||||
border-color: rgba(106, 208, 193, 0.28);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-divider {
|
||||
margin: 0 0 8px;
|
||||
color: #ffcf9a;
|
||||
font-size: 0.72rem;
|
||||
font-family: "Futura PT Cond Bold", "Josefin Sans", "Roboto", sans-serif;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.16em;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-chip {
|
||||
background: linear-gradient(180deg, rgba(246, 197, 84, 0.95), rgba(194, 132, 22, 0.95));
|
||||
color: #18120d;
|
||||
border: 1px solid rgba(255,255,255,0.14);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-list li {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-wound-block {
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid rgba(255,255,255,0.08);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-chat-wound-link {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-effect-damage {
|
||||
color: #ff9d8d;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell .sla-effect-heal {
|
||||
color: #97f0bb;
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell.sla-chat-shell-ebb {
|
||||
--sla-chat-bg: linear-gradient(180deg, #112726 0%, #0b1516 100%);
|
||||
--sla-chat-accent: #2dc1b3;
|
||||
--sla-chat-accent-soft: rgba(45, 193, 179, 0.18);
|
||||
--sla-chat-line: rgba(70, 205, 188, 0.44);
|
||||
--sla-chat-glow: rgba(70, 205, 188, 0.22);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell.sla-chat-shell-reload,
|
||||
.mosh.sla-chat-shell.sla-chat-shell-item-update,
|
||||
.mosh.sla-chat-shell.sla-chat-shell-actor-update {
|
||||
--sla-chat-line: rgba(214, 187, 122, 0.34);
|
||||
--sla-chat-bg: linear-gradient(180deg, #18181b 0%, #101014 100%);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell.sla-chat-shell-item {
|
||||
--sla-chat-line: rgba(178, 135, 228, 0.34);
|
||||
--sla-chat-bg: linear-gradient(180deg, #17131b 0%, #0e0d12 100%);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell.sla-chat-variant-sla {
|
||||
--sla-chat-accent: #cf3148;
|
||||
--sla-chat-accent-soft: rgba(207, 49, 72, 0.18);
|
||||
--sla-chat-line: rgba(207, 49, 72, 0.48);
|
||||
--sla-chat-glow: rgba(207, 49, 72, 0.22);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell.sla-chat-variant-mort {
|
||||
--sla-chat-bg: linear-gradient(180deg, #181416 0%, #0f0c0d 100%);
|
||||
--sla-chat-panel: rgba(25, 20, 22, 0.95);
|
||||
--sla-chat-accent: #b09aa7;
|
||||
--sla-chat-accent-soft: rgba(176, 154, 167, 0.14);
|
||||
--sla-chat-line: rgba(176, 154, 167, 0.42);
|
||||
--sla-chat-glow: rgba(176, 154, 167, 0.2);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell.sla-chat-variant-shiver {
|
||||
--sla-chat-bg: linear-gradient(180deg, #131b23 0%, #0b1016 100%);
|
||||
--sla-chat-panel: rgba(16, 25, 35, 0.95);
|
||||
--sla-chat-accent: #78b8ff;
|
||||
--sla-chat-accent-soft: rgba(120, 184, 255, 0.16);
|
||||
--sla-chat-line: rgba(120, 184, 255, 0.42);
|
||||
--sla-chat-glow: rgba(120, 184, 255, 0.2);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell.sla-chat-variant-darknight {
|
||||
--sla-chat-bg: linear-gradient(180deg, #18131f 0%, #0d0b11 100%);
|
||||
--sla-chat-panel: rgba(23, 18, 31, 0.95);
|
||||
--sla-chat-accent: #8b73ff;
|
||||
--sla-chat-accent-soft: rgba(139, 115, 255, 0.16);
|
||||
--sla-chat-line: rgba(139, 115, 255, 0.42);
|
||||
--sla-chat-glow: rgba(139, 115, 255, 0.2);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell.sla-chat-variant-tek {
|
||||
--sla-chat-bg: linear-gradient(180deg, #141c1d 0%, #0b1112 100%);
|
||||
--sla-chat-panel: rgba(18, 29, 29, 0.95);
|
||||
--sla-chat-accent: #52d3be;
|
||||
--sla-chat-accent-soft: rgba(82, 211, 190, 0.16);
|
||||
--sla-chat-line: rgba(82, 211, 190, 0.42);
|
||||
--sla-chat-glow: rgba(82, 211, 190, 0.22);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell.sla-chat-variant-ops {
|
||||
--sla-chat-bg: linear-gradient(180deg, #1a1712 0%, #100d09 100%);
|
||||
--sla-chat-panel: rgba(28, 23, 16, 0.95);
|
||||
--sla-chat-accent: #e1b15d;
|
||||
--sla-chat-accent-soft: rgba(225, 177, 93, 0.16);
|
||||
--sla-chat-line: rgba(225, 177, 93, 0.42);
|
||||
--sla-chat-glow: rgba(225, 177, 93, 0.2);
|
||||
}
|
||||
|
||||
.mosh.sla-chat-shell.sla-chat-variant-ebb {
|
||||
--sla-chat-bg: linear-gradient(180deg, #112726 0%, #0b1516 100%);
|
||||
--sla-chat-panel: rgba(13, 33, 33, 0.95);
|
||||
--sla-chat-accent: #2dc1b3;
|
||||
--sla-chat-accent-soft: rgba(45, 193, 179, 0.18);
|
||||
--sla-chat-line: rgba(70, 205, 188, 0.44);
|
||||
--sla-chat-glow: rgba(70, 205, 188, 0.22);
|
||||
}
|
||||
|
||||
.mosh .sla-wound-effect-link {
|
||||
border: 1px solid rgba(255, 193, 118, 0.55);
|
||||
background: linear-gradient(180deg, rgba(45, 23, 14, 0.95), rgba(27, 14, 9, 0.95));
|
||||
color: #ffd6a0;
|
||||
}
|
||||
|
||||
.mosh .sla-wound-effect-link:hover,
|
||||
.mosh .sla-wound-effect-link:focus {
|
||||
background: linear-gradient(180deg, rgba(79, 36, 16, 0.98), rgba(44, 20, 9, 0.98));
|
||||
color: #fff0d1;
|
||||
box-shadow: 0 0 0 1px rgba(255, 193, 118, 0.22), 0 0 14px rgba(255, 193, 118, 0.16);
|
||||
}
|
||||
.mosh .description,
|
||||
.mosh .description .body,
|
||||
.mosh .rollcontainer .body {
|
||||
@@ -3946,6 +4601,46 @@ p {
|
||||
background: rgba(141, 31, 24, 0.08);
|
||||
}
|
||||
|
||||
.mosh .sla-chat-section-heading {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.mosh .sla-chat-severity-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.18rem 0.62rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(0,0,0,0.12);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.14em;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.mosh .sla-severity-minor,
|
||||
.mosh .sla-chat-severity-pill.sla-severity-minor {
|
||||
background: linear-gradient(180deg, rgba(255, 216, 140, 0.32), rgba(255, 189, 79, 0.18));
|
||||
border-color: rgba(224, 152, 24, 0.42);
|
||||
color: #9b5b00;
|
||||
}
|
||||
|
||||
.mosh .sla-severity-severe,
|
||||
.mosh .sla-chat-severity-pill.sla-severity-severe {
|
||||
background: linear-gradient(180deg, rgba(255, 145, 145, 0.24), rgba(233, 63, 63, 0.14));
|
||||
border-color: rgba(179, 27, 27, 0.34);
|
||||
color: #9e1717;
|
||||
}
|
||||
|
||||
.mosh .sla-severity-critical,
|
||||
.mosh .sla-chat-severity-pill.sla-severity-critical {
|
||||
background: linear-gradient(180deg, rgba(232, 247, 255, 0.94), rgba(196, 233, 255, 0.82));
|
||||
border-color: rgba(84, 145, 199, 0.42);
|
||||
color: #611120;
|
||||
box-shadow: 0 0 14px rgba(126, 196, 245, 0.16);
|
||||
}
|
||||
|
||||
.mosh .sla-chat-section-target {
|
||||
border-color: rgba(35, 73, 98, 0.35);
|
||||
background: rgba(35, 73, 98, 0.06);
|
||||
@@ -4028,6 +4723,25 @@ p {
|
||||
color: #136235;
|
||||
}
|
||||
|
||||
.mosh .severity[data-severity="1"],
|
||||
.mosh .severity[data-severity="2"] {
|
||||
color: #ffca61;
|
||||
}
|
||||
|
||||
.mosh .severity[data-severity="3"],
|
||||
.mosh .severity[data-severity="4"] {
|
||||
color: #ff7b7b;
|
||||
}
|
||||
|
||||
.mosh .severity[data-severity="5"],
|
||||
.mosh .severity[data-severity="6"],
|
||||
.mosh .severity[data-severity="7"],
|
||||
.mosh .severity[data-severity="8"],
|
||||
.mosh .severity[data-severity="9"] {
|
||||
color: #eef9ff;
|
||||
text-shadow: 0 0 10px rgba(162, 231, 255, 0.25);
|
||||
}
|
||||
|
||||
.mosh .sla-chat-chip {
|
||||
display: inline-block;
|
||||
margin-bottom: 8px;
|
||||
@@ -4088,16 +4802,12 @@ p {
|
||||
}
|
||||
|
||||
.mosh.sla-dossier-sheet .headergrid {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
grid-template-rows: auto auto auto auto;
|
||||
grid-template-columns: 1.15fr 1.15fr 0.9fr 0.95fr 0.95fr 0.95fr;
|
||||
grid-template-rows: auto auto auto;
|
||||
gap: 0.45rem 0.7rem;
|
||||
padding: 1.2rem 1rem 0.95rem;
|
||||
}
|
||||
|
||||
.mosh.sla-dossier-sheet .headergrid {
|
||||
grid-template-columns: 1.1fr 1.1fr 0.9fr 1fr 1fr;
|
||||
}
|
||||
|
||||
.mosh.sla-dossier-sheet .sla-sheet-wordmark {
|
||||
grid-column: 1 / -1;
|
||||
text-align: left;
|
||||
@@ -4117,7 +4827,7 @@ p {
|
||||
}
|
||||
|
||||
.mosh.sla-dossier-sheet .header-field-lad {
|
||||
grid-column: 1 / span 2;
|
||||
grid-column: 1;
|
||||
grid-row: 3;
|
||||
align-self: start;
|
||||
}
|
||||
@@ -4128,23 +4838,32 @@ p {
|
||||
}
|
||||
|
||||
.mosh.sla-dossier-sheet .header-field-race {
|
||||
grid-column: 5;
|
||||
grid-column: 5 / span 2;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.mosh.sla-dossier-sheet .header-field-sla-rank {
|
||||
grid-column: 3;
|
||||
grid-row: 4;
|
||||
grid-column: 4;
|
||||
grid-row: 3;
|
||||
}
|
||||
|
||||
.mosh.sla-dossier-sheet .header-field-height {
|
||||
grid-column: 4;
|
||||
grid-row: 4;
|
||||
grid-column: 5;
|
||||
grid-row: 3;
|
||||
}
|
||||
|
||||
.mosh.sla-dossier-sheet .header-field-weight {
|
||||
grid-column: 5;
|
||||
grid-row: 4;
|
||||
grid-column: 6;
|
||||
grid-row: 3;
|
||||
}
|
||||
|
||||
.mosh.sla-dossier-sheet .header-field-rest {
|
||||
grid-column: 2;
|
||||
grid-row: 3;
|
||||
align-self: start;
|
||||
justify-self: start;
|
||||
width: 100%;
|
||||
max-width: 78px;
|
||||
}
|
||||
|
||||
.mosh.sla-dossier-sheet .abilities,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Badge shape -->
|
||||
<path d="M6,2 H26 L30,6 V30 H2 V6 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||
<!-- Notch at top (lanyard hole) -->
|
||||
<rect x="13" y="0" width="6" height="4" rx="1" fill="currentColor"/>
|
||||
<!-- Head -->
|
||||
<circle cx="16" cy="12" r="4" fill="currentColor"/>
|
||||
<!-- Shoulders -->
|
||||
<path d="M8,26 C8,20 24,20 24,26" fill="currentColor"/>
|
||||
<!-- ID stripe at bottom -->
|
||||
<rect x="5" y="26" width="22" height="2" fill="currentColor" opacity="0.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 575 B |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Hexagonal speech bubble -->
|
||||
<polygon points="4,4 28,4 28,22 20,22 16,28 12,22 4,22" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linejoin="round" fill-rule="evenodd"/>
|
||||
<!-- Signal bars inside -->
|
||||
<rect x="8" y="14" width="3" height="5" fill="currentColor"/>
|
||||
<rect x="13" y="11" width="3" height="8" fill="currentColor"/>
|
||||
<rect x="18" y="8" width="3" height="11" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 489 B |
@@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Outer ring -->
|
||||
<circle cx="16" cy="16" r="13" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||
<!-- Inner ring -->
|
||||
<circle cx="16" cy="16" r="5" fill="none" stroke="currentColor" stroke-width="1.5"/>
|
||||
<!-- Crosshairs -->
|
||||
<line x1="16" y1="3" x2="16" y2="9" stroke="currentColor" stroke-width="2"/>
|
||||
<line x1="16" y1="23" x2="16" y2="29" stroke="currentColor" stroke-width="2"/>
|
||||
<line x1="3" y1="16" x2="9" y2="16" stroke="currentColor" stroke-width="2"/>
|
||||
<line x1="23" y1="16" x2="29" y2="16" stroke="currentColor" stroke-width="2"/>
|
||||
<!-- Corner tick marks -->
|
||||
<line x1="11" y1="5" x2="12.5" y2="5" stroke="currentColor" stroke-width="1.5"/>
|
||||
<line x1="21" y1="5" x2="19.5" y2="5" stroke="currentColor" stroke-width="1.5"/>
|
||||
<!-- Center dot -->
|
||||
<circle cx="16" cy="16" r="2" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 897 B |
@@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Cylinder top ellipse -->
|
||||
<ellipse cx="16" cy="6" rx="12" ry="4" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||
<!-- Cylinder body -->
|
||||
<line x1="4" y1="6" x2="4" y2="24" stroke="currentColor" stroke-width="2"/>
|
||||
<line x1="28" y1="6" x2="28" y2="24" stroke="currentColor" stroke-width="2"/>
|
||||
<!-- Cylinder bottom ellipse -->
|
||||
<ellipse cx="16" cy="24" rx="12" ry="4" fill="currentColor" stroke="currentColor" stroke-width="2"/>
|
||||
<!-- Data bands -->
|
||||
<ellipse cx="16" cy="12" rx="12" ry="4" fill="none" stroke="currentColor" stroke-width="1" stroke-dasharray="3 2"/>
|
||||
<ellipse cx="16" cy="18" rx="12" ry="4" fill="none" stroke="currentColor" stroke-width="1" stroke-dasharray="3 2"/>
|
||||
<!-- Access port -->
|
||||
<rect x="13" y="4" width="6" height="4" rx="1" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 866 B |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Stylus body -->
|
||||
<polygon points="8,4 24,4 28,8 16,30 4,8" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||
<!-- Stylus grip band -->
|
||||
<line x1="6" y1="10" x2="26" y2="10" stroke="currentColor" stroke-width="1.5"/>
|
||||
<!-- Tip -->
|
||||
<polygon points="16,25 13,29 19,29" fill="currentColor"/>
|
||||
<!-- SLA insignia on stylus face -->
|
||||
<text x="16" y="19" text-anchor="middle" font-family="sans-serif" font-size="7" font-weight="bold" fill="currentColor">SLA</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 567 B |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Main crate -->
|
||||
<rect x="3" y="8" width="26" height="20" fill="none" stroke="currentColor" stroke-width="2" rx="1"/>
|
||||
<!-- Lid -->
|
||||
<rect x="3" y="4" width="26" height="6" fill="currentColor" rx="1"/>
|
||||
<!-- Center clasp -->
|
||||
<rect x="13" y="6" width="6" height="4" rx="1" fill="currentColor" opacity="0.6"/>
|
||||
<!-- Horizontal divider on crate -->
|
||||
<line x1="3" y1="18" x2="29" y2="18" stroke="currentColor" stroke-width="1.5"/>
|
||||
<!-- Corner brackets -->
|
||||
<path d="M3,13 L3,10 L6,10" fill="none" stroke="currentColor" stroke-width="1.5"/>
|
||||
<path d="M29,13 L29,10 L26,10" fill="none" stroke="currentColor" stroke-width="1.5"/>
|
||||
<path d="M3,23 L3,26 L6,26" fill="none" stroke="currentColor" stroke-width="1.5"/>
|
||||
<path d="M29,23 L29,26 L26,26" fill="none" stroke="currentColor" stroke-width="1.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 881 B |
@@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- File/tablet body -->
|
||||
<path d="M5,2 H22 L28,8 V30 H5 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||
<!-- Folded corner -->
|
||||
<path d="M22,2 L22,8 L28,8" fill="none" stroke="currentColor" stroke-width="1.5"/>
|
||||
<!-- Text lines -->
|
||||
<line x1="9" y1="13" x2="24" y2="13" stroke="currentColor" stroke-width="1.5"/>
|
||||
<line x1="9" y1="17" x2="24" y2="17" stroke="currentColor" stroke-width="1.5"/>
|
||||
<line x1="9" y1="21" x2="20" y2="21" stroke="currentColor" stroke-width="1.5"/>
|
||||
<!-- Classified stamp -->
|
||||
<rect x="7" y="24" width="10" height="4" rx="0.5" fill="none" stroke="currentColor" stroke-width="1.2" transform="rotate(-8 12 26)"/>
|
||||
<line x1="8" y1="26" x2="16" y2="26" stroke="currentColor" stroke-width="1" transform="rotate(-8 12 26)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 859 B |
@@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Emitter housing -->
|
||||
<polygon points="12,6 20,6 22,12 10,12" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<!-- Lens circle -->
|
||||
<circle cx="16" cy="9" r="3" fill="none" stroke="currentColor" stroke-width="1.5" opacity="0.7"/>
|
||||
<!-- Beam spread (lower trapezoid) -->
|
||||
<polygon points="10,12 22,12 28,28 4,28" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<!-- Beam rays -->
|
||||
<line x1="16" y1="12" x2="16" y2="28" stroke="currentColor" stroke-width="1" opacity="0.5"/>
|
||||
<line x1="13" y1="12" x2="8" y2="28" stroke="currentColor" stroke-width="1"/>
|
||||
<line x1="19" y1="12" x2="24" y2="28" stroke="currentColor" stroke-width="1"/>
|
||||
<!-- Mounting bracket -->
|
||||
<rect x="14" y="2" width="4" height="5" rx="1" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 894 B |
@@ -0,0 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Ruler body -->
|
||||
<rect x="2" y="12" width="28" height="8" rx="1" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||
<!-- Major tick marks -->
|
||||
<line x1="9" y1="12" x2="9" y2="17" stroke="currentColor" stroke-width="1.5"/>
|
||||
<line x1="16" y1="12" x2="16" y2="20" stroke="currentColor" stroke-width="2"/>
|
||||
<line x1="23" y1="12" x2="23" y2="17" stroke="currentColor" stroke-width="1.5"/>
|
||||
<!-- Minor ticks -->
|
||||
<line x1="5" y1="12" x2="5" y2="15" stroke="currentColor" stroke-width="1"/>
|
||||
<line x1="12" y1="12" x2="12" y2="15" stroke="currentColor" stroke-width="1"/>
|
||||
<line x1="19" y1="12" x2="19" y2="15" stroke="currentColor" stroke-width="1"/>
|
||||
<line x1="26" y1="12" x2="26" y2="15" stroke="currentColor" stroke-width="1"/>
|
||||
<!-- Arrow / range indicator above -->
|
||||
<line x1="4" y1="8" x2="28" y2="8" stroke="currentColor" stroke-width="1.5"/>
|
||||
<polygon points="4,8 8,6 8,10" fill="currentColor"/>
|
||||
<polygon points="28,8 24,6 24,10" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Map pin shape -->
|
||||
<path d="M16,2 C10,2 6,7 6,12 C6,19 16,30 16,30 C16,30 26,19 26,12 C26,7 22,2 16,2 Z" fill="currentColor" stroke="currentColor" stroke-width="2"/>
|
||||
<!-- Inner ring -->
|
||||
<circle cx="16" cy="12" r="5" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||
<!-- Center dot -->
|
||||
<circle cx="16" cy="12" r="2.5" fill="currentColor"/>
|
||||
<!-- Angular bracket decorations -->
|
||||
<path d="M10,5 L8,7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path d="M22,5 L24,7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 644 B |
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Waveform bars -->
|
||||
<rect x="2" y="12" width="3" height="8" rx="1" fill="currentColor"/>
|
||||
<rect x="7" y="7" width="3" height="18" rx="1" fill="currentColor"/>
|
||||
<rect x="12" y="4" width="3" height="24" rx="1" fill="currentColor"/>
|
||||
<rect x="17" y="9" width="3" height="14" rx="1" fill="currentColor"/>
|
||||
<rect x="22" y="6" width="3" height="20" rx="1" fill="currentColor"/>
|
||||
<rect x="27" y="13" width="3" height="6" rx="1" fill="currentColor"/>
|
||||
<!-- Base line -->
|
||||
<line x1="2" y1="29" x2="30" y2="29" stroke="currentColor" stroke-width="1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 631 B |
@@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Outer border -->
|
||||
<rect x="2" y="2" width="28" height="28" fill="none" stroke="currentColor" stroke-width="2" rx="1"/>
|
||||
<!-- Grid lines -->
|
||||
<line x1="11" y1="2" x2="11" y2="30" stroke="currentColor" stroke-width="1"/>
|
||||
<line x1="21" y1="2" x2="21" y2="30" stroke="currentColor" stroke-width="1"/>
|
||||
<line x1="2" y1="11" x2="30" y2="11" stroke="currentColor" stroke-width="1"/>
|
||||
<line x1="2" y1="21" x2="30" y2="21" stroke="currentColor" stroke-width="1"/>
|
||||
<!-- Sector fills — industrial blocks -->
|
||||
<rect x="3" y="3" width="7" height="7" fill="currentColor"/>
|
||||
<rect x="13" y="13" width="7" height="7" fill="currentColor" opacity="0.6"/>
|
||||
<rect x="22" y="22" width="7" height="7" fill="currentColor"/>
|
||||
<rect x="22" y="3" width="7" height="7" fill="currentColor"/>
|
||||
<!-- Location marker -->
|
||||
<polygon points="16,17 14,21 18,21" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 940 B |
@@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Hex gear outer -->
|
||||
<polygon points="16,2 27,8 27,20 16,26 5,20 5,8" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linejoin="round"/>
|
||||
<!-- Gear teeth on hex edges -->
|
||||
<polygon points="16,1 18,4 14,4" fill="currentColor"/>
|
||||
<polygon points="27.5,7.5 27,11 24.5,9.5" fill="currentColor"/>
|
||||
<polygon points="27.5,20.5 24.5,22.5 27,19" fill="currentColor"/>
|
||||
<polygon points="16,27 14,24 18,24" fill="currentColor"/>
|
||||
<polygon points="4.5,20.5 5,19 7.5,22.5" fill="currentColor"/>
|
||||
<polygon points="4.5,7.5 7.5,9.5 5,11" fill="currentColor"/>
|
||||
<!-- Inner hex cutout -->
|
||||
<polygon points="16,9 21,12 21,18 16,21 11,18 11,12" fill="currentColor"/>
|
||||
<!-- Center circle -->
|
||||
<circle cx="16" cy="15" r="3.5" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||
<circle cx="16" cy="15" r="1.5" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 910 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Speaker cone -->
|
||||
<polygon points="4,11 12,11 20,5 20,27 12,21 4,21" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<!-- Sound wave arcs -->
|
||||
<path d="M23,10 C26,12 26,20 23,22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M25,7 C30,11 30,21 25,25" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
|
||||
<!-- Base line -->
|
||||
<rect x="4" y="21" width="8" height="5" rx="0.5" fill="currentColor" opacity="0.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 609 B |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Outer frame -->
|
||||
<rect x="2" y="3" width="28" height="26" fill="none" stroke="currentColor" stroke-width="2" rx="1"/>
|
||||
<!-- Header row -->
|
||||
<rect x="2" y="3" width="28" height="6" fill="currentColor" rx="1"/>
|
||||
<!-- Row dividers -->
|
||||
<line x1="2" y1="15" x2="30" y2="15" stroke="currentColor" stroke-width="1"/>
|
||||
<line x1="2" y1="21" x2="30" y2="21" stroke="currentColor" stroke-width="1"/>
|
||||
<!-- Column dividers -->
|
||||
<line x1="10" y1="9" x2="10" y2="29" stroke="currentColor" stroke-width="1"/>
|
||||
<line x1="21" y1="9" x2="21" y2="29" stroke="currentColor" stroke-width="1"/>
|
||||
<!-- Die symbol in first col -->
|
||||
<rect x="4" y="11" width="4" height="4" rx="0.5" fill="none" stroke="currentColor" stroke-width="1"/>
|
||||
<circle cx="6" cy="13" r="0.8" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 851 B |
@@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- 2x2 tile grid with isometric feel -->
|
||||
<!-- Top-left tile -->
|
||||
<rect x="2" y="2" width="13" height="13" rx="1" fill="currentColor" stroke="currentColor" stroke-width="1.5"/>
|
||||
<!-- Top-right tile -->
|
||||
<rect x="17" y="2" width="13" height="13" rx="1" fill="currentColor" opacity="0.5" stroke="currentColor" stroke-width="1.5"/>
|
||||
<!-- Bottom-left tile -->
|
||||
<rect x="2" y="17" width="13" height="13" rx="1" fill="currentColor" opacity="0.5" stroke="currentColor" stroke-width="1.5"/>
|
||||
<!-- Bottom-right tile -->
|
||||
<rect x="17" y="17" width="13" height="13" rx="1" fill="currentColor" stroke="currentColor" stroke-width="1.5"/>
|
||||
<!-- Corner brackets on tiles -->
|
||||
<path d="M4,7 L4,4 L7,4" fill="none" stroke="currentColor" stroke-width="1.2"/>
|
||||
<path d="M19,7 L19,4 L22,4" fill="none" stroke="currentColor" stroke-width="1.2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 907 B |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Hex base -->
|
||||
<polygon points="16,3 28,10 28,22 16,29 4,22 4,10" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||
<!-- Inner ring -->
|
||||
<polygon points="16,8 23,12 23,20 16,24 9,20 9,12" fill="none" stroke="currentColor" stroke-width="1"/>
|
||||
<!-- Head silhouette -->
|
||||
<circle cx="16" cy="14" r="3.5" fill="currentColor"/>
|
||||
<!-- Shoulders -->
|
||||
<path d="M10,23 C10,19 22,19 22,23" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 509 B |
@@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<!-- Main wall line heavy -->
|
||||
<line x1="4" y1="16" x2="28" y2="16" stroke="currentColor" stroke-width="4"/>
|
||||
<!-- Wall battlements (top) -->
|
||||
<rect x="5" y="10" width="4" height="6" fill="currentColor"/>
|
||||
<rect x="13" y="10" width="4" height="6" fill="currentColor"/>
|
||||
<rect x="21" y="10" width="4" height="6" fill="currentColor"/>
|
||||
<!-- Wall base foundation -->
|
||||
<rect x="3" y="20" width="26" height="4" rx="1" fill="currentColor" opacity="0.5"/>
|
||||
<!-- Door gap indicator -->
|
||||
<rect x="14" y="16" width="4" height="4" fill="currentColor" opacity="0"/>
|
||||
<line x1="14" y1="16" x2="14" y2="24" stroke="currentColor" stroke-width="1" stroke-dasharray="2 2" opacity="0.7"/>
|
||||
<line x1="18" y1="16" x2="18" y2="24" stroke="currentColor" stroke-width="1" stroke-dasharray="2 2" opacity="0.7"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 869 B |
@@ -141,6 +141,30 @@ function decorateSkillWithTraitPreview(skill, traitRows = []) {
|
||||
return skill;
|
||||
}
|
||||
|
||||
function getPurchasableItemTotalCost(itemData = {}) {
|
||||
const system = itemData?.system ?? itemData?.data ?? {};
|
||||
const unitCost = Number(system.cost ?? 0) || 0;
|
||||
const quantity = Math.max(1, Number(system.quantity ?? 1) || 1);
|
||||
return Math.round(unitCost * quantity * 100) / 100;
|
||||
}
|
||||
|
||||
function isAmmoPurchaseItem(itemData = {}) {
|
||||
const system = itemData?.system ?? itemData?.data ?? {};
|
||||
const sla = system?.sla ?? {};
|
||||
const name = String(itemData?.name ?? "").trim().toLowerCase();
|
||||
const category = String(sla.category ?? "").trim().toLowerCase();
|
||||
const calibre = String(sla.calibre ?? "").trim();
|
||||
const ammoTag = String(sla.ammoTag ?? "").trim();
|
||||
return category === "ammunition" || Boolean((calibre && ammoTag) || name.startsWith("ammo:"));
|
||||
}
|
||||
|
||||
function formatSlaTimestamp() {
|
||||
return new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle: "short",
|
||||
timeStyle: "short"
|
||||
}).format(new Date());
|
||||
}
|
||||
|
||||
const SLA_SPECIES_RULES = {
|
||||
Human: [
|
||||
{ key: "adaptable", label: "Adaptable", summary: "Once per session, reroll one Stat check and take the better result.", optional: false, default: true }
|
||||
@@ -258,6 +282,9 @@ export class MothershipActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
superData.sla.fluxNotes ??= { value: "" };
|
||||
superData.sla.ebbNotes ??= { value: "" };
|
||||
superData.sla.speciesRules ??= {};
|
||||
superData.sla.sessionAbilityUsed ??= false;
|
||||
superData.sla.contacts ??= [];
|
||||
superData.sla.activityLog ??= { value: "" };
|
||||
const activeTheme = this.actor.getFlag("sla-mothership", "sheetTheme") || "modern";
|
||||
data.slaSheetTheme = activeTheme;
|
||||
data.data.slaSheetTheme = activeTheme;
|
||||
@@ -271,6 +298,15 @@ export class MothershipActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
const fluxValue = Number(superData.sla.flux?.value ?? 0);
|
||||
const fluxMax = Math.max(0, Number(superData.sla.flux?.max ?? 0));
|
||||
data.data.slaFluxPercent = fluxMax > 0 ? Math.min(100, Math.max(0, Math.round((fluxValue / fluxMax) * 100))) : 0;
|
||||
// Vevaphon morph data
|
||||
data.data.isVevaphon = this.actor.isSlaVevaphon?.() ?? false;
|
||||
if (data.data.isVevaphon) {
|
||||
const instab = Math.max(0, Math.min(12, Number(superData.sla.vevaphonInstability?.value ?? 0) || 0));
|
||||
data.data.vevaphonInstabilityPercent = Math.round((instab / 12) * 100);
|
||||
data.data.vevaphonInstabilityElevated = instab >= 6;
|
||||
data.data.vevaphonInstabilityCritical = instab >= 10;
|
||||
data.data.vevaphonInstabilityClass = instab >= 10 ? "instability-critical" : instab >= 6 ? "instability-elevated" : "instability-nominal";
|
||||
}
|
||||
data.data.slaSpeciesTraitList = String(superData.sla.speciesNotes?.value ?? "").split(/\n+/).map((entry) => entry.trim()).filter(Boolean);
|
||||
data.data.slaPackageSkillList = String(superData.sla.packageSkills?.value ?? "").split(/\s*,\s*/).map((entry) => entry.trim()).filter(Boolean);
|
||||
data.data.slaSpeciesRules = getSpeciesRules(String(superData.sla.species?.value ?? "").trim(), superData.sla.speciesRules);
|
||||
@@ -283,6 +319,17 @@ export class MothershipActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
disadvantage: String(this._slaTraitPickerSelection?.disadvantage ?? "").trim()
|
||||
};
|
||||
data.data.slaPrometheusStatus = this.actor.getSlaPrometheusStatus?.() ?? { enabled: false, inCombat: false, currentRound: 0, roundsUntilPulse: null, pulseThisRound: false };
|
||||
|
||||
// Contacts
|
||||
const rawContacts = Array.isArray(superData.sla.contacts) ? superData.sla.contacts : [];
|
||||
data.data.slaContacts = rawContacts.map((c) => ({
|
||||
id: String(c.id ?? foundry.utils.randomID()),
|
||||
name: String(c.name ?? "Unknown"),
|
||||
role: String(c.role ?? ""),
|
||||
notes: String(c.notes ?? ""),
|
||||
disposition: ["ally", "neutral", "hostile", "unknown"].includes(c.disposition) ? c.disposition : "neutral"
|
||||
}));
|
||||
data.data.slaContactCount = data.data.slaContacts.length;
|
||||
const traitRows = buildSlaTraitRows(data.data.persTraits ?? []);
|
||||
data.data.persTraitsEnhanced = traitRows;
|
||||
data.data.traitCount = traitRows.length;
|
||||
@@ -654,19 +701,26 @@ export class MothershipActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
//Quantity adjuster
|
||||
html.on('mousedown', '.item-quantity', ev => {
|
||||
const li = ev.currentTarget.closest(".item");
|
||||
//const item = duplicate(this.actor.getEmbeddedDocument("Item", li.dataset.itemId))
|
||||
var item;
|
||||
item = foundry.utils.duplicate(this.actor.getEmbeddedDocument("Item", li.dataset.itemId));
|
||||
|
||||
let amount = item.system.quantity;
|
||||
const sourceItem = this.actor.getEmbeddedDocument("Item", li.dataset.itemId);
|
||||
if (!sourceItem) return;
|
||||
const item = foundry.utils.duplicate(sourceItem);
|
||||
const amount = Math.max(0, Number(item.system?.quantity ?? 0) || 0);
|
||||
let delta = 0;
|
||||
|
||||
if (event.button == 0) {
|
||||
item.system.quantity = Number(amount) + 1;
|
||||
} else if (event.button == 2) {
|
||||
item.system.quantity = Number(amount) - 1;
|
||||
if (ev.button === 0) {
|
||||
delta = 1;
|
||||
} else if (ev.button === 2) {
|
||||
delta = -1;
|
||||
}
|
||||
|
||||
this.actor.updateEmbeddedDocuments('Item', [item]);
|
||||
if (!delta) return;
|
||||
item.system.quantity = Math.max(0, amount + delta);
|
||||
|
||||
this.actor.updateEmbeddedDocuments('Item', [item]).then(async () => {
|
||||
if (delta > 0) {
|
||||
await this._applySlaQuantityIncreaseDebit(sourceItem, delta);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//Severity adjuster
|
||||
@@ -906,8 +960,141 @@ export class MothershipActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
this.actor.triggerSlaFluxPanic({ source: 'Manual Flux panic check', panicOnFailure: true, forcePanic: true });
|
||||
});
|
||||
|
||||
html.find('.sla-panic-button').click(ev => {
|
||||
this.actor.rollTable("panicCheck", null, null, null, null, null, null);
|
||||
// Vevaphon morph form shift
|
||||
html.find('.sla-morph-shift').click(async (ev) => {
|
||||
const formName = ev.currentTarget.dataset.form;
|
||||
if (!formName) return;
|
||||
await this.actor.setSlaVevaphonMorphForm?.(formName);
|
||||
this.render(false);
|
||||
});
|
||||
|
||||
// Vevaphon instability +/-
|
||||
html.find('.sla-instability-increase').click(async () => {
|
||||
await this.actor.adjustSlaVevaphonInstability?.(1);
|
||||
this.render(false);
|
||||
});
|
||||
html.find('.sla-instability-decrease').click(async () => {
|
||||
await this.actor.adjustSlaVevaphonInstability?.(-1);
|
||||
this.render(false);
|
||||
});
|
||||
|
||||
// SCL fractional adjustment buttons
|
||||
html.find('.sla-scl-adjust').click(async (ev) => {
|
||||
ev.preventDefault();
|
||||
const delta = parseFloat(ev.currentTarget.dataset.delta ?? 0);
|
||||
if (!delta) return;
|
||||
const current = parseFloat(String(this.actor.system?.sla?.scl?.value ?? "0").replace(",", ".")) || 0;
|
||||
const next = Math.max(0, Math.round((current + delta) * 10) / 10);
|
||||
const formatted = Number.isInteger(next) ? String(next) : next.toFixed(1);
|
||||
await this.actor.update({ "system.sla.scl.value": formatted });
|
||||
this.render(false);
|
||||
});
|
||||
|
||||
// Session ability tracking — checkbox
|
||||
html.find('.sla-session-ability-used').on('change', async (ev) => {
|
||||
ev.stopPropagation();
|
||||
await this.actor.update({ "system.sla.sessionAbilityUsed": ev.currentTarget.checked });
|
||||
});
|
||||
|
||||
// New Session reset button
|
||||
html.find('.sla-new-session').click(async (ev) => {
|
||||
ev.preventDefault();
|
||||
const confirmed = await Dialog.confirm({
|
||||
title: "New Session",
|
||||
content: "<p>Start a new session? This will reset the trauma response session ability for this operative.</p>",
|
||||
defaultYes: true
|
||||
});
|
||||
if (!confirmed) return;
|
||||
await this.actor.update({ "system.sla.sessionAbilityUsed": false });
|
||||
ui.notifications.info(`${this.actor.name}: session ability reset for new session.`);
|
||||
this.render(false);
|
||||
});
|
||||
|
||||
// Rest button — short or long rest dialog
|
||||
html.find('.sla-rest-button').click(async (ev) => {
|
||||
ev.preventDefault();
|
||||
const restType = await new Promise((resolve) => {
|
||||
new Dialog({
|
||||
title: "Take a Rest",
|
||||
content: `
|
||||
<p>What kind of rest is <strong>${this.actor.name}</strong> taking?</p>
|
||||
<p><strong>Short Rest:</strong> Removes non-permanent conditions. No stat recovery.</p>
|
||||
<p><strong>Long Rest:</strong> Removes non-permanent conditions and fully restores Flux (if Ebb user).</p>
|
||||
`,
|
||||
buttons: {
|
||||
short: { icon: '<i class="fas fa-moon"></i>', label: "Short Rest", callback: () => resolve("short") },
|
||||
long: { icon: '<i class="fas fa-bed"></i>', label: "Long Rest", callback: () => resolve("long") },
|
||||
cancel: { icon: '<i class="fas fa-times"></i>', label: "Cancel", callback: () => resolve(null) }
|
||||
},
|
||||
default: "short"
|
||||
}).render(true);
|
||||
});
|
||||
if (!restType) return;
|
||||
|
||||
const updates = {};
|
||||
// Remove temporary conditions (items of type "condition")
|
||||
const conditionIds = this.actor.items
|
||||
.filter((item) => item.type === "condition")
|
||||
.map((item) => item.id);
|
||||
if (conditionIds.length > 0) {
|
||||
await this.actor.deleteEmbeddedDocuments("Item", conditionIds);
|
||||
}
|
||||
|
||||
if (restType === "long") {
|
||||
// Restore Flux to max for Ebb users
|
||||
const isEbb = this.actor.isSlaEbbUser?.() ?? false;
|
||||
if (isEbb) {
|
||||
const fluxMax = Math.max(0, Number(this.actor.system?.sla?.flux?.max ?? 0));
|
||||
updates["system.sla.flux.value"] = fluxMax;
|
||||
await this.actor.updateSlaFluxState?.(fluxMax, { save: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await this.actor.update(updates);
|
||||
}
|
||||
|
||||
const condMsg = conditionIds.length > 0 ? ` Cleared ${conditionIds.length} condition(s).` : "";
|
||||
const fluxMsg = restType === "long" && (this.actor.isSlaEbbUser?.() ?? false) ? " Flux restored." : "";
|
||||
ui.notifications.info(`${this.actor.name}: ${restType === "long" ? "Long" : "Short"} rest complete.${condMsg}${fluxMsg}`);
|
||||
await this.actor.addSlaActivityLog?.(`${restType === "long" ? "Long" : "Short"} rest.${condMsg}${fluxMsg}`);
|
||||
this.render(false);
|
||||
});
|
||||
|
||||
// Contact add button
|
||||
html.find('.sla-contact-add').click(async (ev) => {
|
||||
ev.preventDefault();
|
||||
await this._slaContactDialog(null);
|
||||
});
|
||||
|
||||
// Contact edit button
|
||||
html.find('.sla-contact-edit').click(async (ev) => {
|
||||
ev.preventDefault();
|
||||
const contactId = String(ev.currentTarget.dataset.contactId ?? "").trim();
|
||||
if (!contactId) return;
|
||||
await this._slaContactDialog(contactId);
|
||||
});
|
||||
|
||||
// Contact delete button
|
||||
html.find('.sla-contact-delete').click(async (ev) => {
|
||||
ev.preventDefault();
|
||||
const contactId = String(ev.currentTarget.dataset.contactId ?? "").trim();
|
||||
if (!contactId) return;
|
||||
const confirmed = await Dialog.confirm({
|
||||
title: "Delete Contact",
|
||||
content: "<p>Remove this contact from the operative's network?</p>",
|
||||
defaultYes: false
|
||||
});
|
||||
if (!confirmed) return;
|
||||
const contacts = Array.isArray(this.actor.system?.sla?.contacts) ? [...this.actor.system.sla.contacts] : [];
|
||||
const updated = contacts.filter((c) => c.id !== contactId);
|
||||
await this.actor.update({ "system.sla.contacts": updated });
|
||||
this.render(false);
|
||||
});
|
||||
|
||||
html.find('.sla-panic-button').click(async ev => {
|
||||
await this.actor.rollTable("panicCheck", null, null, null, null, null, null);
|
||||
await this.actor.addSlaActivityLog?.("Panic check triggered.");
|
||||
});
|
||||
|
||||
html.find('.sla-wound-button').click(ev => {
|
||||
@@ -1041,6 +1228,91 @@ export class MothershipActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
async _onDrop(event) {
|
||||
const data = TextEditor.getDragEventData(event);
|
||||
if (data?.type !== "Item") return super._onDrop(event);
|
||||
|
||||
const droppedItem = await Item.implementation.fromDropData(data).catch(() => null);
|
||||
if (!droppedItem) return super._onDrop(event);
|
||||
|
||||
if (droppedItem.parent?.uuid === this.actor.uuid) {
|
||||
return super._onDrop(event);
|
||||
}
|
||||
|
||||
const shouldDebitPurchase = !droppedItem.parent || droppedItem.parent.documentName !== "Actor";
|
||||
const itemData = droppedItem.toObject();
|
||||
delete itemData._id;
|
||||
delete itemData.folder;
|
||||
const created = await this.actor.createEmbeddedDocuments("Item", [itemData], {
|
||||
slaPurchaseDebit: true,
|
||||
slaPurchaseSource: data.uuid ?? droppedItem.uuid ?? ""
|
||||
});
|
||||
if (shouldDebitPurchase) {
|
||||
await this._applySlaPurchaseDebit(created);
|
||||
}
|
||||
return created;
|
||||
}
|
||||
|
||||
async _applySlaPurchaseDebit(createdDocs = []) {
|
||||
const documents = Array.isArray(createdDocs) ? createdDocs : [createdDocs];
|
||||
const total = Math.round(documents.reduce((sum, doc) => sum + getPurchasableItemTotalCost(doc?.toObject?.() ?? doc), 0) * 100) / 100;
|
||||
if (!total) return;
|
||||
|
||||
const currentCredits = Number(this.actor.system?.credits?.value ?? 0) || 0;
|
||||
const newCredits = Math.round((currentCredits - total) * 100) / 100;
|
||||
const updateData = { "system.credits.value": newCredits };
|
||||
|
||||
const itemNames = documents.map((doc) => doc?.name).filter(Boolean).join(", ");
|
||||
const ammoDocs = documents
|
||||
.map((doc) => doc?.toObject?.() ?? doc)
|
||||
.filter((doc) => isAmmoPurchaseItem(doc));
|
||||
if (ammoDocs.length) {
|
||||
const stamp = formatSlaTimestamp();
|
||||
const currentLedger = String(this.actor.system?.sla?.ammoLedger?.value ?? "").trim();
|
||||
const purchaseEntries = ammoDocs.map((doc) => {
|
||||
const system = doc?.system ?? doc?.data ?? {};
|
||||
const quantity = Math.max(1, Number(system.quantity ?? 1) || 1);
|
||||
const unitCost = Number(system.cost ?? 0) || 0;
|
||||
const lineCost = Math.round(unitCost * quantity * 100) / 100;
|
||||
return `[${stamp}] PURCHASE | ${doc?.name ?? "Ammo"} | ${quantity} round${quantity === 1 ? "" : "s"} | -${lineCost} cR | balance ${newCredits} cR`;
|
||||
});
|
||||
const ledgerLines = [...purchaseEntries, ...currentLedger.split("\n").filter(Boolean)].slice(0, 60);
|
||||
updateData["system.sla.ammoLedger.value"] = ledgerLines.join("\n");
|
||||
}
|
||||
|
||||
await this.actor.update(updateData);
|
||||
|
||||
ui.notifications.info(`${this.actor.name}: deducted ${total} cR for ${itemNames || "purchased item"}. Credits now ${newCredits} cR.`);
|
||||
await this.actor.addSlaActivityLog?.(`Purchased ${itemNames || "item"} for ${total} cR. Credits now ${newCredits} cR.`);
|
||||
}
|
||||
|
||||
async _applySlaQuantityIncreaseDebit(itemDoc, increase = 0) {
|
||||
const added = Math.max(0, Number(increase ?? 0) || 0);
|
||||
if (!added || !itemDoc) return;
|
||||
|
||||
const unitCost = Number(itemDoc.system?.cost ?? 0) || 0;
|
||||
const total = Math.round(unitCost * added * 100) / 100;
|
||||
if (!total) return;
|
||||
|
||||
const currentCredits = Number(this.actor.system?.credits?.value ?? 0) || 0;
|
||||
const newCredits = Math.round((currentCredits - total) * 100) / 100;
|
||||
const updateData = { "system.credits.value": newCredits };
|
||||
|
||||
if (isAmmoPurchaseItem(itemDoc)) {
|
||||
const stamp = formatSlaTimestamp();
|
||||
const currentLedger = String(this.actor.system?.sla?.ammoLedger?.value ?? "").trim();
|
||||
const entry = `[${stamp}] PURCHASE | ${itemDoc.name ?? "Ammo"} | ${added} round${added === 1 ? "" : "s"} | -${total} cR | balance ${newCredits} cR`;
|
||||
const ledgerLines = [entry, ...currentLedger.split("\n").filter(Boolean)].slice(0, 60);
|
||||
updateData["system.sla.ammoLedger.value"] = ledgerLines.join("\n");
|
||||
}
|
||||
|
||||
await this.actor.update(updateData);
|
||||
ui.notifications.info(`${this.actor.name}: deducted ${total} cR for ${added} more ${itemDoc.name ?? "item"}. Credits now ${newCredits} cR.`);
|
||||
await this.actor.addSlaActivityLog?.(`Purchased +${added} ${itemDoc.name ?? "item"} for ${total} cR. Credits now ${newCredits} cR.`);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Handle creating a new Owned Item for the actor using initial data defined in the HTML dataset
|
||||
* @param {Event} event The originating click event
|
||||
@@ -1183,6 +1455,76 @@ export class MothershipActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a dialog for adding or editing a contact
|
||||
* @param {string|null} contactId null = add new, string = edit existing
|
||||
*/
|
||||
async _slaContactDialog(contactId) {
|
||||
const contacts = Array.isArray(this.actor.system?.sla?.contacts) ? [...this.actor.system.sla.contacts] : [];
|
||||
const existing = contactId ? contacts.find((c) => c.id === contactId) : null;
|
||||
const isNew = !existing;
|
||||
|
||||
const name = existing?.name ?? "";
|
||||
const role = existing?.role ?? "";
|
||||
const notes = existing?.notes ?? "";
|
||||
const disposition = existing?.disposition ?? "neutral";
|
||||
|
||||
const content = `
|
||||
<div style="display:grid; gap:8px; padding:8px 0;">
|
||||
<div>
|
||||
<label style="font-size:0.8rem; font-weight:600; display:block; margin-bottom:4px;">Name</label>
|
||||
<input type="text" id="sla-contact-name" value="${name}" placeholder="Contact name" style="width:100%;" />
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size:0.8rem; font-weight:600; display:block; margin-bottom:4px;">Role / Occupation</label>
|
||||
<input type="text" id="sla-contact-role" value="${role}" placeholder="e.g. Fixer, Fence, Squad medic" style="width:100%;" />
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size:0.8rem; font-weight:600; display:block; margin-bottom:4px;">Disposition</label>
|
||||
<select id="sla-contact-disposition" style="width:100%;">
|
||||
<option value="ally" ${disposition === "ally" ? "selected" : ""}>Ally</option>
|
||||
<option value="neutral" ${disposition === "neutral" ? "selected" : ""}>Neutral</option>
|
||||
<option value="hostile" ${disposition === "hostile" ? "selected" : ""}>Hostile</option>
|
||||
<option value="unknown" ${disposition === "unknown" ? "selected" : ""}>Unknown</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size:0.8rem; font-weight:600; display:block; margin-bottom:4px;">Notes</label>
|
||||
<textarea id="sla-contact-notes" rows="3" placeholder="Sector, history, how they know the operative..." style="width:100%;">${notes}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const result = await Dialog.prompt({
|
||||
title: isNew ? "Add Contact" : "Edit Contact",
|
||||
content,
|
||||
label: isNew ? "Add" : "Save",
|
||||
callback: (html) => {
|
||||
const nameVal = String(html.find("#sla-contact-name").val() ?? "").trim();
|
||||
if (!nameVal) return null;
|
||||
return {
|
||||
id: existing?.id ?? foundry.utils.randomID(),
|
||||
name: nameVal,
|
||||
role: String(html.find("#sla-contact-role").val() ?? "").trim(),
|
||||
disposition: String(html.find("#sla-contact-disposition").val() ?? "neutral"),
|
||||
notes: String(html.find("#sla-contact-notes").val() ?? "").trim()
|
||||
};
|
||||
},
|
||||
rejectClose: false
|
||||
});
|
||||
|
||||
if (!result) return;
|
||||
|
||||
let updated;
|
||||
if (isNew) {
|
||||
updated = [...contacts, result];
|
||||
} else {
|
||||
updated = contacts.map((c) => c.id === contactId ? result : c);
|
||||
}
|
||||
await this.actor.update({ "system.sla.contacts": updated });
|
||||
this.render(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend and override the sheet header buttons
|
||||
* @override
|
||||
|
||||
@@ -599,9 +599,123 @@ export class MothershipActor extends Actor {
|
||||
return rollStringParsed;
|
||||
}
|
||||
|
||||
clampSlaRollTarget(rollTarget, rollString = "") {
|
||||
const numericTarget = Number(rollTarget);
|
||||
if (!Number.isFinite(numericTarget)) return rollTarget;
|
||||
const usesD100 = String(rollString ?? "").includes("1d100");
|
||||
if (!usesD100) return numericTarget;
|
||||
return Math.min(98, numericTarget);
|
||||
}
|
||||
|
||||
isSlaAutomaticFailure(total, rollString = "") {
|
||||
const numericTotal = Number(total);
|
||||
if (!Number.isFinite(numericTotal)) return false;
|
||||
const usesD100 = String(rollString ?? "").includes("1d100");
|
||||
return usesD100 && numericTotal === 99;
|
||||
}
|
||||
|
||||
getSlaChatVariant(context = {}) {
|
||||
const actorClass = String(this.system?.class?.value ?? "").trim();
|
||||
const employer = String(this.system?.sla?.employer?.value ?? "").trim();
|
||||
const sector = String(this.system?.sla?.sector?.value ?? "").trim();
|
||||
const skill = String(context?.skill ?? "").trim();
|
||||
const skillCategory = String(context?.skillCategory ?? "").trim();
|
||||
const tableName = String(context?.tableName ?? "").trim();
|
||||
const header = String(context?.msgHeader ?? "").trim();
|
||||
const weaponName = String(context?.weapon?.name ?? "").trim();
|
||||
const itemName = String(context?.item?.name ?? "").trim();
|
||||
const extra = String(context?.extra ?? "").trim();
|
||||
|
||||
const haystack = [
|
||||
actorClass,
|
||||
employer,
|
||||
sector,
|
||||
skill,
|
||||
skillCategory,
|
||||
tableName,
|
||||
header,
|
||||
weaponName,
|
||||
itemName,
|
||||
extra
|
||||
].join(" ").toLowerCase();
|
||||
|
||||
if (context?.isSlaEbbSkill || /\bebb\b|ebon|brain\swaste|flux|telekinesis|blast|awareness/.test(haystack)) {
|
||||
return { key: "ebb", label: "Ebb", emblem: "EBB" };
|
||||
}
|
||||
if (/\bdark\s?night\b/.test(haystack)) {
|
||||
return { key: "darknight", label: "DarkNight", emblem: "DN" };
|
||||
}
|
||||
if (/\bshiver\b/.test(haystack)) {
|
||||
return { key: "shiver", label: "Shiver", emblem: "SV" };
|
||||
}
|
||||
if (/\bmort\b|mortuary|undertaker|corpse|cadaver/.test(haystack)) {
|
||||
return { key: "mort", label: "Mort", emblem: "MT" };
|
||||
}
|
||||
if (/\btek\b|technical|engineer|engineering|medic|medical|science|research/.test(haystack)) {
|
||||
return { key: "tek", label: "Tek", emblem: "TK" };
|
||||
}
|
||||
if (/\bops\b|operations|opscon|ops\scon|operative|squad/.test(haystack)) {
|
||||
return { key: "ops", label: "Ops", emblem: "OP" };
|
||||
}
|
||||
return { key: "sla", label: "SLA", emblem: "SLA" };
|
||||
}
|
||||
|
||||
getSlaSeverityMeta({ severity = null, tableName = "", specialRoll = "", tableResultNumber = null } = {}) {
|
||||
const rawSeverity = Number(severity);
|
||||
if (Number.isFinite(rawSeverity)) {
|
||||
if (rawSeverity >= 5) return { key: "critical", label: "Critical", cssClass: "sla-severity-critical" };
|
||||
if (rawSeverity >= 3) return { key: "severe", label: "Severe", cssClass: "sla-severity-severe" };
|
||||
return { key: "minor", label: "Minor", cssClass: "sla-severity-minor" };
|
||||
}
|
||||
|
||||
const name = String(tableName ?? "").toLowerCase();
|
||||
const special = String(specialRoll ?? "").toLowerCase();
|
||||
const numericResult = Number(tableResultNumber);
|
||||
|
||||
if (!Number.isFinite(numericResult)) {
|
||||
return { key: "minor", label: "Minor", cssClass: "sla-severity-minor" };
|
||||
}
|
||||
|
||||
const isPanic = special === "paniccheck" || name.includes("panic");
|
||||
const isWound = name.includes("wound");
|
||||
|
||||
if (isPanic) {
|
||||
if (numericResult >= 16) return { key: "critical", label: "Catastrophic", cssClass: "sla-severity-critical" };
|
||||
if (numericResult >= 9) return { key: "severe", label: "Severe", cssClass: "sla-severity-severe" };
|
||||
return { key: "minor", label: "Minor", cssClass: "sla-severity-minor" };
|
||||
}
|
||||
|
||||
if (isWound) {
|
||||
if (numericResult >= 8) return { key: "critical", label: "Critical", cssClass: "sla-severity-critical" };
|
||||
if (numericResult >= 4) return { key: "severe", label: "Severe", cssClass: "sla-severity-severe" };
|
||||
return { key: "minor", label: "Minor", cssClass: "sla-severity-minor" };
|
||||
}
|
||||
|
||||
return { key: "minor", label: "Minor", cssClass: "sla-severity-minor" };
|
||||
}
|
||||
|
||||
async addSlaActivityLog(entry, { maxEntries = 40 } = {}) {
|
||||
const text = String(entry ?? "").trim();
|
||||
if (!text || this.type !== "character") return null;
|
||||
|
||||
const timestamp = new Date().toLocaleString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit"
|
||||
});
|
||||
const currentLog = String(this.system?.sla?.activityLog?.value ?? "").trim();
|
||||
const existingLines = currentLog ? currentLog.split("\n").filter(Boolean) : [];
|
||||
const nextLines = [`[${timestamp}] ${text}`, ...existingLines].slice(0, Math.max(1, maxEntries));
|
||||
await this.update({ "system.sla.activityLog.value": nextLines.join("\n") });
|
||||
return nextLines[0];
|
||||
}
|
||||
|
||||
//central roll parsing function | TAKES '1d100',[Foundry roll object],true,true,41,'<' | RETURNS enriched Foundry roll object
|
||||
parseRollResult(rollString, rollResult, zeroBased, checkCrit, rollTarget, comparison, specialRoll) {
|
||||
//init vars
|
||||
rollTarget = this.clampSlaRollTarget(rollTarget, rollString);
|
||||
let doubles = new Set([0, 11, 22, 33, 44, 55, 66, 77, 88, 99]);
|
||||
let enrichedRollResult = rollResult;
|
||||
let rollFormula = enrichedRollResult.formula;
|
||||
@@ -670,29 +784,29 @@ export class MothershipActor extends Actor {
|
||||
if (rollString.includes("[")) {
|
||||
//set whether each die succeeded
|
||||
//die 0
|
||||
if (comparison === '<' && die0value < rollTarget && die0value < 90) {
|
||||
if (comparison === '<' && die0value < rollTarget && !this.isSlaAutomaticFailure(die0value, rollString)) {
|
||||
die0success = true;
|
||||
}
|
||||
if (comparison === '<=' && die0value <= rollTarget && die0value < 90) {
|
||||
if (comparison === '<=' && die0value <= rollTarget && !this.isSlaAutomaticFailure(die0value, rollString)) {
|
||||
die0success = true;
|
||||
}
|
||||
if (comparison === '>' && die0value > rollTarget && die0value < 90) {
|
||||
if (comparison === '>' && die0value > rollTarget && !this.isSlaAutomaticFailure(die0value, rollString)) {
|
||||
die0success = true;
|
||||
}
|
||||
if (comparison === '>=' && die0value >= rollTarget && die0value < 90) {
|
||||
if (comparison === '>=' && die0value >= rollTarget && !this.isSlaAutomaticFailure(die0value, rollString)) {
|
||||
die0success = true;
|
||||
}
|
||||
//die 1
|
||||
if (comparison === '<' && die1value < rollTarget && die1value < 90) {
|
||||
if (comparison === '<' && die1value < rollTarget && !this.isSlaAutomaticFailure(die1value, rollString)) {
|
||||
die1success = true;
|
||||
}
|
||||
if (comparison === '<=' && die1value <= rollTarget && die1value < 90) {
|
||||
if (comparison === '<=' && die1value <= rollTarget && !this.isSlaAutomaticFailure(die1value, rollString)) {
|
||||
die1success = true;
|
||||
}
|
||||
if (comparison === '>' && die1value > rollTarget && die1value < 90) {
|
||||
if (comparison === '>' && die1value > rollTarget && !this.isSlaAutomaticFailure(die1value, rollString)) {
|
||||
die1success = true;
|
||||
}
|
||||
if (comparison === '>=' && die1value >= rollTarget && die1value < 90) {
|
||||
if (comparison === '>=' && die1value >= rollTarget && !this.isSlaAutomaticFailure(die1value, rollString)) {
|
||||
die1success = true;
|
||||
}
|
||||
//set whether each die are a crit
|
||||
@@ -834,8 +948,8 @@ export class MothershipActor extends Actor {
|
||||
//add data point: detect success/failure
|
||||
if (rollTarget || rollTarget === 0) {
|
||||
//check for auto failure
|
||||
if (enrichedRollResult.total >= 90) {
|
||||
//result >= 90 is a failure
|
||||
if (this.isSlaAutomaticFailure(enrichedRollResult.total, rollString)) {
|
||||
// result 99 on d100 is always a failure
|
||||
enrichedRollResult.success = false;
|
||||
} else {
|
||||
//compare values based on compararison setting
|
||||
@@ -910,7 +1024,7 @@ export class MothershipActor extends Actor {
|
||||
compareIcon = '<i class="fas fa-greater-than-equal"></i>';
|
||||
}
|
||||
//prepare formula
|
||||
if (rollTarget) {
|
||||
if (rollTarget || rollTarget === 0) {
|
||||
//show dice against target
|
||||
diceFormula = rollString + ' ' + compareIcon + ' ' + rollTarget;
|
||||
} else {
|
||||
@@ -1335,6 +1449,12 @@ export class MothershipActor extends Actor {
|
||||
}
|
||||
//generate chat message
|
||||
//prepare data
|
||||
const severityMeta = this.getSlaSeverityMeta({
|
||||
tableName,
|
||||
specialRoll,
|
||||
tableResultNumber
|
||||
});
|
||||
|
||||
let messageData = {
|
||||
actor: this,
|
||||
tableResult: tableResult,
|
||||
@@ -1349,7 +1469,14 @@ export class MothershipActor extends Actor {
|
||||
woundText: woundText,
|
||||
secondRoll: secondRoll,
|
||||
parsedRollResult2: parsedRollResult2,
|
||||
specialRoll: specialRoll
|
||||
specialRoll: specialRoll,
|
||||
severityMeta: severityMeta,
|
||||
chatStyle: this.getSlaChatVariant({
|
||||
tableName,
|
||||
msgHeader: tableName,
|
||||
isSlaEbbSkill: false,
|
||||
extra: specialRoll
|
||||
})
|
||||
};
|
||||
//prepare template
|
||||
messageTemplate = 'systems/sla-mothership/templates/chat/rollTable.html';
|
||||
@@ -1368,6 +1495,7 @@ export class MothershipActor extends Actor {
|
||||
}, {
|
||||
keepId: true
|
||||
});
|
||||
await this.addSlaActivityLog?.(`${tableName}: ${parsedRollResult.success ? "success" : "failure"} on ${rollString}${tableResultNumber !== null ? ` -> ${tableResultNumber}` : ""}.`);
|
||||
if (game.modules.get("dice-so-nice") && game.modules.get("dice-so-nice").active) {
|
||||
//log what was done
|
||||
slaDebug(`Rolled on table ID: ${tableId}, with: rollString:${rollString}, aimFor:${aimFor}, zeroBased:${zeroBased}, checkCrit:${checkCrit}, rollAgainst:${rollAgainst}, comparison:${comparison}`);
|
||||
@@ -2472,7 +2600,13 @@ export class MothershipActor extends Actor {
|
||||
targetingResult: targetingResult,
|
||||
needsDesc: needsDesc,
|
||||
woundEffect: woundEffect,
|
||||
specialRoll: specialRoll
|
||||
specialRoll: specialRoll,
|
||||
chatStyle: this.getSlaChatVariant({
|
||||
weapon,
|
||||
msgHeader,
|
||||
isSlaEbbSkill: false,
|
||||
extra: ammoContext?.label ?? ""
|
||||
})
|
||||
};
|
||||
let chatData = {
|
||||
user: game.user.id,
|
||||
@@ -2487,6 +2621,7 @@ export class MothershipActor extends Actor {
|
||||
const content = await foundry.applications.handlebars.renderTemplate(template, messageData);
|
||||
chatData.content = content;
|
||||
await ChatMessage.create(chatData);
|
||||
await this.addSlaActivityLog?.(`${weapon.name}: damage roll resolved.${targetingResult ? ` ${targetingResult.targetName} took ${targetingResult.finalDamage} damage.` : ""}`);
|
||||
//log what was done
|
||||
console.log(`Rolled damage on:${weapon.name}`);
|
||||
//return messageData
|
||||
@@ -2504,6 +2639,13 @@ export class MothershipActor extends Actor {
|
||||
} else {
|
||||
rollTarget = rollTargetOverride;
|
||||
}
|
||||
rollTarget = this.clampSlaRollTarget(rollTarget, rollString);
|
||||
if (effectiveRollBreakdown && (String(rollString ?? "").includes("1d100"))) {
|
||||
effectiveRollBreakdown = {
|
||||
...effectiveRollBreakdown,
|
||||
total: rollTarget
|
||||
};
|
||||
}
|
||||
//roll the dice
|
||||
//parse the roll string
|
||||
let parsedRollString = this.parseRollString(rollString, aimFor);
|
||||
@@ -2666,9 +2808,9 @@ export class MothershipActor extends Actor {
|
||||
}
|
||||
if (ammoUsage) {
|
||||
const roundLabel = ammoUsage.shots === 1 ? 'round' : 'rounds';
|
||||
const ammoPrefix = `<strong>${ammoUsage.label}</strong> fire with <strong>${ammoUsage.ammoLabel}</strong> spent <strong>${ammoUsage.shots}</strong> ${roundLabel}. Magazine <strong>${ammoUsage.loaded}/${weapon.system.shots}</strong>. Reserve <strong>${ammoUsage.reserve}</strong>. Ammo cost <strong>${ammoUsage.totalCost}</strong> cR. ${ammoUsage.ammoRule}`;
|
||||
const ammoPrefix = `<strong>${ammoUsage.label}</strong> fire with <strong>${ammoUsage.ammoLabel}</strong> spent <strong>${ammoUsage.shots}</strong> ${roundLabel}. Magazine <strong>${ammoUsage.loaded}/${weapon.system.shots}</strong>. Reserve <strong>${ammoUsage.reserve}</strong>. Firing spend <strong>${ammoUsage.totalCost}</strong> cR. ${ammoUsage.ammoRule}`;
|
||||
const accountingText = ammoAccounting
|
||||
? ` Credits now <strong>${ammoAccounting.newCredits}</strong> cR. Running ammo spend <strong>${ammoAccounting.spendTotal}</strong> cR.`
|
||||
? ` Credits now <strong>${ammoAccounting.newCredits}</strong> cR. Running firing spend <strong>${ammoAccounting.spendTotal}</strong> cR.`
|
||||
: "";
|
||||
flavorText = flavorText ? `${ammoPrefix}${accountingText}<br>${flavorText}` : `${ammoPrefix}${accountingText}`;
|
||||
}
|
||||
@@ -2958,7 +3100,14 @@ export class MothershipActor extends Actor {
|
||||
targetingResult: targetingResult,
|
||||
firstEdition: game.settings.get('sla-mothership', 'firstEdition'),
|
||||
useCalm: game.settings.get('sla-mothership', 'useCalm'),
|
||||
androidPanic: game.settings.get('sla-mothership', 'androidPanic')
|
||||
androidPanic: game.settings.get('sla-mothership', 'androidPanic'),
|
||||
chatStyle: this.getSlaChatVariant({
|
||||
weapon,
|
||||
skill,
|
||||
skillCategory: String(attackContext?.skillCategory ?? "").trim(),
|
||||
msgHeader,
|
||||
isSlaEbbSkill
|
||||
})
|
||||
};
|
||||
//prepare template
|
||||
messageTemplate = 'systems/sla-mothership/templates/chat/rollCheck.html';
|
||||
@@ -2977,6 +3126,11 @@ export class MothershipActor extends Actor {
|
||||
}, {
|
||||
keepId: true
|
||||
});
|
||||
const skillLabel = String(skill ?? "").trim();
|
||||
const actionLabel = weapon
|
||||
? weapon.name
|
||||
: (skillLabel ? `${attributeLabel} / ${skillLabel}` : attributeLabel);
|
||||
await this.addSlaActivityLog?.(`${actionLabel}: ${parsedRollResult.success ? "success" : "failure"} (${parsedRollResult.total} vs ${rollTarget}).`);
|
||||
//is DSN active?
|
||||
if (game.modules.get("dice-so-nice") && game.modules.get("dice-so-nice").active) {
|
||||
//log what was done
|
||||
@@ -5424,7 +5578,8 @@ export class MothershipActor extends Actor {
|
||||
insert: rollInsert,
|
||||
onlyDesc: true,
|
||||
swapNameDesc,
|
||||
swapName
|
||||
swapName,
|
||||
severityMeta: this.getSlaSeverityMeta({ severity: item?.system?.severity })
|
||||
};
|
||||
|
||||
const chatData = {
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
import { SLADrugSystem } from "../sla-drug-system.js";
|
||||
|
||||
function isAmmoPurchaseItem(itemData = {}) {
|
||||
const system = itemData?.system ?? {};
|
||||
const sla = system?.sla ?? {};
|
||||
const name = String(itemData?.name ?? "").trim().toLowerCase();
|
||||
const category = String(sla.category ?? "").trim().toLowerCase();
|
||||
const calibre = String(sla.calibre ?? "").trim();
|
||||
const ammoTag = String(sla.ammoTag ?? "").trim();
|
||||
return category === "ammunition" || Boolean((calibre && ammoTag) || name.startsWith("ammo:"));
|
||||
}
|
||||
|
||||
function formatSlaTimestamp() {
|
||||
return new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle: "short",
|
||||
timeStyle: "short"
|
||||
}).format(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the basic ItemSheet with some very simple modifications
|
||||
* @extends {ItemSheet}
|
||||
@@ -128,11 +145,38 @@ export class MothershipItemSheet extends foundry.appv1.sheets.ItemSheet {
|
||||
|
||||
async _updateObject(event, formData) {
|
||||
const updateData = foundry.utils.expandObject(formData);
|
||||
const currentQty = Math.max(0, Number(this.item.system?.quantity ?? 0) || 0);
|
||||
if (Object.prototype.hasOwnProperty.call(updateData?.system ?? {}, "weight")) {
|
||||
const parsed = Number(updateData.system.weight ?? 0) || 0;
|
||||
updateData.system.weight = Math.round(parsed * 10) / 10;
|
||||
}
|
||||
await this.item.update(updateData, { diff: false });
|
||||
|
||||
const nextQtyRaw = updateData?.system?.quantity;
|
||||
const nextQty = nextQtyRaw === undefined ? currentQty : Math.max(0, Number(nextQtyRaw ?? 0) || 0);
|
||||
const increase = Math.max(0, nextQty - currentQty);
|
||||
const actor = this.item.parent;
|
||||
if (!increase || actor?.documentName !== "Actor") return;
|
||||
|
||||
const unitCost = Number(updateData?.system?.cost ?? this.item.system?.cost ?? 0) || 0;
|
||||
const total = Math.round(unitCost * increase * 100) / 100;
|
||||
if (!total) return;
|
||||
|
||||
const currentCredits = Number(actor.system?.credits?.value ?? 0) || 0;
|
||||
const newCredits = Math.round((currentCredits - total) * 100) / 100;
|
||||
const updateActorData = { "system.credits.value": newCredits };
|
||||
|
||||
if (isAmmoPurchaseItem(this.item.toObject())) {
|
||||
const stamp = formatSlaTimestamp();
|
||||
const currentLedger = String(actor.system?.sla?.ammoLedger?.value ?? "").trim();
|
||||
const entry = `[${stamp}] PURCHASE | ${this.item.name ?? "Ammo"} | ${increase} round${increase === 1 ? "" : "s"} | -${total} cR | balance ${newCredits} cR`;
|
||||
const ledgerLines = [entry, ...currentLedger.split("\n").filter(Boolean)].slice(0, 60);
|
||||
updateActorData["system.sla.ammoLedger.value"] = ledgerLines.join("\n");
|
||||
}
|
||||
|
||||
await actor.update(updateActorData);
|
||||
ui.notifications.info(`${actor.name}: deducted ${total} cR for ${increase} more ${this.item.name ?? "item"}. Credits now ${newCredits} cR.`);
|
||||
await actor.addSlaActivityLog?.(`Purchased +${increase} ${this.item.name ?? "item"} for ${total} cR. Credits now ${newCredits} cR.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -175,16 +175,37 @@ Hooks.once('init', async function () {
|
||||
|
||||
Hooks.on("renderActorDirectory", (app, html) => {
|
||||
installSlaSidebarButtons(app, html);
|
||||
if (!game.user?.isGM) enforceSlaPlayerDirectoryRestrictions("actors", html);
|
||||
});
|
||||
|
||||
Hooks.on("renderSidebarTab", (app, html) => {
|
||||
const isActorsTab = app?.tabName === "actors" || app?.options?.id === "actors";
|
||||
if (!isActorsTab) return;
|
||||
installSlaSidebarButtons(app, html);
|
||||
if (isActorsTab) installSlaSidebarButtons(app, html);
|
||||
if (!game.user?.isGM) {
|
||||
hideSlaRestrictedSidebarTabs();
|
||||
enforceSlaPlayerSidebarTabRestrictions(app, html);
|
||||
}
|
||||
});
|
||||
|
||||
Hooks.on("renderSceneDirectory", (_app, html) => {
|
||||
if (!game.user?.isGM) enforceSlaPlayerDirectoryRestrictions("scenes", html);
|
||||
});
|
||||
|
||||
Hooks.on("renderJournalDirectory", (_app, html) => {
|
||||
if (!game.user?.isGM) enforceSlaPlayerDirectoryRestrictions("journal", html);
|
||||
});
|
||||
|
||||
Hooks.on("renderRollTableDirectory", (_app, html) => {
|
||||
if (!game.user?.isGM) enforceSlaPlayerDirectoryRestrictions("tables", html);
|
||||
});
|
||||
|
||||
Hooks.on("renderCompendiumDirectory", (_app, html) => {
|
||||
if (!game.user?.isGM) enforceSlaPlayerDirectoryRestrictions("compendium", html);
|
||||
});
|
||||
|
||||
Hooks.on("renderCombatTracker", (app, html) => {
|
||||
installSlaCombatTrackerControls(app, html);
|
||||
if (!game.user?.isGM) enforceSlaPlayerDirectoryRestrictions("combat", html);
|
||||
});
|
||||
|
||||
Hooks.on("updateCombat", async (combat, changed) => {
|
||||
@@ -247,6 +268,7 @@ Hooks.once("ready", async function () {
|
||||
}
|
||||
|
||||
patchSlaInitiativeSystem();
|
||||
if (!game.user?.isGM) hideSlaRestrictedSidebarTabs();
|
||||
|
||||
});
|
||||
|
||||
@@ -1249,3 +1271,366 @@ export function formatCreditsNumber(num) {
|
||||
return num.toString() + 'cr';
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// SLA Industries Pause Screen Animation
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
let slaPauseRefreshHandle = null;
|
||||
|
||||
function injectSlaPauseAnimation() {
|
||||
document.body?.classList.add("sla-pause-active");
|
||||
if (document.querySelector('.sla-pause-root')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const svgHTML = `
|
||||
<div class="sla-pause-root" aria-label="SLA Industries – Game Paused">
|
||||
<div class="sla-pause-stage">
|
||||
<svg class="sla-pause-svg" viewBox="-120 -120 240 260" xmlns="http://www.w3.org/2000/svg" role="img">
|
||||
<defs>
|
||||
<filter id="sla-glow-red" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="3.5" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<filter id="sla-glow-ring" x="-30%" y="-30%" width="160%" height="160%">
|
||||
<feGaussianBlur stdDeviation="1.5" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<radialGradient id="sla-bg-grad" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#1a0000" stop-opacity="0.95"/>
|
||||
<stop offset="100%" stop-color="#000000" stop-opacity="0.0"/>
|
||||
</radialGradient>
|
||||
<clipPath id="sla-clip-outer">
|
||||
<circle cx="0" cy="0" r="108"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<circle cx="0" cy="0" r="108" fill="url(#sla-bg-grad)" class="sla-pause-core-glow"/>
|
||||
<g class="sla-ring-outer">
|
||||
<animateTransform attributeName="transform" type="rotate" from="0 0 0" to="360 0 0" dur="18s" repeatCount="indefinite"/>
|
||||
<circle cx="0" cy="0" r="90"
|
||||
fill="none"
|
||||
stroke="#8b1a1a"
|
||||
stroke-width="0.8"
|
||||
stroke-dasharray="6 4"
|
||||
opacity="0.7"
|
||||
filter="url(#sla-glow-ring)"
|
||||
/>
|
||||
<circle cx="0" cy="-90" r="5" fill="#cc2222" filter="url(#sla-glow-red)" class="sla-sat"/>
|
||||
<circle cx="77.9" cy="45" r="5" fill="#cc2222" filter="url(#sla-glow-red)" class="sla-sat"/>
|
||||
<circle cx="-77.9" cy="45" r="5" fill="#cc2222" filter="url(#sla-glow-red)" class="sla-sat"/>
|
||||
<line x1="0" y1="-86" x2="0" y2="-94" stroke="#8b1a1a" stroke-width="1" transform="rotate(0)"/>
|
||||
<line x1="0" y1="-86" x2="0" y2="-94" stroke="#8b1a1a" stroke-width="1" transform="rotate(30)"/>
|
||||
<line x1="0" y1="-86" x2="0" y2="-94" stroke="#8b1a1a" stroke-width="1" transform="rotate(60)"/>
|
||||
<line x1="0" y1="-86" x2="0" y2="-94" stroke="#8b1a1a" stroke-width="1" transform="rotate(90)"/>
|
||||
<line x1="0" y1="-86" x2="0" y2="-94" stroke="#8b1a1a" stroke-width="1" transform="rotate(120)"/>
|
||||
<line x1="0" y1="-86" x2="0" y2="-94" stroke="#8b1a1a" stroke-width="1" transform="rotate(150)"/>
|
||||
<line x1="0" y1="-86" x2="0" y2="-94" stroke="#8b1a1a" stroke-width="1" transform="rotate(180)"/>
|
||||
<line x1="0" y1="-86" x2="0" y2="-94" stroke="#8b1a1a" stroke-width="1" transform="rotate(210)"/>
|
||||
<line x1="0" y1="-86" x2="0" y2="-94" stroke="#8b1a1a" stroke-width="1" transform="rotate(240)"/>
|
||||
<line x1="0" y1="-86" x2="0" y2="-94" stroke="#8b1a1a" stroke-width="1" transform="rotate(270)"/>
|
||||
<line x1="0" y1="-86" x2="0" y2="-94" stroke="#8b1a1a" stroke-width="1" transform="rotate(300)"/>
|
||||
<line x1="0" y1="-86" x2="0" y2="-94" stroke="#8b1a1a" stroke-width="1" transform="rotate(330)"/>
|
||||
</g>
|
||||
<g class="sla-ring-inner">
|
||||
<animateTransform attributeName="transform" type="rotate" from="360 0 0" to="0 0 0" dur="13s" repeatCount="indefinite"/>
|
||||
<circle cx="0" cy="0" r="62"
|
||||
fill="none"
|
||||
stroke="#6b1010"
|
||||
stroke-width="0.6"
|
||||
stroke-dasharray="4 6"
|
||||
opacity="0.6"
|
||||
filter="url(#sla-glow-ring)"
|
||||
/>
|
||||
<circle cx="53.7" cy="-31" r="3.5" fill="#dd4422" filter="url(#sla-glow-red)" class="sla-sat-inner"/>
|
||||
<circle cx="-53.7" cy="-31" r="3.5" fill="#dd4422" filter="url(#sla-glow-red)" class="sla-sat-inner"/>
|
||||
<circle cx="0" cy="62" r="3.5" fill="#dd4422" filter="url(#sla-glow-red)" class="sla-sat-inner"/>
|
||||
</g>
|
||||
<g filter="url(#sla-glow-red)" class="sla-pause-logo-core">
|
||||
<polygon
|
||||
points="0,-38 33,-19 33,19 0,38 -33,19 -33,-19"
|
||||
fill="#1a0000"
|
||||
stroke="#cc2222"
|
||||
stroke-width="1.2"
|
||||
opacity="0.9"
|
||||
/>
|
||||
<text
|
||||
x="0" y="14"
|
||||
text-anchor="middle"
|
||||
font-family="'Oswald', 'Impact', 'Arial Narrow', sans-serif"
|
||||
font-size="38"
|
||||
font-weight="700"
|
||||
letter-spacing="3"
|
||||
fill="#ff2222"
|
||||
class="sla-pause-logo-text"
|
||||
>SLA</text>
|
||||
</g>
|
||||
<line x1="-70" y1="48" x2="70" y2="48" stroke="#8b1a1a" stroke-width="0.5" opacity="0.7"/>
|
||||
<text
|
||||
x="0" y="68"
|
||||
text-anchor="middle"
|
||||
font-family="'Oswald', 'Courier New', monospace"
|
||||
font-size="11.5"
|
||||
font-weight="500"
|
||||
letter-spacing="3.2"
|
||||
fill="#ff7b7b"
|
||||
class="sla-pause-standby"
|
||||
>PLEASE STAND BY</text>
|
||||
<text
|
||||
x="0" y="94"
|
||||
text-anchor="middle"
|
||||
font-family="'Oswald', 'Arial Narrow', sans-serif"
|
||||
font-size="8"
|
||||
font-weight="300"
|
||||
letter-spacing="3"
|
||||
fill="#b13b3b"
|
||||
opacity="0.92"
|
||||
>SLA INDUSTRIES</text>
|
||||
<line
|
||||
x1="0" y1="-90" x2="0" y2="0"
|
||||
stroke="#ff2222"
|
||||
stroke-width="0.8"
|
||||
opacity="0.18"
|
||||
class="sla-pause-scan"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>`;
|
||||
document.body?.insertAdjacentHTML('beforeend', svgHTML);
|
||||
}
|
||||
|
||||
function removeSlaPauseAnimation() {
|
||||
document.querySelector(".sla-pause-root")?.remove();
|
||||
document.body?.classList.remove("sla-pause-active");
|
||||
}
|
||||
|
||||
function queueSlaPauseRefresh(delay = 0) {
|
||||
window.clearTimeout(slaPauseRefreshHandle);
|
||||
slaPauseRefreshHandle = window.setTimeout(() => {
|
||||
if (!game?.paused) return;
|
||||
injectSlaPauseAnimation();
|
||||
}, delay);
|
||||
}
|
||||
|
||||
Hooks.once('ready', () => {
|
||||
if (game.paused) {
|
||||
queueSlaPauseRefresh(0);
|
||||
queueSlaPauseRefresh(60);
|
||||
queueSlaPauseRefresh(180);
|
||||
}
|
||||
});
|
||||
|
||||
Hooks.on('pauseGame', (paused) => {
|
||||
if (paused) {
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => injectSlaPauseAnimation()));
|
||||
queueSlaPauseRefresh(0);
|
||||
queueSlaPauseRefresh(60);
|
||||
queueSlaPauseRefresh(180);
|
||||
queueSlaPauseRefresh(360);
|
||||
} else {
|
||||
removeSlaPauseAnimation();
|
||||
}
|
||||
});
|
||||
|
||||
function getSlaHtmlRoot(html) {
|
||||
return html instanceof HTMLElement ? html : html?.[0] ?? null;
|
||||
}
|
||||
|
||||
function hideSlaRestrictedSidebarTabs() {
|
||||
[
|
||||
"#sidebar-tabs [data-tab='tables']",
|
||||
"#sidebar-tabs [aria-controls='tables']",
|
||||
"#sidebar-tabs [data-tab='compendium']",
|
||||
"#sidebar-tabs [aria-controls='compendium']"
|
||||
].forEach((selector) => {
|
||||
document.querySelectorAll(selector).forEach((el) => {
|
||||
el.closest("li")?.remove();
|
||||
if (el instanceof HTMLElement) el.remove();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function removeSlaElements(root, selectors = []) {
|
||||
selectors.forEach((selector) => {
|
||||
root.querySelectorAll(selector).forEach((el) => el.remove());
|
||||
});
|
||||
}
|
||||
|
||||
function pruneEmptySlaFolders(root) {
|
||||
const folders = Array.from(root.querySelectorAll(".directory-item.folder"));
|
||||
folders.reverse().forEach((folder) => {
|
||||
const visibleDocuments = Array.from(folder.querySelectorAll(".directory-item.document, li.document")).filter((node) => node.offsetParent !== null);
|
||||
const visibleFolders = Array.from(folder.querySelectorAll(":scope > .directory-list > .directory-item.folder, :scope > ol > li.folder")).filter((node) => node.offsetParent !== null);
|
||||
if (!visibleDocuments.length && !visibleFolders.length) folder.remove();
|
||||
});
|
||||
}
|
||||
|
||||
function shouldShowSlaPlayerJournal(entry) {
|
||||
if (!entry) return false;
|
||||
const name = String(entry.name ?? "").trim();
|
||||
if (/^operative handbook$/i.test(name)) return true;
|
||||
if (entry.getFlag?.("sla-mothership", "playerVisible") === true) return true;
|
||||
return entry.testUserPermission?.(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) ?? false;
|
||||
}
|
||||
|
||||
function filterSlaJournalDirectory(root) {
|
||||
const rows = Array.from(root.querySelectorAll("[data-entry-id], [data-document-id]"));
|
||||
rows.forEach((row) => {
|
||||
const id = row.dataset.entryId ?? row.dataset.documentId;
|
||||
if (!id) return;
|
||||
const entry = game.journal?.get?.(id);
|
||||
if (!shouldShowSlaPlayerJournal(entry)) row.remove();
|
||||
});
|
||||
pruneEmptySlaFolders(root);
|
||||
}
|
||||
|
||||
function enforceSlaPlayerDirectoryRestrictions(kind, html) {
|
||||
const root = getSlaHtmlRoot(html);
|
||||
if (!root || game.user?.isGM) return;
|
||||
|
||||
removeSlaElements(root, [
|
||||
".directory-header .header-actions",
|
||||
".directory-header .action-buttons",
|
||||
".sla-sidebar-actions",
|
||||
".qbm-scene-directory-actions",
|
||||
".qbm-launch",
|
||||
".sla-world-tools-launch",
|
||||
".sla-create-operative",
|
||||
".sla-create-random-operative",
|
||||
".sla-refresh-content",
|
||||
".sla-gear-cache-open",
|
||||
".sla-briefing-desk-open",
|
||||
".sla-bpn-dispatch-open",
|
||||
".sla-npc-director-open",
|
||||
".sla-npc-director-actor-open",
|
||||
".sla-ops-clock-open",
|
||||
".npp-generate-sidebar-btn"
|
||||
]);
|
||||
|
||||
if (kind === "journal") {
|
||||
filterSlaJournalDirectory(root);
|
||||
}
|
||||
|
||||
if (kind === "tables" || kind === "compendium") {
|
||||
root.innerHTML = "";
|
||||
}
|
||||
}
|
||||
|
||||
function enforceSlaPlayerSidebarTabRestrictions(app, html) {
|
||||
const tabName = app?.tabName ?? app?.options?.id ?? "";
|
||||
if (tabName === "actors") return enforceSlaPlayerDirectoryRestrictions("actors", html);
|
||||
if (tabName === "scenes") return enforceSlaPlayerDirectoryRestrictions("scenes", html);
|
||||
if (tabName === "journal") return enforceSlaPlayerDirectoryRestrictions("journal", html);
|
||||
if (tabName === "tables") return enforceSlaPlayerDirectoryRestrictions("tables", html);
|
||||
if (tabName === "compendium") return enforceSlaPlayerDirectoryRestrictions("compendium", html);
|
||||
if (tabName === "combat") return enforceSlaPlayerDirectoryRestrictions("combat", html);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// SLA Industries — UI Icon Injection
|
||||
// Replaces Foundry's Font Awesome icons in the sidebar and scene controls
|
||||
// by walking the live DOM and applying inline CSS mask-image styles.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
const SLA_ICON_BASE = "systems/sla-mothership/icons/ui/";
|
||||
|
||||
const SLA_SIDEBAR_ICONS = {
|
||||
chat: "chat",
|
||||
combat: "combat",
|
||||
scenes: "scenes",
|
||||
actors: "actors",
|
||||
items: "items",
|
||||
journal: "journal",
|
||||
tables: "tables",
|
||||
playlists: "playlists",
|
||||
compendium: "compendium",
|
||||
settings: "settings",
|
||||
};
|
||||
|
||||
const SLA_CONTROL_ICONS = {
|
||||
token: "token",
|
||||
measure: "measure",
|
||||
tiles: "tiles",
|
||||
drawings: "drawings",
|
||||
walls: "walls",
|
||||
lighting: "lighting",
|
||||
sounds: "sounds",
|
||||
notes: "notes",
|
||||
};
|
||||
|
||||
function _slaApplyIconMask(el, iconFile) {
|
||||
const url = `${SLA_ICON_BASE}${iconFile}.svg`;
|
||||
// Measure font size BEFORE zeroing it — FA icons scale with font-size
|
||||
const fs = parseFloat(getComputedStyle(el).fontSize) || 18;
|
||||
const sz = Math.round(fs * 1.15) + "px";
|
||||
|
||||
// font-size:0 → collapses the ::before FA glyph to nothing
|
||||
// currentColor → inherits the button's text colour (white/grey/active from Foundry)
|
||||
// mask → cuts that fill into our SVG shape
|
||||
el.style.setProperty("font-size", "0", "important");
|
||||
el.style.setProperty("line-height", "0", "important");
|
||||
el.style.setProperty("width", sz, "important");
|
||||
el.style.setProperty("height", sz, "important");
|
||||
el.style.setProperty("display", "inline-flex", "important");
|
||||
el.style.setProperty("align-items", "center", "important");
|
||||
el.style.setProperty("justify-content", "center", "important");
|
||||
el.style.setProperty("background-color", "currentColor", "important");
|
||||
el.style.setProperty("-webkit-mask", `url('${url}') center/contain no-repeat`, "important");
|
||||
el.style.setProperty("mask", `url('${url}') center/contain no-repeat`, "important");
|
||||
el.setAttribute("data-sla-icon", iconFile);
|
||||
}
|
||||
|
||||
function injectSlaIcons() {
|
||||
let found = 0;
|
||||
|
||||
// ── Sidebar tab icons ─────────────────────────────────────────────────
|
||||
// Foundry v13 confirmed structure:
|
||||
// <button class="ui-control plain icon fa-solid fa-comments" data-tab="chat" ...>
|
||||
// The BUTTON itself is the icon — no child <i> element.
|
||||
document.querySelectorAll(
|
||||
"#sidebar-tabs button[data-tab], #sidebar-tabs [data-tab].icon"
|
||||
).forEach(btn => {
|
||||
const name = btn.dataset.tab;
|
||||
const file = SLA_SIDEBAR_ICONS[name];
|
||||
if (!file || btn.hasAttribute("data-sla-icon")) return;
|
||||
_slaApplyIconMask(btn, file);
|
||||
found++;
|
||||
});
|
||||
|
||||
// ── Scene / layer control icons ───────────────────────────────────────
|
||||
// Foundry v13 scene controls are also buttons — search the whole doc
|
||||
// since we don't yet know the container ID.
|
||||
document.querySelectorAll(
|
||||
"button[data-control], [data-control].icon, [data-control].ui-control"
|
||||
).forEach(btn => {
|
||||
const name = btn.dataset.control;
|
||||
const file = SLA_CONTROL_ICONS[name];
|
||||
if (!file || btn.hasAttribute("data-sla-icon")) return;
|
||||
_slaApplyIconMask(btn, file);
|
||||
found++;
|
||||
});
|
||||
|
||||
if (found > 0) {
|
||||
console.log(`SLA Industries | Applied ${found} custom icons.`);
|
||||
} else {
|
||||
console.warn("SLA Industries | injectSlaIcons: no icons matched — dumping HTML:");
|
||||
console.warn("sidebar:", document.querySelector("#sidebar-tabs")?.outerHTML?.slice(0, 1200));
|
||||
const ctrlDump = document.querySelector("#ui-left, #scene-controls, #controls");
|
||||
console.warn("controls:", ctrlDump?.outerHTML?.slice(0, 1200) ?? "(not found — try: " + [...document.querySelectorAll("[data-control]")].map(e=>e.tagName+'.'+e.className).join(', ') + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// Run once on ready, then re-run whenever Foundry re-renders these components
|
||||
Hooks.once("ready", () => {
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => injectSlaIcons()));
|
||||
});
|
||||
Hooks.on("renderSidebar", () => requestAnimationFrame(() => injectSlaIcons()));
|
||||
Hooks.on("renderSidebarTab", () => requestAnimationFrame(() => injectSlaIcons()));
|
||||
Hooks.on("changeSidebarTab", () => requestAnimationFrame(() => injectSlaIcons()));
|
||||
Hooks.on("renderSceneControls", () => requestAnimationFrame(() => injectSlaIcons()));
|
||||
|
||||
@@ -487,6 +487,23 @@ export class SLADrugSystem {
|
||||
|
||||
await this.resolveExpiredEffects(actor);
|
||||
|
||||
// Drug stacking warning — check if any OTHER drug is already active
|
||||
const allActiveDrugEffects = this.getActorDrugEffects(actor);
|
||||
const otherActiveDrugs = allActiveDrugEffects.filter((effect) => {
|
||||
const flag = this.getDrugEffectFlag(effect);
|
||||
return flag?.stage === "active" && flag?.drugId && flag.drugId !== def.id;
|
||||
});
|
||||
if (otherActiveDrugs.length > 0) {
|
||||
const activeNames = [...new Set(otherActiveDrugs.map((e) => this.getDrugEffectFlag(e)?.drugName ?? e.name))].join(", ");
|
||||
const proceed = await Dialog.confirm({
|
||||
title: "Drug Stacking Warning",
|
||||
content: `<p><strong>${actor.name}</strong> already has active drug effects from: <strong>${activeNames}</strong>.</p>
|
||||
<p>Combining drugs can cause unpredictable or lethal interactions. Do you want to proceed?</p>`,
|
||||
defaultYes: false
|
||||
});
|
||||
if (!proceed) return { ok: false, reason: "stacking-cancelled" };
|
||||
}
|
||||
|
||||
const quantity = Math.max(0, Number(item.system?.quantity ?? 0));
|
||||
if (consume && quantity < 1) {
|
||||
ui.notifications.warn(`${actor.name} has no ${def.name} doses remaining.`);
|
||||
|
||||
@@ -8,7 +8,9 @@ export const SLA_SPECIES_STAT_ADJUSTMENTS = {
|
||||
"Wraith Raider": { sanity: 15 },
|
||||
Shaktar: { body: 10, fear: 20 },
|
||||
"Stormer 313 Malice": { fear: 25 },
|
||||
"Stormer 711 Xeno": { fear: 25 }
|
||||
"Stormer 711 Xeno": { fear: 25 },
|
||||
// Vevaphon: strong, fast, resilient biogenetic chassis — psychologically shattered
|
||||
"Stormer Vevaphon": { strength: 10, combat: 10, body: 15, speed: 5, sanity: -10 }
|
||||
};
|
||||
|
||||
export function getSlaSpeciesStatAdjustments(speciesName = "") {
|
||||
|
||||
@@ -69,6 +69,10 @@ const SPECIES_DOSSIER = {
|
||||
"Advanced Carrien": {
|
||||
identity: "Cunning, dangerous mutants with strong survival instincts and an uneasy fit inside official structures.",
|
||||
fieldPressure: "They thrive in broken districts and dirty jobs, but prejudice follows them into every briefing room."
|
||||
},
|
||||
"Stormer Vevaphon": {
|
||||
identity: "Sleight Industries' cancelled polymorphic Stormer line: a shape-shifting bioweapon that can be anything the mission needs — and that is precisely the problem.",
|
||||
fieldPressure: "They are terrifying in infiltration, ambush, and close quarters, but every form change costs them a piece of who they are. Long ops risk unravelling them entirely."
|
||||
}
|
||||
};
|
||||
|
||||
@@ -76,7 +80,8 @@ const SPECIES_MIN_WOUNDS = {
|
||||
Frother: 3,
|
||||
Shaktar: 3,
|
||||
"Stormer 313 Malice": 3,
|
||||
"Stormer 711 Xeno": 3
|
||||
"Stormer 711 Xeno": 3,
|
||||
"Stormer Vevaphon": 3
|
||||
};
|
||||
|
||||
const SKILL_IMAGE_OVERRIDES = {
|
||||
@@ -201,6 +206,19 @@ const SPECIES_MOTHERSHIP_PROFILES = {
|
||||
health: "1d10+11"
|
||||
},
|
||||
description: "Clever survivor profile. Strong Intellect and workable Strength make Carrien dangerous problem-solvers with uneven social acceptance."
|
||||
},
|
||||
"Stormer Vevaphon": {
|
||||
rolls: {
|
||||
strength: "2d10+35",
|
||||
speed: "2d10+28",
|
||||
intellect: "2d10+20",
|
||||
combat: "2d10+35",
|
||||
sanity: "2d10+12",
|
||||
fear: "2d10+23",
|
||||
body: "2d10+22",
|
||||
health: "1d10+12"
|
||||
},
|
||||
description: "Polymorphic bioweapon with excellent combat and physical resilience, above-average speed, and severely degraded Sanity reflecting continuous identity diffusion from shapeshifting."
|
||||
}
|
||||
};
|
||||
|
||||
@@ -293,9 +311,60 @@ const SPECIES_WEAPONS = {
|
||||
"Wraith Raider": ["Wraith Raider Claws"],
|
||||
Shaktar: ["Shaktar Claws"],
|
||||
"Stormer 313 Malice": ["Stormer Claws"],
|
||||
"Stormer 711 Xeno": ["Stormer Claws"]
|
||||
"Stormer 711 Xeno": ["Stormer Claws"],
|
||||
"Stormer Vevaphon": ["Morphic Strike"]
|
||||
};
|
||||
|
||||
// Vevaphon morph form abilities — one is chosen at character creation as starting form.
|
||||
// Each form grants stat bonuses and a special ability but costs 1 Instability when activated.
|
||||
const VEVAPHON_MORPH_STARTERS = [
|
||||
{
|
||||
name: "Brute Form",
|
||||
summary: "A hulking, armoured chassis. STR+10, BODY+10. Immune to Knockdown. Cannot be concealed.",
|
||||
statMods: { strength: 10, body: 10 },
|
||||
special: "Knockdown Immunity",
|
||||
instabilityCost: 1,
|
||||
description: "The Vevaphon expands its frame into a dense combat shell. Slabs of biogenetic armour plate reinforce the torso and limbs. Effective in close-quarters breach work. Every activation costs 1 Instability."
|
||||
},
|
||||
{
|
||||
name: "Stalker Form",
|
||||
summary: "A lean, predatory chassis built for silence. SPD+10, Stealth+20. Cannot wear armour.",
|
||||
statMods: { speed: 10 },
|
||||
skillMods: { Stealth: 20 },
|
||||
special: "Armour Incompatible",
|
||||
instabilityCost: 1,
|
||||
description: "The Vevaphon elongates and flattens, redistributing mass for near-silent movement. Standard armour cannot bond to the shifting surface. Ideal for covert approach and ambush. Every activation costs 1 Instability."
|
||||
},
|
||||
{
|
||||
name: "Raptor Form",
|
||||
summary: "A fast striking chassis built around offensive output. COM+10, SPD+5. Attacks score one extra wound on a critical.",
|
||||
statMods: { combat: 10, speed: 5 },
|
||||
special: "Critical Wound Bonus",
|
||||
instabilityCost: 1,
|
||||
description: "The Vevaphon reshapes its strike limbs into natural weapons optimised for penetrating force. Designed for rapid target elimination at close range. Every activation costs 1 Instability."
|
||||
}
|
||||
];
|
||||
|
||||
// Instability minor effects (triggered at 6+ Instability, rolled each scene)
|
||||
const VEVAPHON_INSTABILITY_MINOR = [
|
||||
"Involuntary surface texturing — skin shifts pattern mid-conversation. Social rolls at -10.",
|
||||
"Limb proportions drift. Fine motor tasks at -10 until end of scene.",
|
||||
"Voice shifts register unexpectedly. Communications may be misidentified.",
|
||||
"Brief facial blurring. Anyone attempting to describe the Vevaphon gives contradictory reports.",
|
||||
"Pain from realignment. Take 1 point of Stress.",
|
||||
"Form flickers. One randomly selected stat drops by 5 until the Vevaphon rests."
|
||||
];
|
||||
|
||||
// Instability Morph Panic table (triggered at 10+ Instability on a failed SANITY save)
|
||||
const VEVAPHON_INSTABILITY_TABLE = [
|
||||
{ roll: 1, result: "Cascade Shift", effect: "Vevaphon immediately shifts to a random morph form, even mid-combat. Costs 1 additional Instability." },
|
||||
{ roll: 2, result: "Identity Bleed", effect: "Forgets assigned name and call sign until end of scene. Acts on instinct — GM adjudicates behaviour." },
|
||||
{ roll: 3, result: "Partial Lock", effect: "One limb freezes in incorrect form. -10 to all physical rolls using that limb." },
|
||||
{ roll: 4, result: "Tissue Rejection", effect: "Takes 1d5 damage as the body fights itself. Armour does not apply." },
|
||||
{ roll: 5, result: "Threat Imprint", effect: "Becomes locked on nearest visible target. Must make a Sanity save to perform any action not directed at that target." },
|
||||
{ roll: 6, result: "Full Reversion", effect: "Collapses to base form, losing all morph bonuses. Spends next action recovering. Lose 1 Instability." }
|
||||
];
|
||||
|
||||
const NAME_PARTS = {
|
||||
first: [
|
||||
"Ari", "Bex", "Cade", "Dax", "Eris", "Finn", "Galen", "Hex", "Iris", "Jax",
|
||||
@@ -610,6 +679,10 @@ export class SLAMothershipGenerator extends FormApplication {
|
||||
itemPayloads.push(...this.buildEbbItems(species.name));
|
||||
}
|
||||
|
||||
if (species.name === "Stormer Vevaphon") {
|
||||
itemPayloads.push(...this.buildVevaphonItems(derived.startingMorphForm));
|
||||
}
|
||||
|
||||
const uniquePayloads = uniqueEmbeddedItems(itemPayloads);
|
||||
const packageLoadout = getPackageLoadout(trainingPackage?.name ?? "", data.equipment.packageLoadouts ?? {});
|
||||
const packageIssueKit = getPackageIssueKit(trainingPackage?.name ?? "");
|
||||
@@ -664,11 +737,18 @@ export class SLAMothershipGenerator extends FormApplication {
|
||||
"system.sla.ebbRating.value": derived.ebbRating,
|
||||
"system.other.stressdesc.value": buildTraumaResponseText(species, trainingPackage),
|
||||
"system.sla.fluxNotes.value": this.isEbbSpecies(species.name)
|
||||
? `Starting Flux reserve: ${derived.fluxMax}. Ebb users begin with at least 20 Flux and spend from that reserve when channeling. Fear is used as the control save.`
|
||||
? `Starting Flux reserve: ${derived.fluxMax} (rolled 20 + 1d10). Flux is randomised at creation (range 21–31). Spend from this reserve when channeling; Fear is the control save.`
|
||||
: "",
|
||||
"system.sla.ebbNotes.value": this.isEbbSpecies(species.name)
|
||||
? `Starter discipline package: ${(EBB_STARTERS[species.name] ?? []).join(", ")}`
|
||||
: "",
|
||||
...(species.name === "Stormer Vevaphon" ? {
|
||||
"system.sla.morphForm.value": derived.startingMorphForm ?? "Brute Form",
|
||||
"system.sla.vevaphonInstability.value": 0,
|
||||
"system.sla.vevaphonInstability.max": 12,
|
||||
"system.sla.vevaphonInstability.label": "Instability",
|
||||
"system.sla.morphNotes.value": buildVevaphonMorphNotes(derived.startingMorphForm)
|
||||
} : {}),
|
||||
"system.biography": description,
|
||||
"system.notes": buildNotes(species, trainingPackage, packageLoadout, packageIssueKit)
|
||||
};
|
||||
@@ -815,6 +895,40 @@ export class SLAMothershipGenerator extends FormApplication {
|
||||
.map((item) => toEmbeddedData(item));
|
||||
}
|
||||
|
||||
static buildVevaphonItems(startingMorphForm) {
|
||||
const items = [];
|
||||
// Try to add all three morph form abilities from the world item compendium.
|
||||
// They may or may not exist as world items — we fall back to building minimal
|
||||
// ability documents directly if not found.
|
||||
for (const morph of VEVAPHON_MORPH_STARTERS) {
|
||||
const worldItem = findWorldItem(morph.name, "ability");
|
||||
if (worldItem) {
|
||||
const embedded = toEmbeddedData(worldItem);
|
||||
items.push(embedded);
|
||||
} else {
|
||||
// Build a minimal ability document so the character isn't empty-handed.
|
||||
items.push({
|
||||
name: morph.name,
|
||||
type: "ability",
|
||||
img: "icons/svg/upgrade.svg",
|
||||
system: {
|
||||
description: morph.description,
|
||||
summary: morph.summary,
|
||||
special: morph.special,
|
||||
sla: {
|
||||
morphForm: true,
|
||||
instabilityCost: morph.instabilityCost,
|
||||
statMods: morph.statMods ?? {},
|
||||
skillMods: morph.skillMods ?? {},
|
||||
isStartingForm: morph.name === startingMorphForm
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
static isEbbSpecies(speciesName) {
|
||||
return ["Ebon", "Brain Waster"].includes(String(speciesName ?? ""));
|
||||
}
|
||||
@@ -952,7 +1066,16 @@ function deriveProfile(rolls, packageName, speciesName) {
|
||||
|
||||
if (["Ebon", "Brain Waster"].includes(speciesName)) {
|
||||
profile.ebbRating = clamp(Math.ceil(profile.intellect / 20) + Number(mods.ebbRating ?? 0), 1, 8);
|
||||
profile.fluxMax = clamp(Math.max(20, Math.floor(profile.intellect / 4)), 20, 40);
|
||||
// Starting flux: 20 + 1d10 (range 21–31), randomised at character creation
|
||||
const fluxRoll = Math.floor(Math.random() * 11) + 1; // 1–11 → simulates d10 with extended top end
|
||||
profile.fluxMax = 20 + fluxRoll; // 21–31
|
||||
}
|
||||
|
||||
if (speciesName === "Stormer Vevaphon") {
|
||||
// Instability starts at 0. Pick a random starting morph form.
|
||||
profile.morphInstability = 0;
|
||||
const morphIndex = Math.floor(Math.random() * VEVAPHON_MORPH_STARTERS.length);
|
||||
profile.startingMorphForm = VEVAPHON_MORPH_STARTERS[morphIndex].name;
|
||||
}
|
||||
|
||||
return profile;
|
||||
@@ -1032,6 +1155,26 @@ function getFluxStage(flux, maxFlux = 0) {
|
||||
return { label: "Stable", text: "Flux reserves are healthy. Ebb use remains disciplined and contained." };
|
||||
}
|
||||
|
||||
function buildVevaphonMorphNotes(startingMorphForm) {
|
||||
const form = VEVAPHON_MORPH_STARTERS.find((entry) => entry.name === startingMorphForm) ?? VEVAPHON_MORPH_STARTERS[0];
|
||||
const allForms = VEVAPHON_MORPH_STARTERS.map((entry) => `${entry.name}: ${entry.summary}`).join("\n");
|
||||
return [
|
||||
`Starting Morph Form: ${form.name}`,
|
||||
`${form.description}`,
|
||||
``,
|
||||
`Available Forms:`,
|
||||
allForms,
|
||||
``,
|
||||
`Instability (0–12): Each morph activation costs 1 Instability. At 6+, minor effects trigger each scene. At 10+, failed Sanity saves trigger Morph Panic. At 12, the Vevaphon is lost — immediate retirement or death.`,
|
||||
``,
|
||||
`Minor Effects (6+ Instability):`,
|
||||
VEVAPHON_INSTABILITY_MINOR.join("\n"),
|
||||
``,
|
||||
`Morph Panic Table (10+ Instability, failed Sanity):`,
|
||||
VEVAPHON_INSTABILITY_TABLE.map((entry) => `${entry.roll}. ${entry.result}: ${entry.effect}`).join("\n")
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function buildTraumaResponseText(species, trainingPackage) {
|
||||
return buildSlaTraumaResponseText(species?.name ?? "", trainingPackage?.name ?? "");
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
MANIFEST-000039
|
||||
MANIFEST-000075
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/03/31-15:50:48.583476 17054f000 Recovering log #38
|
||||
2026/03/31-15:50:48.583855 17054f000 Delete type=0 #38
|
||||
2026/03/31-15:50:48.583882 17054f000 Delete type=3 #37
|
||||
2026/04/14-21:44:03.701190 170dcb000 Recovering log #73
|
||||
2026/04/14-21:44:03.701635 170dcb000 Delete type=3 #71
|
||||
2026/04/14-21:44:03.701777 170dcb000 Delete type=0 #73
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2026/03/29-21:14:27.556257 16ffc3000 Recovering log #34
|
||||
2026/03/29-21:14:27.556633 16ffc3000 Delete type=3 #32
|
||||
2026/03/29-21:14:27.556668 16ffc3000 Delete type=0 #34
|
||||
2026/04/14-14:29:42.528096 1725ef000 Recovering log #70
|
||||
2026/04/14-14:29:42.528525 1725ef000 Delete type=0 #70
|
||||
2026/04/14-14:29:42.528564 1725ef000 Delete type=3 #69
|
||||
2026/04/14-21:41:04.230515 172707000 Level-0 table #74: started
|
||||
2026/04/14-21:41:04.230654 172707000 Level-0 table #74: 0 bytes OK
|
||||
2026/04/14-21:41:04.230837 172707000 Delete type=0 #72
|
||||
2026/04/14-21:41:04.231926 172707000 Manual compaction at level-0 from '!items!1r6ruvSdxn2bHHIk' @ 72057594037927935 : 1 .. '!items!zbKI2RCl5h3vowoy' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1 +1 @@
|
||||
MANIFEST-000039
|
||||
MANIFEST-000075
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/03/31-15:50:48.585617 170d5b000 Recovering log #38
|
||||
2026/03/31-15:50:48.585981 170d5b000 Delete type=0 #38
|
||||
2026/03/31-15:50:48.586009 170d5b000 Delete type=3 #37
|
||||
2026/04/14-21:44:03.703209 171de3000 Recovering log #73
|
||||
2026/04/14-21:44:03.703506 171de3000 Delete type=3 #71
|
||||
2026/04/14-21:44:03.703538 171de3000 Delete type=0 #73
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2026/03/29-21:14:27.559077 1717e7000 Recovering log #34
|
||||
2026/03/29-21:14:27.559760 1717e7000 Delete type=3 #32
|
||||
2026/03/29-21:14:27.559790 1717e7000 Delete type=0 #34
|
||||
2026/04/14-14:29:42.530946 171de3000 Recovering log #70
|
||||
2026/04/14-14:29:42.531303 171de3000 Delete type=0 #70
|
||||
2026/04/14-14:29:42.531327 171de3000 Delete type=3 #69
|
||||
2026/04/14-21:41:04.231427 172707000 Level-0 table #74: started
|
||||
2026/04/14-21:41:04.231573 172707000 Level-0 table #74: 0 bytes OK
|
||||
2026/04/14-21:41:04.231822 172707000 Delete type=0 #72
|
||||
2026/04/14-21:41:04.231960 172707000 Manual compaction at level-0 from '!items!0ZAwhewvLBwX3opf' @ 72057594037927935 : 1 .. '!items!z0avrplKe6M0RcXk' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1 +1 @@
|
||||
MANIFEST-000039
|
||||
MANIFEST-000075
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/03/31-15:50:48.587337 171d73000 Recovering log #38
|
||||
2026/03/31-15:50:48.587684 171d73000 Delete type=0 #38
|
||||
2026/03/31-15:50:48.587707 171d73000 Delete type=3 #37
|
||||
2026/04/14-21:44:03.705381 1725ef000 Recovering log #73
|
||||
2026/04/14-21:44:03.705808 1725ef000 Delete type=3 #71
|
||||
2026/04/14-21:44:03.705977 1725ef000 Delete type=0 #73
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2026/03/29-21:14:27.561319 170fdb000 Recovering log #34
|
||||
2026/03/29-21:14:27.561677 170fdb000 Delete type=3 #32
|
||||
2026/03/29-21:14:27.561715 170fdb000 Delete type=0 #34
|
||||
2026/04/14-14:29:42.533049 170dcb000 Recovering log #70
|
||||
2026/04/14-14:29:42.533339 170dcb000 Delete type=0 #70
|
||||
2026/04/14-14:29:42.533362 170dcb000 Delete type=3 #69
|
||||
2026/04/14-21:41:04.229609 172707000 Level-0 table #74: started
|
||||
2026/04/14-21:41:04.229909 172707000 Level-0 table #74: 0 bytes OK
|
||||
2026/04/14-21:41:04.230444 172707000 Delete type=0 #72
|
||||
2026/04/14-21:41:04.231906 172707000 Manual compaction at level-0 from '!items!1HNvL7XEUcMQbLps' @ 72057594037927935 : 1 .. '!items!zRzb3Laiuw4ac6p0' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1 +1 @@
|
||||
MANIFEST-000039
|
||||
MANIFEST-000075
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/03/31-15:50:48.589215 17054f000 Recovering log #38
|
||||
2026/03/31-15:50:48.589634 17054f000 Delete type=0 #38
|
||||
2026/03/31-15:50:48.589656 17054f000 Delete type=3 #37
|
||||
2026/04/14-21:44:03.707592 170dcb000 Recovering log #73
|
||||
2026/04/14-21:44:03.708035 170dcb000 Delete type=3 #71
|
||||
2026/04/14-21:44:03.708118 170dcb000 Delete type=0 #73
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2026/03/29-21:14:27.563010 1707cf000 Recovering log #34
|
||||
2026/03/29-21:14:27.563276 1707cf000 Delete type=3 #32
|
||||
2026/03/29-21:14:27.563303 1707cf000 Delete type=0 #34
|
||||
2026/04/14-14:29:42.534696 1725ef000 Recovering log #70
|
||||
2026/04/14-14:29:42.534932 1725ef000 Delete type=0 #70
|
||||
2026/04/14-14:29:42.534950 1725ef000 Delete type=3 #69
|
||||
2026/04/14-21:41:04.230890 172707000 Level-0 table #74: started
|
||||
2026/04/14-21:41:04.231026 172707000 Level-0 table #74: 0 bytes OK
|
||||
2026/04/14-21:41:04.231365 172707000 Delete type=0 #72
|
||||
2026/04/14-21:41:04.231944 172707000 Manual compaction at level-0 from '!macros!2xRvRAixuocjCm40' @ 72057594037927935 : 1 .. '!macros!ilBclUmPwcd7aNQK' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1 +1 @@
|
||||
MANIFEST-000039
|
||||
MANIFEST-000075
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/03/31-15:50:48.590832 171567000 Recovering log #38
|
||||
2026/03/31-15:50:48.591262 171567000 Delete type=0 #38
|
||||
2026/03/31-15:50:48.591291 171567000 Delete type=3 #37
|
||||
2026/04/14-21:44:03.709360 171de3000 Recovering log #73
|
||||
2026/04/14-21:44:03.709675 171de3000 Delete type=3 #71
|
||||
2026/04/14-21:44:03.709706 171de3000 Delete type=0 #73
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2026/03/29-21:14:27.564411 1717e7000 Recovering log #34
|
||||
2026/03/29-21:14:27.564754 1717e7000 Delete type=3 #32
|
||||
2026/03/29-21:14:27.564784 1717e7000 Delete type=0 #34
|
||||
2026/04/14-14:29:42.536078 171de3000 Recovering log #70
|
||||
2026/04/14-14:29:42.536336 171de3000 Delete type=0 #70
|
||||
2026/04/14-14:29:42.536359 171de3000 Delete type=3 #69
|
||||
2026/04/14-21:41:04.232630 172707000 Level-0 table #74: started
|
||||
2026/04/14-21:41:04.232740 172707000 Level-0 table #74: 0 bytes OK
|
||||
2026/04/14-21:41:04.232976 172707000 Delete type=0 #72
|
||||
2026/04/14-21:41:04.234457 172707000 Manual compaction at level-0 from '!macros!2u4a5gEogtLd3XQz' @ 72057594037927935 : 1 .. '!macros!trU0DtBJvJCPddsQ' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1 +1 @@
|
||||
MANIFEST-000039
|
||||
MANIFEST-000075
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/03/31-15:50:48.592976 170d5b000 Recovering log #38
|
||||
2026/03/31-15:50:48.593456 170d5b000 Delete type=0 #38
|
||||
2026/03/31-15:50:48.593476 170d5b000 Delete type=3 #37
|
||||
2026/04/14-21:44:03.710794 170dcb000 Recovering log #73
|
||||
2026/04/14-21:44:03.711256 170dcb000 Delete type=3 #71
|
||||
2026/04/14-21:44:03.711288 170dcb000 Delete type=0 #73
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2026/03/29-21:14:27.565839 1707cf000 Recovering log #34
|
||||
2026/03/29-21:14:27.566214 1707cf000 Delete type=3 #32
|
||||
2026/03/29-21:14:27.566242 1707cf000 Delete type=0 #34
|
||||
2026/04/14-14:29:42.537344 1715d7000 Recovering log #70
|
||||
2026/04/14-14:29:42.537597 1715d7000 Delete type=0 #70
|
||||
2026/04/14-14:29:42.537618 1715d7000 Delete type=3 #69
|
||||
2026/04/14-21:41:04.232083 172707000 Level-0 table #74: started
|
||||
2026/04/14-21:41:04.232272 172707000 Level-0 table #74: 0 bytes OK
|
||||
2026/04/14-21:41:04.232554 172707000 Delete type=0 #72
|
||||
2026/04/14-21:41:04.234426 172707000 Manual compaction at level-0 from '!macros!0Yw4QWSAeOuUzRqB' @ 72057594037927935 : 1 .. '!macros!z5V92LyXHjfMxnRQ' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1 +1 @@
|
||||
MANIFEST-000039
|
||||
MANIFEST-000077
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/03/31-15:50:48.594786 171567000 Recovering log #38
|
||||
2026/03/31-15:50:48.595201 171567000 Delete type=0 #38
|
||||
2026/03/31-15:50:48.595227 171567000 Delete type=3 #37
|
||||
2026/04/14-21:44:03.713901 1725ef000 Recovering log #75
|
||||
2026/04/14-21:44:03.714351 1725ef000 Delete type=0 #75
|
||||
2026/04/14-21:44:03.714384 1725ef000 Delete type=3 #73
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2026/03/29-21:14:27.567335 170fdb000 Recovering log #34
|
||||
2026/03/29-21:14:27.567720 170fdb000 Delete type=3 #32
|
||||
2026/03/29-21:14:27.567753 170fdb000 Delete type=0 #34
|
||||
2026/04/14-14:29:42.538470 1725ef000 Recovering log #72
|
||||
2026/04/14-14:29:42.538719 1725ef000 Delete type=3 #71
|
||||
2026/04/14-14:29:42.538747 1725ef000 Delete type=0 #72
|
||||
2026/04/14-21:41:04.233774 172707000 Level-0 table #76: started
|
||||
2026/04/14-21:41:04.233930 172707000 Level-0 table #76: 0 bytes OK
|
||||
2026/04/14-21:41:04.234304 172707000 Delete type=0 #74
|
||||
2026/04/14-21:41:04.234513 172707000 Manual compaction at level-0 from '!macros!06NSvDYTtmkeUtLq' @ 72057594037927935 : 1 .. '!macros!zLRsweb35Lw0NMev' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1 +1 @@
|
||||
MANIFEST-000039
|
||||
MANIFEST-000075
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/03/31-15:50:48.597410 170d5b000 Recovering log #38
|
||||
2026/03/31-15:50:48.598033 170d5b000 Delete type=0 #38
|
||||
2026/03/31-15:50:48.598068 170d5b000 Delete type=3 #37
|
||||
2026/04/14-21:44:03.716230 1715d7000 Recovering log #73
|
||||
2026/04/14-21:44:03.716588 1715d7000 Delete type=3 #71
|
||||
2026/04/14-21:44:03.716645 1715d7000 Delete type=0 #73
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2026/03/29-21:14:27.568770 1707cf000 Recovering log #34
|
||||
2026/03/29-21:14:27.569097 1707cf000 Delete type=3 #32
|
||||
2026/03/29-21:14:27.569138 1707cf000 Delete type=0 #34
|
||||
2026/04/14-14:29:42.540072 170dcb000 Recovering log #70
|
||||
2026/04/14-14:29:42.540350 170dcb000 Delete type=0 #70
|
||||
2026/04/14-14:29:42.540376 170dcb000 Delete type=3 #69
|
||||
2026/04/14-21:41:04.233064 172707000 Level-0 table #74: started
|
||||
2026/04/14-21:41:04.233246 172707000 Level-0 table #74: 0 bytes OK
|
||||
2026/04/14-21:41:04.233613 172707000 Delete type=0 #72
|
||||
2026/04/14-21:41:04.234486 172707000 Manual compaction at level-0 from '!tables!1vCm4ElRPotQXgNB' @ 72057594037927935 : 1 .. '!tables.results!kqKpQAXyLTEEyz6Z.xdwwswzfoPuVifvw' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1 +1 @@
|
||||
MANIFEST-000039
|
||||
MANIFEST-000075
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2026/03/31-15:50:48.600045 17054f000 Recovering log #38
|
||||
2026/03/31-15:50:48.600385 17054f000 Delete type=0 #38
|
||||
2026/03/31-15:50:48.600414 17054f000 Delete type=3 #37
|
||||
2026/04/14-21:44:03.718522 170dcb000 Recovering log #73
|
||||
2026/04/14-21:44:03.718938 170dcb000 Delete type=3 #71
|
||||
2026/04/14-21:44:03.718979 170dcb000 Delete type=0 #73
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2026/03/29-21:14:27.570654 170fdb000 Recovering log #34
|
||||
2026/03/29-21:14:27.571032 170fdb000 Delete type=3 #32
|
||||
2026/03/29-21:14:27.571059 170fdb000 Delete type=0 #34
|
||||
2026/04/14-14:29:42.542090 1715d7000 Recovering log #70
|
||||
2026/04/14-14:29:42.542385 1715d7000 Delete type=0 #70
|
||||
2026/04/14-14:29:42.542403 1715d7000 Delete type=3 #69
|
||||
2026/04/14-21:41:04.234643 172707000 Level-0 table #74: started
|
||||
2026/04/14-21:41:04.234976 172707000 Level-0 table #74: 0 bytes OK
|
||||
2026/04/14-21:41:04.235710 172707000 Delete type=0 #72
|
||||
2026/04/14-21:41:04.240761 172707000 Manual compaction at level-0 from '!tables!31YibfjueXuZdNLb' @ 72057594037927935 : 1 .. '!tables.results!ypcoikqHLhnc9tNs.zJkYrssfrdWBSeRm' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"id": "sla-mothership",
|
||||
"title": "SLA Mothership",
|
||||
"description": "An SLA Industries conversion built on the unofficial Mothership system for Foundry VTT, preserving Mothership's core mechanics while adding SLA flux, Ebb, BPN, and equipment support.",
|
||||
"version": "0.5.1",
|
||||
"version": "0.6.1",
|
||||
"esmodules": [
|
||||
"module/mosh.js"
|
||||
],
|
||||
|
||||
@@ -11,24 +11,24 @@
|
||||
"value": 10,
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
"label" : "Health"
|
||||
"label": "Health"
|
||||
},
|
||||
"hits": {
|
||||
"value": 0,
|
||||
"min": 0,
|
||||
"max" : 2,
|
||||
"label" : "Wounds"
|
||||
"max": 2,
|
||||
"label": "Wounds"
|
||||
},
|
||||
"netHP": {
|
||||
"value": 20,
|
||||
"min": 0,
|
||||
"max" : 20,
|
||||
"label" : "Net HP"
|
||||
"max": 20,
|
||||
"label": "Net HP"
|
||||
},
|
||||
"bleeding": {
|
||||
"value": 0,
|
||||
"min": 0,
|
||||
"label" : "Bleeding"
|
||||
"label": "Bleeding"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -38,9 +38,9 @@
|
||||
],
|
||||
"biography": "",
|
||||
"notes": "",
|
||||
"weight" : {
|
||||
"current" : 0,
|
||||
"capacity" : 0
|
||||
"weight": {
|
||||
"current": 0,
|
||||
"capacity": 0
|
||||
},
|
||||
"class": {
|
||||
"value": ""
|
||||
@@ -59,8 +59,8 @@
|
||||
},
|
||||
"xp": {
|
||||
"value": 0,
|
||||
"html" : 0,
|
||||
"selectedSkill" : ""
|
||||
"html": 0,
|
||||
"selectedSkill": ""
|
||||
},
|
||||
"attributes": {
|
||||
"level": {
|
||||
@@ -140,13 +140,13 @@
|
||||
"value": 2,
|
||||
"min": 2,
|
||||
"max": 20,
|
||||
"label" : "Stress"
|
||||
"label": "Stress"
|
||||
},
|
||||
"resolve": {
|
||||
"value": 0,
|
||||
"min": 0,
|
||||
"max": 15,
|
||||
"label" : "Resolve"
|
||||
"label": "Resolve"
|
||||
},
|
||||
"stressdesc": {
|
||||
"value": ""
|
||||
@@ -216,6 +216,22 @@
|
||||
},
|
||||
"ebbNotes": {
|
||||
"value": ""
|
||||
},
|
||||
"morphForm": {
|
||||
"value": ""
|
||||
},
|
||||
"vevaphonInstability": {
|
||||
"value": 0,
|
||||
"max": 12,
|
||||
"label": "Instability"
|
||||
},
|
||||
"morphNotes": {
|
||||
"value": ""
|
||||
},
|
||||
"sessionAbilityUsed": false,
|
||||
"contacts": [],
|
||||
"activityLog": {
|
||||
"value": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -237,7 +253,7 @@
|
||||
"max": 99,
|
||||
"label": "Combat",
|
||||
"rollLabel": "Combat Check",
|
||||
"enabled" : true
|
||||
"enabled": true
|
||||
},
|
||||
"instinct": {
|
||||
"value": 10,
|
||||
@@ -245,7 +261,7 @@
|
||||
"max": 99,
|
||||
"label": "Instinct",
|
||||
"rollLabel": "Instinct Check",
|
||||
"enabled" : true
|
||||
"enabled": true
|
||||
},
|
||||
"speed": {
|
||||
"value": 10,
|
||||
@@ -253,7 +269,7 @@
|
||||
"max": 99,
|
||||
"label": "Speed",
|
||||
"rollLabel": "Speed Check",
|
||||
"enabled" : false
|
||||
"enabled": false
|
||||
},
|
||||
"loyalty": {
|
||||
"value": 10,
|
||||
@@ -261,7 +277,7 @@
|
||||
"max": 99,
|
||||
"label": "Loyalty",
|
||||
"rollLabel": "Loyalty Check",
|
||||
"enabled" : false
|
||||
"enabled": false
|
||||
},
|
||||
"armor": {
|
||||
"value": 0,
|
||||
@@ -272,7 +288,7 @@
|
||||
"cover": "none",
|
||||
"label": "Armor",
|
||||
"rollLabel": "Armor Save",
|
||||
"enabled" : false
|
||||
"enabled": false
|
||||
},
|
||||
"sanity": {
|
||||
"value": 10,
|
||||
@@ -280,7 +296,7 @@
|
||||
"max": 99,
|
||||
"label": "Sanity",
|
||||
"rollLabel": "Sanity Save",
|
||||
"enabled" : false
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -295,18 +311,18 @@
|
||||
"xp": {
|
||||
"value": 1
|
||||
},
|
||||
"images" : {
|
||||
"layout" : "systems/sla-mothership/images/Galloway_Top.png",
|
||||
"beauty" : "systems/sla-mothership/images/Galloway_Top.png"
|
||||
"images": {
|
||||
"layout": "systems/sla-mothership/images/Galloway_Top.png",
|
||||
"beauty": "systems/sla-mothership/images/Galloway_Top.png"
|
||||
},
|
||||
"runSetup" : true,
|
||||
"megadamage" : {
|
||||
"html" : "",
|
||||
"hits" : [],
|
||||
"menu" : {
|
||||
"html" : ""
|
||||
"runSetup": true,
|
||||
"megadamage": {
|
||||
"html": "",
|
||||
"hits": [],
|
||||
"menu": {
|
||||
"html": ""
|
||||
},
|
||||
"open" : false
|
||||
"open": false
|
||||
},
|
||||
"stats": {
|
||||
"armor": {
|
||||
@@ -370,9 +386,9 @@
|
||||
"hull": {
|
||||
"value": 10,
|
||||
"max": 10,
|
||||
"75" : 0,
|
||||
"50" : 0,
|
||||
"25" : 0
|
||||
"75": 0,
|
||||
"50": 0,
|
||||
"25": 0
|
||||
},
|
||||
"fuel": {
|
||||
"value": 10,
|
||||
@@ -400,10 +416,10 @@
|
||||
},
|
||||
"upgrades": {
|
||||
"value": 0,
|
||||
"max" : 0
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"weapon-stats" : {
|
||||
"weapon-stats": {
|
||||
"weapons": {
|
||||
"value": 0,
|
||||
"max": 1
|
||||
@@ -416,7 +432,6 @@
|
||||
"value": 0,
|
||||
"max": 1
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -447,7 +462,7 @@
|
||||
"quantity": 1,
|
||||
"weight": 0,
|
||||
"carryState": "carried",
|
||||
"cost" : 0,
|
||||
"cost": 0,
|
||||
"sla": {
|
||||
"category": "",
|
||||
"power": "",
|
||||
@@ -495,7 +510,7 @@
|
||||
"ammo": 10,
|
||||
"shots": 1,
|
||||
"curShots": 0,
|
||||
"shotsPerFire" : 1,
|
||||
"shotsPerFire": 1,
|
||||
"useAmmo": false,
|
||||
"ammoType": "",
|
||||
"ammoCalibre": "",
|
||||
@@ -511,22 +526,22 @@
|
||||
"ammoReserveAp": 0,
|
||||
"ammoReserveHe": 0,
|
||||
"ammoReserveHeap": 0,
|
||||
"wound":"",
|
||||
"wound": "",
|
||||
"critDmg": "",
|
||||
"woundEffect": "",
|
||||
"bonus": 0,
|
||||
"weight": 0,
|
||||
"carryState": "equipped",
|
||||
"equipped": true,
|
||||
"cost" : 0,
|
||||
"cost": 0,
|
||||
"ranges": {
|
||||
"short": 0,
|
||||
"medium": 0,
|
||||
"long": 0,
|
||||
"value" : ""
|
||||
"value": ""
|
||||
},
|
||||
"settings" : {
|
||||
"firstEdition" : false
|
||||
"settings": {
|
||||
"firstEdition": false
|
||||
},
|
||||
"sla": {
|
||||
"weaponType": "",
|
||||
@@ -548,14 +563,14 @@
|
||||
"base"
|
||||
],
|
||||
"armorPoints": 1,
|
||||
"damageReduction":0,
|
||||
"damageReduction": 0,
|
||||
"speed": "",
|
||||
"oxygenMax": 0,
|
||||
"oxygenCurrent": 0,
|
||||
"oxygenCurrent": 0,
|
||||
"weight": 0,
|
||||
"carryState": "equipped",
|
||||
"equipped": true,
|
||||
"cost" : 0,
|
||||
"cost": 0,
|
||||
"sla": {
|
||||
"burden": "",
|
||||
"coverage": "",
|
||||
@@ -572,9 +587,9 @@
|
||||
"base"
|
||||
],
|
||||
"severity": 1,
|
||||
"treatment" : {
|
||||
"value" : 0,
|
||||
"html" : ""
|
||||
"treatment": {
|
||||
"value": 0,
|
||||
"html": ""
|
||||
},
|
||||
"sla": {
|
||||
"category": "",
|
||||
@@ -622,7 +637,7 @@
|
||||
"base"
|
||||
],
|
||||
"text": "",
|
||||
"job" : ""
|
||||
"job": ""
|
||||
},
|
||||
"repair": {
|
||||
"templates": [
|
||||
@@ -637,9 +652,9 @@
|
||||
],
|
||||
"quantity": 1,
|
||||
"hull": 1,
|
||||
"totalHull" : 1,
|
||||
"feature" : "",
|
||||
"offline" : false
|
||||
"totalHull": 1,
|
||||
"feature": "",
|
||||
"offline": false
|
||||
},
|
||||
"class": {
|
||||
"templates": [
|
||||
@@ -702,4 +717,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,13 @@
|
||||
<div class="headerinputtext">L.A.D.</div>
|
||||
<label class="sla-header-checkbox" title="Life After Death">
|
||||
<input type="checkbox" name="system.sla.lad" data-dtype="Boolean" {{checked system.sla.lad}} />
|
||||
<span>Life After Death</span>
|
||||
<span>L.A.D.</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-field header-field-rest">
|
||||
<div class="headerinputtext"> </div>
|
||||
<button type="button" class="sla-sheet-action sla-rest-button sla-header-rest-pill" title="Take a short or long rest">Rest</button>
|
||||
</div>
|
||||
<div class="header-field header-field-credits">
|
||||
<div class="headerinputtext">{{ localize 'Mosh.Credits' }}</div>
|
||||
<div class="headerinputfield sla-header-input charname"><input name="system.credits.value" class="noborder" type="text"
|
||||
@@ -53,7 +57,7 @@
|
||||
</div>
|
||||
|
||||
<div class="header-field header-field-sla-rank header-field-compact">
|
||||
<div class="headerinputtext">SLA Rank</div>
|
||||
<div class="headerinputtext">S.C.L.</div>
|
||||
<div class="headerinputfield sla-header-input charname"><input name="system.attributes.level.value" class="noborder"
|
||||
type="text" value="{{system.attributes.level.value}}" placeholder="" /></div>
|
||||
</div>
|
||||
@@ -68,7 +72,6 @@
|
||||
<div class="headerinputfield sla-header-input charname"><input name="system.sla.weightText.value" class="noborder" type="text"
|
||||
value="{{system.sla.weightText.value}}" placeholder="" /></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -202,8 +205,15 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
<!-- Trauma Response -->
|
||||
<div class="" style="margin-left: 6px; margin-right: 0px; flex: 0; grid-column-start: 1; grid-column-end: 3;">
|
||||
<div class="sla-trauma-panel" style="margin-left: 6px; margin-right: 0px; flex: 0; grid-column-start: 1; grid-column-end: 3;">
|
||||
<div class="resource-label sla-stat-heading sla-trauma-heading">{{ localize 'Mosh.TraumaResponse' }}</div>
|
||||
<div class="sla-trauma-controls">
|
||||
<label class="sla-trauma-toggle" title="Mark the trauma response as used this session">
|
||||
<input type="checkbox" class="sla-session-ability-used" {{checked system.sla.sessionAbilityUsed}} />
|
||||
<span>Used This Session</span>
|
||||
</label>
|
||||
<button type="button" class="sla-inline-mini-button sla-new-session" title="Reset trauma response use for a new session">New Session</button>
|
||||
</div>
|
||||
<textarea name="system.other.stressdesc.value" rows="4" class="textarea-input trauma-response-box" type="text"
|
||||
data-target="system.other.stressdesc.value" placeholder="Trauma response">{{system.other.stressdesc.value}}</textarea>
|
||||
</div>
|
||||
@@ -377,6 +387,7 @@
|
||||
<a class="tab-select" data-tab="skills">{{ localize 'Mosh.Skills' }}</a>
|
||||
<a class="tab-select" data-tab="weapons">{{ localize 'Mosh.Weapons' }}</a>
|
||||
<a class="tab-select" data-tab="items">{{ localize 'Mosh.Items' }}</a>
|
||||
<a class="tab-select" data-tab="drugs">Drugs</a>
|
||||
<a class="tab-select" data-tab="sla">SLA</a>
|
||||
<a class="tab-select" data-tab="traits">Traits</a>
|
||||
<a class="tab-select" data-tab="special-rules">Special Rules</a>
|
||||
@@ -395,8 +406,13 @@
|
||||
{{!-- Notes Tab --}}
|
||||
<div class="tab biography" data-group="primary" data-tab="notes">
|
||||
<div class="sla-card" style="margin-bottom: 12px;">
|
||||
<div class="sla-card-label">Ammo Spend Ledger</div>
|
||||
<div class="sla-card-copy">Total spent: {{system.sla.ammoSpendTotal.value}} cR</div>
|
||||
<div class="sla-card-label">Operation Log</div>
|
||||
<div class="sla-card-copy">Recent rolls, outcomes, and field actions for this operative.</div>
|
||||
<textarea readonly rows="10" class="textarea-input">{{system.sla.activityLog.value}}</textarea>
|
||||
</div>
|
||||
<div class="sla-card" style="margin-bottom: 12px;">
|
||||
<div class="sla-card-label">Ammo Economy Ledger</div>
|
||||
<div class="sla-card-copy">Firing spend total: {{system.sla.ammoSpendTotal.value}} cR. Purchases are logged below as separate entries.</div>
|
||||
<textarea readonly rows="6" class="textarea-input">{{system.sla.ammoLedger.value}}</textarea>
|
||||
</div>
|
||||
{{editor enriched.notes target="system.notes" button=true editable=true}}
|
||||
@@ -816,24 +832,6 @@
|
||||
{{/if}}
|
||||
{{!-- Owned Items Tab --}}
|
||||
<div class="tab items" data-group="primary" data-tab="items">
|
||||
{{#if drugAlerts.length}}
|
||||
<div class="sla-drug-alerts">
|
||||
{{#each drugAlerts as |alert id|}}
|
||||
<div class="sla-drug-alert {{alert.state}}">
|
||||
<div class="sla-drug-alert-main">
|
||||
<div class="sla-drug-alert-name">{{alert.name}}</div>
|
||||
<div class="sla-drug-alert-meta">
|
||||
<span class="sla-drug-alert-state">{{alert.stateLabel}}</span>
|
||||
{{#if alert.duration}}<span class="sla-drug-alert-duration">{{alert.duration}}</span>{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="sla-drug-alert-summary">{{alert.summary}}</div>
|
||||
<button type="button" class="drug-close-btn drug-close" data-item-id="{{alert.itemId}}" data-drug-id="{{alert.drugId}}">Close</button>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if system.settings.firstEdition}}
|
||||
<!-- ARMOR -->
|
||||
<ol class="items-list" role="table">
|
||||
@@ -916,7 +914,65 @@
|
||||
</ol>
|
||||
{{/if}}
|
||||
|
||||
<!-- DRUGS -->
|
||||
<!-- ITEMS -->
|
||||
<ol class="items-list">
|
||||
<li class="item flexrow item-header">
|
||||
<div class="item-image"></div>
|
||||
<div class="skill-stat" style="flex-grow: 1.5;">{{ localize 'Mosh.ItemName' }}</div>
|
||||
<div class="skill-stat">{{ localize 'Mosh.Quantity' }}</div>
|
||||
<div class="skill-stat">{{ localize 'Mosh.Weight' }}</div>
|
||||
<div class="skill-stat">Carry</div>
|
||||
<div class="skill-stat">{{ localize 'Mosh.Value' }}</div>
|
||||
<div class="item-controls">
|
||||
<a class="item-control item-create" title="{{ localize 'Mosh.CreateItem' }}" data-type="item"><i class="fas fa-plus"></i> Add</a>
|
||||
</div>
|
||||
</li>
|
||||
{{#each gear as |item id|}}
|
||||
<li class="item flexrow dropitem" data-item-id="{{item._id}}">
|
||||
<div class="item-image"><img src="{{item.img}}" title="{{item.name}}" width="24" height="24" /></div>
|
||||
<h4 class="skill-name list-roll description-roll" style="flex-grow: 1.54;">{{item.name}}</h4>
|
||||
<div class="skill-stat list-roll item-quantity">{{item.system.quantity}}</div>
|
||||
<div class="skill-stat">{{item.slaCarry.totalWeight}} kg</div>
|
||||
<div class="skill-stat">
|
||||
<div class="sla-carry-toggle-row">
|
||||
<button type="button" class="sla-carry-toggle {{#if (compare 'str' item.slaCarry.state '===' 'equipped')}}active{{/if}}" data-state="equipped">Eqp</button>
|
||||
<button type="button" class="sla-carry-toggle {{#if (compare 'str' item.slaCarry.state '===' 'stowed')}}active{{/if}}" data-state="stowed">Stow</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-stat">{{item.system.cost}}</div>
|
||||
<div class="item-controls">
|
||||
<a class="item-control item-edit" title="{{ localize 'Mosh.EditItem' }}"><i class="fas fa-edit"></i></a>
|
||||
<a class="item-control item-delete" title="{{ localize 'Mosh.DeleteItem' }}"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
<div class="item flex-group-left item-header">
|
||||
<div class="skill-stat" style="flex-grow: 1.5;">{{ localize 'Mosh.CarryingCapacity' }}: {{system.weight.capacity}} kg</div>
|
||||
<div class="skill-stat">{{ localize 'Mosh.CurrentWeight' }}: {{system.weight.current}} kg</div>
|
||||
<div class="skill-stat">Stowed: {{system.weight.stowed}} kg</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="tab items" data-group="primary" data-tab="drugs">
|
||||
{{#if drugAlerts.length}}
|
||||
<div class="sla-drug-alerts">
|
||||
{{#each drugAlerts as |alert id|}}
|
||||
<div class="sla-drug-alert {{alert.state}}">
|
||||
<div class="sla-drug-alert-main">
|
||||
<div class="sla-drug-alert-name">{{alert.name}}</div>
|
||||
<div class="sla-drug-alert-meta">
|
||||
<span class="sla-drug-alert-state">{{alert.stateLabel}}</span>
|
||||
{{#if alert.duration}}<span class="sla-drug-alert-duration">{{alert.duration}}</span>{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="sla-drug-alert-summary">{{alert.summary}}</div>
|
||||
<button type="button" class="drug-close-btn drug-close" data-item-id="{{alert.itemId}}" data-drug-id="{{alert.drugId}}">Close</button>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<ol class="items-list sla-drug-list">
|
||||
<li class="item flexrow item-header">
|
||||
<div class="item-image"></div>
|
||||
@@ -957,46 +1013,6 @@
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
|
||||
<!-- ITEMS -->
|
||||
<ol class="items-list">
|
||||
<li class="item flexrow item-header">
|
||||
<div class="item-image"></div>
|
||||
<div class="skill-stat" style="flex-grow: 1.5;">{{ localize 'Mosh.ItemName' }}</div>
|
||||
<div class="skill-stat">{{ localize 'Mosh.Quantity' }}</div>
|
||||
<div class="skill-stat">{{ localize 'Mosh.Weight' }}</div>
|
||||
<div class="skill-stat">Carry</div>
|
||||
<div class="skill-stat">{{ localize 'Mosh.Value' }}</div>
|
||||
<div class="item-controls">
|
||||
<a class="item-control item-create" title="{{ localize 'Mosh.CreateItem' }}" data-type="item"><i class="fas fa-plus"></i> Add</a>
|
||||
</div>
|
||||
</li>
|
||||
{{#each gear as |item id|}}
|
||||
<li class="item flexrow dropitem" data-item-id="{{item._id}}">
|
||||
<div class="item-image"><img src="{{item.img}}" title="{{item.name}}" width="24" height="24" /></div>
|
||||
<h4 class="skill-name list-roll description-roll" style="flex-grow: 1.54;">{{item.name}}</h4>
|
||||
<div class="skill-stat list-roll item-quantity">{{item.system.quantity}}</div>
|
||||
<div class="skill-stat">{{item.slaCarry.totalWeight}} kg</div>
|
||||
<div class="skill-stat">
|
||||
<div class="sla-carry-toggle-row">
|
||||
<button type="button" class="sla-carry-toggle {{#if (compare 'str' item.slaCarry.state '===' 'equipped')}}active{{/if}}" data-state="equipped">Eqp</button>
|
||||
<button type="button" class="sla-carry-toggle {{#if (compare 'str' item.slaCarry.state '===' 'stowed')}}active{{/if}}" data-state="stowed">Stow</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-stat">{{item.system.cost}}</div>
|
||||
<div class="item-controls">
|
||||
<a class="item-control item-edit" title="{{ localize 'Mosh.EditItem' }}"><i class="fas fa-edit"></i></a>
|
||||
<a class="item-control item-delete" title="{{ localize 'Mosh.DeleteItem' }}"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
<div class="item flex-group-left item-header">
|
||||
<div class="skill-stat" style="flex-grow: 1.5;">{{ localize 'Mosh.CarryingCapacity' }}: {{system.weight.capacity}} kg</div>
|
||||
<div class="skill-stat">{{ localize 'Mosh.CurrentWeight' }}: {{system.weight.current}} kg</div>
|
||||
<div class="skill-stat">Stowed: {{system.weight.stowed}} kg</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{{!-- Owned Skills Tab --}}
|
||||
<div class="tab items" data-group="primary" data-tab="skills">
|
||||
@@ -1070,7 +1086,7 @@
|
||||
<li class="item flexrow dropitem" data-item-id="{{condition._id}}">
|
||||
<div class="item-image"><img src="{{condition.img}}" title="{{condition.name}}" width="24" height="24"/></div>
|
||||
<div class="skill-name description-roll list-roll">{{condition.name}}</div>
|
||||
<div class="skill-stat list-roll severity">{{condition.system.severity}}</div>
|
||||
<div class="skill-stat list-roll severity" data-severity="{{condition.system.severity}}">{{condition.system.severity}}</div>
|
||||
<div class="skill-stat">
|
||||
{{#if condition.system.treatment.html}}
|
||||
<div class="treatment-button list-roll flex" style="margin: 0; position: relative; top: 50%; -ms-transform: translateY(-50%); transform: translateY(-50%);" data-key="condition" data-label="condition" data-max="3" data-min="0">
|
||||
@@ -1098,7 +1114,7 @@
|
||||
<div class="tab items" data-group="primary" data-tab="weapons">
|
||||
<div class="sla-card" style="margin-bottom: 12px;">
|
||||
<div class="sla-card-label">Weapons Readiness</div>
|
||||
<div class="sla-card-copy">Ammo stacks: {{slaAmmoInventory.stackCount}} | Total loose rounds: {{slaAmmoInventory.roundCount}} | Running ammo spend: {{system.sla.ammoSpendTotal.value}} cR</div>
|
||||
<div class="sla-card-copy">Ammo stacks: {{slaAmmoInventory.stackCount}} | Total loose rounds: {{slaAmmoInventory.roundCount}} | Running firing spend: {{system.sla.ammoSpendTotal.value}} cR</div>
|
||||
<div class="sla-card-copy">Carry: {{slaCarry.current}} / {{slaCarry.capacity}} kg | {{slaCarry.state}}</div>
|
||||
<div class="sla-action-row" style="margin-top: 8px;">
|
||||
<button type="button" class="sla-sheet-action weapon-reload-all">Reload All</button>
|
||||
|
||||
@@ -391,7 +391,7 @@
|
||||
<li class="item flexrow dropitem" data-item-id="{{condition._id}}">
|
||||
<div class="item-image"><img src="{{condition.img}}" title="{{condition.name}}" width="24" height="24"/></div>
|
||||
<div class="skill-name description-roll list-roll">{{condition.name}}</div>
|
||||
<div class="skill-stat list-roll severity">{{condition.system.severity}}</div>
|
||||
<div class="skill-stat list-roll severity" data-severity="{{condition.system.severity}}">{{condition.system.severity}}</div>
|
||||
<div class="skill-stat">
|
||||
{{#if condition.system.treatment.html}}
|
||||
<div class="treatment-button list-roll flex" style="margin: 0; position: relative; top: 50%; -ms-transform: translateY(-50%); transform: translateY(-50%);" data-key="condition" data-label="condition" data-max="3" data-min="0">
|
||||
|
||||
@@ -1,88 +1,97 @@
|
||||
<div class="mosh" {{#if actor._id}}data-actor-id="{{actor._id}}" {{/if}}
|
||||
<div class="mosh sla-chat-shell sla-chat-shell-item" {{#if actor._id}}data-actor-id="{{actor._id}}" {{/if}}
|
||||
{{#if item._id}}data-item-id="{{item._id}}" {{/if}} {{#if tokenId}}data-token-id="{{tokenId}}" {{/if}}>
|
||||
<div class="showinfo"></div>
|
||||
<div class="showlessinfo"></div>
|
||||
<div class="rollcontainer">
|
||||
<div class="flexrow">
|
||||
{{#if swapNameDesc}}
|
||||
<div class="rollweaponh1" >{{swapName}}</div>
|
||||
{{else}}
|
||||
{{#if (compare 'int' item.name.length '>=' 22)}}
|
||||
<div class="rollweaponh1" style="font-size: 0.75rem; padding-top: 7px;">{{item.name}}</div>
|
||||
{{else}}
|
||||
<div class="rollweaponh1" >{{item.name}}</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if (compare 'str' item.type '===' 'ability')}}
|
||||
{{#if (compare 'str' actor.img '===' 'icons/svg/mystery-man.svg')}}
|
||||
<div style="text-align: right"><img class="roll-image" src="systems/sla-mothership/images/icons/ui/attributes/instinct.png" /></div>
|
||||
{{else}}
|
||||
<div style="text-align: right"><img class="roll-image" src="{{actor.img}}" /></div>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<div style="text-align: right"><img class="roll-image" src="{{item.img}}" /></div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="sla-chat-hero">
|
||||
<div class="sla-chat-hero-main">
|
||||
<div class="sla-chat-kicker-row">
|
||||
<div class="sla-chat-kicker">Item Detail</div>
|
||||
<div class="sla-chat-meta-row">
|
||||
{{#if (compare 'str' item.system.sla.impact '===' 'heal')}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-heal">HLG</span>
|
||||
{{else if item.system.roll}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-attack">ATK</span>
|
||||
{{else if (compare 'str' item.type '===' 'ability')}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-ebb">ABL</span>
|
||||
{{else}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-item">ITM</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{#if swapNameDesc}}
|
||||
<div class="rollweaponh1 sla-chat-hero-title">{{swapName}}</div>
|
||||
{{else}}
|
||||
{{#if (compare 'int' item.name.length '>=' 22)}}
|
||||
<div class="rollweaponh1 sla-chat-hero-title sla-chat-hero-title-tight">{{item.name}}</div>
|
||||
{{else}}
|
||||
<div class="rollweaponh1 sla-chat-hero-title">{{item.name}}</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="sla-chat-hero-media">
|
||||
{{#if (compare 'str' item.type '===' 'ability')}}
|
||||
{{#if (compare 'str' actor.img '===' 'icons/svg/mystery-man.svg')}}
|
||||
<img class="roll-image" src="systems/sla-mothership/images/icons/ui/attributes/instinct.png" />
|
||||
{{else}}
|
||||
<img class="roll-image" src="{{actor.img}}" />
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<img class="roll-image" src="{{item.img}}" />
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if item.system.roll}}
|
||||
<div class="description">
|
||||
<div class="description sla-chat-section {{#if (compare 'str' item.system.sla.impact '===' 'heal')}}sla-chat-section-heal{{else}}sla-chat-section-damage{{/if}}">
|
||||
{{#if (compare 'str' item.system.sla.impact '===' 'heal')}}
|
||||
<div class="body">You restore <strong>[[{{item.system.roll}}[damage]]]</strong> points of healing.</div>
|
||||
{{else}}
|
||||
<div class="body">You inflict <strong>[[{{item.system.roll}}[damage]]]</strong> points of damage.</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div style="margin-bottom : 10px;"></div>
|
||||
<div style="border-bottom: 1px solid #999; height: 0; width: 100%;"></div>
|
||||
<div style="margin-bottom : 5px;"></div>
|
||||
{{/if}}
|
||||
|
||||
{{#if item.system.severity}}
|
||||
<div class="description">
|
||||
<div class="body"><strong>{{ localize 'Mosh.Severity' }}: </strong>{{item.system.severity}}</div>
|
||||
<div class="description sla-chat-section {{severityMeta.cssClass}}" data-severity="{{item.system.severity}}">
|
||||
<div class="sla-chat-section-heading">
|
||||
<span class="sla-chat-severity-pill {{severityMeta.cssClass}}">{{severityMeta.label}}</span>
|
||||
</div>
|
||||
<div class="body"><strong>{{ localize 'Mosh.Severity' }}:</strong> {{item.system.severity}}</div>
|
||||
</div>
|
||||
<div style="margin-bottom : 5px;"></div>
|
||||
<div style="border-bottom: 1px solid #999; height: 0; width: 100%;"></div>
|
||||
<div style="margin-bottom : 5px;"></div>
|
||||
{{/if}}
|
||||
|
||||
{{#if item.system.armorPoints}}
|
||||
<div class="description">
|
||||
<div class="body"><strong>{{ localize 'Mosh.ArmorPoints' }}: </strong>{{item.system.armorPoints}}</div>
|
||||
<div class="description sla-chat-section">
|
||||
<div class="body"><strong>{{ localize 'Mosh.ArmorPoints' }}:</strong> {{item.system.armorPoints}}</div>
|
||||
</div>
|
||||
<div style="margin-bottom : 5px;"></div>
|
||||
<div style="border-bottom: 1px solid #999; height: 0; width: 100%;"></div>
|
||||
<div style="margin-bottom : 5px;"></div>
|
||||
{{/if}}
|
||||
|
||||
{{#if item.system.damageReduction}}
|
||||
<div class="description">
|
||||
<div class="body"><strong>{{ localize 'Mosh.DMGReduction' }}: </strong>{{item.system.damageReduction}}</div>
|
||||
<div class="description sla-chat-section">
|
||||
<div class="body"><strong>{{ localize 'Mosh.DMGReduction' }}:</strong> {{item.system.damageReduction}}</div>
|
||||
</div>
|
||||
<div style="margin-bottom : 5px;"></div>
|
||||
<div style="border-bottom: 1px solid #999; height: 0; width: 100%;"></div>
|
||||
<div style="margin-bottom : 5px;"></div>
|
||||
{{/if}}
|
||||
|
||||
{{#if swapNameDesc}}
|
||||
<div class="description">
|
||||
<div class="description sla-chat-section">
|
||||
<div class="body">{{{item.name}}}</div>
|
||||
</div>
|
||||
<div style="margin-bottom : 16px;"></div>
|
||||
{{else if (compare 'str' item.system.type '===' 'repair')}}
|
||||
<div class="description">
|
||||
<div class="description sla-chat-section">
|
||||
{{#if item.system.major}}
|
||||
<div class="body"><strong>{{ localize 'Mosh.MajorRepair' }}</strong></div>
|
||||
{{else}}
|
||||
<div class="body"><strong>{{ localize 'Mosh.MinorRepair' }}</strong></div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div style="margin-bottom : 5px;"></div>
|
||||
<div style="border-bottom: 1px solid #999; height: 0; width: 100%;"></div>
|
||||
<div style="margin-bottom : 5px;"></div>
|
||||
<div class="description">
|
||||
<div class="description sla-chat-section">
|
||||
<div class="body">{{{item.name}}}</div>
|
||||
</div>
|
||||
<div style="margin-bottom : 16px;"></div>
|
||||
{{else}}
|
||||
<div class="description">
|
||||
<div class="description sla-chat-section">
|
||||
<div class="body">{{{item.system.description}}}</div>
|
||||
</div>
|
||||
<div style="margin-bottom : 16px;"></div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
<div class="mosh" {{#if actor._id}}data-actor-id="{{actor._id}}" {{/if}}
|
||||
<div class="mosh sla-chat-shell sla-chat-shell-actor-update" {{#if actor._id}}data-actor-id="{{actor._id}}" {{/if}}
|
||||
{{#if item._id}}data-item-id="{{item._id}}" {{/if}} {{#if tokenId}}data-token-id="{{tokenId}}" {{/if}}>
|
||||
<div class="rollcontainer">
|
||||
<div class="flexrow" style="margin-bottom: 5px;">
|
||||
{{#if (compare 'int' msgHeader.length '>=' 22)}}
|
||||
<div class="rollweaponh1" style="font-size: 0.75rem; padding-top: 7px;">{{{msgHeader}}}</div>
|
||||
{{else}}
|
||||
<div class="rollweaponh1" >{{{msgHeader}}}</div>
|
||||
{{/if}}
|
||||
<div style="text-align: right"><img class="roll-image" src="{{{msgImgPath}}}" /></div>
|
||||
<div class="sla-chat-hero">
|
||||
<div class="sla-chat-hero-main">
|
||||
<div class="sla-chat-kicker">Actor Update</div>
|
||||
{{#if (compare 'int' msgHeader.length '>=' 22)}}
|
||||
<div class="rollweaponh1 sla-chat-hero-title sla-chat-hero-title-tight">{{{msgHeader}}}</div>
|
||||
{{else}}
|
||||
<div class="rollweaponh1 sla-chat-hero-title">{{{msgHeader}}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="sla-chat-hero-media">
|
||||
<img class="roll-image" src="{{{msgImgPath}}}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="description"" style="margin-bottom: 20px;">
|
||||
<div class="description sla-chat-section">
|
||||
<div class="body">
|
||||
{{{msgFlavor}}}
|
||||
{{#if modRollString}}
|
||||
<div class="description" style="margin-bottom: 10px;"></div>
|
||||
{{{parsedRollResult.rollHtml}}}
|
||||
{{else}}
|
||||
<br><br>
|
||||
<div class="sla-chat-rollframe">{{{parsedRollResult.rollHtml}}}</div>
|
||||
{{/if}}
|
||||
{{{msgOutcome}}}
|
||||
{{#if halfDamage}}
|
||||
<br><br>@UUID[Compendium.mosh.macros_hotbar_0e.eyGvbBi1BhWSgfUP]{Make a Panic Check}
|
||||
<div class="sla-chat-section sla-chat-section-danger">
|
||||
<div class="body">@UUID[Compendium.mosh.macros_hotbar_0e.eyGvbBi1BhWSgfUP]{Make a Panic Check}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
<div class="mosh" {{#if actor._id}}data-actor-id="{{actor._id}}" {{/if}}
|
||||
<div class="mosh sla-chat-shell sla-chat-shell-item-update" {{#if actor._id}}data-actor-id="{{actor._id}}" {{/if}}
|
||||
{{#if item._id}}data-item-id="{{item._id}}" {{/if}} {{#if tokenId}}data-token-id="{{tokenId}}" {{/if}}>
|
||||
<div class="rollcontainer">
|
||||
<div class="flexrow" style="margin-bottom : 5px;">
|
||||
{{#if (compare 'int' msgHeader.length '>=' 22)}}
|
||||
<div class="rollweaponh1" style="font-size: 0.75rem; padding-top: 7px;">{{{msgHeader}}}</div>
|
||||
{{else}}
|
||||
<div class="rollweaponh1" >{{{msgHeader}}}</div>
|
||||
{{/if}}
|
||||
<div style="text-align: right"><img class="roll-image" src="{{{msgImgPath}}}" /></div>
|
||||
<div class="sla-chat-hero">
|
||||
<div class="sla-chat-hero-main">
|
||||
<div class="sla-chat-kicker-row">
|
||||
<div class="sla-chat-kicker">Item Update</div>
|
||||
<div class="sla-chat-meta-row">
|
||||
<span class="sla-chat-type-badge sla-chat-type-item">UPD</span>
|
||||
</div>
|
||||
</div>
|
||||
{{#if (compare 'int' msgHeader.length '>=' 22)}}
|
||||
<div class="rollweaponh1 sla-chat-hero-title sla-chat-hero-title-tight">{{{msgHeader}}}</div>
|
||||
{{else}}
|
||||
<div class="rollweaponh1 sla-chat-hero-title">{{{msgHeader}}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="sla-chat-hero-media">
|
||||
<img class="roll-image" src="{{{msgImgPath}}}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="description" style="margin-bottom: 10px;">
|
||||
<div class="description sla-chat-section sla-chat-section-target">
|
||||
<div class="body">{{{flavorText}}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
<div class="mosh" {{#if actor._id}}data-actor-id="{{actor._id}}" {{/if}}
|
||||
<div class="mosh sla-chat-shell sla-chat-shell-reload" {{#if actor._id}}data-actor-id="{{actor._id}}" {{/if}}
|
||||
{{#if item._id}}data-item-id="{{item._id}}" {{/if}} {{#if tokenId}}data-token-id="{{tokenId}}" {{/if}}>
|
||||
<div class="rollcontainer">
|
||||
<div class="flexrow" style="margin-bottom : 5px;">
|
||||
{{#if (compare 'int' item.name.length '>=' 22)}}
|
||||
<div class="rollweaponh1" style="font-size: 0.75rem; padding-top: 7px;">{{{item.name}}}</div>
|
||||
{{else}}
|
||||
<div class="rollweaponh1" >{{{item.name}}}</div>
|
||||
{{/if}}
|
||||
<div style="text-align: right"><img class="roll-image" src="systems/sla-mothership/images/icons/ui/macros/reload.png" /></div>
|
||||
<div class="sla-chat-hero">
|
||||
<div class="sla-chat-hero-main">
|
||||
<div class="sla-chat-kicker-row">
|
||||
<div class="sla-chat-kicker">Reload Action</div>
|
||||
<div class="sla-chat-meta-row">
|
||||
<span class="sla-chat-type-badge sla-chat-type-reload">RLD</span>
|
||||
</div>
|
||||
</div>
|
||||
{{#if (compare 'int' item.name.length '>=' 22)}}
|
||||
<div class="rollweaponh1 sla-chat-hero-title sla-chat-hero-title-tight">{{{item.name}}}</div>
|
||||
{{else}}
|
||||
<div class="rollweaponh1 sla-chat-hero-title">{{{item.name}}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="sla-chat-hero-media">
|
||||
<img class="roll-image" src="systems/sla-mothership/images/icons/ui/macros/reload.png" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="description sla-chat-section sla-chat-section-target">
|
||||
<div class="body">{{{msgBody}}}</div>
|
||||
</div>
|
||||
<div style="margin-bottom : 16px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,38 +1,58 @@
|
||||
<div class="mosh{{#if isSlaEbbSkill}} sla-ebb-chat{{/if}}" {{#if actor._id}}data-actor-id="{{actor._id}}" {{/if}}
|
||||
<div class="mosh sla-chat-shell sla-chat-shell-check{{#if isSlaEbbSkill}} sla-chat-shell-ebb{{/if}} sla-chat-variant-{{chatStyle.key}}{{#if parsedRollResult.success}} sla-chat-shell-success{{else}} sla-chat-shell-failure{{/if}}{{#if parsedRollResult.critical}} sla-chat-shell-critical{{/if}}" {{#if actor._id}}data-actor-id="{{actor._id}}" {{/if}}
|
||||
{{#if weapon._id}}data-item-id="{{weapon._id}}" {{/if}} {{#if tokenId}}data-token-id="{{tokenId}}" {{/if}}>
|
||||
<div class="rollcontainer">
|
||||
<div class="flexrow" style="margin-bottom : 5px;">
|
||||
{{#if (compare 'int' msgHeader.length '>=' 22)}}
|
||||
<div class="rollweaponh1" style="font-size: 0.75rem; padding-top: 7px;">{{{msgHeader}}}</div>
|
||||
{{else}}
|
||||
<div class="rollweaponh1" >{{{msgHeader}}}</div>
|
||||
{{/if}}
|
||||
<div style="text-align: right"><img class="roll-image" src="{{{msgImgPath}}}" /></div>
|
||||
<div class="sla-chat-hero">
|
||||
<div class="sla-chat-hero-main">
|
||||
<div class="sla-chat-kicker-row">
|
||||
<div class="sla-chat-kicker">{{#if (compare 'str' specialRoll '===' 'damage')}}Attack Roll{{else if isSlaEbbSkill}}Ebb Check{{else}}Normal Roll{{/if}}</div>
|
||||
<div class="sla-chat-meta-row">
|
||||
{{#if (compare 'str' specialRoll '===' 'damage')}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-attack">ATK</span>
|
||||
{{else if isSlaEbbSkill}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-ebb">EBB</span>
|
||||
{{else}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-normal">CHK</span>
|
||||
{{/if}}
|
||||
<div class="sla-chat-deptline"><span class="sla-chat-emblem">{{chatStyle.emblem}}</span><span class="sla-chat-dept">{{chatStyle.label}}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
{{#if (compare 'int' msgHeader.length '>=' 22)}}
|
||||
<div class="rollweaponh1 sla-chat-hero-title sla-chat-hero-title-tight">{{{msgHeader}}}</div>
|
||||
{{else}}
|
||||
<div class="rollweaponh1 sla-chat-hero-title">{{{msgHeader}}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="sla-chat-hero-media">
|
||||
<img class="roll-image" src="{{{msgImgPath}}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if (compare 'str' specialRoll '!==' 'damage')}}
|
||||
{{{parsedRollResult.outcomeHtml}}}
|
||||
<div class="description" style="margin-bottom: 10px;">
|
||||
<div class="body">
|
||||
{{#if (compare 'int' parsedRollResult.total '>=' 90)}}
|
||||
<span class="high-failure">{{ localize 'Mosh.YouRolled90OrHigher' }}</span>
|
||||
{{else}}
|
||||
{{#if isSlaEbbSkill}}
|
||||
<div class="sla-chat-summary">
|
||||
{{#if (compare 'int' parsedRollResult.total '>=' 90)}}
|
||||
<span class="high-failure">{{ localize 'Mosh.YouRolled90OrHigher' }}</span>
|
||||
{{else}}
|
||||
{{#if isSlaEbbSkill}}
|
||||
{{#if (compare 'int' skillValue '>' 0)}}
|
||||
You {{{outcomeVerb}}} {{{comparisonText}}} your <strong>{{{attribute}}}</strong> plus the <strong>{{{skill}}}</strong> discipline.
|
||||
{{else}}
|
||||
You {{{outcomeVerb}}} {{{comparisonText}}} your <strong>{{{attribute}}}</strong> while channeling <strong>{{{skill}}}</strong>.
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{else}}
|
||||
{{#if (compare 'int' skillValue '>' 0)}}
|
||||
You {{{outcomeVerb}}} {{{comparisonText}}} your <strong>{{{attribute}}}</strong> plus <strong>{{{skill}}}</strong> skill bonus.
|
||||
{{else}}
|
||||
You {{{outcomeVerb}}} {{{comparisonText}}} your <strong>{{{attribute}}}</strong>.
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{{parsedRollResult.rollHtml}}}
|
||||
|
||||
<div class="sla-chat-rollframe">
|
||||
{{{parsedRollResult.outcomeHtml}}}
|
||||
{{{parsedRollResult.rollHtml}}}
|
||||
</div>
|
||||
|
||||
{{#if isSlaEbbSkill}}
|
||||
<div class="sla-chat-section sla-chat-section-ebb-meta">
|
||||
<div class="sla-chat-section-title">Ebb Discipline</div>
|
||||
@@ -51,78 +71,50 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
<div class="description" style="margin-bottom : 20px;">
|
||||
|
||||
{{#if (compare 'str' specialRoll '===' 'damage')}}
|
||||
<div class="description sla-chat-section">{{{flavorText}}}</div>
|
||||
<div class="sla-chat-section sla-chat-section-damage">{{{flavorText}}}</div>
|
||||
{{#if needsDesc}}
|
||||
<div style="margin-bottom : 10px;"></div>
|
||||
<div class="grid grid-2col" style="grid-template-columns: 10px 75px auto">
|
||||
<div style="border-bottom:1px solid #999; height: 50%;"></div>
|
||||
<div style= "font-size:0.65rem; text-align:center;">DESCRIPTION</div>
|
||||
<div style="border-bottom:1px solid #999; height: 50%;"></div>
|
||||
</div>
|
||||
<div style="margin-bottom : 10px;"></div>
|
||||
<div class="description">
|
||||
<div class="sla-chat-divider">Description</div>
|
||||
<div class="description sla-chat-section">
|
||||
<div class="body">{{{weapon.system.description}}}</div>
|
||||
{{#if woundEffect}}
|
||||
<div class="body">
|
||||
<br><strong>{{ localize 'Mosh.WoundEffect' }}</strong>
|
||||
<div style="margin-bottom : 3px;"></div>
|
||||
{{{woundEffect}}}
|
||||
<div class="body sla-chat-wound-block">
|
||||
<strong>{{ localize 'Mosh.WoundEffect' }}</strong>
|
||||
<div class="sla-chat-wound-link">{{{woundEffect}}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div style="margin-bottom : 16px;"></div>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if parsedRollResult.success}}
|
||||
{{#if flavorText}}<div class="sla-chat-section sla-chat-section-success">{{{flavorText}}}</div>{{/if}}
|
||||
{{#if criticalSuccessOptions.length}}
|
||||
<div class="sla-chat-section sla-chat-section-success">
|
||||
<div class="sla-chat-section-title">Critical Success Options</div>
|
||||
<ol class="sla-chat-list">
|
||||
{{#each criticalSuccessOptions as |option|}}
|
||||
<li>{{option}}</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if weapon}}
|
||||
<div class="description sla-chat-section">{{{flavorText}}}</div>
|
||||
{{#if criticalSuccessOptions.length}}
|
||||
<div class="sla-chat-section sla-chat-section-success">
|
||||
<div class="sla-chat-section-title">Critical Success Options</div>
|
||||
<ol class="sla-chat-list">
|
||||
{{#each criticalSuccessOptions as |option|}}
|
||||
<li>{{option}}</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if needsDesc}}
|
||||
<div style="margin-bottom : 10px;"></div>
|
||||
<div class="grid grid-2col" style="grid-template-columns: 10px 75px auto">
|
||||
<div style="border-bottom:1px solid #999; height: 50%;"></div>
|
||||
<div style= "font-size:0.65rem; text-align:center;">{{ localize 'Mosh.Description' }}</div>
|
||||
<div style="border-bottom:1px solid #999; height: 50%;"></div>
|
||||
</div>
|
||||
<div style="margin-bottom : 10px;"></div>
|
||||
<div class="description">
|
||||
<div class="sla-chat-divider">{{ localize 'Mosh.Description' }}</div>
|
||||
<div class="description sla-chat-section">
|
||||
<div class="body">{{{weapon.system.description}}}</div>
|
||||
{{#if woundEffect}}
|
||||
<div class="body">
|
||||
<br><strong>{{ localize 'Mosh.WoundEffect' }}</strong>
|
||||
<div style="margin-bottom : 3px;"></div>
|
||||
{{{woundEffect}}}
|
||||
<div class="body sla-chat-wound-block">
|
||||
<strong>{{ localize 'Mosh.WoundEffect' }}</strong>
|
||||
<div class="sla-chat-wound-link">{{{woundEffect}}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div style="margin-bottom : 16px;"></div>
|
||||
{{else}}
|
||||
<div class="sla-chat-section">{{{flavorText}}}</div>
|
||||
{{#if criticalSuccessOptions.length}}
|
||||
<div class="sla-chat-section sla-chat-section-success">
|
||||
<div class="sla-chat-section-title">Critical Success Options</div>
|
||||
<ol class="sla-chat-list">
|
||||
{{#each criticalSuccessOptions as |option|}}
|
||||
<li>{{option}}</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if flavorText}}<div class="sla-chat-section">{{{flavorText}}}</div>{{/if}}
|
||||
{{#if flavorText}}<div class="sla-chat-section sla-chat-section-danger">{{{flavorText}}}</div>{{/if}}
|
||||
{{#if criticalFailureTable}}
|
||||
<div class="sla-chat-section sla-chat-section-danger">
|
||||
<div class="sla-chat-section-title">{{criticalFailureTable.label}}</div>
|
||||
@@ -132,15 +124,18 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if critFail}}
|
||||
<br><br>@UUID[Compendium.mosh.macros_hotbar_1e.IgWvaabVqzKDzLL4]{Make a Panic Check}
|
||||
<div class="sla-chat-section sla-chat-section-danger">
|
||||
<div class="body">@UUID[Compendium.mosh.macros_hotbar_1e.IgWvaabVqzKDzLL4]{Make a Panic Check}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if targetingResult}}
|
||||
<div class="sla-chat-section sla-chat-section-target">
|
||||
<div class="sla-chat-section-title">Target Resolution</div>
|
||||
<div class="body"><strong>{{targetingResult.targetName}}</strong> took <strong>{{targetingResult.finalDamage}}</strong> damage.</div>
|
||||
<div class="body">Rolled {{targetingResult.rawDamage}}. Armour reduced {{targetingResult.armourReduced}}{{#if targetingResult.armourPiercing}} after AP/anti-armour effects{{/if}}.</div>
|
||||
<div class="body">Rolled {{targetingResult.rawDamage}}. Armour reduced {{targetingResult.armourReduced}}{{#if targetingResult.armourPiercing}} after AP or anti-armour effects{{/if}}.</div>
|
||||
{{#if targetingResult.additionalTargets}}
|
||||
<div class="body">{{targetingResult.additionalTargets}} additional targeted token(s) were ignored for this automatic application.</div>
|
||||
{{/if}}
|
||||
@@ -152,6 +147,5 @@
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,45 @@
|
||||
<div class="mosh" {{#if actor._id}}data-actor-id="{{actor._id}}" {{/if}}
|
||||
<div class="mosh sla-chat-shell sla-chat-shell-table sla-chat-variant-{{chatStyle.key}}{{#if parsedRollResult.success}} sla-chat-shell-success{{else}} sla-chat-shell-failure{{/if}}" {{#if actor._id}}data-actor-id="{{actor._id}}" {{/if}}
|
||||
{{#if item._id}}data-item-id="{{item._id}}" {{/if}} {{#if tokenId}}data-token-id="{{tokenId}}" {{/if}}>
|
||||
<div class="rollcontainer">
|
||||
<div class="flexrow" style="margin-bottom : 5px;">
|
||||
<div class="rollweaponh1">{{{tableName}}}</div>
|
||||
<div style="text-align: right"><img class="roll-image" src="{{{tableImg}}}" /></div>
|
||||
<div class="sla-chat-hero">
|
||||
<div class="sla-chat-hero-main">
|
||||
<div class="sla-chat-kicker-row">
|
||||
<div class="sla-chat-kicker">Table Roll</div>
|
||||
<div class="sla-chat-meta-row">
|
||||
{{#if (compare 'str' specialRoll '===' 'panicCheck')}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-panic">PNC</span>
|
||||
{{else if (compare 'str' tableName '===' 'Gunshot Wound')}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-wound">WND</span>
|
||||
{{else if (compare 'str' tableName '===' 'Bleeding Wound')}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-wound">WND</span>
|
||||
{{else if (compare 'str' tableName '===' 'Fire & Explosives Wound')}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-wound">WND</span>
|
||||
{{else if (compare 'str' tableName '===' 'Gore & Massive Wound')}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-wound">WND</span>
|
||||
{{else}}
|
||||
<span class="sla-chat-type-badge sla-chat-type-table">TBL</span>
|
||||
{{/if}}
|
||||
<div class="sla-chat-deptline"><span class="sla-chat-emblem">{{chatStyle.emblem}}</span><span class="sla-chat-dept">{{chatStyle.label}}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rollweaponh1 sla-chat-hero-title">{{{tableName}}}</div>
|
||||
</div>
|
||||
<div class="sla-chat-hero-media">
|
||||
<img class="roll-image" src="{{{tableImg}}}" />
|
||||
</div>
|
||||
</div>
|
||||
{{{parsedRollResult.outcomeHtml}}}
|
||||
<div class="description sla-chat-section" style="margin-bottom: 10px;">
|
||||
|
||||
<div class="description sla-chat-section">
|
||||
<div class="body">{{{msgDesc}}}</div>
|
||||
</div>
|
||||
{{{parsedRollResult.rollHtml}}}
|
||||
|
||||
<div class="sla-chat-rollframe">
|
||||
{{{parsedRollResult.outcomeHtml}}}
|
||||
{{{parsedRollResult.rollHtml}}}
|
||||
</div>
|
||||
|
||||
{{#if secondRoll}}
|
||||
<div class="description sla-chat-section" style="margin-bottom: 10px;">
|
||||
<div class="description sla-chat-section">
|
||||
{{#if (compare 'str' specialRoll '===' 'panicCheck')}}
|
||||
<div class="body">{{ localize 'Mosh.RollingForPanicEffect' }}</div>
|
||||
{{/if}}
|
||||
@@ -19,34 +47,57 @@
|
||||
<div class="body">{{ localize 'Mosh.RollingForMaintenanceIssue' }}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{{parsedRollResult2.rollHtml}}}
|
||||
<div class="sla-chat-rollframe">
|
||||
{{{parsedRollResult2.rollHtml}}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if parsedRollResult.success}}
|
||||
<div class="description sla-chat-section sla-chat-section-success" style="margin-bottom : 20px;">{{{flavorText}}}</div>
|
||||
<div class="description sla-chat-section sla-chat-section-success">{{{flavorText}}}</div>
|
||||
{{else}}
|
||||
{{#if woundText}}
|
||||
{{{woundText}}}<br><br>
|
||||
<div class="sla-chat-section sla-chat-section-danger {{severityMeta.cssClass}}">
|
||||
<div class="sla-chat-section-heading">
|
||||
<span class="sla-chat-severity-pill {{severityMeta.cssClass}}">{{severityMeta.label}}</span>
|
||||
</div>
|
||||
{{{woundText}}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (compare 'str' tableResultType '===' 'document')}}
|
||||
<ol class="table-results">
|
||||
{{#each tableResult}}
|
||||
<li class="table-result flexrow" data-result-id="{{{this._id}}}">
|
||||
<img class="result-image" src="{{{this.img}}}">
|
||||
<div class="result-text"><a class="content-link broken" draggable="true" data-type="Item" data-uuid="undefined"><i class="fas fa-unlink"></i>{{{this.description}}}</a></div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
<div class="sla-chat-section sla-chat-section-target">
|
||||
<div class="sla-chat-section-title">Linked Results</div>
|
||||
<ol class="table-results">
|
||||
{{#each tableResult}}
|
||||
<li class="table-result flexrow" data-result-id="{{{this._id}}}">
|
||||
<img class="result-image" src="{{{this.img}}}">
|
||||
<div class="result-text"><a class="content-link broken" draggable="true" data-type="Item" data-uuid="undefined"><i class="fas fa-unlink"></i>{{{this.description}}}</a></div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (compare 'str' tableResultType '===' 'text')}}
|
||||
{{#if tableResultEdited}}
|
||||
<div class="description sla-chat-section sla-chat-section-danger" style="margin-bottom : 20px;">{{{tableResultEdited}}}{{{tableResultFooter}}}</div>
|
||||
<div class="description sla-chat-section sla-chat-section-danger {{severityMeta.cssClass}}">
|
||||
<div class="sla-chat-section-heading">
|
||||
<span class="sla-chat-severity-pill {{severityMeta.cssClass}}">{{severityMeta.label}}</span>
|
||||
</div>
|
||||
{{{tableResultEdited}}}{{{tableResultFooter}}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="description sla-chat-section sla-chat-section-danger" style="margin-bottom : 20px;">{{{tableResult.[0].description}}}{{{tableResultFooter}}}</div>
|
||||
<div class="description sla-chat-section sla-chat-section-danger {{severityMeta.cssClass}}">
|
||||
<div class="sla-chat-section-heading">
|
||||
<span class="sla-chat-severity-pill {{severityMeta.cssClass}}">{{severityMeta.label}}</span>
|
||||
</div>
|
||||
{{{tableResult.[0].description}}}{{{tableResultFooter}}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if (compare 'str' specialRoll '===' 'maintenanceCheck')}}
|
||||
{{#if critFail}}
|
||||
<br><br>@UUID[Compendium.mosh.macros_hotbar_1e.IgWvaabVqzKDzLL4]{Make a Panic Check}
|
||||
<div class="sla-chat-section sla-chat-section-danger">
|
||||
<div class="body">@UUID[Compendium.mosh.macros_hotbar_1e.IgWvaabVqzKDzLL4]{Make a Panic Check}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||