Fix as per CSV sheet tracking + creature explanation

This commit is contained in:
2026-05-17 17:43:33 +02:00
parent a572c66678
commit 374854cc8b
99 changed files with 2716 additions and 464 deletions
+13
View File
@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<!-- Claw / trait mark: three slash marks with a radiating spark -->
<g fill="none" stroke="#c0392b" stroke-linecap="round" stroke-linejoin="round">
<!-- Three diagonal claw marks -->
<line x1="22" y1="20" x2="48" y2="62" stroke-width="7" opacity="0.9"/>
<line x1="36" y1="18" x2="58" y2="60" stroke-width="7" opacity="0.75"/>
<line x1="50" y1="18" x2="68" y2="58" stroke-width="7" opacity="0.6"/>
<!-- Bolt / energy accent -->
<polyline points="60,30 52,50 64,50 54,72" stroke="#e74c3c" stroke-width="4" opacity="0.85"/>
</g>
<!-- Outer ring frame -->
<circle cx="50" cy="50" r="46" fill="none" stroke="#7f1d1d" stroke-width="3" opacity="0.5"/>
</svg>

After

Width:  |  Height:  |  Size: 750 B

+13
View File
@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<!-- Resonation icon: stylised signal/energy wave within a circuit frame -->
<circle cx="50" cy="50" r="46" fill="none" stroke="#c8a94a" stroke-width="3" opacity="0.85"/>
<circle cx="50" cy="50" r="36" fill="none" stroke="#c8a94a" stroke-width="1.5" opacity="0.45"/>
<!-- Wave / resonation lines -->
<path d="M18 50 Q26 34 34 50 Q42 66 50 50 Q58 34 66 50 Q74 66 82 50"
fill="none" stroke="#dd9b2a" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>
<!-- Corner circuit nodes -->
<circle cx="50" cy="18" r="4" fill="#c8a94a" opacity="0.9"/>
<circle cx="50" cy="82" r="4" fill="#c8a94a" opacity="0.9"/>
<line x1="50" y1="22" x2="50" y2="37" stroke="#c8a94a" stroke-width="2" opacity="0.6"/>
<line x1="50" y1="63" x2="50" y2="78" stroke="#c8a94a" stroke-width="2" opacity="0.6"/>
</svg>

After

Width:  |  Height:  |  Size: 892 B

+722 -95
View File
@@ -167,6 +167,39 @@
border: 1px solid rgba(196, 154, 69, 0.16); border: 1px solid rgba(196, 154, 69, 0.16);
border-radius: calc(8px - 4px); border-radius: calc(8px - 4px);
} }
.application.mgne fieldset .editor,
.application.mgne fieldset .editor-content,
.application.mgne fieldset .ProseMirror,
.application.mgne fieldset .document-editor {
color: #d9c6ae;
background: transparent;
}
.application.mgne fieldset .editor-content p,
.application.mgne fieldset .ProseMirror p,
.application.mgne fieldset .editor-content li,
.application.mgne fieldset .ProseMirror li {
color: #d9c6ae;
}
.application.mgne fieldset .editor-content h1,
.application.mgne fieldset .ProseMirror h1,
.application.mgne fieldset .editor-content h2,
.application.mgne fieldset .ProseMirror h2,
.application.mgne fieldset .editor-content h3,
.application.mgne fieldset .ProseMirror h3 {
color: #c49a45;
}
.application.mgne fieldset .editor-content .is-empty::before,
.application.mgne fieldset .ProseMirror .is-empty::before {
color: rgba(196, 154, 69, 0.4);
}
.application.mgne fieldset .editor-menu button {
color: #ab8b68;
background: rgba(19, 16, 15, 0.55);
}
.application.mgne fieldset .editor-menu button:hover {
color: #d9c6ae;
background: rgba(196, 154, 69, 0.2);
}
.application.mgne legend { .application.mgne legend {
font-family: "CastorTwoMGNE", "Palatino Linotype", serif; font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
text-transform: uppercase; text-transform: uppercase;
@@ -337,10 +370,15 @@
min-width: 0; min-width: 0;
} }
.application.mgne .condition-value-grid { .application.mgne .condition-value-grid {
grid-template-columns: max-content 1fr; grid-template-columns: max-content minmax(auto, 9rem);
align-items: center; align-items: center;
margin-bottom: 0.6rem; margin-bottom: 0.6rem;
} }
.application.mgne .condition-value-grid select {
width: auto;
min-width: 5rem;
max-width: 9rem;
}
.application.mgne .condition-flag-grid { .application.mgne .condition-flag-grid {
grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr)); grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
gap: 0.3rem 0.6rem; gap: 0.3rem 0.6rem;
@@ -375,7 +413,6 @@
.application.mgne .resource-box-compact > label { .application.mgne .resource-box-compact > label {
font-size: 0.68rem; font-size: 0.68rem;
letter-spacing: 0.11em; letter-spacing: 0.11em;
color: #ab8b68;
} }
.application.mgne .resource-box-inline { .application.mgne .resource-box-inline {
flex-direction: row; flex-direction: row;
@@ -610,25 +647,6 @@
justify-content: flex-start; justify-content: flex-start;
margin-top: 0.12rem; margin-top: 0.12rem;
} }
.application.mgne .resonation-blocked-banner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.4rem;
margin-top: 0.2rem;
padding: 0.28rem 0.5rem;
border-radius: 4px;
background: rgba(180, 40, 20, 0.22);
border-left: 3px solid rgba(220, 80, 40, 0.7);
color: #e69062;
font-size: 0.73rem;
}
.application.mgne .resonation-blocked-banner .btn-clear-block {
flex-shrink: 0;
padding: 0.1rem 0.5rem;
font-size: 0.7rem;
cursor: pointer;
}
.application.mgne .resource-box-actions-rest { .application.mgne .resource-box-actions-rest {
gap: 0.32rem; gap: 0.32rem;
flex-wrap: wrap; flex-wrap: wrap;
@@ -695,10 +713,22 @@
gap: 0.35rem; gap: 0.35rem;
align-items: center; align-items: center;
width: fit-content; width: fit-content;
padding: 0.1rem 0.3rem 0.1rem 0.2rem;
border-radius: 4px;
transition: background 0.15s, color 0.15s;
} }
.application.mgne .checkbox-line.active { .application.mgne .checkbox-line.active {
color: #dd6b2d; color: #f5dfa0;
font-weight: 600; font-weight: 600;
text-shadow: 0 0 8px rgba(196, 154, 69, 0.55);
background: rgba(196, 154, 69, 0.14);
border: 1px solid rgba(196, 154, 69, 0.28);
margin: -1px;
}
.application.mgne .checkbox-line:not(.active) {
color: #ab8b68;
border: 1px solid transparent;
margin: -1px;
} }
.application.mgne .check-grid input[type="checkbox"], .application.mgne .check-grid input[type="checkbox"],
.application.mgne .checkbox-line input[type="checkbox"] { .application.mgne .checkbox-line input[type="checkbox"] {
@@ -787,6 +817,572 @@
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
} }
.item-weight-badge {
display: inline-flex;
align-items: center;
padding: 0 0.45em;
border-radius: 3px;
font-family: "LoraMGNE", "Book Antiqua", serif;
font-size: 0.7em;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
white-space: nowrap;
border: 1px solid transparent;
}
.item-weight-badge.weight-trivial {
color: rgba(171, 139, 104, 0.45);
border-color: rgba(171, 139, 104, 0.2);
}
.item-weight-badge.weight-light {
color: #ab8b68;
border-color: rgba(171, 139, 104, 0.35);
background: rgba(171, 139, 104, 0.08);
}
.item-weight-badge.weight-normal {
color: #c49a45;
border-color: rgba(196, 154, 69, 0.4);
background: rgba(196, 154, 69, 0.1);
}
.item-weight-badge.weight-heavy {
color: #7f1d17;
border-color: rgba(127, 29, 23, 0.45);
background: rgba(127, 29, 23, 0.12);
}
.load-display {
font-weight: 700;
letter-spacing: 0.04em;
cursor: default;
}
.load-overloaded {
color: #7f1d17 !important;
text-shadow: 0 0 6px rgba(127, 29, 23, 0.5);
}
.resource-box-overloaded {
border-color: rgba(127, 29, 23, 0.6) !important;
background: rgba(127, 29, 23, 0.1) !important;
}
.resource-box-overloaded .resource-label-accent {
color: #7f1d17 !important;
}
.item-name[data-tooltip] {
cursor: help;
}
.item-name[data-tooltip]:hover {
text-decoration: underline dotted rgba(196, 154, 69, 0.55);
}
#tooltip.mgne-item-tooltip {
max-width: 22rem;
font-family: "LoraMGNE", "Book Antiqua", serif;
font-size: 0.82rem;
line-height: 1.5;
color: #ccb292;
background: rgba(51, 38, 30, 0.66);
border: 1px solid rgba(196, 154, 69, 0.35);
border-radius: 4px;
padding: 0.5rem 0.7rem;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.55);
}
.item-broken-badge {
display: inline-block;
margin-left: 0.3em;
padding: 0.05em 0.4em;
font-size: 0.65em;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #7f1d17;
background: rgba(127, 29, 23, 0.14);
border: 1px solid rgba(127, 29, 23, 0.45);
border-radius: 3px;
vertical-align: middle;
}
.item-burned-badge {
color: #c49a45;
background: rgba(196, 154, 69, 0.12);
border-color: rgba(196, 154, 69, 0.4);
}
.item-durability-badge {
display: inline-flex;
align-items: center;
gap: 0.25em;
padding: 0.1em 0.45em;
font-size: 0.7em;
font-weight: 700;
color: rgba(171, 139, 104, 0.65);
border: 1px solid rgba(171, 139, 104, 0.2);
border-radius: 3px;
white-space: nowrap;
letter-spacing: 0.03em;
}
.item-durability-badge.durability-broken {
color: #7f1d17;
border-color: rgba(127, 29, 23, 0.4);
background: rgba(127, 29, 23, 0.1);
text-decoration: line-through;
}
.item-row-broken {
opacity: 0.6;
filter: grayscale(0.4);
}
.item-row-broken .item-name {
text-decoration: line-through;
color: rgba(204, 178, 146, 0.55);
}
.weapon-properties-grid {
display: flex;
flex-wrap: wrap;
gap: 0.35rem 0.6rem;
padding: 0.5rem 0.6rem;
background: rgba(171, 139, 104, 0.04);
border: 1px solid rgba(171, 139, 104, 0.12);
border-radius: 4px;
margin-bottom: 0.5rem;
}
.weapon-properties-grid .property-check {
display: flex;
align-items: center;
gap: 0.3em;
font-family: "LoraMGNE", "Book Antiqua", serif;
font-size: 0.82rem;
color: #ccb292;
cursor: pointer;
white-space: nowrap;
padding: 0.15em 0.4em;
border-radius: 3px;
transition: background 0.15s;
}
.weapon-properties-grid .property-check:hover {
background: rgba(196, 154, 69, 0.08);
}
.weapon-properties-grid .property-check input[type="checkbox"] {
accent-color: #c49a45;
}
.weapon-property-badge {
display: inline-block;
margin-left: 0.3em;
padding: 0.05em 0.35em;
font-size: 0.62em;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: rgba(196, 154, 69, 0.85);
background: rgba(196, 154, 69, 0.1);
border: 1px solid rgba(196, 154, 69, 0.3);
border-radius: 3px;
vertical-align: middle;
}
.creature-trait-trigger {
font-size: 0.82em;
color: rgba(204, 178, 146, 0.65);
font-style: italic;
margin-left: 0.2em;
}
.creature-traits-container {
border: 1px solid rgba(127, 29, 23, 0.35);
border-left: 3px solid rgba(127, 29, 23, 0.65);
border-radius: 4px;
background: linear-gradient(135deg, rgba(127, 29, 23, 0.05), transparent 60%), rgba(64, 47, 37, 0.85);
padding: 0.6rem 0.75rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.creature-traits-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
border-bottom: 1px solid rgba(127, 29, 23, 0.25);
padding-bottom: 0.4rem;
margin-bottom: 0.1rem;
}
.creature-traits-header h3 {
margin: 0;
font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
font-size: 0.82rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: rgba(127, 29, 23, 0.9);
}
.creature-traits-header button {
font-size: 0.72rem;
padding: 0.15em 0.6em;
background: rgba(127, 29, 23, 0.12);
border: 1px solid rgba(127, 29, 23, 0.35);
color: rgba(127, 29, 23, 0.8);
border-radius: 3px;
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.creature-traits-header button:hover {
background: rgba(127, 29, 23, 0.22);
color: #aa271f;
}
.creature-traits-list {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.creature-trait-card {
background: rgba(19, 16, 15, 0.3);
border: 1px solid rgba(127, 29, 23, 0.2);
border-radius: 3px;
padding: 0.35rem 0.5rem;
transition: background 0.15s, border-color 0.15s;
}
.creature-trait-card:hover {
background: rgba(127, 29, 23, 0.08);
border-color: rgba(127, 29, 23, 0.35);
}
.creature-trait-card-header {
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
}
.creature-trait-name {
display: flex;
align-items: center;
gap: 0.35em;
font-family: "LoraMGNE", "Book Antiqua", serif;
font-size: 0.88rem;
font-weight: 600;
color: #ccb292;
flex: 1;
min-width: 0;
cursor: default;
}
.creature-trait-name i {
color: rgba(127, 29, 23, 0.7);
font-size: 0.75em;
}
.creature-trait-trigger {
font-size: 0.75rem;
font-style: italic;
color: rgba(204, 178, 146, 0.55);
white-space: nowrap;
}
.creature-trait-card .item-actions {
display: flex;
gap: 0.25rem;
margin-left: auto;
flex-shrink: 0;
}
.creature-trait-card .item-actions button {
width: 1.6rem;
height: 1.6rem;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: 1px solid rgba(171, 139, 104, 0.18);
color: rgba(171, 139, 104, 0.5);
border-radius: 3px;
cursor: pointer;
font-size: 0.72rem;
transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.creature-trait-card .item-actions button:hover {
background: rgba(171, 139, 104, 0.1);
color: #ab8b68;
border-color: rgba(171, 139, 104, 0.35);
}
.creature-meta-row {
display: flex;
align-items: center;
gap: 1rem;
flex-wrap: wrap;
padding: 0.45rem 0.6rem;
background: rgba(64, 47, 37, 0.5);
border: 1px solid rgba(183, 70, 31, 0.2);
border-radius: 4px;
}
.creature-meta-label {
font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.07em;
color: rgba(183, 70, 31, 0.8);
white-space: nowrap;
}
.creature-type-group {
display: flex;
align-items: center;
gap: 0.75rem;
flex: 1;
}
.creature-type-checkbox {
display: flex;
align-items: center;
gap: 0.3em;
font-size: 0.83rem;
color: #ccb292;
cursor: pointer;
user-select: none;
}
.creature-type-checkbox input[type="checkbox"] {
accent-color: #b7461f;
width: 0.9rem;
height: 0.9rem;
cursor: pointer;
}
.creature-number-group {
display: flex;
align-items: center;
gap: 0.4rem;
flex-shrink: 0;
}
.creature-number-input {
width: 4rem;
text-align: center;
font-family: "LoraMGNE", "Book Antiqua", serif;
font-size: 0.9rem;
color: #ccb292;
background: rgba(17, 12, 10, 0.72);
border: 1px solid rgba(183, 70, 31, 0.3);
border-radius: 3px;
padding: 0.15em 0.3em;
}
.creature-number-input:focus {
outline: none;
border-color: rgba(183, 70, 31, 0.6);
background: rgba(17, 12, 10, 0.9);
}
.creature-action-table-section .action-table-drop-zone {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
border-radius: 4px;
min-height: 2.4rem;
font-size: 0.88rem;
color: #ccb292;
}
.creature-action-table-section .action-table-drop-zone i {
font-size: 1em;
color: #b7461f;
}
.creature-action-table-section .action-table-drop-zone .action-table-name {
font-weight: 600;
font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
color: #ccb292;
}
.creature-action-table-section .action-table-drop-zone.drop-hint {
border: 2px dashed rgba(183, 70, 31, 0.35);
background: rgba(183, 70, 31, 0.04);
color: rgba(204, 178, 146, 0.5);
font-style: italic;
justify-content: center;
cursor: default;
transition: background 0.15s, border-color 0.15s;
}
.creature-action-table-section .action-table-drop-zone.drop-hint i {
color: rgba(183, 70, 31, 0.5);
}
.creature-action-table-section .action-table-drop-zone.drop-hint:hover,
.creature-action-table-section .action-table-drop-zone.drop-hint.drag-over {
background: rgba(183, 70, 31, 0.09);
border-color: rgba(183, 70, 31, 0.6);
color: rgba(204, 178, 146, 0.7);
}
.creature-action-table-section .action-table-drop-zone.drop-hint:hover i,
.creature-action-table-section .action-table-drop-zone.drop-hint.drag-over i {
color: #b7461f;
}
.creature-action-table-section .action-table-buttons {
display: flex;
gap: 0.3rem;
align-items: center;
}
.creature-action-table-section .action-table-buttons button {
font-size: 0.75rem;
padding: 0.15em 0.55em;
background: rgba(183, 70, 31, 0.1);
border: 1px solid rgba(183, 70, 31, 0.3);
color: rgba(183, 70, 31, 0.8);
border-radius: 3px;
cursor: pointer;
transition: background 0.15s, color 0.15s;
display: flex;
align-items: center;
gap: 0.3em;
}
.creature-action-table-section .action-table-buttons button:hover {
background: rgba(183, 70, 31, 0.22);
color: #b7461f;
}
.application.mgne.party .party-credits-row {
display: flex;
align-items: center;
gap: 0.6rem;
margin-top: 0.5rem;
padding: 0.4rem 0.6rem;
border: 1px solid rgba(196, 154, 69, 0.35);
border-radius: 6px;
background: rgba(17, 12, 10, 0.7);
}
.application.mgne.party .party-credits-stepper {
display: flex;
align-items: center;
gap: 0.3rem;
}
.application.mgne.party .credits-input {
width: 5rem;
text-align: center;
font-size: 1.05rem;
font-weight: 700;
font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
color: #c49a45;
background: rgba(19, 16, 15, 0.6);
border: 1px solid rgba(196, 154, 69, 0.3);
border-radius: 6px;
padding: 0.2rem 0.4rem;
}
.application.mgne.party .credits-symbol {
color: #c49a45;
font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
font-size: 1.1rem;
font-weight: 700;
margin: 0 0.1rem;
}
.application.mgne.party .credits-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 2rem;
padding: 0.15rem 0.4rem;
font-size: 0.75rem;
font-weight: 700;
border: 1px solid rgba(171, 139, 104, 0.28);
border-radius: 6px;
background: rgba(17, 12, 10, 0.8);
color: #ab8b68;
cursor: pointer;
user-select: none;
transition: background 0.15s, border-color 0.15s;
}
.application.mgne.party .credits-btn:hover {
background: rgba(196, 154, 69, 0.18);
border-color: rgba(196, 154, 69, 0.55);
color: #c49a45;
}
.application.mgne.party .party-member-list {
display: flex;
flex-direction: column;
gap: 0.25rem;
margin-top: 0.4rem;
}
.application.mgne.party .party-member-row {
display: grid;
grid-template-columns: 2rem 1fr 5rem 6rem 5.5rem;
gap: 0.5rem;
align-items: center;
padding: 0.3rem 0.4rem;
border-radius: 6px;
}
.application.mgne.party .party-member-row.party-member-header {
color: #7e664f;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 1px solid rgba(171, 139, 104, 0.18);
padding-bottom: 0.25rem;
margin-bottom: 0.1rem;
}
.application.mgne.party .party-member-row:not(.party-member-header) {
background: rgba(17, 12, 10, 0.75);
border: 1px solid transparent;
}
.application.mgne.party .party-member-row:not(.party-member-header):hover {
border-color: rgba(196, 154, 69, 0.22);
background: rgba(17, 12, 10, 0.9);
}
.application.mgne.party .party-member-portrait {
width: 2rem;
height: 2rem;
object-fit: cover;
border-radius: 3px;
border: 1px solid rgba(171, 139, 104, 0.25);
}
.application.mgne.party .party-member-name a {
color: #ccb292;
font-weight: 700;
font-size: 0.9rem;
}
.application.mgne.party .party-member-name a:hover {
color: #c49a45;
}
.application.mgne.party .party-member-hp {
color: #dd6b2d;
font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
font-size: 0.82rem;
font-weight: 700;
text-align: center;
}
.application.mgne.party .party-member-type {
color: #7e664f;
font-size: 0.78rem;
font-style: italic;
}
.application.mgne.party .party-loot-list {
display: flex;
flex-direction: column;
gap: 0.25rem;
margin-top: 0.4rem;
}
.application.mgne.party .party-loot-row {
display: grid;
grid-template-columns: 2rem 1fr 7rem 4rem;
gap: 0.5rem;
align-items: center;
padding: 0.3rem 0.4rem;
border-radius: 6px;
}
.application.mgne.party .party-loot-row.party-loot-header {
color: #7e664f;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 1px solid rgba(171, 139, 104, 0.18);
padding-bottom: 0.25rem;
margin-bottom: 0.1rem;
}
.application.mgne.party .party-loot-row:not(.party-loot-header) {
background: rgba(17, 12, 10, 0.75);
border: 1px solid transparent;
}
.application.mgne.party .party-loot-row:not(.party-loot-header):hover {
border-color: rgba(196, 154, 69, 0.22);
background: rgba(17, 12, 10, 0.9);
}
.application.mgne.party .item-portrait {
width: 2rem;
height: 2rem;
object-fit: cover;
border-radius: 3px;
border: 1px solid rgba(171, 139, 104, 0.25);
}
.application.mgne.party .item-type-label {
color: #7e664f;
font-size: 0.78rem;
font-style: italic;
}
.application.mgne.party .party-drop-hint {
color: rgba(126, 102, 79, 0.7);
font-size: 0.75rem;
font-style: italic;
text-align: center;
margin-top: 0.5rem;
padding: 0.3rem;
border: 1px dashed rgba(171, 139, 104, 0.18);
border-radius: 6px;
}
.application.mgne.roll-dialog .window-content { .application.mgne.roll-dialog .window-content {
padding: 0.55rem; padding: 0.55rem;
background: radial-gradient(circle at top left, rgba(221, 107, 45, 0.14), transparent 26%), linear-gradient(180deg, rgba(27, 21, 18, 0.26), rgba(19, 16, 15, 0.22)), url("../assets/ui/page_background.webp") center center / cover no-repeat, linear-gradient(180deg, #1b1512, #13100f 120%); background: radial-gradient(circle at top left, rgba(221, 107, 45, 0.14), transparent 26%), linear-gradient(180deg, rgba(27, 21, 18, 0.26), rgba(19, 16, 15, 0.22)), url("../assets/ui/page_background.webp") center center / cover no-repeat, linear-gradient(180deg, #1b1512, #13100f 120%);
@@ -895,52 +1491,117 @@
} }
.mgne-chat-card .chat-formula { .mgne-chat-card .chat-formula {
color: #52453c; color: #52453c;
font-size: 0.72rem;
font-style: italic;
margin: 0;
} }
.mgne-chat-card .chat-formula code { .mgne-chat-card .chat-formula code {
color: #ab8b68; color: #ccb292;
background: rgba(19, 16, 15, 0.65); background: rgba(19, 16, 15, 0.75);
padding: 0.08em 0.38em; padding: 0.1em 0.42em;
border-radius: 3px; border-radius: 3px;
font-style: normal;
} }
.mgne-chat-card .chat-outcome { .mgne-chat-card .chat-result-line {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: end;
gap: 0.6rem; padding: 0.38rem 0.55rem;
border: 1px solid rgba(196, 154, 69, 0.28);
border-radius: 6px;
background: rgba(19, 16, 15, 0.78);
}
.mgne-chat-card .chat-result-label {
font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 0.62rem;
color: #ccb292;
}
.mgne-chat-card .chat-result-total {
font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
font-size: 1.08rem;
color: #ccb292;
}
.mgne-chat-card .chat-outcome.has-tooltip,
.mgne-chat-card .chat-damage-total.has-tooltip {
cursor: pointer;
user-select: none;
}
.mgne-chat-card .chat-outcome.has-tooltip:hover,
.mgne-chat-card .chat-damage-total.has-tooltip:hover {
filter: brightness(1.12);
}
.mgne-chat-card .chat-outcome .chat-tooltip-icon,
.mgne-chat-card .chat-damage-total .chat-tooltip-icon {
opacity: 0.55;
font-size: 0.75em;
margin-left: 0.3rem;
vertical-align: middle;
transition: opacity 0.15s;
}
.mgne-chat-card .chat-outcome.tooltip-open .chat-tooltip-icon,
.mgne-chat-card .chat-damage-total.tooltip-open .chat-tooltip-icon {
opacity: 1;
}
.mgne-chat-card .chat-dice-tooltip {
margin-top: 0.4rem;
padding: 0.4rem 0.55rem;
background: rgba(19, 16, 15, 0.85);
border: 1px solid rgba(171, 139, 104, 0.22);
border-radius: 6px;
}
.mgne-chat-card .chat-dice-tooltip .dice-roll {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.mgne-chat-card .chat-dice-tooltip .dice-formula {
font-family: "Courier New", monospace;
font-size: 0.72rem;
color: rgba(204, 178, 146, 0.65);
}
.mgne-chat-card .chat-dice-tooltip .dice-tooltip {
font-size: 0.78rem;
}
.mgne-chat-card .chat-dice-tooltip .dice {
margin: 0.15rem 0;
}
.mgne-chat-card .chat-dice-tooltip .die-icon,
.mgne-chat-card .chat-dice-tooltip .die-face {
color: #c49a45;
font-size: 0.8rem;
}
.mgne-chat-card .chat-dice-tooltip .part-formula {
color: rgba(204, 178, 146, 0.7);
font-size: 0.72rem;
}
.mgne-chat-card .chat-dice-tooltip .part-total {
color: #ccb292;
font-weight: 600;
}
.mgne-chat-card .chat-dice-tooltip .max {
color: #82b0a6;
}
.mgne-chat-card .chat-dice-tooltip .min {
color: #e48a59;
}
.mgne-chat-card .chat-dice-tooltip .dice-total {
font-family: "CastorTwoMGNE", serif;
font-size: 1.05rem;
color: #c49a45;
text-align: right;
border-top: 1px solid rgba(171, 139, 104, 0.2);
padding-top: 0.2rem;
margin-top: 0.15rem;
}
.mgne-chat-card .chat-outcome {
font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
text-transform: uppercase;
letter-spacing: 0.08em;
margin: 0; margin: 0;
padding: 0.42rem 0.65rem 0.42rem 0.8rem; padding: 0.3rem 0.55rem;
border-radius: 6px; border-radius: 6px;
background: rgba(19, 16, 15, 0.72); background: rgba(19, 16, 15, 0.72);
border-left: 3px solid rgba(221, 107, 45, 0.7); border-left: 3px solid rgba(221, 107, 45, 0.7);
}
.mgne-chat-card .chat-outcome-label {
font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
text-transform: uppercase;
letter-spacing: 0.1em;
font-size: 0.88rem;
color: #e69062; color: #e69062;
line-height: 1.1; font-size: 0.8rem;
}
.mgne-chat-card .chat-outcome-total {
font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
font-size: 1.7rem;
line-height: 1;
color: rgba(204, 178, 146, 0.88);
text-shadow: 0 0 18px rgba(221, 107, 45, 0.18);
flex-shrink: 0;
}
.mgne-chat-card .chat-damage-total {
text-align: center;
font-family: "CastorTwoMGNE", "Palatino Linotype", serif;
font-size: 2.2rem;
line-height: 1;
color: #e38450;
text-shadow: 0 0 20px rgba(183, 70, 31, 0.3);
padding: 0.3rem 0;
} }
.mgne-chat-card .chat-special { .mgne-chat-card .chat-special {
padding: 0.42rem 0.55rem; padding: 0.42rem 0.55rem;
@@ -948,26 +1609,6 @@
background: rgba(17, 12, 10, 0.85); background: rgba(17, 12, 10, 0.85);
color: #ccb292; color: #ccb292;
} }
.mgne-chat-card .chat-omen-remind {
padding: 0.38rem 0.55rem;
border-left: 3px solid rgba(196, 154, 69, 0.6);
background: rgba(30, 22, 8, 0.82);
color: #c9a84c;
font-size: 0.75rem;
margin: 0;
font-style: italic;
}
.mgne-chat-card .chat-omen-neutralize {
border-left-color: rgba(79, 125, 115, 0.7);
color: #9bc0b8;
}
.mgne-chat-card .chat-omen-used {
padding: 0.28rem 0.55rem;
color: #c9a84c;
font-size: 0.74rem;
margin: 0;
font-style: italic;
}
.mgne-chat-card.outcome-critical-success, .mgne-chat-card.outcome-critical-success,
.mgne-chat-card.outcome-success, .mgne-chat-card.outcome-success,
.mgne-chat-card.outcome-steady { .mgne-chat-card.outcome-steady {
@@ -976,18 +1617,8 @@
.mgne-chat-card.outcome-critical-success .chat-outcome, .mgne-chat-card.outcome-critical-success .chat-outcome,
.mgne-chat-card.outcome-success .chat-outcome, .mgne-chat-card.outcome-success .chat-outcome,
.mgne-chat-card.outcome-steady .chat-outcome { .mgne-chat-card.outcome-steady .chat-outcome {
border-left-color: rgba(79, 125, 115, 0.8);
}
.mgne-chat-card.outcome-critical-success .chat-outcome-label,
.mgne-chat-card.outcome-success .chat-outcome-label,
.mgne-chat-card.outcome-steady .chat-outcome-label {
color: #9bc0b8; color: #9bc0b8;
} border-left-color: rgba(79, 125, 115, 0.8);
.mgne-chat-card.outcome-critical-success .chat-outcome-total,
.mgne-chat-card.outcome-success .chat-outcome-total,
.mgne-chat-card.outcome-steady .chat-outcome-total {
color: rgba(155, 192, 184, 0.9);
text-shadow: 0 0 18px rgba(79, 125, 115, 0.3);
} }
.mgne-chat-card.outcome-failure, .mgne-chat-card.outcome-failure,
.mgne-chat-card.outcome-broken, .mgne-chat-card.outcome-broken,
@@ -997,15 +1628,11 @@
.mgne-chat-card.outcome-failure .chat-outcome, .mgne-chat-card.outcome-failure .chat-outcome,
.mgne-chat-card.outcome-broken .chat-outcome, .mgne-chat-card.outcome-broken .chat-outcome,
.mgne-chat-card.outcome-fumble .chat-outcome { .mgne-chat-card.outcome-fumble .chat-outcome {
color: #eaa37c;
border-left-color: rgba(221, 107, 45, 0.8); border-left-color: rgba(221, 107, 45, 0.8);
} }
.mgne-chat-card.outcome-failure .chat-outcome-label, .mgne-chat-card.mode-apply-damage .chat-result-total,
.mgne-chat-card.outcome-broken .chat-outcome-label, .mgne-chat-card.mode-damage .chat-result-total {
.mgne-chat-card.outcome-fumble .chat-outcome-label {
color: #eaa37c;
}
.mgne-chat-card.mode-apply-damage .chat-damage-total,
.mgne-chat-card.mode-damage .chat-damage-total {
color: #e38450; color: #e38450;
} }
.mgne-chat-card .chat-card-actions { .mgne-chat-card .chat-card-actions {
+21
View File
@@ -20,6 +20,7 @@ Hooks.once("init", () => {
character: models.MGNECharacter, character: models.MGNECharacter,
creature: models.MGNECreature, creature: models.MGNECreature,
companion: models.MGNECompanion, companion: models.MGNECompanion,
party: models.MGNEParty,
} }
CONFIG.Combat.documentClass = documents.MGNECombat CONFIG.Combat.documentClass = documents.MGNECombat
@@ -32,6 +33,7 @@ Hooks.once("init", () => {
"resonance-core": models.MGNEResonanceCore, "resonance-core": models.MGNEResonanceCore,
artifact: models.MGNEArtifact, artifact: models.MGNEArtifact,
feature: models.MGNEFeature, feature: models.MGNEFeature,
"creature-trait": models.MGNECreatureTrait,
} }
foundry.applications.sheets.ActorSheetV2 && foundry.documents.collections.Actors.unregisterSheet( foundry.applications.sheets.ActorSheetV2 && foundry.documents.collections.Actors.unregisterSheet(
@@ -43,6 +45,7 @@ Hooks.once("init", () => {
foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, applications.MGNECharacterSheet, { types: ["character"], makeDefault: true, label: SYSTEM.actorTypes.character.label }) foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, applications.MGNECharacterSheet, { types: ["character"], makeDefault: true, label: SYSTEM.actorTypes.character.label })
foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, applications.MGNECreatureSheet, { types: ["creature"], makeDefault: true, label: SYSTEM.actorTypes.creature.label }) foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, applications.MGNECreatureSheet, { types: ["creature"], makeDefault: true, label: SYSTEM.actorTypes.creature.label })
foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, applications.MGNECompanionSheet, { types: ["companion"], makeDefault: true, label: SYSTEM.actorTypes.companion.label }) foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, applications.MGNECompanionSheet, { types: ["companion"], makeDefault: true, label: SYSTEM.actorTypes.companion.label })
foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, applications.MGNEPartySheet, { types: ["party"], makeDefault: true, label: SYSTEM.actorTypes.party.label })
foundry.applications.sheets.ItemSheetV2 && foundry.documents.collections.Items.unregisterSheet( foundry.applications.sheets.ItemSheetV2 && foundry.documents.collections.Items.unregisterSheet(
"core", "core",
@@ -57,8 +60,15 @@ Hooks.once("init", () => {
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEResonanceCoreSheet, { types: ["resonance-core"], makeDefault: true, label: SYSTEM.itemTypes["resonance-core"].label }) foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEResonanceCoreSheet, { types: ["resonance-core"], makeDefault: true, label: SYSTEM.itemTypes["resonance-core"].label })
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEArtifactSheet, { types: ["artifact"], makeDefault: true, label: SYSTEM.itemTypes.artifact.label }) foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEArtifactSheet, { types: ["artifact"], makeDefault: true, label: SYSTEM.itemTypes.artifact.label })
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEFeatureSheet, { types: ["feature"], makeDefault: true, label: SYSTEM.itemTypes.feature.label }) foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEFeatureSheet, { types: ["feature"], makeDefault: true, label: SYSTEM.itemTypes.feature.label })
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNECreatureTraitSheet, { types: ["creature-trait"], makeDefault: true, label: SYSTEM.itemTypes["creature-trait"].label })
Handlebars.registerHelper("isEqual", (left, right) => left === right) Handlebars.registerHelper("isEqual", (left, right) => left === right)
Handlebars.registerHelper("includes", (collection, value) => {
if (!collection) return false
if (collection instanceof Set) return collection.has(value)
if (Array.isArray(collection)) return collection.includes(value)
return false
})
}) })
Hooks.once("setup", () => { Hooks.once("setup", () => {
@@ -111,6 +121,17 @@ Hooks.on("renderChatMessageHTML", (message, element) => {
const root = element instanceof HTMLElement ? element : element?.[0] const root = element instanceof HTMLElement ? element : element?.[0]
if (!root) return if (!root) return
// Dice tooltip toggle
root.querySelectorAll("[data-action='toggle-dice-tooltip']").forEach(trigger => {
trigger.addEventListener("click", () => {
const tooltip = trigger.closest(".chat-card-body")?.querySelector(".chat-dice-tooltip")
if (!tooltip) return
const isHidden = tooltip.hidden
tooltip.hidden = !isHidden
trigger.classList.toggle("tooltip-open", isHidden)
})
})
root.querySelectorAll(".mgne-roll-damage-btn").forEach(btn => { root.querySelectorAll(".mgne-roll-damage-btn").forEach(btn => {
btn.addEventListener("click", async () => { btn.addEventListener("click", async () => {
const actorId = btn.dataset.actorId const actorId = btn.dataset.actorId
+145 -33
View File
@@ -4,7 +4,8 @@
"ActorTypes": { "ActorTypes": {
"character": "Character", "character": "Character",
"creature": "Creature", "creature": "Creature",
"companion": "Companion" "companion": "Companion",
"party": "Party"
}, },
"ItemTypes": { "ItemTypes": {
"weapon": "Weapon", "weapon": "Weapon",
@@ -13,7 +14,8 @@
"equipment": "Equipment", "equipment": "Equipment",
"resonance-core": "Resonance Core", "resonance-core": "Resonance Core",
"artifact": "Artifact", "artifact": "Artifact",
"feature": "Feature" "feature": "Feature",
"creature-trait": "Creature Trait"
}, },
"Abilities": { "Abilities": {
"agility": "Agility", "agility": "Agility",
@@ -37,6 +39,24 @@
"melee": "Melee", "melee": "Melee",
"ranged": "Ranged" "ranged": "Ranged"
}, },
"WeaponProperties": {
"ammo": "Ammo",
"awkward": "Awkward",
"binding": "Binding",
"durant": "Durant",
"finesse": "Finesse",
"fling": "Fling",
"fragile": "Fragile",
"glinting": "Glinting",
"overbearing": "Overbearing",
"parrying": "Parrying",
"precise": "Precise",
"razored": "Razored",
"ringing": "Ringing",
"two-handed": "Two-Handed",
"unwieldy": "Unwieldy",
"versatile": "Versatile"
},
"Resonations": { "Resonations": {
"accelerate": "Accelerate", "accelerate": "Accelerate",
"blast": "Blast", "blast": "Blast",
@@ -71,7 +91,9 @@
"daily": "Daily Resources", "daily": "Daily Resources",
"equipment": "Equipment", "equipment": "Equipment",
"features": "Features", "features": "Features",
"notes": "Notes" "notes": "Notes",
"members": "Members",
"loot": "Loot"
}, },
"Character": { "Character": {
"Background": "Background", "Background": "Background",
@@ -92,6 +114,7 @@
"ResonancePerDay": "Resonance per Day", "ResonancePerDay": "Resonance per Day",
"ArtifactSync": "Artifact Sync", "ArtifactSync": "Artifact Sync",
"CarryingCapacity": "Carrying Capacity", "CarryingCapacity": "Carrying Capacity",
"Load": "Load",
"Rations": "Rations", "Rations": "Rations",
"Kiffol": "Kiffol", "Kiffol": "Kiffol",
"Weapons": "Weapons", "Weapons": "Weapons",
@@ -231,22 +254,23 @@
}, },
"Creature": { "Creature": {
"Special": "Special", "Special": "Special",
"Traits": "Traits",
"AddTrait": "Add Trait",
"Type": "Type",
"Number": "Number",
"ActionTable": "Action Table",
"RollAction": "Roll Action",
"DropTableHint": "Drop a Roll Table here to link it",
"ClearTable": "Clear",
"OpenTable": "Open Table",
"NoTableLinked": "No action table is linked to this creature.",
"TableNotFound": "The linked action table could not be found.",
"Types": {
"Human": "Human",
"Construct": "Construct",
"Animal": "Animal"
},
"FIELDS": { "FIELDS": {
"abilities": {
"label": "Abilities",
"agility": {
"label": "Agility"
},
"presence": {
"label": "Presence"
},
"strength": {
"label": "Strength"
},
"toughness": {
"label": "Toughness"
}
},
"hp": { "hp": {
"label": "HP", "label": "HP",
"value": { "value": {
@@ -265,20 +289,17 @@
"label": "Armor Die" "label": "Armor Die"
} }
}, },
"attack": { "creatureType": {
"label": "Attack", "label": "Type"
"damage": { },
"label": "Damage" "number": {
} "label": "Number"
},
"actionTableUuid": {
"label": "Action Table"
}, },
"description": { "description": {
"label": "Description" "label": "Description"
},
"special": {
"label": "Special"
},
"notes": {
"label": "Notes"
} }
} }
}, },
@@ -354,6 +375,27 @@
} }
} }
}, },
"Party": {
"Members": "Members",
"Loot": "Shared Loot",
"Credits": "Credits",
"MoveUp": "Move Up",
"MoveDown": "Move Down",
"RemoveMember": "Remove Member",
"DropMemberHint": "Drop a Character or Companion actor here to add them to the party.",
"DropLootHint": "Drop items here to add them to the shared loot.",
"FIELDS": {
"memberRefs": {
"label": "Members"
},
"credits": {
"label": "Credits"
},
"notes": {
"label": "Notes"
}
}
},
"DataModel": { "DataModel": {
"abilities": { "abilities": {
"agility": { "agility": {
@@ -373,6 +415,7 @@
"Common": { "Common": {
"Attack": "Attack", "Attack": "Attack",
"ArmorDie": "Armor Die", "ArmorDie": "Armor Die",
"ArmorSave": "Armor Save",
"ArtifactId": "Artifact Id", "ArtifactId": "Artifact Id",
"Broken": "Broken", "Broken": "Broken",
"BurnedOut": "Burned Out", "BurnedOut": "Burned Out",
@@ -383,6 +426,7 @@
"Current": "Current", "Current": "Current",
"Damage": "Damage", "Damage": "Damage",
"Depleted": "Depleted", "Depleted": "Depleted",
"Durability": "Durability",
"Defense": "Defense", "Defense": "Defense",
"Delete": "Delete", "Delete": "Delete",
"Description": "Description", "Description": "Description",
@@ -416,15 +460,23 @@
"Unsynchronized": "Unsynchronized", "Unsynchronized": "Unsynchronized",
"Usage": "Usage", "Usage": "Usage",
"UsageDie": "Usage Die", "UsageDie": "Usage Die",
"DurabilityDie": "Durability Die",
"RollDurability": "Roll Durability",
"Used": "Used", "Used": "Used",
"Value": "Value" "Value": "Value",
"Weight": "Weight",
"Name": "Name",
"Type": "Type"
}, },
"Empty": { "Empty": {
"NoArtifacts": "No artifacts yet.", "NoArtifacts": "No artifacts yet.",
"NoEquipment": "No equipment yet.", "NoEquipment": "No equipment yet.",
"NoFeatures": "No features yet.", "NoFeatures": "No features yet.",
"NoResonanceCores": "No resonance cores yet.", "NoResonanceCores": "No resonance cores yet.",
"NoWeapons": "No weapons yet." "NoTraits": "No traits yet.",
"NoWeapons": "No weapons yet.",
"NoMembers": "No members yet. Drop a character or companion here.",
"NoLoot": "No shared loot yet. Drop items here."
}, },
"RulesSnapshot": { "RulesSnapshot": {
"Checks": "Checks use d20 + ability vs DR.", "Checks": "Checks use d20 + ability vs DR.",
@@ -462,6 +514,8 @@
"ItemBroken": "{item} is broken.", "ItemBroken": "{item} is broken.",
"ItemBurnedOut": "{item} is burned out.", "ItemBurnedOut": "{item} is burned out.",
"ItemDepleted": "{item} is already depleted.", "ItemDepleted": "{item} is already depleted.",
"ItemDurabilityDepleted": "{item} durability is already depleted.",
"NoArmorEquipped": "No armor or shield equipped.",
"ResonancePerDayReached": "{actor} has already used all resonations for today.", "ResonancePerDayReached": "{actor} has already used all resonations for today.",
"ResonationBlocked": "{actor} cannot invoke Resonations — feedback block active (1 hour). Clear it manually once the hour has passed.", "ResonationBlocked": "{actor} cannot invoke Resonations — feedback block active (1 hour). Clear it manually once the hour has passed.",
"ResonationFeedbackBlocked": "Feedback! {actor} suffers D2 damage (bypasses armor) and cannot invoke Resonations for 1 hour.", "ResonationFeedbackBlocked": "Feedback! {actor} suffers D2 damage (bypasses armor) and cannot invoke Resonations for 1 hour.",
@@ -473,6 +527,7 @@
"ApplyDamageTo": "Apply {amount} damage to {target}", "ApplyDamageTo": "Apply {amount} damage to {target}",
"AppliedDamageText": "Applied damage: {amount}.", "AppliedDamageText": "Applied damage: {amount}.",
"ArmorAbsorbed": "Armor absorbed {amount}.", "ArmorAbsorbed": "Armor absorbed {amount}.",
"ArmorSave": "Armor Save",
"ArmorDegradedCritical": "Critical: {item} armor downgraded to {die}.", "ArmorDegradedCritical": "Critical: {item} armor downgraded to {die}.",
"ArmorNothingToDegrade": "Critical: no armor to downgrade.", "ArmorNothingToDegrade": "Critical: no armor to downgrade.",
"AttackFumble": "Attack fumble: the weapon breaks.", "AttackFumble": "Attack fumble: the weapon breaks.",
@@ -504,6 +559,9 @@
"ItemAttackLabel": "{item} Attack", "ItemAttackLabel": "{item} Attack",
"ItemDamageLabel": "{item} Damage", "ItemDamageLabel": "{item} Damage",
"ItemNowDepleted": "The item is depleted.", "ItemNowDepleted": "The item is depleted.",
"ItemNowBroken": "The item is now broken.",
"ItemDurabilityLabel": "{item} Durability",
"DurabilityLabel": "{item} Durability Check",
"ItemUsageLabel": "{item} Usage", "ItemUsageLabel": "{item} Usage",
"MoraleBrokenText": "The actor breaks down and may flee, surrender, or grant +d4 damage to opponents.", "MoraleBrokenText": "The actor breaks down and may flee, surrender, or grant +d4 damage to opponents.",
"MoraleCheck": "Morale Check", "MoraleCheck": "Morale Check",
@@ -536,8 +594,10 @@
"Mode": { "Mode": {
"action": "Action", "action": "Action",
"apply-damage": "Damage", "apply-damage": "Damage",
"armor": "Armor",
"check": "Check", "check": "Check",
"damage": "Damage", "damage": "Damage",
"durability": "Durability",
"flee": "Fleeing", "flee": "Fleeing",
"generic": "Roll", "generic": "Roll",
"morale": "Morale", "morale": "Morale",
@@ -573,6 +633,12 @@
}, },
"broken": { "broken": {
"label": "Broken" "label": "Broken"
},
"durabilityDie": {
"label": "Durability Die"
},
"weight": {
"label": "Weight"
} }
} }
}, },
@@ -592,6 +658,12 @@
}, },
"broken": { "broken": {
"label": "Broken" "label": "Broken"
},
"durabilityDie": {
"label": "Durability Die"
},
"weight": {
"label": "Weight"
} }
} }
}, },
@@ -611,6 +683,12 @@
}, },
"broken": { "broken": {
"label": "Broken" "label": "Broken"
},
"durabilityDie": {
"label": "Durability Die"
},
"weight": {
"label": "Weight"
} }
} }
}, },
@@ -636,6 +714,15 @@
}, },
"consumable": { "consumable": {
"label": "Consumable" "label": "Consumable"
},
"broken": {
"label": "Broken"
},
"durabilityDie": {
"label": "Durability Die"
},
"weight": {
"label": "Weight"
} }
} }
}, },
@@ -652,6 +739,15 @@
}, },
"burnedOut": { "burnedOut": {
"label": "Burned Out" "label": "Burned Out"
},
"broken": {
"label": "Broken"
},
"durabilityDie": {
"label": "Durability Die"
},
"weight": {
"label": "Weight"
} }
} }
}, },
@@ -674,6 +770,12 @@
}, },
"synchronizedTo": { "synchronizedTo": {
"label": "Synchronized To" "label": "Synchronized To"
},
"durabilityDie": {
"label": "Durability Die"
},
"weight": {
"label": "Weight"
} }
} }
}, },
@@ -686,13 +788,22 @@
"label": "Feature Id" "label": "Feature Id"
} }
} }
},
"CreatureTrait": {
"Trigger": "Trigger",
"TriggerPlaceholder": "e.g. When the creature attacks, on a critical hit…",
"FIELDS": {
"description": { "label": "Description" },
"trigger": { "label": "Trigger" }
}
} }
}, },
"TYPES": { "TYPES": {
"Actor": { "Actor": {
"character": "Character", "character": "Character",
"creature": "Creature", "creature": "Creature",
"companion": "Companion" "companion": "Companion",
"party": "Party"
}, },
"Item": { "Item": {
"weapon": "Weapon", "weapon": "Weapon",
@@ -701,7 +812,8 @@
"equipment": "Equipment", "equipment": "Equipment",
"resonance-core": "Resonance Core", "resonance-core": "Resonance Core",
"artifact": "Artifact", "artifact": "Artifact",
"feature": "Feature" "feature": "Feature",
"creature-trait": "Creature Trait"
} }
} }
} }
+25
View File
@@ -157,6 +157,31 @@
margin: 0; margin: 0;
} }
// ProseMirror / toggled editor inside fieldset — enforce contrast on dark bg
.application.mgne fieldset {
.editor,
.editor-content,
.ProseMirror,
.document-editor {
color: lighten(@parchment, 8%);
background: transparent;
}
.editor-content,
.ProseMirror {
p, li { color: lighten(@parchment, 8%); }
h1, h2, h3 { color: @gold-acid; }
.is-empty::before { color: fade(@gold-acid, 40%); }
}
.editor-menu button {
color: @bone;
background: fade(@bg-void, 55%);
&:hover {
color: lighten(@parchment, 8%);
background: fade(@gold-acid, 20%);
}
}
}
.application.mgne legend { .application.mgne legend {
.caps-heading(); .caps-heading();
color: @gold-acid; color: @gold-acid;
+61
View File
@@ -85,6 +85,67 @@
color: @parchment; color: @parchment;
} }
.mgne-chat-card .chat-outcome,
.mgne-chat-card .chat-damage-total {
&.has-tooltip {
cursor: pointer;
user-select: none;
&:hover { filter: brightness(1.12); }
}
.chat-tooltip-icon {
opacity: 0.55;
font-size: 0.75em;
margin-left: 0.3rem;
vertical-align: middle;
transition: opacity 0.15s;
}
&.tooltip-open .chat-tooltip-icon { opacity: 1; }
}
// Dice tooltip reveal panel
.mgne-chat-card .chat-dice-tooltip {
margin-top: 0.4rem;
padding: 0.4rem 0.55rem;
background: fade(@bg-void, 85%);
border: 1px solid fade(@bone, 22%);
border-radius: @radius-sm;
// Foundry's standard dice-roll markup
.dice-roll {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.dice-formula {
font-family: "Courier New", monospace;
font-size: 0.72rem;
color: fade(@parchment, 65%);
}
.dice-tooltip {
font-size: 0.78rem;
}
.dice {
margin: 0.15rem 0;
}
.die-icon, .die-face {
color: @gold-acid;
font-size: 0.8rem;
}
.part-formula { color: fade(@parchment, 70%); font-size: 0.72rem; }
.part-total { color: @parchment; font-weight: 600; }
.max { color: lighten(@verdigris, 20%); }
.min { color: lighten(@ember-bright, 10%); }
.dice-total {
font-family: "CastorTwoMGNE", serif;
font-size: 1.05rem;
color: @gold-acid;
text-align: right;
border-top: 1px solid fade(@bone, 20%);
padding-top: 0.2rem;
margin-top: 0.15rem;
}
}
.mgne-chat-card .chat-outcome { .mgne-chat-card .chat-outcome {
.caps-heading(); .caps-heading();
margin: 0; margin: 0;
+670 -2
View File
@@ -123,9 +123,15 @@
} }
.application.mgne .condition-value-grid { .application.mgne .condition-value-grid {
grid-template-columns: max-content 1fr; grid-template-columns: max-content minmax(auto, 9rem);
align-items: center; align-items: center;
margin-bottom: 0.6rem; margin-bottom: 0.6rem;
select {
width: auto;
min-width: 5rem;
max-width: 9rem;
}
} }
.application.mgne .condition-flag-grid { .application.mgne .condition-flag-grid {
@@ -530,10 +536,23 @@
gap: 0.35rem; gap: 0.35rem;
align-items: center; align-items: center;
width: fit-content; width: fit-content;
padding: 0.1rem 0.3rem 0.1rem 0.2rem;
border-radius: 4px;
transition: background 0.15s, color 0.15s;
&.active { &.active {
color: @ember-bright; color: #f5dfa0;
font-weight: 600; font-weight: 600;
text-shadow: 0 0 8px fade(@gold-acid, 55%);
background: fade(@gold-acid, 14%);
border: 1px solid fade(@gold-acid, 28%);
margin: -1px; // compensate border so layout doesn't shift
}
&:not(.active) {
color: @bone;
border: 1px solid transparent;
margin: -1px;
} }
} }
@@ -641,3 +660,652 @@
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
} }
// ─── Weight badges ────────────────────────────────────────────────────────────
.item-weight-badge {
display: inline-flex;
align-items: center;
padding: 0 0.45em;
border-radius: 3px;
font-family: @font-body;
font-size: 0.7em;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
white-space: nowrap;
border: 1px solid transparent;
&.weight-trivial {
color: fade(@bone, 45%);
border-color: fade(@bone, 20%);
}
&.weight-light {
color: @bone;
border-color: fade(@bone, 35%);
background: fade(@bone, 8%);
}
&.weight-normal {
color: @gold-acid;
border-color: fade(@gold-acid, 40%);
background: fade(@gold-acid, 10%);
}
&.weight-heavy {
color: @blood;
border-color: fade(@blood, 45%);
background: fade(@blood, 12%);
}
}
// ─── Load display ─────────────────────────────────────────────────────────────
.load-display {
font-weight: 700;
letter-spacing: 0.04em;
cursor: default;
}
.load-overloaded {
color: @blood !important;
text-shadow: 0 0 6px fade(@blood, 50%);
}
.resource-box-overloaded {
border-color: fade(@blood, 60%) !important;
background: fade(@blood, 10%) !important;
.resource-label-accent {
color: @blood !important;
}
}
// ─── Item description tooltip ─────────────────────────────────────────────────
.item-name[data-tooltip] {
cursor: help;
&:hover { text-decoration: underline dotted fade(@gold-acid, 55%); }
}
#tooltip.mgne-item-tooltip {
max-width: 22rem;
font-family: @font-body;
font-size: 0.82rem;
line-height: 1.5;
color: @parchment;
background: darken(@bg-panel-soft, 4%);
border: 1px solid fade(@gold-acid, 35%);
border-radius: 4px;
padding: 0.5rem 0.7rem;
box-shadow: 0 4px 14px fade(#000, 55%);
}
// ─── Broken / Durability badges ───────────────────────────────────────────────
.item-broken-badge {
display: inline-block;
margin-left: 0.3em;
padding: 0.05em 0.4em;
font-size: 0.65em;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: @blood;
background: fade(@blood, 14%);
border: 1px solid fade(@blood, 45%);
border-radius: 3px;
vertical-align: middle;
}
.item-burned-badge {
color: @gold-acid;
background: fade(@gold-acid, 12%);
border-color: fade(@gold-acid, 40%);
}
.item-durability-badge {
display: inline-flex;
align-items: center;
gap: 0.25em;
padding: 0.1em 0.45em;
font-size: 0.7em;
font-weight: 700;
color: fade(@bone, 65%);
border: 1px solid fade(@bone, 20%);
border-radius: 3px;
white-space: nowrap;
letter-spacing: 0.03em;
&.durability-broken {
color: @blood;
border-color: fade(@blood, 40%);
background: fade(@blood, 10%);
text-decoration: line-through;
}
}
.item-row-broken {
opacity: 0.6;
filter: grayscale(0.4);
.item-name {
text-decoration: line-through;
color: fade(@parchment, 55%);
}
}
// ─── Weapon properties ────────────────────────────────────────────────────────
.weapon-properties-grid {
display: flex;
flex-wrap: wrap;
gap: 0.35rem 0.6rem;
padding: 0.5rem 0.6rem;
background: fade(@bone, 4%);
border: 1px solid fade(@bone, 12%);
border-radius: 4px;
margin-bottom: 0.5rem;
.property-check {
display: flex;
align-items: center;
gap: 0.3em;
font-family: @font-body;
font-size: 0.82rem;
color: @parchment;
cursor: pointer;
white-space: nowrap;
padding: 0.15em 0.4em;
border-radius: 3px;
transition: background 0.15s;
&:hover { background: fade(@gold-acid, 8%); }
input[type="checkbox"] { accent-color: @gold-acid; }
}
}
.weapon-property-badge {
display: inline-block;
margin-left: 0.3em;
padding: 0.05em 0.35em;
font-size: 0.62em;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: fade(@gold-acid, 85%);
background: fade(@gold-acid, 10%);
border: 1px solid fade(@gold-acid, 30%);
border-radius: 3px;
vertical-align: middle;
}
// ─── Creature trait trigger ───────────────────────────────────────────────────
.creature-trait-trigger {
font-size: 0.82em;
color: fade(@parchment, 65%);
font-style: italic;
margin-left: 0.2em;
}
// ─── Creature traits container ────────────────────────────────────────────────
.creature-traits-container {
border: 1px solid fade(@blood, 35%);
border-left: 3px solid fade(@blood, 65%);
border-radius: 4px;
background: linear-gradient(135deg, fade(@blood, 5%), transparent 60%),
fade(@bg-panel-soft, 85%);
padding: 0.6rem 0.75rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.creature-traits-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
border-bottom: 1px solid fade(@blood, 25%);
padding-bottom: 0.4rem;
margin-bottom: 0.1rem;
h3 {
margin: 0;
font-family: @font-display;
font-size: 0.82rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: fade(@blood, 90%);
}
button {
font-size: 0.72rem;
padding: 0.15em 0.6em;
background: fade(@blood, 12%);
border: 1px solid fade(@blood, 35%);
color: fade(@blood, 80%);
border-radius: 3px;
cursor: pointer;
transition: background 0.15s, color 0.15s;
&:hover {
background: fade(@blood, 22%);
color: lighten(@blood, 10%);
}
}
}
.creature-traits-list {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.creature-trait-card {
background: fade(@bg-void, 30%);
border: 1px solid fade(@blood, 20%);
border-radius: 3px;
padding: 0.35rem 0.5rem;
transition: background 0.15s, border-color 0.15s;
&:hover {
background: fade(@blood, 8%);
border-color: fade(@blood, 35%);
}
}
.creature-trait-card-header {
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
}
.creature-trait-name {
display: flex;
align-items: center;
gap: 0.35em;
font-family: @font-body;
font-size: 0.88rem;
font-weight: 600;
color: @parchment;
flex: 1;
min-width: 0;
cursor: default;
i { color: fade(@blood, 70%); font-size: 0.75em; }
}
.creature-trait-trigger {
font-size: 0.75rem;
font-style: italic;
color: fade(@parchment, 55%);
white-space: nowrap;
}
.creature-trait-card .item-actions {
display: flex;
gap: 0.25rem;
margin-left: auto;
flex-shrink: 0;
button {
width: 1.6rem;
height: 1.6rem;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: 1px solid fade(@bone, 18%);
color: fade(@bone, 50%);
border-radius: 3px;
cursor: pointer;
font-size: 0.72rem;
transition: background 0.15s, color 0.15s, border-color 0.15s;
&:hover {
background: fade(@bone, 10%);
color: @bone;
border-color: fade(@bone, 35%);
}
}
}
// ============================================================
// CREATURE SHEET — META ROW + ACTION TABLE
// ============================================================
.creature-meta-row {
display: flex;
align-items: center;
gap: 1rem;
flex-wrap: wrap;
padding: 0.45rem 0.6rem;
background: fade(@bg-panel-soft, 50%);
border: 1px solid fade(@ember, 20%);
border-radius: 4px;
}
.creature-meta-label {
font-family: @font-display;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.07em;
color: fade(@ember, 80%);
white-space: nowrap;
}
.creature-type-group {
display: flex;
align-items: center;
gap: 0.75rem;
flex: 1;
}
.creature-type-checkbox {
display: flex;
align-items: center;
gap: 0.3em;
font-size: 0.83rem;
color: @parchment;
cursor: pointer;
user-select: none;
input[type="checkbox"] {
accent-color: @ember;
width: 0.9rem;
height: 0.9rem;
cursor: pointer;
}
}
.creature-number-group {
display: flex;
align-items: center;
gap: 0.4rem;
flex-shrink: 0;
}
.creature-number-input {
width: 4rem;
text-align: center;
font-family: @font-body;
font-size: 0.9rem;
color: @parchment;
background: @bg-input;
border: 1px solid fade(@ember, 30%);
border-radius: 3px;
padding: 0.15em 0.3em;
&:focus {
outline: none;
border-color: fade(@ember, 60%);
background: fade(@bg-input, 90%);
}
}
.creature-action-table-section {
.action-table-drop-zone {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
border-radius: 4px;
min-height: 2.4rem;
font-size: 0.88rem;
color: @parchment;
i { font-size: 1em; color: @ember; }
.action-table-name {
font-weight: 600;
font-family: @font-display;
color: @parchment;
}
&.drop-hint {
border: 2px dashed fade(@ember, 35%);
background: fade(@ember, 4%);
color: fade(@parchment, 50%);
font-style: italic;
justify-content: center;
cursor: default;
transition: background 0.15s, border-color 0.15s;
i { color: fade(@ember, 50%); }
&:hover, &.drag-over {
background: fade(@ember, 9%);
border-color: fade(@ember, 60%);
color: fade(@parchment, 70%);
i { color: @ember; }
}
}
}
.action-table-buttons {
display: flex;
gap: 0.3rem;
align-items: center;
button {
font-size: 0.75rem;
padding: 0.15em 0.55em;
background: fade(@ember, 10%);
border: 1px solid fade(@ember, 30%);
color: fade(@ember, 80%);
border-radius: 3px;
cursor: pointer;
transition: background 0.15s, color 0.15s;
display: flex;
align-items: center;
gap: 0.3em;
&:hover {
background: fade(@ember, 22%);
color: @ember;
}
}
}
}
// ============================================================
// PARTY ACTOR SHEET
// ============================================================
.application.mgne.party {
.party-credits-row {
display: flex;
align-items: center;
gap: 0.6rem;
margin-top: 0.5rem;
padding: 0.4rem 0.6rem;
border: 1px solid fade(@gold-acid, 35%);
border-radius: @radius-sm;
background: fade(@bg-input, 70%);
}
.party-credits-stepper {
display: flex;
align-items: center;
gap: 0.3rem;
}
.credits-input {
width: 5rem;
text-align: center;
font-size: 1.05rem;
font-weight: 700;
font-family: @font-display;
color: @gold-acid;
background: fade(@bg-void, 60%);
border: 1px solid fade(@gold-acid, 30%);
border-radius: @radius-sm;
padding: 0.2rem 0.4rem;
}
.credits-symbol {
color: @gold-acid;
font-family: @font-display;
font-size: 1.1rem;
font-weight: 700;
margin: 0 0.1rem;
}
.credits-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 2rem;
padding: 0.15rem 0.4rem;
font-size: 0.75rem;
font-weight: 700;
border: 1px solid fade(@bone, 28%);
border-radius: @radius-sm;
background: fade(@bg-input, 80%);
color: @bone;
cursor: pointer;
user-select: none;
transition: background 0.15s, border-color 0.15s;
&:hover {
background: fade(@gold-acid, 18%);
border-color: fade(@gold-acid, 55%);
color: @gold-acid;
}
}
}
// ── Party member list ─────────────────────────────────────────
.application.mgne.party .party-member-list {
display: flex;
flex-direction: column;
gap: 0.25rem;
margin-top: 0.4rem;
}
.application.mgne.party .party-member-row {
display: grid;
grid-template-columns: 2rem 1fr 5rem 6rem 5.5rem;
gap: 0.5rem;
align-items: center;
padding: 0.3rem 0.4rem;
border-radius: @radius-sm;
&.party-member-header {
color: @dust;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 1px solid fade(@bone, 18%);
padding-bottom: 0.25rem;
margin-bottom: 0.1rem;
}
&:not(.party-member-header) {
background: fade(@bg-input, 75%);
border: 1px solid transparent;
&:hover {
border-color: fade(@gold-acid, 22%);
background: fade(@bg-input, 90%);
}
}
}
.application.mgne.party .party-member-portrait {
width: 2rem;
height: 2rem;
object-fit: cover;
border-radius: 3px;
border: 1px solid fade(@bone, 25%);
}
.application.mgne.party .party-member-name a {
color: @parchment;
font-weight: 700;
font-size: 0.9rem;
&:hover { color: @gold-acid; }
}
.application.mgne.party .party-member-hp {
color: @ember-bright;
font-family: @font-display;
font-size: 0.82rem;
font-weight: 700;
text-align: center;
}
.application.mgne.party .party-member-type {
color: @dust;
font-size: 0.78rem;
font-style: italic;
}
// ── Party loot list ────────────────────────────────────────────
.application.mgne.party .party-loot-list {
display: flex;
flex-direction: column;
gap: 0.25rem;
margin-top: 0.4rem;
}
.application.mgne.party .party-loot-row {
display: grid;
grid-template-columns: 2rem 1fr 7rem 4rem;
gap: 0.5rem;
align-items: center;
padding: 0.3rem 0.4rem;
border-radius: @radius-sm;
&.party-loot-header {
color: @dust;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 1px solid fade(@bone, 18%);
padding-bottom: 0.25rem;
margin-bottom: 0.1rem;
}
&:not(.party-loot-header) {
background: fade(@bg-input, 75%);
border: 1px solid transparent;
&:hover {
border-color: fade(@gold-acid, 22%);
background: fade(@bg-input, 90%);
}
}
}
.application.mgne.party .item-portrait {
width: 2rem;
height: 2rem;
object-fit: cover;
border-radius: 3px;
border: 1px solid fade(@bone, 25%);
}
.application.mgne.party .item-type-label {
color: @dust;
font-size: 0.78rem;
font-style: italic;
}
// ── Party drop hint ────────────────────────────────────────────
.application.mgne.party .party-drop-hint {
color: fade(@dust, 70%);
font-size: 0.75rem;
font-style: italic;
text-align: center;
margin-top: 0.5rem;
padding: 0.3rem;
border: 1px dashed fade(@bone, 18%);
border-radius: @radius-sm;
}
+2
View File
@@ -3,6 +3,7 @@ export { default as MGNEItemSheet } from "./sheets/base-item-sheet.mjs"
export { default as MGNECharacterSheet } from "./sheets/character-sheet.mjs" export { default as MGNECharacterSheet } from "./sheets/character-sheet.mjs"
export { default as MGNECreatureSheet } from "./sheets/creature-sheet.mjs" export { default as MGNECreatureSheet } from "./sheets/creature-sheet.mjs"
export { default as MGNECompanionSheet } from "./sheets/companion-sheet.mjs" export { default as MGNECompanionSheet } from "./sheets/companion-sheet.mjs"
export { default as MGNEPartySheet } from "./sheets/party-sheet.mjs"
export { default as MGNEWeaponSheet } from "./sheets/weapon-sheet.mjs" export { default as MGNEWeaponSheet } from "./sheets/weapon-sheet.mjs"
export { default as MGNEArmorSheet } from "./sheets/armor-sheet.mjs" export { default as MGNEArmorSheet } from "./sheets/armor-sheet.mjs"
export { default as MGNEShieldSheet } from "./sheets/shield-sheet.mjs" export { default as MGNEShieldSheet } from "./sheets/shield-sheet.mjs"
@@ -10,3 +11,4 @@ export { default as MGNEEquipmentSheet } from "./sheets/equipment-sheet.mjs"
export { default as MGNEResonanceCoreSheet } from "./sheets/resonance-core-sheet.mjs" export { default as MGNEResonanceCoreSheet } from "./sheets/resonance-core-sheet.mjs"
export { default as MGNEArtifactSheet } from "./sheets/artifact-sheet.mjs" export { default as MGNEArtifactSheet } from "./sheets/artifact-sheet.mjs"
export { default as MGNEFeatureSheet } from "./sheets/feature-sheet.mjs" export { default as MGNEFeatureSheet } from "./sheets/feature-sheet.mjs"
export { default as MGNECreatureTraitSheet } from "./sheets/creature-trait-sheet.mjs"
@@ -64,6 +64,8 @@ export default class MGNEActorSheet extends HandlebarsApplicationMixin(foundry.a
switch (rollType) { switch (rollType) {
case "ability": case "ability":
return this.document.rollAbility(target.dataset.abilityId) return this.document.rollAbility(target.dataset.abilityId)
case "armor":
return this.document.rollArmorSave()
case "defense": case "defense":
return this.document.rollDefense() return this.document.rollDefense()
case "weapon": case "weapon":
@@ -78,6 +80,8 @@ export default class MGNEActorSheet extends HandlebarsApplicationMixin(foundry.a
return this.document.rollResonation(itemId) return this.document.rollResonation(itemId)
case "morale": case "morale":
return this.document.rollMorale() return this.document.rollMorale()
case "durability":
return this.document.rollDurability(itemId)
case "usage": case "usage":
return this.document.rollUsage(itemId) return this.document.rollUsage(itemId)
default: default:
+11 -7
View File
@@ -2,6 +2,10 @@ import MGNEActorSheet from "./base-actor-sheet.mjs"
import { SYSTEM } from "../../config/system.mjs" import { SYSTEM } from "../../config/system.mjs"
import { buildCharacterSelectOptions } from "./select-options.mjs" import { buildCharacterSelectOptions } from "./select-options.mjs"
export function stripHtml(html) {
return (html ?? "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim()
}
export default class MGNECharacterSheet extends MGNEActorSheet { export default class MGNECharacterSheet extends MGNEActorSheet {
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
classes: ["character"], classes: ["character"],
@@ -79,16 +83,16 @@ export default class MGNECharacterSheet extends MGNEActorSheet {
break break
case "equipment": case "equipment":
context.tab = context.tabs.equipment context.tab = context.tabs.equipment
context.weapons = doc.itemTypes.weapon context.weapons = doc.itemTypes.weapon.map(i => ({ ...i, tooltip: stripHtml(i.system.description) }))
context.armors = doc.itemTypes.armor context.armors = doc.itemTypes.armor.map(i => ({ ...i, tooltip: stripHtml(i.system.description) }))
context.shields = doc.itemTypes.shield context.shields = doc.itemTypes.shield.map(i => ({ ...i, tooltip: stripHtml(i.system.description) }))
context.equipmentItems = doc.itemTypes.equipment context.equipmentItems = doc.itemTypes.equipment.map(i => ({ ...i, tooltip: stripHtml(i.system.description) }))
context.cores = doc.itemTypes["resonance-core"] context.cores = doc.itemTypes["resonance-core"].map(i => ({ ...i, tooltip: stripHtml(i.system.description) }))
context.artifacts = doc.itemTypes.artifact context.artifacts = doc.itemTypes.artifact.map(i => ({ ...i, tooltip: stripHtml(i.system.description) }))
break break
case "features": case "features":
context.tab = context.tabs.features context.tab = context.tabs.features
context.features = doc.itemTypes.feature context.features = doc.itemTypes.feature.map(i => ({ ...i, tooltip: stripHtml(i.system.description) }))
break break
case "notes": case "notes":
context.tab = context.tabs.notes context.tab = context.tabs.notes
@@ -1,5 +1,4 @@
import MGNEActorSheet from "./base-actor-sheet.mjs" import MGNEActorSheet from "./base-actor-sheet.mjs"
import { SYSTEM } from "../../config/system.mjs"
export default class MGNECompanionSheet extends MGNEActorSheet { export default class MGNECompanionSheet extends MGNEActorSheet {
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
@@ -13,14 +12,4 @@ export default class MGNECompanionSheet extends MGNEActorSheet {
static PARTS = { static PARTS = {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/companion-main.hbs" }, main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/companion-main.hbs" },
} }
async _prepareContext() {
const context = await super._prepareContext()
context.abilityList = SYSTEM.abilityOrder.map(id => ({
id,
...SYSTEM.abilities[id],
value: context.source.system.abilities?.[id]?.value ?? 0,
}))
return context
}
} }
+65 -6
View File
@@ -1,12 +1,17 @@
import MGNEActorSheet from "./base-actor-sheet.mjs" import MGNEActorSheet from "./base-actor-sheet.mjs"
import { SYSTEM } from "../../config/system.mjs" import { stripHtml } from "./character-sheet.mjs"
export default class MGNECreatureSheet extends MGNEActorSheet { export default class MGNECreatureSheet extends MGNEActorSheet {
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
classes: ["creature"], classes: ["creature"],
position: { position: {
width: 760, width: 760,
height: 640, height: 680,
},
actions: {
rollActionTable: MGNECreatureSheet.prototype._rollActionTable,
clearActionTable: MGNECreatureSheet.prototype._clearActionTable,
openActionTable: MGNECreatureSheet.prototype._openActionTable,
}, },
} }
@@ -14,13 +19,67 @@ export default class MGNECreatureSheet extends MGNEActorSheet {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/creature-main.hbs" }, main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/creature-main.hbs" },
} }
_processSubmitData(event, form, submitData) {
// Foundry sends null for unchecked checkboxes in a SetField array — strip them
if (Array.isArray(submitData.system?.creatureType)) {
submitData.system.creatureType = submitData.system.creatureType.filter(v => v != null && v !== "")
}
return super._processSubmitData(event, form, submitData)
}
async _prepareContext() { async _prepareContext() {
const context = await super._prepareContext() const context = await super._prepareContext()
context.abilityList = SYSTEM.abilityOrder.map(id => ({ context.traits = (this.document.itemTypes["creature-trait"] ?? [])
id, .map(i => ({ ...i, tooltip: stripHtml(i.system.description) }))
...SYSTEM.abilities[id],
value: context.source.system.abilities?.[id]?.value ?? 0, // Resolve linked action table
const uuid = this.document.system.actionTableUuid
if (uuid) {
const table = await fromUuid(uuid).catch(() => null)
context.actionTable = table ? { name: table.name, uuid } : null
} else {
context.actionTable = null
}
// Build creature type checkboxes
const typeSet = this.document.system.creatureType ?? new Set()
context.creatureTypes = ["human", "construct", "animal"].map(key => ({
key,
label: game.i18n.localize(`MGNE.Creature.Types.${key.charAt(0).toUpperCase() + key.slice(1)}`),
checked: typeSet.has(key),
})) }))
return context return context
} }
async _onDrop(event) {
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
if (data?.type === "RollTable") {
const table = await fromUuid(data.uuid)
if (table) {
await this.document.update({ "system.actionTableUuid": data.uuid })
return
}
}
return super._onDrop(event)
}
async _rollActionTable() {
const uuid = this.document.system.actionTableUuid
if (!uuid) return ui.notifications.warn(game.i18n.localize("MGNE.Creature.NoTableLinked"))
const table = await fromUuid(uuid).catch(() => null)
if (!table) return ui.notifications.warn(game.i18n.localize("MGNE.Creature.TableNotFound"))
await table.draw()
}
async _clearActionTable() {
await this.document.update({ "system.actionTableUuid": "" })
}
async _openActionTable() {
const uuid = this.document.system.actionTableUuid
if (!uuid) return
const table = await fromUuid(uuid).catch(() => null)
if (table) table.sheet.render(true)
}
} }
@@ -0,0 +1,7 @@
import MGNEItemSheet from "./base-item-sheet.mjs"
export default class MGNECreatureTraitSheet extends MGNEItemSheet {
static PARTS = {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/creature-trait.hbs" },
}
}
+162
View File
@@ -0,0 +1,162 @@
import MGNEActorSheet from "./base-actor-sheet.mjs"
const SYSTEM_ID = "fvtt-machine-gods-noxian-expanse"
const PARTY_LOOT_TYPES = new Set(["weapon", "armor", "shield", "equipment", "resonance-core", "artifact"])
export default class MGNEPartySheet extends MGNEActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["party"],
position: { width: 820, height: 640 },
actions: {
openMember: MGNEPartySheet.#onOpenMember,
removeMember: MGNEPartySheet.#onRemoveMember,
moveMemberUp: MGNEPartySheet.#onMoveMemberUp,
moveMemberDown: MGNEPartySheet.#onMoveMemberDown,
adjustCredits: MGNEPartySheet.#onAdjustCredits,
},
}
/** @override */
static PARTS = {
main: { template: `systems/${SYSTEM_ID}/templates/party-main.hbs` },
tabs: { template: `systems/${SYSTEM_ID}/templates/party-tabs.hbs` },
members: { template: `systems/${SYSTEM_ID}/templates/party-members.hbs` },
loot: { template: `systems/${SYSTEM_ID}/templates/party-loot.hbs` },
notes: { template: `systems/${SYSTEM_ID}/templates/party-notes.hbs` },
}
tabGroups = { sheet: "members" }
#getTabs() {
const tabs = {
members: { id: "members", group: "sheet", label: game.i18n.localize("MGNE.Tabs.members") },
loot: { id: "loot", group: "sheet", label: game.i18n.localize("MGNE.Tabs.loot") },
notes: { id: "notes", group: "sheet", label: game.i18n.localize("MGNE.Tabs.notes") },
}
for (const tab of Object.values(tabs)) {
tab.active = this.tabGroups[tab.group] === tab.id
tab.cssClass = tab.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
/** @override */
async _preparePartContext(partId, context) {
const doc = this.document
switch (partId) {
case "members": {
context.tab = context.tabs.members
// Build member list using actorId for moves; store refIdx (position in
// memberRefs) so move actions always operate on the correct slot even
// when some refs point to deleted actors.
const refs = doc.system.memberRefs ?? []
const members = []
for (let refIdx = 0; refIdx < refs.length; refIdx++) {
const actor = game.actors?.get(refs[refIdx].id)
if (!actor) continue
members.push({
id: actor.id,
refIdx,
name: actor.name,
img: actor.img,
type: actor.type,
typeLabel: game.i18n.localize(`TYPES.Actor.${actor.type}`),
hp: actor.system.hp
? `${actor.system.hp.value ?? "—"}/${actor.system.hp.max ?? "—"}`
: "—",
})
}
// isFirst/isLast based on visible list, but swap uses refIdx
for (let vi = 0; vi < members.length; vi++) {
members[vi].isFirst = vi === 0
members[vi].isLast = vi === members.length - 1
}
context.members = members
break
}
case "loot": {
context.tab = context.tabs.loot
context.lootItems = doc.items.contents
.filter(i => PARTY_LOOT_TYPES.has(i.type))
.map(i => ({
id: i.id,
img: i.img,
name: i.name,
typeLabel: game.i18n.localize(`TYPES.Item.${i.type}`),
}))
break
}
case "notes":
context.tab = context.tabs.notes
break
}
return context
}
/** @override */
async _onDrop(event) {
if (!this.isEditable) return
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
if (data.type === "Actor") {
const actor = await fromUuid(data.uuid)
if (!actor || !["character", "companion"].includes(actor.type)) return
const refs = foundry.utils.deepClone(this.document.system.memberRefs ?? [])
if (refs.some(r => r.id === actor.id)) return
refs.push({ id: actor.id })
return this.document.update({ "system.memberRefs": refs })
}
if (data.type === "Item") {
const item = await fromUuid(data.uuid)
if (!item || !PARTY_LOOT_TYPES.has(item.type)) return
return this.document.createEmbeddedDocuments("Item", [item.toObject()])
}
}
// ── Actions ─────────────────────────────────────────────────────────────────
static async #onOpenMember(_event, target) {
const actor = game.actors?.get(target.dataset.actorId)
if (actor) actor.sheet.render(true)
}
static async #onRemoveMember(_event, target) {
const id = target.dataset.actorId
const refs = (this.document.system.memberRefs ?? []).filter(r => r.id !== id)
await this.document.update({ "system.memberRefs": refs })
}
static async #onMoveMemberUp(_event, target) {
const refIdx = parseInt(target.dataset.refIdx, 10)
if (refIdx <= 0) return
const refs = foundry.utils.deepClone(this.document.system.memberRefs ?? []);
[refs[refIdx - 1], refs[refIdx]] = [refs[refIdx], refs[refIdx - 1]]
await this.document.update({ "system.memberRefs": refs })
}
static async #onMoveMemberDown(_event, target) {
const refIdx = parseInt(target.dataset.refIdx, 10)
const refs = foundry.utils.deepClone(this.document.system.memberRefs ?? [])
if (refIdx >= refs.length - 1) return;
[refs[refIdx], refs[refIdx + 1]] = [refs[refIdx + 1], refs[refIdx]]
await this.document.update({ "system.memberRefs": refs })
}
static async #onAdjustCredits(_event, target) {
const delta = parseInt(target.dataset.delta, 10)
const current = this.document.system.credits ?? 0
await this.document.update({ "system.credits": Math.max(0, current + delta) })
}
}
@@ -31,12 +31,13 @@ export function buildSharedSelectOptions() {
armorPenalties: numericOptions(0, 6), armorPenalties: numericOptions(0, 6),
shieldPenalties: numericOptions(0, 4), shieldPenalties: numericOptions(0, 4),
weaponCategories: objectOptions(SYSTEM.weaponCategories), weaponCategories: objectOptions(SYSTEM.weaponCategories),
weaponProperties: Object.entries(SYSTEM.weaponProperties).map(([key, p]) => ({ value: key, label: p.label, hint: p.hint })),
weightCategories: objectOptions(SYSTEM.weightCategories),
usageDice: objectOptions(SYSTEM.usageDieChoices), usageDice: objectOptions(SYSTEM.usageDieChoices),
armorDice: objectOptions(SYSTEM.armorDieChoices), armorDice: objectOptions(SYSTEM.armorDieChoices),
omenDice: objectOptions(SYSTEM.omenDieChoices), omenDice: objectOptions(SYSTEM.omenDieChoices),
resonanceList: objectOptions(SYSTEM.resonanceList), resonanceList: objectOptions(SYSTEM.resonanceList),
equipmentSubtypes: objectOptions(SYSTEM.equipmentSubtypes), equipmentSubtypes: objectOptions(SYSTEM.equipmentSubtypes),
artifactIds: objectOptions(SYSTEM.artifactChoices),
featureIds: objectOptions(SYSTEM.featureChoices), featureIds: objectOptions(SYSTEM.featureChoices),
} }
} }
+29
View File
@@ -28,6 +28,7 @@ export const SYSTEM = {
character: { id: "character", label: "Character" }, character: { id: "character", label: "Character" },
creature: { id: "creature", label: "Creature" }, creature: { id: "creature", label: "Creature" },
companion: { id: "companion", label: "Companion" }, companion: { id: "companion", label: "Companion" },
party: { id: "party", label: "Party" },
}, },
itemTypes: { itemTypes: {
weapon: { id: "weapon", label: "Weapon", icon: itemIcon("weapon") }, weapon: { id: "weapon", label: "Weapon", icon: itemIcon("weapon") },
@@ -37,6 +38,7 @@ export const SYSTEM = {
"resonance-core": { id: "resonance-core", label: "Resonance Core", icon: itemIcon("resonance-core") }, "resonance-core": { id: "resonance-core", label: "Resonance Core", icon: itemIcon("resonance-core") },
artifact: { id: "artifact", label: "Artifact", icon: itemIcon("artifact") }, artifact: { id: "artifact", label: "Artifact", icon: itemIcon("artifact") },
feature: { id: "feature", label: "Feature", icon: itemIcon("feature") }, feature: { id: "feature", label: "Feature", icon: itemIcon("feature") },
"creature-trait": { id: "creature-trait", label: "Creature Trait", icon: itemIcon("creature-trait") },
}, },
abilities: { abilities: {
agility: { id: "agility", label: "Agility" }, agility: { id: "agility", label: "Agility" },
@@ -63,10 +65,37 @@ export const SYSTEM = {
armorDieChoices: dieChoiceLabels(["d12", "d10", "d8", "d6", "d4", "d2", "0"]), armorDieChoices: dieChoiceLabels(["d12", "d10", "d8", "d6", "d4", "d2", "0"]),
omenDice: ["d2", "d4", "d6", "d8"], omenDice: ["d2", "d4", "d6", "d8"],
omenDieChoices: dieChoiceLabels(["d2", "d4", "d6", "d8"]), omenDieChoices: dieChoiceLabels(["d2", "d4", "d6", "d8"]),
weightCategories: {
trivial: "Trivial",
light: "Light",
normal: "Normal",
heavy: "Heavy",
},
weaponCategories: { weaponCategories: {
melee: "Melee", melee: "Melee",
ranged: "Ranged", ranged: "Ranged",
}, },
weaponProperties: {
ammo: { label: "Ammo", hint: "Requires ammunition. Improvised in melee or without ammo." },
awkward: { label: "Awkward", hint: "+1 DR to attacks." },
binding: { label: "Binding", hint: "Beat DR by 4+ on same-size or smaller target: inflict Restrained." },
durant: { label: "Durant", hint: "Durability die is D8." },
finesse: { label: "Finesse", hint: "Attacks can use either Presence or Strength." },
fling: { label: "Fling", hint: "Can make ranged attacks. Roll D4 Usage Die to avoid losing it." },
fragile: { label: "Fragile", hint: "Durability die is D4." },
glinting: { label: "Glinting", hint: "After attacking, next attack vs same target this combat gains -1 DR." },
overbearing:{ label: "Overbearing",hint: "+2 damage." },
parrying: { label: "Parrying", hint: "-1 DR to avoiding melee attacks while wielding." },
precise: { label: "Precise", hint: "-1 DR to hit." },
razored: { label: "Razored", hint: "Beat DR by 4+: inflict Bleeding (1)." },
ringing: { label: "Ringing", hint: "Beat DR by 4+ and target not Stunned: inflict Stunned (1)." },
"two-handed":{ label: "Two-Handed",hint: "Requires both hands." },
unwieldy: { label: "Unwieldy", hint: "Cannot attack more than once per Round." },
versatile: { label: "Versatile", hint: "Two-handed: -1 DR to hit and +1 damage." },
},
get weaponPropertyLabels() {
return Object.fromEntries(Object.entries(this.weaponProperties).map(([k, v]) => [k, v.label]))
},
resonanceList: { resonanceList: {
accelerate: "Accelerate", accelerate: "Accelerate",
blast: "Blast", blast: "Blast",
+14 -1
View File
@@ -93,6 +93,10 @@ export default class MGNEActor extends Actor {
return result return result
} }
async rollArmorSave() {
return MGNERoll.rollArmorSave(this)
}
async rollWeapon(itemId) { async rollWeapon(itemId) {
const item = this.items.get(itemId) const item = this.items.get(itemId)
if (!item) return null if (!item) return null
@@ -129,7 +133,10 @@ export default class MGNEActor extends Actor {
async rollProfileAttack() { async rollProfileAttack() {
const attackBaseLabel = normalizeGenericActionLabel(this.system.attack?.label ?? t("MGNE.Common.Attack"), t("MGNE.Common.Attack")) const attackBaseLabel = normalizeGenericActionLabel(this.system.attack?.label ?? t("MGNE.Common.Attack"), t("MGNE.Common.Attack"))
const attackLabel = formatActionLabel(attackBaseLabel, t("MGNE.Common.Attack")) const attackLabel = formatActionLabel(attackBaseLabel, t("MGNE.Common.Attack"))
const result = await this.rollAbility("strength", { // Creatures have no ability scores; ability value defaults to 0 via roll.mjs
const result = await MGNERoll.promptCheck({
actor: this,
abilityId: "strength",
label: attackLabel, label: attackLabel,
rollType: "attack", rollType: "attack",
}) })
@@ -220,6 +227,12 @@ export default class MGNEActor extends Actor {
return item.rollUsage() return item.rollUsage()
} }
async rollDurability(itemId) {
const item = this.items.get(itemId)
if (!item) return null
return MGNERoll.rollDurability(item)
}
async quickRest() { async quickRest() {
const roll = await (new Roll("1d4")).evaluate() const roll = await (new Roll("1d4")).evaluate()
const hp = this.system.hp?.value ?? 0 const hp = this.system.hp?.value ?? 0
+86
View File
@@ -57,11 +57,15 @@ async function renderCard(context) {
const normalizedEyebrow = `${eyebrow}`.trim().toLowerCase() const normalizedEyebrow = `${eyebrow}`.trim().toLowerCase()
const normalizedLabel = `${context.label ?? ""}`.trim().toLowerCase() const normalizedLabel = `${context.label ?? ""}`.trim().toLowerCase()
// Render dice tooltip HTML if a roll was provided
const diceTooltip = context._roll ? await context._roll.render() : null
return foundry.applications.handlebars.renderTemplate(`systems/${SYSTEM_ID}/templates/chat-message.hbs`, { return foundry.applications.handlebars.renderTemplate(`systems/${SYSTEM_ID}/templates/chat-message.hbs`, {
...context, ...context,
modeClass: context.mode ?? "generic", modeClass: context.mode ?? "generic",
eyebrow: "", eyebrow: "",
outcomeClass, outcomeClass,
diceTooltip,
}) })
} }
@@ -156,6 +160,7 @@ export default class MGNERoll {
damageItemId: showDamageButton ? item.id : null, damageItemId: showDamageButton ? item.id : null,
damageFormula: showDamageButton ? (item.system.damage || "1") : null, damageFormula: showDamageButton ? (item.system.damage || "1") : null,
damageCritical: showDamageButton && critical, damageCritical: showDamageButton && critical,
_roll: roll,
}) })
await ChatMessage.create({ await ChatMessage.create({
@@ -181,6 +186,7 @@ export default class MGNERoll {
total: roll.total, total: roll.total,
outcome: broken ? t("MGNE.Roll.OutcomeBroken") : t("MGNE.Roll.OutcomeSteady"), outcome: broken ? t("MGNE.Roll.OutcomeBroken") : t("MGNE.Roll.OutcomeSteady"),
specialText: broken ? t("MGNE.Roll.MoraleBrokenText") : "", specialText: broken ? t("MGNE.Roll.MoraleBrokenText") : "",
_roll: roll,
}) })
await ChatMessage.create({ await ChatMessage.create({
@@ -236,6 +242,7 @@ export default class MGNERoll {
showApplyButton: true, showApplyButton: true,
damageTotal: roll.total, damageTotal: roll.total,
damageCritical: isCritical, damageCritical: isCritical,
_roll: roll,
}) })
await ChatMessage.create({ await ChatMessage.create({
@@ -267,6 +274,7 @@ export default class MGNERoll {
showApplyButton: true, showApplyButton: true,
damageTotal: roll.total, damageTotal: roll.total,
damageCritical: isCritical, damageCritical: isCritical,
_roll: roll,
}) })
await ChatMessage.create({ await ChatMessage.create({
@@ -278,6 +286,39 @@ export default class MGNERoll {
return { roll } return { roll }
} }
static async rollArmorSave(actor) {
const formula = actor.getArmorRollFormula()
const items = actor.getEquippedArmorItems()
if (formula === "0") {
ui.notifications.warn(t("MGNE.Notification.NoArmorEquipped"))
return null
}
const roll = await (new Roll(formula)).evaluate()
const armorNames = items.map(i => i.name).join(" + ")
const contentHtml = await renderCard({
mode: "armor",
actorName: actor.name,
actorImg: actor.img,
label: t("MGNE.Roll.ArmorSave"),
subtitle: armorNames,
formula: roll.formula,
total: roll.total,
outcome: f("MGNE.Roll.ArmorAbsorbed", { amount: roll.total }),
_roll: roll,
})
await ChatMessage.create({
speaker: ChatMessage.getSpeaker({ actor }),
rolls: [roll],
content: contentHtml,
})
return roll
}
static async rollUsage(item) { static async rollUsage(item) {
const currentDie = item.system.usageDie const currentDie = item.system.usageDie
if (!currentDie || currentDie === "depleted") { if (!currentDie || currentDie === "depleted") {
@@ -303,6 +344,7 @@ export default class MGNERoll {
total: roll.total, total: roll.total,
outcome: depleted ? f("MGNE.Roll.DowngradedTo", { die: nextDie.toUpperCase() }) : t("MGNE.Roll.NoChange"), outcome: depleted ? f("MGNE.Roll.DowngradedTo", { die: nextDie.toUpperCase() }) : t("MGNE.Roll.NoChange"),
specialText: depleted && nextDie === "depleted" ? t("MGNE.Roll.ItemNowDepleted") : "", specialText: depleted && nextDie === "depleted" ? t("MGNE.Roll.ItemNowDepleted") : "",
_roll: roll,
}) })
await ChatMessage.create({ await ChatMessage.create({
@@ -314,6 +356,48 @@ export default class MGNERoll {
return { roll, depleted, nextDie } return { roll, depleted, nextDie }
} }
static async rollDurability(item) {
const currentDie = item.system.durabilityDie
if (!currentDie || currentDie === "depleted") {
ui.notifications.warn(f("MGNE.Notification.ItemDurabilityDepleted", { item: item.name }))
return null
}
const roll = await (new Roll(`1${currentDie}`)).evaluate()
const degraded = roll.total <= 2
const nextDie = degraded ? stepDownDie(currentDie) : currentDie
const nowBroken = degraded && nextDie === "depleted"
const updates = { "system.durabilityDie": nextDie }
if (nowBroken) {
updates["system.broken"] = true
if ("equipped" in (item.system ?? {})) updates["system.equipped"] = false
}
await item.update(updates)
const contentHtml = await renderCard({
mode: "durability",
actorName: item.parent?.name ?? item.name,
actorImg: item.img,
label: f("MGNE.Roll.DurabilityLabel", { item: item.name }),
subtitle: f("MGNE.Roll.CurrentDie", { die: currentDie.toUpperCase() }),
formula: roll.formula,
total: roll.total,
outcome: degraded
? f("MGNE.Roll.DowngradedTo", { die: nextDie.toUpperCase() })
: t("MGNE.Roll.NoChange"),
specialText: nowBroken ? t("MGNE.Roll.ItemNowBroken") : "",
_roll: roll,
})
await ChatMessage.create({
speaker: ChatMessage.getSpeaker({ actor: item.parent ?? null }),
rolls: [roll],
content: contentHtml,
})
return { roll, degraded, nextDie, nowBroken }
}
static async applyDamageCard({ actor, sourceActor = null, sourceItem = null, amount, armorRoll = null, appliedDamage, newHp, breakText = "", defenseFumbleText = "", criticalArmorText = "" }) { static async applyDamageCard({ actor, sourceActor = null, sourceItem = null, amount, armorRoll = null, appliedDamage, newHp, breakText = "", defenseFumbleText = "", criticalArmorText = "" }) {
const contentHtml = await renderCard({ const contentHtml = await renderCard({
mode: "apply-damage", mode: "apply-damage",
@@ -335,6 +419,7 @@ export default class MGNERoll {
criticalArmorText, criticalArmorText,
breakText ? f("MGNE.Roll.BreakText", { text: breakText }) : "", breakText ? f("MGNE.Roll.BreakText", { text: breakText }) : "",
]), ]),
_roll: armorRoll ?? null,
}) })
await ChatMessage.create({ await ChatMessage.create({
@@ -359,6 +444,7 @@ export default class MGNERoll {
total: roll.total, total: roll.total,
outcome, outcome,
specialText, specialText,
_roll: roll,
}) })
await ChatMessage.create({ await ChatMessage.create({
+2
View File
@@ -1,6 +1,7 @@
export { default as MGNECharacter } from "./character.mjs" export { default as MGNECharacter } from "./character.mjs"
export { default as MGNECreature } from "./creature.mjs" export { default as MGNECreature } from "./creature.mjs"
export { default as MGNECompanion } from "./companion.mjs" export { default as MGNECompanion } from "./companion.mjs"
export { default as MGNEParty } from "./party.mjs"
export { default as MGNEWeapon } from "./weapon.mjs" export { default as MGNEWeapon } from "./weapon.mjs"
export { default as MGNEArmor } from "./armor.mjs" export { default as MGNEArmor } from "./armor.mjs"
export { default as MGNEShield } from "./shield.mjs" export { default as MGNEShield } from "./shield.mjs"
@@ -8,3 +9,4 @@ export { default as MGNEEquipment } from "./equipment.mjs"
export { default as MGNEResonanceCore } from "./resonance-core.mjs" export { default as MGNEResonanceCore } from "./resonance-core.mjs"
export { default as MGNEArtifact } from "./artifact.mjs" export { default as MGNEArtifact } from "./artifact.mjs"
export { default as MGNEFeature } from "./feature.mjs" export { default as MGNEFeature } from "./feature.mjs"
export { default as MGNECreatureTrait } from "./creature-trait.mjs"
+8
View File
@@ -12,8 +12,16 @@ export default class MGNEArmor extends foundry.abstract.TypeDataModel {
choices: SYSTEM.armorDieChoices, choices: SYSTEM.armorDieChoices,
}), }),
penalty: numberField(0, 0, 6), penalty: numberField(0, 0, 6),
weight: new foundry.data.fields.StringField({
required: true, nullable: false, initial: "heavy",
choices: SYSTEM.weightCategories,
}),
equipped: booleanField(false), equipped: booleanField(false),
broken: booleanField(false), broken: booleanField(false),
durabilityDie: new foundry.data.fields.StringField({
required: true, nullable: false, initial: "d6",
choices: SYSTEM.usageDieChoices,
}),
} }
} }
+8 -6
View File
@@ -5,12 +5,6 @@ export default class MGNEArtifact extends foundry.abstract.TypeDataModel {
static defineSchema() { static defineSchema() {
return { return {
description: htmlField(""), description: htmlField(""),
artifactId: new foundry.data.fields.StringField({
required: true,
nullable: false,
initial: "shiver-lens",
choices: SYSTEM.artifactChoices,
}),
synchronized: booleanField(false), synchronized: booleanField(false),
synchronizedTo: stringField(""), synchronizedTo: stringField(""),
usageDie: new foundry.data.fields.StringField({ usageDie: new foundry.data.fields.StringField({
@@ -20,6 +14,14 @@ export default class MGNEArtifact extends foundry.abstract.TypeDataModel {
choices: SYSTEM.usageDieChoices, choices: SYSTEM.usageDieChoices,
}), }),
broken: booleanField(false), broken: booleanField(false),
durabilityDie: new foundry.data.fields.StringField({
required: true, nullable: false, initial: "d6",
choices: SYSTEM.usageDieChoices,
}),
weight: new foundry.data.fields.StringField({
required: true, nullable: false, initial: "normal",
choices: SYSTEM.weightCategories,
}),
} }
} }
+28
View File
@@ -45,6 +45,34 @@ export default class MGNECharacter extends foundry.abstract.TypeDataModel {
this.syncLimit = Math.max(0, this.abilities.toughness?.value ?? 0) this.syncLimit = Math.max(0, this.abilities.toughness?.value ?? 0)
this.syncRemaining = Math.max(0, this.syncLimit - (this.artifactSync.used ?? 0)) this.syncRemaining = Math.max(0, this.syncLimit - (this.artifactSync.used ?? 0))
this.armorFormula = this.parent?.getArmorRollFormula?.() ?? "0" this.armorFormula = this.parent?.getArmorRollFormula?.() ?? "0"
// Compute current load per RAW:
// trivial = 0, light = 10 per slot, normal = 1, heavy = fills remaining capacity (max 1)
let normalLoad = 0
let lightCount = 0
let heavyCount = 0
for (const item of (this.parent?.items ?? [])) {
if (item.system?.carried === false) continue // not being carried
const w = item.system?.weight ?? "normal"
if (w === "trivial") continue
else if (w === "light") lightCount++
else if (w === "normal") normalLoad++
else if (w === "heavy") heavyCount++
}
normalLoad += Math.floor(lightCount / 10)
this.lightItemCount = lightCount
this.heavyItemCount = heavyCount
if (heavyCount >= 2) {
// Can't carry two heavy items — automatically overloaded
this.currentLoad = this.carryCapacity + (heavyCount - 1)
} else if (heavyCount === 1) {
// Heavy fills remaining capacity; other items fit alongside it
this.currentLoad = Math.max(normalLoad, this.carryCapacity)
} else {
this.currentLoad = normalLoad
}
this.overloaded = this.currentLoad > this.carryCapacity
} }
/** @override */ /** @override */
+1 -2
View File
@@ -1,12 +1,11 @@
import { SYSTEM } from "../config/system.mjs" import { SYSTEM } from "../config/system.mjs"
import { abilitySchema, htmlField, numberField, stringField, trackSchema } from "./shared.mjs" import { htmlField, numberField, stringField, trackSchema } from "./shared.mjs"
export default class MGNECompanion extends foundry.abstract.TypeDataModel { export default class MGNECompanion extends foundry.abstract.TypeDataModel {
static defineSchema() { static defineSchema() {
const fields = foundry.data.fields const fields = foundry.data.fields
return { return {
abilities: abilitySchema(),
hp: trackSchema(1, 1), hp: trackSchema(1, 1),
morale: numberField(7, 2, 12), morale: numberField(7, 2, 12),
armor: new fields.SchemaField({ armor: new fields.SchemaField({
+13
View File
@@ -0,0 +1,13 @@
import { htmlField, stringField } from "./shared.mjs"
export default class MGNECreatureTrait extends foundry.abstract.TypeDataModel {
static defineSchema() {
return {
description: htmlField(""),
trigger: stringField(""),
}
}
/** @override */
static LOCALIZATION_PREFIXES = ["MGNE.CreatureTrait"]
}
+20 -8
View File
@@ -1,27 +1,39 @@
import { SYSTEM } from "../config/system.mjs" import { SYSTEM } from "../config/system.mjs"
import { abilitySchema, htmlField, numberField, stringField, trackSchema } from "./shared.mjs" import { htmlField, numberField, stringField, trackSchema } from "./shared.mjs"
const CREATURE_TYPES = ["human", "construct", "animal"]
export default class MGNECreature extends foundry.abstract.TypeDataModel { export default class MGNECreature extends foundry.abstract.TypeDataModel {
static defineSchema() { static defineSchema() {
const fields = foundry.data.fields const fields = foundry.data.fields
return { return {
abilities: abilitySchema(),
hp: trackSchema(1, 1), hp: trackSchema(1, 1),
morale: numberField(7, 2, 12), morale: numberField(7, 2, 12),
armor: new fields.SchemaField({ armor: new fields.SchemaField({
die: new fields.StringField({ required: true, nullable: false, initial: "0", choices: SYSTEM.armorDieChoices }), die: new fields.StringField({ required: true, nullable: false, initial: "0", choices: SYSTEM.armorDieChoices }),
}), }),
attack: new fields.SchemaField({ creatureType: new fields.SetField(
label: stringField("Attack"), new fields.StringField({ required: true, choices: CREATURE_TYPES }),
damage: stringField("1d4"), { required: true, nullable: false, initial: [] }
}), ),
number: stringField("1"),
actionTableUuid: stringField(""),
description: htmlField(""), description: htmlField(""),
special: htmlField(""),
notes: htmlField(""),
} }
} }
/** @override */ /** @override */
static LOCALIZATION_PREFIXES = ["MGNE.Creature"] static LOCALIZATION_PREFIXES = ["MGNE.Creature"]
/** @override */
static migrateData(source) {
// Remove old attack field if present (no longer part of the schema)
if ("attack" in source) delete source.attack
// Form submissions send null for unchecked checkboxes in array fields — filter them out
if (Array.isArray(source.creatureType)) {
source.creatureType = source.creatureType.filter(v => v != null && v !== "")
}
return super.migrateData(source)
}
} }
+9
View File
@@ -21,6 +21,15 @@ export default class MGNEEquipment extends foundry.abstract.TypeDataModel {
choices: SYSTEM.usageDieChoices, choices: SYSTEM.usageDieChoices,
}), }),
consumable: booleanField(false), consumable: booleanField(false),
broken: booleanField(false),
durabilityDie: new foundry.data.fields.StringField({
required: true, nullable: false, initial: "d6",
choices: SYSTEM.usageDieChoices,
}),
weight: new foundry.data.fields.StringField({
required: true, nullable: false, initial: "normal",
choices: SYSTEM.weightCategories,
}),
} }
} }
+20
View File
@@ -0,0 +1,20 @@
import { htmlField } from "./shared.mjs"
export default class MGNEParty extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
return {
memberRefs: new fields.ArrayField(
new fields.SchemaField({
id: new fields.StringField({ required: true, nullable: false, blank: false }),
})
),
credits: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }),
notes: htmlField(""),
}
}
/** @override */
static LOCALIZATION_PREFIXES = ["MGNE.Party"]
}
+9
View File
@@ -18,6 +18,15 @@ export default class MGNEResonanceCore extends foundry.abstract.TypeDataModel {
choices: SYSTEM.usageDieChoices, choices: SYSTEM.usageDieChoices,
}), }),
burnedOut: booleanField(false), burnedOut: booleanField(false),
broken: booleanField(false),
durabilityDie: new foundry.data.fields.StringField({
required: true, nullable: false, initial: "d6",
choices: SYSTEM.usageDieChoices,
}),
weight: new foundry.data.fields.StringField({
required: true, nullable: false, initial: "trivial",
choices: SYSTEM.weightCategories,
}),
} }
} }
+8
View File
@@ -12,8 +12,16 @@ export default class MGNEShield extends foundry.abstract.TypeDataModel {
choices: SYSTEM.armorDieChoices, choices: SYSTEM.armorDieChoices,
}), }),
penalty: numberField(0, 0, 4), penalty: numberField(0, 0, 4),
weight: new foundry.data.fields.StringField({
required: true, nullable: false, initial: "normal",
choices: SYSTEM.weightCategories,
}),
equipped: booleanField(false), equipped: booleanField(false),
broken: booleanField(false), broken: booleanField(false),
durabilityDie: new foundry.data.fields.StringField({
required: true, nullable: false, initial: "d6",
choices: SYSTEM.usageDieChoices,
}),
} }
} }
+28 -1
View File
@@ -13,7 +13,14 @@ export default class MGNEWeapon extends foundry.abstract.TypeDataModel {
}), }),
damage: stringField("1d4"), damage: stringField("1d4"),
range: stringField("Touch"), range: stringField("Touch"),
properties: stringField(""), properties: new foundry.data.fields.SetField(
new foundry.data.fields.StringField({ required: true, nullable: false, blank: false }),
{ required: true, nullable: false, initial: [] }
),
weight: new foundry.data.fields.StringField({
required: true, nullable: false, initial: "normal",
choices: SYSTEM.weightCategories,
}),
usageDie: new foundry.data.fields.StringField({ usageDie: new foundry.data.fields.StringField({
required: true, required: true,
nullable: false, nullable: false,
@@ -23,9 +30,29 @@ export default class MGNEWeapon extends foundry.abstract.TypeDataModel {
quantity: numberField(1, 0), quantity: numberField(1, 0),
equipped: booleanField(false), equipped: booleanField(false),
broken: booleanField(false), broken: booleanField(false),
durabilityDie: new foundry.data.fields.StringField({
required: true, nullable: false, initial: "d6",
choices: SYSTEM.usageDieChoices,
}),
} }
} }
/** @override */ /** @override */
static LOCALIZATION_PREFIXES = ["MGNE.Weapon"] static LOCALIZATION_PREFIXES = ["MGNE.Weapon"]
/**
* Migrate old string-based properties field to the new SetField format.
* @override
*/
static migrateData(source) {
// Old data stored properties as a plain string; convert to empty array
if (typeof source.properties === "string") {
source.properties = []
}
// Remove any null/undefined/blank entries that may have crept in
if (Array.isArray(source.properties)) {
source.properties = source.properties.filter(p => p != null && p !== "")
}
return super.migrateData(source)
}
} }
View File
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000015 MANIFEST-000025
-15
View File
@@ -1,15 +0,0 @@
2026/05/10-10:14:24.425893 7ff663fff6c0 Recovering log #12
2026/05/10-10:14:24.435701 7ff663fff6c0 Delete type=3 #10
2026/05/10-10:14:24.435782 7ff663fff6c0 Delete type=0 #12
2026/05/10-10:15:09.159943 7ff6637fe6c0 Level-0 table #18: started
2026/05/10-10:15:09.164167 7ff6637fe6c0 Level-0 table #18: 1354 bytes OK
2026/05/10-10:15:09.170159 7ff6637fe6c0 Delete type=0 #16
2026/05/10-10:15:09.193554 7ff6637fe6c0 Manual compaction at level-0 from '!items!mgne-arm-chainshirt' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
2026/05/10-10:15:09.193605 7ff6637fe6c0 Manual compaction at level-1 from '!items!mgne-arm-chainshirt' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 25 : 1
2026/05/10-10:15:09.193611 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/05/10-10:15:09.197820 7ff6637fe6c0 Generated table #19@1: 1 keys, 685 bytes
2026/05/10-10:15:09.197839 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 685 bytes
2026/05/10-10:15:09.204193 7ff6637fe6c0 compacted to: files[ 0 0 2 0 0 0 0 ]
2026/05/10-10:15:09.204306 7ff6637fe6c0 Delete type=2 #14
2026/05/10-10:15:09.204434 7ff6637fe6c0 Delete type=2 #18
2026/05/10-10:15:09.221921 7ff6637fe6c0 Manual compaction at level-1 from '!items!null' @ 25 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
+3 -15
View File
@@ -1,15 +1,3 @@
2026/05/10-00:06:25.472144 7fe7209fd6c0 Recovering log #8 2026/05/17-15:40:00.779700 7f1642bfd6c0 Recovering log #22
2026/05/10-00:06:25.483556 7fe7209fd6c0 Delete type=3 #6 2026/05/17-15:40:00.789740 7f1642bfd6c0 Delete type=3 #20
2026/05/10-00:06:25.483623 7fe7209fd6c0 Delete type=0 #8 2026/05/17-15:40:00.789807 7f1642bfd6c0 Delete type=0 #22
2026/05/10-09:35:40.443132 7fe6d37fe6c0 Level-0 table #13: started
2026/05/10-09:35:40.446256 7fe6d37fe6c0 Level-0 table #13: 1354 bytes OK
2026/05/10-09:35:40.453014 7fe6d37fe6c0 Delete type=0 #11
2026/05/10-09:35:40.470483 7fe6d37fe6c0 Manual compaction at level-0 from '!items!mgne-arm-chainshirt' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
2026/05/10-09:35:40.476731 7fe6d37fe6c0 Manual compaction at level-1 from '!items!mgne-arm-chainshirt' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 17 : 1
2026/05/10-09:35:40.476735 7fe6d37fe6c0 Compacting 1@1 + 1@2 files
2026/05/10-09:35:40.479976 7fe6d37fe6c0 Generated table #14@1: 1 keys, 685 bytes
2026/05/10-09:35:40.480043 7fe6d37fe6c0 Compacted 1@1 + 1@2 files => 685 bytes
2026/05/10-09:35:40.486274 7fe6d37fe6c0 compacted to: files[ 0 0 2 0 0 0 0 ]
2026/05/10-09:35:40.486346 7fe6d37fe6c0 Delete type=2 #9
2026/05/10-09:35:40.486470 7fe6d37fe6c0 Delete type=2 #13
2026/05/10-09:35:40.504107 7fe6d37fe6c0 Manual compaction at level-1 from '!items!null' @ 17 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
View File
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000015 MANIFEST-000025
+3 -15
View File
@@ -1,15 +1,3 @@
2026/05/10-10:14:24.400732 7ff671fef6c0 Recovering log #12 2026/05/17-15:40:00.750239 7f16423fc6c0 Recovering log #22
2026/05/10-10:14:24.411244 7ff671fef6c0 Delete type=3 #10 2026/05/17-15:40:00.760411 7f16423fc6c0 Delete type=3 #20
2026/05/10-10:14:24.411312 7ff671fef6c0 Delete type=0 #12 2026/05/17-15:40:00.760461 7f16423fc6c0 Delete type=0 #22
2026/05/10-10:15:09.101581 7ff6637fe6c0 Level-0 table #18: started
2026/05/10-10:15:09.104730 7ff6637fe6c0 Level-0 table #18: 4899 bytes OK
2026/05/10-10:15:09.111016 7ff6637fe6c0 Delete type=0 #16
2026/05/10-10:15:09.121512 7ff6637fe6c0 Manual compaction at level-0 from '!actors!mgne-comp-beguiled-noble' @ 72057594037927935 : 1 .. '!actors!null' @ 0 : 0; will stop at (end)
2026/05/10-10:15:09.141299 7ff6637fe6c0 Manual compaction at level-1 from '!actors!mgne-comp-beguiled-noble' @ 72057594037927935 : 1 .. '!actors!null' @ 0 : 0; will stop at '!actors!null' @ 13 : 1
2026/05/10-10:15:09.141307 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/05/10-10:15:09.144336 7ff6637fe6c0 Generated table #19@1: 1 keys, 1984 bytes
2026/05/10-10:15:09.144349 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 1984 bytes
2026/05/10-10:15:09.150185 7ff6637fe6c0 compacted to: files[ 0 0 2 0 0 0 0 ]
2026/05/10-10:15:09.150242 7ff6637fe6c0 Delete type=2 #14
2026/05/10-10:15:09.150350 7ff6637fe6c0 Delete type=2 #18
2026/05/10-10:15:09.159856 7ff6637fe6c0 Manual compaction at level-1 from '!actors!null' @ 13 : 1 .. '!actors!null' @ 0 : 0; will stop at (end)
+15 -15
View File
@@ -1,15 +1,15 @@
2026/05/10-00:06:25.447076 7fe7211fe6c0 Recovering log #8 2026/05/17-13:26:05.090885 7f16423fc6c0 Recovering log #17
2026/05/10-00:06:25.457896 7fe7211fe6c0 Delete type=3 #6 2026/05/17-13:26:05.101315 7f16423fc6c0 Delete type=3 #15
2026/05/10-00:06:25.457952 7fe7211fe6c0 Delete type=0 #8 2026/05/17-13:26:05.101364 7f16423fc6c0 Delete type=0 #17
2026/05/10-09:35:40.366450 7fe6d37fe6c0 Level-0 table #13: started 2026/05/17-15:39:46.172949 7f1641bfb6c0 Level-0 table #23: started
2026/05/10-09:35:40.369656 7fe6d37fe6c0 Level-0 table #13: 4899 bytes OK 2026/05/17-15:39:46.177184 7f1641bfb6c0 Level-0 table #23: 4899 bytes OK
2026/05/10-09:35:40.375570 7fe6d37fe6c0 Delete type=0 #11 2026/05/17-15:39:46.183241 7f1641bfb6c0 Delete type=0 #21
2026/05/10-09:35:40.404682 7fe6d37fe6c0 Manual compaction at level-0 from '!actors!mgne-comp-beguiled-noble' @ 72057594037927935 : 1 .. '!actors!null' @ 0 : 0; will stop at (end) 2026/05/17-15:39:46.215307 7f1641bfb6c0 Manual compaction at level-0 from '!actors!mgne-comp-beguiled-noble' @ 72057594037927935 : 1 .. '!actors!null' @ 0 : 0; will stop at (end)
2026/05/10-09:35:40.404711 7fe6d37fe6c0 Manual compaction at level-1 from '!actors!mgne-comp-beguiled-noble' @ 72057594037927935 : 1 .. '!actors!null' @ 0 : 0; will stop at '!actors!null' @ 9 : 1 2026/05/17-15:39:46.224653 7f1641bfb6c0 Manual compaction at level-1 from '!actors!mgne-comp-beguiled-noble' @ 72057594037927935 : 1 .. '!actors!null' @ 0 : 0; will stop at '!actors!null' @ 17 : 1
2026/05/10-09:35:40.404715 7fe6d37fe6c0 Compacting 1@1 + 1@2 files 2026/05/17-15:39:46.224663 7f1641bfb6c0 Compacting 1@1 + 1@2 files
2026/05/10-09:35:40.407947 7fe6d37fe6c0 Generated table #14@1: 1 keys, 1984 bytes 2026/05/17-15:39:46.227820 7f1641bfb6c0 Generated table #24@1: 1 keys, 1984 bytes
2026/05/10-09:35:40.407960 7fe6d37fe6c0 Compacted 1@1 + 1@2 files => 1984 bytes 2026/05/17-15:39:46.227830 7f1641bfb6c0 Compacted 1@1 + 1@2 files => 1984 bytes
2026/05/10-09:35:40.413847 7fe6d37fe6c0 compacted to: files[ 0 0 2 0 0 0 0 ] 2026/05/17-15:39:46.233855 7f1641bfb6c0 compacted to: files[ 0 0 2 0 0 0 0 ]
2026/05/10-09:35:40.413959 7fe6d37fe6c0 Delete type=2 #9 2026/05/17-15:39:46.233946 7f1641bfb6c0 Delete type=2 #19
2026/05/10-09:35:40.414504 7fe6d37fe6c0 Delete type=2 #13 2026/05/17-15:39:46.234305 7f1641bfb6c0 Delete type=2 #23
2026/05/10-09:35:40.433958 7fe6d37fe6c0 Manual compaction at level-1 from '!actors!null' @ 9 : 1 .. '!actors!null' @ 0 : 0; will stop at (end) 2026/05/17-15:39:46.253812 7f1641bfb6c0 Manual compaction at level-1 from '!actors!null' @ 17 : 1 .. '!actors!null' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
View File
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000015 MANIFEST-000025
+3 -15
View File
@@ -1,15 +1,3 @@
2026/05/10-10:14:24.387289 7ff663fff6c0 Recovering log #12 2026/05/17-15:40:00.736377 7f1643bff6c0 Recovering log #22
2026/05/10-10:14:24.397491 7ff663fff6c0 Delete type=3 #10 2026/05/17-15:40:00.746313 7f1643bff6c0 Delete type=3 #20
2026/05/10-10:14:24.397540 7ff663fff6c0 Delete type=0 #12 2026/05/17-15:40:00.746417 7f1643bff6c0 Delete type=0 #22
2026/05/10-10:15:09.082119 7ff6637fe6c0 Level-0 table #18: started
2026/05/10-10:15:09.085424 7ff6637fe6c0 Level-0 table #18: 10412 bytes OK
2026/05/10-10:15:09.092167 7ff6637fe6c0 Delete type=0 #16
2026/05/10-10:15:09.121491 7ff6637fe6c0 Manual compaction at level-0 from '!items!mgne-feat-11' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
2026/05/10-10:15:09.121551 7ff6637fe6c0 Manual compaction at level-1 from '!items!mgne-feat-11' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 109 : 1
2026/05/10-10:15:09.121558 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/05/10-10:15:09.124679 7ff6637fe6c0 Generated table #19@1: 1 keys, 727 bytes
2026/05/10-10:15:09.124714 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 727 bytes
2026/05/10-10:15:09.130639 7ff6637fe6c0 compacted to: files[ 0 0 2 0 0 0 0 ]
2026/05/10-10:15:09.130747 7ff6637fe6c0 Delete type=2 #14
2026/05/10-10:15:09.130828 7ff6637fe6c0 Delete type=2 #18
2026/05/10-10:15:09.159837 7ff6637fe6c0 Manual compaction at level-1 from '!items!null' @ 109 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
+15 -15
View File
@@ -1,15 +1,15 @@
2026/05/10-00:06:25.433796 7fe7209fd6c0 Recovering log #8 2026/05/17-13:26:05.076690 7f1643bff6c0 Recovering log #17
2026/05/10-00:06:25.444473 7fe7209fd6c0 Delete type=3 #6 2026/05/17-13:26:05.087217 7f1643bff6c0 Delete type=3 #15
2026/05/10-00:06:25.444528 7fe7209fd6c0 Delete type=0 #8 2026/05/17-13:26:05.087270 7f1643bff6c0 Delete type=0 #17
2026/05/10-09:35:40.395081 7fe6d37fe6c0 Level-0 table #13: started 2026/05/17-15:39:46.194394 7f1641bfb6c0 Level-0 table #23: started
2026/05/10-09:35:40.398418 7fe6d37fe6c0 Level-0 table #13: 10411 bytes OK 2026/05/17-15:39:46.197937 7f1641bfb6c0 Level-0 table #23: 10414 bytes OK
2026/05/10-09:35:40.404589 7fe6d37fe6c0 Delete type=0 #11 2026/05/17-15:39:46.204111 7f1641bfb6c0 Delete type=0 #21
2026/05/10-09:35:40.414572 7fe6d37fe6c0 Manual compaction at level-0 from '!items!mgne-feat-11' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end) 2026/05/17-15:39:46.215335 7f1641bfb6c0 Manual compaction at level-0 from '!items!mgne-feat-11' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
2026/05/10-09:35:40.433971 7fe6d37fe6c0 Manual compaction at level-1 from '!items!mgne-feat-11' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 73 : 1 2026/05/17-15:39:46.234408 7f1641bfb6c0 Manual compaction at level-1 from '!items!mgne-feat-11' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 145 : 1
2026/05/10-09:35:40.433978 7fe6d37fe6c0 Compacting 1@1 + 1@2 files 2026/05/17-15:39:46.234419 7f1641bfb6c0 Compacting 1@1 + 1@2 files
2026/05/10-09:35:40.437087 7fe6d37fe6c0 Generated table #14@1: 1 keys, 727 bytes 2026/05/17-15:39:46.238287 7f1641bfb6c0 Generated table #24@1: 1 keys, 727 bytes
2026/05/10-09:35:40.437108 7fe6d37fe6c0 Compacted 1@1 + 1@2 files => 727 bytes 2026/05/17-15:39:46.238317 7f1641bfb6c0 Compacted 1@1 + 1@2 files => 727 bytes
2026/05/10-09:35:40.442891 7fe6d37fe6c0 compacted to: files[ 0 0 2 0 0 0 0 ] 2026/05/17-15:39:46.244549 7f1641bfb6c0 compacted to: files[ 0 0 2 0 0 0 0 ]
2026/05/10-09:35:40.442951 7fe6d37fe6c0 Delete type=2 #9 2026/05/17-15:39:46.244607 7f1641bfb6c0 Delete type=2 #19
2026/05/10-09:35:40.443056 7fe6d37fe6c0 Delete type=2 #13 2026/05/17-15:39:46.244675 7f1641bfb6c0 Delete type=2 #23
2026/05/10-09:35:40.453127 7fe6d37fe6c0 Manual compaction at level-1 from '!items!null' @ 73 : 1 .. '!items!null' @ 0 : 0; will stop at (end) 2026/05/17-15:39:46.253828 7f1641bfb6c0 Manual compaction at level-1 from '!items!null' @ 145 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000016 MANIFEST-000026
+3 -15
View File
@@ -1,15 +1,3 @@
2026/05/10-10:14:24.437910 7ff6717ee6c0 Recovering log #13 2026/05/17-15:40:00.792467 7f16423fc6c0 Recovering log #23
2026/05/10-10:14:24.448329 7ff6717ee6c0 Delete type=3 #11 2026/05/17-15:40:00.805587 7f16423fc6c0 Delete type=3 #21
2026/05/10-10:14:24.448394 7ff6717ee6c0 Delete type=0 #13 2026/05/17-15:40:00.805652 7f16423fc6c0 Delete type=0 #23
2026/05/10-10:15:09.176410 7ff6637fe6c0 Level-0 table #19: started
2026/05/10-10:15:09.181126 7ff6637fe6c0 Level-0 table #19: 36394 bytes OK
2026/05/10-10:15:09.187189 7ff6637fe6c0 Delete type=0 #17
2026/05/10-10:15:09.193581 7ff6637fe6c0 Manual compaction at level-0 from '!tables!mgne-tbl-armor' @ 72057594037927935 : 1 .. '!tables.results!zuw1vPYn2wNmreKL' @ 0 : 0; will stop at (end)
2026/05/10-10:15:09.204527 7ff6637fe6c0 Manual compaction at level-1 from '!tables!mgne-tbl-armor' @ 72057594037927935 : 1 .. '!tables.results!zuw1vPYn2wNmreKL' @ 0 : 0; will stop at '!tables.results!zuw1vPYn2wNmreKL' @ 1225 : 1
2026/05/10-10:15:09.204538 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/05/10-10:15:09.208350 7ff6637fe6c0 Generated table #20@1: 436 keys, 40367 bytes
2026/05/10-10:15:09.208369 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 40367 bytes
2026/05/10-10:15:09.215216 7ff6637fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/05/10-10:15:09.215286 7ff6637fe6c0 Delete type=2 #15
2026/05/10-10:15:09.215397 7ff6637fe6c0 Delete type=2 #19
2026/05/10-10:15:09.221933 7ff6637fe6c0 Manual compaction at level-1 from '!tables.results!zuw1vPYn2wNmreKL' @ 1225 : 1 .. '!tables.results!zuw1vPYn2wNmreKL' @ 0 : 0; will stop at (end)
+15 -15
View File
@@ -1,15 +1,15 @@
2026/05/10-00:06:25.485605 7fe7211fe6c0 Recovering log #8 2026/05/17-13:26:05.132672 7f16423fc6c0 Recovering log #18
2026/05/10-00:06:25.496011 7fe7211fe6c0 Delete type=3 #6 2026/05/17-13:26:05.142773 7f16423fc6c0 Delete type=3 #16
2026/05/10-00:06:25.496075 7fe7211fe6c0 Delete type=0 #8 2026/05/17-13:26:05.142824 7f16423fc6c0 Delete type=0 #18
2026/05/10-09:35:40.453137 7fe6d37fe6c0 Level-0 table #14: started 2026/05/17-15:39:46.374557 7f1641bfb6c0 Level-0 table #24: started
2026/05/10-09:35:40.457072 7fe6d37fe6c0 Level-0 table #14: 36470 bytes OK 2026/05/17-15:39:46.380380 7f1641bfb6c0 Level-0 table #24: 36276 bytes OK
2026/05/10-09:35:40.463127 7fe6d37fe6c0 Delete type=0 #12 2026/05/17-15:39:46.386967 7f1641bfb6c0 Delete type=0 #22
2026/05/10-09:35:40.476711 7fe6d37fe6c0 Manual compaction at level-0 from '!tables!mgne-tbl-armor' @ 72057594037927935 : 1 .. '!tables.results!zGPTmr9d4kGBxfWN' @ 0 : 0; will stop at (end) 2026/05/17-15:39:46.393402 7f1641bfb6c0 Manual compaction at level-0 from '!tables!mgne-tbl-armor' @ 72057594037927935 : 1 .. '!tables.results!zjYp8qq96SSCUgBi' @ 0 : 0; will stop at (end)
2026/05/10-09:35:40.486568 7fe6d37fe6c0 Manual compaction at level-1 from '!tables!mgne-tbl-armor' @ 72057594037927935 : 1 .. '!tables.results!zGPTmr9d4kGBxfWN' @ 0 : 0; will stop at '!tables.results!zku5SGHgSA4RLSlW' @ 660 : 0 2026/05/17-15:39:46.403700 7f1641bfb6c0 Manual compaction at level-1 from '!tables!mgne-tbl-armor' @ 72057594037927935 : 1 .. '!tables.results!zjYp8qq96SSCUgBi' @ 0 : 0; will stop at '!tables.results!zuw1vPYn2wNmreKL' @ 1530 : 0
2026/05/10-09:35:40.486576 7fe6d37fe6c0 Compacting 1@1 + 1@2 files 2026/05/17-15:39:46.403711 7f1641bfb6c0 Compacting 1@1 + 1@2 files
2026/05/10-09:35:40.490562 7fe6d37fe6c0 Generated table #15@1: 436 keys, 40440 bytes 2026/05/17-15:39:46.422351 7f1641bfb6c0 Generated table #25@1: 436 keys, 40426 bytes
2026/05/10-09:35:40.490577 7fe6d37fe6c0 Compacted 1@1 + 1@2 files => 40440 bytes 2026/05/17-15:39:46.422374 7f1641bfb6c0 Compacted 1@1 + 1@2 files => 40426 bytes
2026/05/10-09:35:40.496634 7fe6d37fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ] 2026/05/17-15:39:46.428514 7f1641bfb6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/05/10-09:35:40.496732 7fe6d37fe6c0 Delete type=2 #10 2026/05/17-15:39:46.428618 7f1641bfb6c0 Delete type=2 #20
2026/05/10-09:35:40.496846 7fe6d37fe6c0 Delete type=2 #14 2026/05/17-15:39:46.428754 7f1641bfb6c0 Delete type=2 #24
2026/05/10-09:35:40.504136 7fe6d37fe6c0 Manual compaction at level-1 from '!tables.results!zku5SGHgSA4RLSlW' @ 660 : 0 .. '!tables.results!zGPTmr9d4kGBxfWN' @ 0 : 0; will stop at (end) 2026/05/17-15:39:46.428833 7f1641bfb6c0 Manual compaction at level-1 from '!tables.results!zuw1vPYn2wNmreKL' @ 1530 : 0 .. '!tables.results!zjYp8qq96SSCUgBi' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
View File
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000015 MANIFEST-000025
+3 -15
View File
@@ -1,15 +1,3 @@
2026/05/10-10:14:24.373627 7ff6717ee6c0 Recovering log #12 2026/05/17-15:40:00.721783 7f16433fe6c0 Recovering log #22
2026/05/10-10:14:24.384402 7ff6717ee6c0 Delete type=3 #10 2026/05/17-15:40:00.732595 7f16433fe6c0 Delete type=3 #20
2026/05/10-10:14:24.384452 7ff6717ee6c0 Delete type=0 #12 2026/05/17-15:40:00.732660 7f16433fe6c0 Delete type=0 #22
2026/05/10-10:15:09.092296 7ff6637fe6c0 Level-0 table #18: started
2026/05/10-10:15:09.095463 7ff6637fe6c0 Level-0 table #18: 7117 bytes OK
2026/05/10-10:15:09.101458 7ff6637fe6c0 Delete type=0 #16
2026/05/10-10:15:09.121502 7ff6637fe6c0 Manual compaction at level-0 from '!items!mgne-res-accelerate' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
2026/05/10-10:15:09.130878 7ff6637fe6c0 Manual compaction at level-1 from '!items!mgne-res-accelerate' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 61 : 1
2026/05/10-10:15:09.130883 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/05/10-10:15:09.133861 7ff6637fe6c0 Generated table #19@1: 1 keys, 910 bytes
2026/05/10-10:15:09.133880 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 910 bytes
2026/05/10-10:15:09.141020 7ff6637fe6c0 compacted to: files[ 0 0 2 0 0 0 0 ]
2026/05/10-10:15:09.141125 7ff6637fe6c0 Delete type=2 #14
2026/05/10-10:15:09.141229 7ff6637fe6c0 Delete type=2 #18
2026/05/10-10:15:09.159847 7ff6637fe6c0 Manual compaction at level-1 from '!items!null' @ 61 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
+15 -15
View File
@@ -1,15 +1,15 @@
2026/05/10-00:06:25.421327 7fe7219ff6c0 Recovering log #8 2026/05/17-13:26:05.061355 7f1642bfd6c0 Recovering log #17
2026/05/10-00:06:25.431320 7fe7219ff6c0 Delete type=3 #6 2026/05/17-13:26:05.072897 7f1642bfd6c0 Delete type=3 #15
2026/05/10-00:06:25.431377 7fe7219ff6c0 Delete type=0 #8 2026/05/17-13:26:05.072969 7f1642bfd6c0 Delete type=0 #17
2026/05/10-09:35:40.375666 7fe6d37fe6c0 Level-0 table #13: started 2026/05/17-15:39:46.204241 7f1641bfb6c0 Level-0 table #23: started
2026/05/10-09:35:40.378697 7fe6d37fe6c0 Level-0 table #13: 7117 bytes OK 2026/05/17-15:39:46.207933 7f1641bfb6c0 Level-0 table #23: 7117 bytes OK
2026/05/10-09:35:40.385574 7fe6d37fe6c0 Delete type=0 #11 2026/05/17-15:39:46.215151 7f1641bfb6c0 Delete type=0 #21
2026/05/10-09:35:40.404692 7fe6d37fe6c0 Manual compaction at level-0 from '!items!mgne-res-accelerate' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end) 2026/05/17-15:39:46.215347 7f1641bfb6c0 Manual compaction at level-0 from '!items!mgne-res-accelerate' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
2026/05/10-09:35:40.423667 7fe6d37fe6c0 Manual compaction at level-1 from '!items!mgne-res-accelerate' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 41 : 1 2026/05/17-15:39:46.244725 7f1641bfb6c0 Manual compaction at level-1 from '!items!mgne-res-accelerate' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 81 : 1
2026/05/10-09:35:40.423674 7fe6d37fe6c0 Compacting 1@1 + 1@2 files 2026/05/17-15:39:46.244731 7f1641bfb6c0 Compacting 1@1 + 1@2 files
2026/05/10-09:35:40.426694 7fe6d37fe6c0 Generated table #14@1: 1 keys, 910 bytes 2026/05/17-15:39:46.247722 7f1641bfb6c0 Generated table #24@1: 1 keys, 910 bytes
2026/05/10-09:35:40.426710 7fe6d37fe6c0 Compacted 1@1 + 1@2 files => 910 bytes 2026/05/17-15:39:46.247731 7f1641bfb6c0 Compacted 1@1 + 1@2 files => 910 bytes
2026/05/10-09:35:40.433702 7fe6d37fe6c0 compacted to: files[ 0 0 2 0 0 0 0 ] 2026/05/17-15:39:46.253408 7f1641bfb6c0 compacted to: files[ 0 0 2 0 0 0 0 ]
2026/05/10-09:35:40.433797 7fe6d37fe6c0 Delete type=2 #9 2026/05/17-15:39:46.253506 7f1641bfb6c0 Delete type=2 #19
2026/05/10-09:35:40.433894 7fe6d37fe6c0 Delete type=2 #13 2026/05/17-15:39:46.253663 7f1641bfb6c0 Delete type=2 #23
2026/05/10-09:35:40.443122 7fe6d37fe6c0 Manual compaction at level-1 from '!items!null' @ 41 : 1 .. '!items!null' @ 0 : 0; will stop at (end) 2026/05/17-15:39:46.253842 7f1641bfb6c0 Manual compaction at level-1 from '!items!null' @ 81 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
View File
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000015 MANIFEST-000025
+3 -15
View File
@@ -1,15 +1,3 @@
2026/05/10-10:14:24.413909 7ff671fef6c0 Recovering log #12 2026/05/17-15:40:00.764360 7f16423fc6c0 Recovering log #22
2026/05/10-10:14:24.424039 7ff671fef6c0 Delete type=3 #10 2026/05/17-15:40:00.775885 7f16423fc6c0 Delete type=3 #20
2026/05/10-10:14:24.424092 7ff671fef6c0 Delete type=0 #12 2026/05/17-15:40:00.775980 7f16423fc6c0 Delete type=0 #22
2026/05/10-10:15:09.111128 7ff6637fe6c0 Level-0 table #18: started
2026/05/10-10:15:09.115029 7ff6637fe6c0 Level-0 table #18: 1965 bytes OK
2026/05/10-10:15:09.121359 7ff6637fe6c0 Delete type=0 #16
2026/05/10-10:15:09.121532 7ff6637fe6c0 Manual compaction at level-0 from '!items!mgne-wpn-club' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
2026/05/10-10:15:09.150422 7ff6637fe6c0 Manual compaction at level-1 from '!items!mgne-wpn-club' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 37 : 1
2026/05/10-10:15:09.150429 7ff6637fe6c0 Compacting 1@1 + 1@2 files
2026/05/10-10:15:09.153398 7ff6637fe6c0 Generated table #19@1: 1 keys, 626 bytes
2026/05/10-10:15:09.153408 7ff6637fe6c0 Compacted 1@1 + 1@2 files => 626 bytes
2026/05/10-10:15:09.159589 7ff6637fe6c0 compacted to: files[ 0 0 2 0 0 0 0 ]
2026/05/10-10:15:09.159695 7ff6637fe6c0 Delete type=2 #14
2026/05/10-10:15:09.159778 7ff6637fe6c0 Delete type=2 #18
2026/05/10-10:15:09.159866 7ff6637fe6c0 Manual compaction at level-1 from '!items!null' @ 37 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
+15 -15
View File
@@ -1,15 +1,15 @@
2026/05/10-00:06:25.460392 7fe7211fe6c0 Recovering log #8 2026/05/17-13:26:05.105018 7f16423fc6c0 Recovering log #17
2026/05/10-00:06:25.470093 7fe7211fe6c0 Delete type=3 #6 2026/05/17-13:26:05.114474 7f16423fc6c0 Delete type=3 #15
2026/05/10-00:06:25.470147 7fe7211fe6c0 Delete type=0 #8 2026/05/17-13:26:05.114527 7f16423fc6c0 Delete type=0 #17
2026/05/10-09:35:40.385681 7fe6d37fe6c0 Level-0 table #13: started 2026/05/17-15:39:46.183373 7f1641bfb6c0 Level-0 table #23: started
2026/05/10-09:35:40.389078 7fe6d37fe6c0 Level-0 table #13: 1965 bytes OK 2026/05/17-15:39:46.187330 7f1641bfb6c0 Level-0 table #23: 1965 bytes OK
2026/05/10-09:35:40.394971 7fe6d37fe6c0 Delete type=0 #11 2026/05/17-15:39:46.194074 7f1641bfb6c0 Delete type=0 #21
2026/05/10-09:35:40.404700 7fe6d37fe6c0 Manual compaction at level-0 from '!items!mgne-wpn-club' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end) 2026/05/17-15:39:46.215323 7f1641bfb6c0 Manual compaction at level-0 from '!items!mgne-wpn-club' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
2026/05/10-09:35:40.414585 7fe6d37fe6c0 Manual compaction at level-1 from '!items!mgne-wpn-club' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 25 : 1 2026/05/17-15:39:46.215362 7f1641bfb6c0 Manual compaction at level-1 from '!items!mgne-wpn-club' @ 72057594037927935 : 1 .. '!items!null' @ 0 : 0; will stop at '!items!null' @ 49 : 1
2026/05/10-09:35:40.414592 7fe6d37fe6c0 Compacting 1@1 + 1@2 files 2026/05/17-15:39:46.215370 7f1641bfb6c0 Compacting 1@1 + 1@2 files
2026/05/10-09:35:40.417590 7fe6d37fe6c0 Generated table #14@1: 1 keys, 626 bytes 2026/05/17-15:39:46.218482 7f1641bfb6c0 Generated table #24@1: 1 keys, 626 bytes
2026/05/10-09:35:40.417600 7fe6d37fe6c0 Compacted 1@1 + 1@2 files => 626 bytes 2026/05/17-15:39:46.218497 7f1641bfb6c0 Compacted 1@1 + 1@2 files => 626 bytes
2026/05/10-09:35:40.423435 7fe6d37fe6c0 compacted to: files[ 0 0 2 0 0 0 0 ] 2026/05/17-15:39:46.224247 7f1641bfb6c0 compacted to: files[ 0 0 2 0 0 0 0 ]
2026/05/10-09:35:40.423523 7fe6d37fe6c0 Delete type=2 #9 2026/05/17-15:39:46.224396 7f1641bfb6c0 Delete type=2 #19
2026/05/10-09:35:40.423607 7fe6d37fe6c0 Delete type=2 #13 2026/05/17-15:39:46.224564 7f1641bfb6c0 Delete type=2 #23
2026/05/10-09:35:40.443110 7fe6d37fe6c0 Manual compaction at level-1 from '!items!null' @ 25 : 1 .. '!items!null' @ 0 : 0; will stop at (end) 2026/05/17-15:39:46.253795 7f1641bfb6c0 Manual compaction at level-1 from '!items!null' @ 49 : 1 .. '!items!null' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
+11 -3
View File
@@ -35,9 +35,7 @@
}, },
"creature": { "creature": {
"htmlFields": [ "htmlFields": [
"description", "description"
"special",
"notes"
] ]
}, },
"companion": { "companion": {
@@ -45,6 +43,11 @@
"description", "description",
"notes" "notes"
] ]
},
"party": {
"htmlFields": [
"notes"
]
} }
}, },
"Item": { "Item": {
@@ -82,6 +85,11 @@
"htmlFields": [ "htmlFields": [
"description" "description"
] ]
},
"creature-trait": {
"htmlFields": [
"description"
]
} }
} }
}, },
+17 -1
View File
@@ -5,7 +5,7 @@
<input type="text" name="name" value="{{source.name}}" /> <input type="text" name="name" value="{{source.name}}" />
</div> </div>
</div> </div>
<div class="item-form-grid item-form-grid-two"> <div class="item-form-grid item-form-grid-three">
<div class="item-form-row"> <div class="item-form-row">
<label>{{localize "MGNE.Common.ArmorDie"}}</label> <label>{{localize "MGNE.Common.ArmorDie"}}</label>
<select name="system.armorDie"> <select name="system.armorDie">
@@ -22,6 +22,22 @@
{{/each}} {{/each}}
</select> </select>
</div> </div>
<div class="item-form-row">
<label>{{localize "MGNE.Common.Weight"}}</label>
<select name="system.weight">
{{#each selectOptions.weightCategories}}
<option value="{{value}}" {{#if (isEqual ../source.system.weight value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
<div class="item-form-row">
<label>{{localize "MGNE.Common.DurabilityDie"}}</label>
<select name="system.durabilityDie">
{{#each selectOptions.usageDice}}
<option value="{{value}}" {{#if (isEqual ../source.system.durabilityDie value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
</div> </div>
<div class="item-check-grid check-grid"> <div class="item-check-grid check-grid">
<label><input type="checkbox" name="system.equipped" {{#if source.system.equipped}}checked{{/if}} /> {{localize "MGNE.Common.Equipped"}}</label> <label><input type="checkbox" name="system.equipped" {{#if source.system.equipped}}checked{{/if}} /> {{localize "MGNE.Common.Equipped"}}</label>
+16 -8
View File
@@ -6,14 +6,6 @@
</div> </div>
</div> </div>
<div class="item-form-grid item-form-grid-three"> <div class="item-form-grid item-form-grid-three">
<div class="item-form-row">
<label>{{localize "MGNE.Common.ArtifactId"}}</label>
<select name="system.artifactId">
{{#each selectOptions.artifactIds}}
<option value="{{value}}" {{#if (isEqual ../source.system.artifactId value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
<div class="item-form-row"> <div class="item-form-row">
<label>{{localize "MGNE.Common.UsageDie"}}</label> <label>{{localize "MGNE.Common.UsageDie"}}</label>
<select name="system.usageDie"> <select name="system.usageDie">
@@ -22,6 +14,22 @@
{{/each}} {{/each}}
</select> </select>
</div> </div>
<div class="item-form-row">
<label>{{localize "MGNE.Common.Weight"}}</label>
<select name="system.weight">
{{#each selectOptions.weightCategories}}
<option value="{{value}}" {{#if (isEqual ../source.system.weight value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
<div class="item-form-row">
<label>{{localize "MGNE.Common.DurabilityDie"}}</label>
<select name="system.durabilityDie">
{{#each selectOptions.usageDice}}
<option value="{{value}}" {{#if (isEqual ../source.system.durabilityDie value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
<div class="item-form-row"> <div class="item-form-row">
<label>{{localize "MGNE.Common.SynchronizedTo"}}</label> <label>{{localize "MGNE.Common.SynchronizedTo"}}</label>
<input type="text" name="system.synchronizedTo" value="{{source.system.synchronizedTo}}" /> <input type="text" name="system.synchronizedTo" value="{{source.system.synchronizedTo}}" />
+52 -15
View File
@@ -1,9 +1,12 @@
<section class="tab-panel {{tab.cssClass}}"> <section class="tab-panel {{tab.cssClass}}">
<div class="resource-bar resource-bar-equipment"> <div class="resource-bar resource-bar-equipment">
<div class="resource-box resource-box-single resource-box-compact resource-box-inline resource-box-inline-single"> <div class="resource-box resource-box-single resource-box-compact resource-box-inline resource-box-inline-single {{#if system.overloaded}}resource-box-overloaded{{/if}}">
<label class="resource-label-accent">{{localize "MGNE.Character.CarryingCapacity"}}</label> <label class="resource-label-accent">{{localize "MGNE.Character.Load"}}</label>
<div class="numeric-pill"> <div class="numeric-pill">
<input class="numeric-input numeric-input-readonly" type="number" name="system.carryCapacity" value="{{system.carryCapacity}}" readonly /> <span class="numeric-input numeric-input-readonly load-display {{#if system.overloaded}}load-overloaded{{/if}}"
title="{{system.lightItemCount}} light items (10 = 1 slot){{#if system.heavyItemCount}} · {{system.heavyItemCount}} heavy{{/if}}">
{{system.currentLoad}} / {{system.carryCapacity}}
</span>
</div> </div>
</div> </div>
<div class="resource-box resource-box-single resource-box-compact resource-box-inline resource-box-inline-single"> <div class="resource-box resource-box-single resource-box-compact resource-box-inline resource-box-inline-single">
@@ -27,14 +30,22 @@
<button type="button" data-action="createItem" data-item-type="weapon">{{localize "MGNE.Character.AddWeapon"}}</button> <button type="button" data-action="createItem" data-item-type="weapon">{{localize "MGNE.Character.AddWeapon"}}</button>
</div> </div>
{{#each weapons}} {{#each weapons}}
<div class="item-row" data-item-id="{{id}}"> <div class="item-row {{#if system.broken}}item-row-broken{{/if}}" data-item-id="{{id}}">
<div class="item-name">{{name}}</div> <div class="item-name" {{#if tooltip}}data-tooltip="{{tooltip}}" data-tooltip-class="mgne-item-tooltip"{{/if}}>
{{name}}{{#if system.broken}} <span class="item-broken-badge">{{localize "MGNE.Common.Broken"}}</span>{{/if}}
{{#each system.properties}}<span class="weapon-property-badge">{{lookup @root.config.weaponPropertyLabels this}}</span>{{/each}}
</div>
<div>{{system.damage}}</div> <div>{{system.damage}}</div>
<div>{{lookup @root.config.weaponCategories system.category}}</div> <div>{{lookup @root.config.weaponCategories system.category}}</div>
<span class="item-weight-badge weight-{{system.weight}}">{{lookup @root.config.weightCategories system.weight}}</span>
<span class="item-durability-badge {{#if system.broken}}durability-broken{{/if}}">
<i class="fa-solid fa-shield-halved"></i> {{system.durabilityDie}}
</span>
<div class="item-actions"> <div class="item-actions">
<button type="button" data-action="toggleEquipped">{{#if system.equipped}}{{localize "MGNE.Common.Unequip"}}{{else}}{{localize "MGNE.Common.Equip"}}{{/if}}</button> <button type="button" data-action="toggleEquipped">{{#if system.equipped}}{{localize "MGNE.Common.Unequip"}}{{else}}{{localize "MGNE.Common.Equip"}}{{/if}}</button>
<button type="button" class="rollable" data-roll-type="weapon">{{localize "MGNE.Common.Attack"}}</button> <button type="button" class="rollable" data-roll-type="weapon">{{localize "MGNE.Common.Attack"}}</button>
<button type="button" class="rollable" data-roll-type="damage">{{localize "MGNE.Common.Damage"}}</button> <button type="button" class="rollable" data-roll-type="damage">{{localize "MGNE.Common.Damage"}}</button>
<button type="button" class="rollable" data-roll-type="durability" data-tooltip="{{localize "MGNE.Common.RollDurability"}}">{{localize "MGNE.Common.Durability"}}</button>
<button type="button" class="rollable" data-roll-type="usage">{{localize "MGNE.Common.Usage"}}</button> <button type="button" class="rollable" data-roll-type="usage">{{localize "MGNE.Common.Usage"}}</button>
<button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button> <button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button>
<button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button> <button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button>
@@ -52,24 +63,34 @@
<button type="button" data-action="createItem" data-item-type="shield">{{localize "MGNE.Character.AddShield"}}</button> <button type="button" data-action="createItem" data-item-type="shield">{{localize "MGNE.Character.AddShield"}}</button>
</div> </div>
{{#each armors}} {{#each armors}}
<div class="item-row" data-item-id="{{id}}"> <div class="item-row {{#if system.broken}}item-row-broken{{/if}}" data-item-id="{{id}}">
<div class="item-name">{{name}}</div> <div class="item-name" {{#if tooltip}}data-tooltip="{{tooltip}}" data-tooltip-class="mgne-item-tooltip"{{/if}}>
{{name}}{{#if system.broken}} <span class="item-broken-badge">{{localize "MGNE.Common.Broken"}}</span>{{/if}}
</div>
<div>{{system.armorDie}}</div> <div>{{system.armorDie}}</div>
<div>{{localize "MGNE.Common.Penalty"}} {{system.penalty}}</div> <div>{{localize "MGNE.Common.Penalty"}} {{system.penalty}}</div>
<span class="item-weight-badge weight-{{system.weight}}">{{lookup @root.config.weightCategories system.weight}}</span>
<span class="item-durability-badge {{#if system.broken}}durability-broken{{/if}}"><i class="fa-solid fa-shield-halved"></i> {{system.durabilityDie}}</span>
<div class="item-actions"> <div class="item-actions">
<button type="button" data-action="toggleEquipped">{{#if system.equipped}}{{localize "MGNE.Common.Unequip"}}{{else}}{{localize "MGNE.Common.Equip"}}{{/if}}</button> <button type="button" data-action="toggleEquipped">{{#if system.equipped}}{{localize "MGNE.Common.Unequip"}}{{else}}{{localize "MGNE.Common.Equip"}}{{/if}}</button>
<button type="button" class="rollable" data-roll-type="durability">{{localize "MGNE.Common.Durability"}}</button>
<button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button> <button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button>
<button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button> <button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button>
</div> </div>
</div> </div>
{{/each}} {{/each}}
{{#each shields}} {{#each shields}}
<div class="item-row" data-item-id="{{id}}"> <div class="item-row {{#if system.broken}}item-row-broken{{/if}}" data-item-id="{{id}}">
<div class="item-name">{{name}}</div> <div class="item-name" {{#if tooltip}}data-tooltip="{{tooltip}}" data-tooltip-class="mgne-item-tooltip"{{/if}}>
{{name}}{{#if system.broken}} <span class="item-broken-badge">{{localize "MGNE.Common.Broken"}}</span>{{/if}}
</div>
<div>{{system.armorDie}}</div> <div>{{system.armorDie}}</div>
<div>{{localize "MGNE.ItemTypes.shield"}}</div> <div>{{localize "MGNE.ItemTypes.shield"}}</div>
<span class="item-weight-badge weight-{{system.weight}}">{{lookup @root.config.weightCategories system.weight}}</span>
<span class="item-durability-badge {{#if system.broken}}durability-broken{{/if}}"><i class="fa-solid fa-shield-halved"></i> {{system.durabilityDie}}</span>
<div class="item-actions"> <div class="item-actions">
<button type="button" data-action="toggleEquipped">{{#if system.equipped}}{{localize "MGNE.Common.Unequip"}}{{else}}{{localize "MGNE.Common.Equip"}}{{/if}}</button> <button type="button" data-action="toggleEquipped">{{#if system.equipped}}{{localize "MGNE.Common.Unequip"}}{{else}}{{localize "MGNE.Common.Equip"}}{{/if}}</button>
<button type="button" class="rollable" data-roll-type="durability">{{localize "MGNE.Common.Durability"}}</button>
<button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button> <button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button>
<button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button> <button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button>
</div> </div>
@@ -83,11 +104,16 @@
<button type="button" data-action="createItem" data-item-type="equipment">{{localize "MGNE.Character.AddEquipment"}}</button> <button type="button" data-action="createItem" data-item-type="equipment">{{localize "MGNE.Character.AddEquipment"}}</button>
</div> </div>
{{#each equipmentItems}} {{#each equipmentItems}}
<div class="item-row" data-item-id="{{id}}"> <div class="item-row {{#if system.broken}}item-row-broken{{/if}}" data-item-id="{{id}}">
<div class="item-name">{{name}}</div> <div class="item-name" {{#if tooltip}}data-tooltip="{{tooltip}}" data-tooltip-class="mgne-item-tooltip"{{/if}}>
{{name}}{{#if system.broken}} <span class="item-broken-badge">{{localize "MGNE.Common.Broken"}}</span>{{/if}}
</div>
<div>{{localize "MGNE.Common.QuantityShort"}} {{system.quantity}}</div> <div>{{localize "MGNE.Common.QuantityShort"}} {{system.quantity}}</div>
<div>{{system.usageDie}}</div> <div>{{system.usageDie}}</div>
<span class="item-weight-badge weight-{{system.weight}}">{{lookup @root.config.weightCategories system.weight}}</span>
<span class="item-durability-badge {{#if system.broken}}durability-broken{{/if}}"><i class="fa-solid fa-shield-halved"></i> {{system.durabilityDie}}</span>
<div class="item-actions"> <div class="item-actions">
<button type="button" class="rollable" data-roll-type="durability">{{localize "MGNE.Common.Durability"}}</button>
<button type="button" class="rollable" data-roll-type="usage">{{localize "MGNE.Common.Usage"}}</button> <button type="button" class="rollable" data-roll-type="usage">{{localize "MGNE.Common.Usage"}}</button>
<button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button> <button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button>
<button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button> <button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button>
@@ -104,12 +130,18 @@
<button type="button" data-action="createItem" data-item-type="resonance-core">{{localize "MGNE.Character.AddCore"}}</button> <button type="button" data-action="createItem" data-item-type="resonance-core">{{localize "MGNE.Character.AddCore"}}</button>
</div> </div>
{{#each cores}} {{#each cores}}
<div class="item-row" data-item-id="{{id}}"> <div class="item-row {{#if system.broken}}item-row-broken{{/if}}" data-item-id="{{id}}">
<div class="item-name">{{name}}</div> <div class="item-name" {{#if tooltip}}data-tooltip="{{tooltip}}" data-tooltip-class="mgne-item-tooltip"{{/if}}>
{{name}}{{#if system.broken}} <span class="item-broken-badge">{{localize "MGNE.Common.Broken"}}</span>{{/if}}
{{#if system.burnedOut}} <span class="item-broken-badge item-burned-badge">{{localize "MGNE.Common.BurnedOut"}}</span>{{/if}}
</div>
<div>{{lookup @root.config.resonanceList system.resonationId}}</div> <div>{{lookup @root.config.resonanceList system.resonationId}}</div>
<div>{{system.usageDie}}</div> <div>{{system.usageDie}}</div>
<span class="item-weight-badge weight-{{system.weight}}">{{lookup @root.config.weightCategories system.weight}}</span>
<span class="item-durability-badge {{#if system.broken}}durability-broken{{/if}}"><i class="fa-solid fa-shield-halved"></i> {{system.durabilityDie}}</span>
<div class="item-actions"> <div class="item-actions">
<button type="button" class="rollable" data-roll-type="resonation">{{localize "MGNE.Common.Invoke"}}</button> <button type="button" class="rollable" data-roll-type="resonation">{{localize "MGNE.Common.Invoke"}}</button>
<button type="button" class="rollable" data-roll-type="durability">{{localize "MGNE.Common.Durability"}}</button>
<button type="button" class="rollable" data-roll-type="usage">{{localize "MGNE.Common.Usage"}}</button> <button type="button" class="rollable" data-roll-type="usage">{{localize "MGNE.Common.Usage"}}</button>
<button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button> <button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button>
<button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button> <button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button>
@@ -126,12 +158,17 @@
<button type="button" data-action="createItem" data-item-type="artifact">{{localize "MGNE.Character.AddArtifact"}}</button> <button type="button" data-action="createItem" data-item-type="artifact">{{localize "MGNE.Character.AddArtifact"}}</button>
</div> </div>
{{#each artifacts}} {{#each artifacts}}
<div class="item-row" data-item-id="{{id}}"> <div class="item-row {{#if system.broken}}item-row-broken{{/if}}" data-item-id="{{id}}">
<div class="item-name">{{name}}</div> <div class="item-name" {{#if tooltip}}data-tooltip="{{tooltip}}" data-tooltip-class="mgne-item-tooltip"{{/if}}>
{{name}}{{#if system.broken}} <span class="item-broken-badge">{{localize "MGNE.Common.Broken"}}</span>{{/if}}
</div>
<div>{{#if system.synchronized}}{{localize "MGNE.Common.Synchronized"}}{{else}}{{localize "MGNE.Common.Unsynchronized"}}{{/if}}</div> <div>{{#if system.synchronized}}{{localize "MGNE.Common.Synchronized"}}{{else}}{{localize "MGNE.Common.Unsynchronized"}}{{/if}}</div>
<div>{{system.usageDie}}</div> <div>{{system.usageDie}}</div>
<span class="item-weight-badge weight-{{system.weight}}">{{lookup @root.config.weightCategories system.weight}}</span>
<span class="item-durability-badge {{#if system.broken}}durability-broken{{/if}}"><i class="fa-solid fa-shield-halved"></i> {{system.durabilityDie}}</span>
<div class="item-actions"> <div class="item-actions">
<button type="button" data-action="syncArtifact">{{#if system.synchronized}}{{localize "MGNE.Common.Desync"}}{{else}}{{localize "MGNE.Common.Sync"}}{{/if}}</button> <button type="button" data-action="syncArtifact">{{#if system.synchronized}}{{localize "MGNE.Common.Desync"}}{{else}}{{localize "MGNE.Common.Sync"}}{{/if}}</button>
<button type="button" class="rollable" data-roll-type="durability">{{localize "MGNE.Common.Durability"}}</button>
<button type="button" class="rollable" data-roll-type="usage">{{localize "MGNE.Common.Usage"}}</button> <button type="button" class="rollable" data-roll-type="usage">{{localize "MGNE.Common.Usage"}}</button>
<button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button> <button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button>
<button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button> <button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button>
+1 -1
View File
@@ -5,7 +5,7 @@
</div> </div>
{{#each features}} {{#each features}}
<div class="item-row" data-item-id="{{id}}"> <div class="item-row" data-item-id="{{id}}">
<div class="item-name">{{name}}</div> <div class="item-name" {{#if tooltip}}data-tooltip="{{tooltip}}" data-tooltip-class="mgne-item-tooltip"{{/if}}>{{name}}</div>
<div class="item-actions"> <div class="item-actions">
<button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button> <button type="button" data-action="editItem">{{localize "MGNE.Common.Edit"}}</button>
<button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button> <button type="button" data-action="deleteItem">{{localize "MGNE.Common.Delete"}}</button>
+7
View File
@@ -19,6 +19,13 @@
<small class="ability-defense-sub">{{localize "MGNE.Abilities.agility"}}</small> <small class="ability-defense-sub">{{localize "MGNE.Abilities.agility"}}</small>
</div> </div>
</div> </div>
<div class="ability-card ability-card-armor">
<label class="rollable ability-label" data-roll-type="armor">{{localize "MGNE.Common.ArmorSave"}}</label>
<div class="ability-score ability-score-text">
<span class="ability-defense-main">{{#if system.armorFormula}}{{system.armorFormula}}{{else}}{{/if}}</span>
<small class="ability-defense-sub">{{localize "MGNE.Common.ArmorDie"}}</small>
</div>
</div>
</div> </div>
<div class="grid two"> <div class="grid two">
+9 -2
View File
@@ -11,12 +11,19 @@
{{#if subtitle}}<p class="chat-subtitle"><strong>{{subtitle}}</strong></p>{{/if}} {{#if subtitle}}<p class="chat-subtitle"><strong>{{subtitle}}</strong></p>{{/if}}
{{#if formula}}<p class="chat-formula"><code>{{formula}}</code></p>{{/if}} {{#if formula}}<p class="chat-formula"><code>{{formula}}</code></p>{{/if}}
{{#if outcome}} {{#if outcome}}
<div class="chat-outcome"> <div class="chat-outcome {{#if diceTooltip}}has-tooltip{{/if}}" role="button" tabindex="0" data-action="toggle-dice-tooltip">
<span class="chat-outcome-label">{{outcome}}</span> <span class="chat-outcome-label">{{outcome}}</span>
<span class="chat-outcome-total">{{total}}</span> <span class="chat-outcome-total">{{total}}</span>
{{#if diceTooltip}}<i class="chat-tooltip-icon fa-solid fa-dice"></i>{{/if}}
</div> </div>
{{else if total}} {{else if total}}
<div class="chat-damage-total">{{total}}</div> <div class="chat-damage-total {{#if diceTooltip}}has-tooltip{{/if}}" role="button" tabindex="0" data-action="toggle-dice-tooltip">
{{total}}
{{#if diceTooltip}}<i class="chat-tooltip-icon fa-solid fa-dice"></i>{{/if}}
</div>
{{/if}}
{{#if diceTooltip}}
<div class="chat-dice-tooltip" hidden>{{{diceTooltip}}}</div>
{{/if}} {{/if}}
{{#if specialText}}<p class="chat-special">{{specialText}}</p>{{/if}} {{#if specialText}}<p class="chat-special">{{specialText}}</p>{{/if}}
{{#if omenNeutralizeReminder}}<p class="chat-omen-remind chat-omen-neutralize">💡 {{omenNeutralizeReminder}}</p>{{/if}} {{#if omenNeutralizeReminder}}<p class="chat-omen-remind chat-omen-neutralize">💡 {{omenNeutralizeReminder}}</p>{{/if}}
-15
View File
@@ -42,21 +42,6 @@
</div> </div>
</div> </div>
<div class="ability-grid">
{{#each abilityList}}
<div class="ability-card">
<label class="rollable ability-label" data-roll-type="ability" data-ability-id="{{id}}">{{label}}</label>
<div class="ability-score">
<select class="numeric-input ability-input" name="system.abilities.{{id}}.value">
{{#each @root.selectOptions.abilityValues}}
<option value="{{value}}" {{#if (isEqual ../value value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
</div>
{{/each}}
</div>
<div class="inventory-section"> <div class="inventory-section">
<div class="inventory-header"> <div class="inventory-header">
<h3>{{localize "MGNE.Common.Attack"}}</h3> <h3>{{localize "MGNE.Common.Attack"}}</h3>
+67 -45
View File
@@ -42,45 +42,84 @@
</div> </div>
</div> </div>
<div class="ability-grid"> <!-- Type + Number row -->
{{#each abilityList}} <div class="creature-meta-row">
<div class="ability-card"> <div class="creature-type-group">
<label class="rollable ability-label" data-roll-type="ability" data-ability-id="{{id}}">{{label}}</label> <span class="creature-meta-label">{{localize "MGNE.Creature.Type"}}</span>
<div class="ability-score"> {{#each creatureTypes}}
<select class="numeric-input ability-input" name="system.abilities.{{id}}.value"> <label class="creature-type-checkbox">
{{#each @root.selectOptions.abilityValues}} <input type="checkbox" name="system.creatureType" value="{{key}}" {{#if checked}}checked{{/if}} />
<option value="{{value}}" {{#if (isEqual ../value value)}}selected{{/if}}>{{label}}</option> <span>{{label}}</span>
</label>
{{/each}} {{/each}}
</select>
</div> </div>
<div class="creature-number-group">
<label class="creature-meta-label" for="creature-number">{{localize "MGNE.Creature.Number"}}</label>
<input id="creature-number" class="creature-number-input" type="text" name="system.number" value="{{source.system.number}}" />
</div> </div>
{{/each}}
</div> </div>
<div class="inventory-section"> <!-- Action Table -->
<div class="inventory-section creature-action-table-section">
<div class="inventory-header"> <div class="inventory-header">
<h3>{{localize "MGNE.Common.Attack"}}</h3> <h3>{{localize "MGNE.Creature.ActionTable"}}</h3>
{{#if actionTable}}
<div class="action-table-buttons">
<button type="button" data-action="rollActionTable" class="rollable" data-tooltip="{{localize "MGNE.Creature.RollAction"}}">
<i class="fa-solid fa-dice-d20"></i> {{localize "MGNE.Creature.RollAction"}}
</button>
<button type="button" data-action="openActionTable" data-tooltip="{{localize "MGNE.Creature.OpenTable"}}">
<i class="fa-solid fa-table-list"></i> {{localize "MGNE.Creature.OpenTable"}}
</button>
<button type="button" data-action="clearActionTable" data-tooltip="{{localize "MGNE.Creature.ClearTable"}}">
<i class="fa-solid fa-xmark"></i>
</button>
</div> </div>
<fieldset> {{/if}}
<div class="grid three">
<div>
<label>{{localize "MGNE.Common.Label"}}</label>
<input type="text" name="system.attack.label" value="{{source.system.attack.label}}" />
</div> </div>
<div> <div class="action-table-drop-zone {{#unless actionTable}}drop-hint{{/unless}}">
<label>{{localize "MGNE.Common.Damage"}}</label> {{#if actionTable}}
<input type="text" name="system.attack.damage" value="{{source.system.attack.damage}}" /> <i class="fa-solid fa-table-list"></i>
<span class="action-table-name">{{actionTable.name}}</span>
{{else}}
<i class="fa-solid fa-cloud-arrow-down"></i>
<span>{{localize "MGNE.Creature.DropTableHint"}}</span>
{{/if}}
</div> </div>
<div class="inline-buttons">
<button type="button" class="rollable" data-roll-type="profile-attack">{{localize "MGNE.Common.Attack"}}</button>
<button type="button" class="rollable" data-roll-type="profile-damage">{{localize "MGNE.Common.Damage"}}</button>
<button type="button" class="rollable" data-roll-type="morale">{{localize "MGNE.Common.Morale"}}</button>
</div>
</div>
</fieldset>
</div> </div>
<div class="grid two"> <!-- Traits -->
<div class="creature-traits-container">
<div class="creature-traits-header">
<h3>{{localize "MGNE.Creature.Traits"}}</h3>
<button type="button" data-action="createItem" data-item-type="creature-trait">
<i class="fa-solid fa-plus"></i> {{localize "MGNE.Creature.AddTrait"}}
</button>
</div>
<div class="creature-traits-list">
{{#each traits}}
<div class="creature-trait-card" data-item-id="{{id}}">
<div class="creature-trait-card-header">
<div class="creature-trait-name" {{#if tooltip}}data-tooltip="{{tooltip}}" data-tooltip-class="mgne-item-tooltip"{{/if}}>
<i class="fa-solid fa-bolt-lightning"></i>
<span>{{name}}</span>
</div>
{{#if system.trigger}}
<span class="creature-trait-trigger">{{system.trigger}}</span>
{{/if}}
<div class="item-actions">
<button type="button" data-action="editItem" data-tooltip="{{localize "MGNE.Common.Edit"}}"><i class="fa-solid fa-pen"></i></button>
<button type="button" data-action="deleteItem" data-item-id="{{id}}" data-tooltip="{{localize "MGNE.Common.Delete"}}"><i class="fa-solid fa-trash"></i></button>
</div>
</div>
</div>
{{else}}
<p class="empty-state">{{localize "MGNE.Empty.NoTraits"}}</p>
{{/each}}
</div>
</div>
<!-- Description -->
<div class="inventory-section"> <div class="inventory-section">
<div class="inventory-header"> <div class="inventory-header">
<h3>{{localize "MGNE.Common.Description"}}</h3> <h3>{{localize "MGNE.Common.Description"}}</h3>
@@ -89,22 +128,5 @@
{{formInput systemFields.description enriched=(lookup enrichedFields "description") value=system.description name="system.description" toggled=true}} {{formInput systemFields.description enriched=(lookup enrichedFields "description") value=system.description name="system.description" toggled=true}}
</fieldset> </fieldset>
</div> </div>
<div class="inventory-section">
<div class="inventory-header">
<h3>{{localize "MGNE.Creature.Special"}}</h3>
</div>
<fieldset>
{{formInput systemFields.special enriched=(lookup enrichedFields "special") value=system.special name="system.special" toggled=true}}
</fieldset>
</div>
</div>
<div class="inventory-section">
<div class="inventory-header">
<h3>{{localize "MGNE.Common.Notes"}}</h3>
</div>
<fieldset>
{{formInput systemFields.notes enriched=(lookup enrichedFields "notes") value=system.notes name="system.notes" toggled=true}}
</fieldset>
</div>
</section> </section>
+20
View File
@@ -0,0 +1,20 @@
<section class="mgne-sheet">
<div class="sheet-header">
<img class="item-portrait" src="{{item.img}}" data-edit="img" data-action="editImage" alt="{{item.name}}" />
<div class="header-fields">
<input type="text" name="name" value="{{source.name}}" />
</div>
</div>
<div class="item-form-grid item-form-grid-one">
<div class="item-form-row">
<label>{{localize "MGNE.CreatureTrait.Trigger"}}</label>
<input type="text" name="system.trigger" value="{{source.system.trigger}}" placeholder="{{localize "MGNE.CreatureTrait.TriggerPlaceholder"}}" />
</div>
</div>
<div class="inventory-header">
<h3>{{localize "MGNE.Common.Description"}}</h3>
</div>
<fieldset>
{{formInput systemFields.description enriched=(lookup enrichedFields "description") value=system.description name="system.description" toggled=true}}
</fieldset>
</section>
+17
View File
@@ -14,6 +14,14 @@
{{/each}} {{/each}}
</select> </select>
</div> </div>
<div class="item-form-row">
<label>{{localize "MGNE.Common.Weight"}}</label>
<select name="system.weight">
{{#each selectOptions.weightCategories}}
<option value="{{value}}" {{#if (isEqual ../source.system.weight value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
<div class="item-form-row"> <div class="item-form-row">
<label>{{localize "MGNE.Common.Quantity"}}</label> <label>{{localize "MGNE.Common.Quantity"}}</label>
<input type="number" name="system.quantity" value="{{source.system.quantity}}" /> <input type="number" name="system.quantity" value="{{source.system.quantity}}" />
@@ -26,11 +34,20 @@
{{/each}} {{/each}}
</select> </select>
</div> </div>
<div class="item-form-row">
<label>{{localize "MGNE.Common.DurabilityDie"}}</label>
<select name="system.durabilityDie">
{{#each selectOptions.usageDice}}
<option value="{{value}}" {{#if (isEqual ../source.system.durabilityDie value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
</div> </div>
<div class="item-check-grid item-check-grid-three check-grid"> <div class="item-check-grid item-check-grid-three check-grid">
<label><input type="checkbox" name="system.carried" {{#if source.system.carried}}checked{{/if}} /> {{localize "MGNE.Common.Carried"}}</label> <label><input type="checkbox" name="system.carried" {{#if source.system.carried}}checked{{/if}} /> {{localize "MGNE.Common.Carried"}}</label>
<label><input type="checkbox" name="system.equipped" {{#if source.system.equipped}}checked{{/if}} /> {{localize "MGNE.Common.Equipped"}}</label> <label><input type="checkbox" name="system.equipped" {{#if source.system.equipped}}checked{{/if}} /> {{localize "MGNE.Common.Equipped"}}</label>
<label><input type="checkbox" name="system.consumable" {{#if source.system.consumable}}checked{{/if}} /> {{localize "MGNE.Common.Consumable"}}</label> <label><input type="checkbox" name="system.consumable" {{#if source.system.consumable}}checked{{/if}} /> {{localize "MGNE.Common.Consumable"}}</label>
<label><input type="checkbox" name="system.broken" {{#if source.system.broken}}checked{{/if}} /> {{localize "MGNE.Common.Broken"}}</label>
</div> </div>
<div class="inventory-header"> <div class="inventory-header">
<h3>{{localize "MGNE.Common.Description"}}</h3> <h3>{{localize "MGNE.Common.Description"}}</h3>
+37
View File
@@ -0,0 +1,37 @@
<section class="tab-panel {{tab.cssClass}}">
<div class="inventory-section">
<div class="inventory-header">
<h3>{{localize "MGNE.Party.Loot"}}</h3>
</div>
{{#if lootItems.length}}
<div class="party-loot-list">
<div class="party-loot-row party-loot-header">
<span></span>
<span>{{localize "MGNE.Common.Name"}}</span>
<span>{{localize "MGNE.Common.Type"}}</span>
<span></span>
</div>
{{#each lootItems as |item|}}
<div class="party-loot-row" data-item-id="{{item.id}}">
<img src="{{item.img}}" class="item-portrait" />
<span class="item-name">{{item.name}}</span>
<span class="item-type-label">{{item.typeLabel}}</span>
<div class="item-actions">
<a data-action="editItem" data-item-id="{{item.id}}" data-tooltip="{{localize 'MGNE.Common.Edit'}}">
<i class="fa-solid fa-pen"></i>
</a>
<a data-action="deleteItem" data-item-id="{{item.id}}" data-tooltip="{{localize 'MGNE.Common.Delete'}}">
<i class="fa-solid fa-trash"></i>
</a>
</div>
</div>
{{/each}}
</div>
{{else}}
<p class="empty-section-hint">{{localize "MGNE.Empty.NoLoot"}}</p>
{{/if}}
</div>
<p class="party-drop-hint">{{localize "MGNE.Party.DropLootHint"}}</p>
</section>
+21
View File
@@ -0,0 +1,21 @@
<section class="mgne-sheet mgne-sheet-header">
<div class="sheet-header">
<div class="portrait-column">
<img class="actor-portrait" src="{{actor.img}}" data-edit="img" data-action="editImage" alt="{{actor.name}}" />
</div>
<div class="header-fields">
<input type="text" name="name" value="{{source.name}}" />
<div class="party-credits-row">
<label class="resource-label-accent">{{localize "MGNE.Party.Credits"}}</label>
<div class="party-credits-stepper">
<button type="button" class="credits-btn" data-action="adjustCredits" data-delta="-10">10</button>
<button type="button" class="credits-btn" data-action="adjustCredits" data-delta="-1">1</button>
<input class="numeric-input credits-input" type="number" name="system.credits" value="{{source.system.credits}}" min="0" />
<span class="credits-symbol">₵</span>
<button type="button" class="credits-btn" data-action="adjustCredits" data-delta="1">+1</button>
<button type="button" class="credits-btn" data-action="adjustCredits" data-delta="10">+10</button>
</div>
</div>
</div>
</div>
</section>
+48
View File
@@ -0,0 +1,48 @@
<section class="tab-panel {{tab.cssClass}}">
<div class="inventory-section">
<div class="inventory-header">
<h3>{{localize "MGNE.Party.Members"}}</h3>
</div>
{{#if members.length}}
<div class="party-member-list">
<div class="party-member-row party-member-header">
<span></span>
<span>{{localize "MGNE.Common.Name"}}</span>
<span>{{localize "MGNE.Common.HP"}}</span>
<span>{{localize "MGNE.Common.Type"}}</span>
<span></span>
</div>
{{#each members as |member|}}
<div class="party-member-row" data-actor-id="{{member.id}}">
<img src="{{member.img}}" class="party-member-portrait" />
<span class="party-member-name">
<a data-action="openMember" data-actor-id="{{member.id}}">{{member.name}}</a>
</span>
<span class="party-member-hp">{{member.hp}}</span>
<span class="party-member-type">{{member.typeLabel}}</span>
<div class="item-actions">
{{#unless member.isFirst}}
<a data-action="moveMemberUp" data-ref-idx="{{member.refIdx}}" data-tooltip="{{localize 'MGNE.Party.MoveUp'}}">
<i class="fa-solid fa-chevron-up"></i>
</a>
{{/unless}}
{{#unless member.isLast}}
<a data-action="moveMemberDown" data-ref-idx="{{member.refIdx}}" data-tooltip="{{localize 'MGNE.Party.MoveDown'}}">
<i class="fa-solid fa-chevron-down"></i>
</a>
{{/unless}}
<a data-action="removeMember" data-actor-id="{{member.id}}" data-tooltip="{{localize 'MGNE.Party.RemoveMember'}}">
<i class="fa-solid fa-times"></i>
</a>
</div>
</div>
{{/each}}
</div>
{{else}}
<p class="empty-section-hint">{{localize "MGNE.Empty.NoMembers"}}</p>
{{/if}}
</div>
<p class="party-drop-hint">{{localize "MGNE.Party.DropMemberHint"}}</p>
</section>
+10
View File
@@ -0,0 +1,10 @@
<section class="tab-panel {{tab.cssClass}}">
<div class="inventory-section">
<div class="inventory-header">
<h3>{{localize "MGNE.Common.Notes"}}</h3>
</div>
<fieldset>
{{formInput systemFields.notes enriched=(lookup enrichedFields "notes") value=system.notes name="system.notes" toggled=true}}
</fieldset>
</div>
</section>
+5
View File
@@ -0,0 +1,5 @@
<nav class="sheet-tabs">
{{#each tabs}}
<button type="button" class="tab-button {{cssClass}}" data-action="changeTab" data-group="{{group}}" data-tab="{{id}}">{{label}}</button>
{{/each}}
</nav>
+18 -1
View File
@@ -5,7 +5,7 @@
<input type="text" name="name" value="{{source.name}}" /> <input type="text" name="name" value="{{source.name}}" />
</div> </div>
</div> </div>
<div class="item-form-grid item-form-grid-two"> <div class="item-form-grid item-form-grid-three">
<div class="item-form-row"> <div class="item-form-row">
<label>{{localize "MGNE.Common.UsageDie"}}</label> <label>{{localize "MGNE.Common.UsageDie"}}</label>
<select name="system.usageDie"> <select name="system.usageDie">
@@ -14,9 +14,26 @@
{{/each}} {{/each}}
</select> </select>
</div> </div>
<div class="item-form-row">
<label>{{localize "MGNE.Common.DurabilityDie"}}</label>
<select name="system.durabilityDie">
{{#each selectOptions.usageDice}}
<option value="{{value}}" {{#if (isEqual ../source.system.durabilityDie value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
<div class="item-form-row">
<label>{{localize "MGNE.Common.Weight"}}</label>
<select name="system.weight">
{{#each selectOptions.weightCategories}}
<option value="{{value}}" {{#if (isEqual ../source.system.weight value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
</div> </div>
<div class="item-check-grid check-grid"> <div class="item-check-grid check-grid">
<label><input type="checkbox" name="system.burnedOut" {{#if source.system.burnedOut}}checked{{/if}} /> {{localize "MGNE.Common.BurnedOut"}}</label> <label><input type="checkbox" name="system.burnedOut" {{#if source.system.burnedOut}}checked{{/if}} /> {{localize "MGNE.Common.BurnedOut"}}</label>
<label><input type="checkbox" name="system.broken" {{#if source.system.broken}}checked{{/if}} /> {{localize "MGNE.Common.Broken"}}</label>
</div> </div>
<div class="inventory-header"> <div class="inventory-header">
<h3>{{localize "MGNE.Common.Description"}}</h3> <h3>{{localize "MGNE.Common.Description"}}</h3>
+17 -1
View File
@@ -5,7 +5,7 @@
<input type="text" name="name" value="{{source.name}}" /> <input type="text" name="name" value="{{source.name}}" />
</div> </div>
</div> </div>
<div class="item-form-grid item-form-grid-two"> <div class="item-form-grid item-form-grid-three">
<div class="item-form-row"> <div class="item-form-row">
<label>{{localize "MGNE.Common.ArmorDie"}}</label> <label>{{localize "MGNE.Common.ArmorDie"}}</label>
<select name="system.armorDie"> <select name="system.armorDie">
@@ -22,6 +22,22 @@
{{/each}} {{/each}}
</select> </select>
</div> </div>
<div class="item-form-row">
<label>{{localize "MGNE.Common.Weight"}}</label>
<select name="system.weight">
{{#each selectOptions.weightCategories}}
<option value="{{value}}" {{#if (isEqual ../source.system.weight value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
<div class="item-form-row">
<label>{{localize "MGNE.Common.DurabilityDie"}}</label>
<select name="system.durabilityDie">
{{#each selectOptions.usageDice}}
<option value="{{value}}" {{#if (isEqual ../source.system.durabilityDie value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
</div> </div>
<div class="item-check-grid check-grid"> <div class="item-check-grid check-grid">
<label><input type="checkbox" name="system.equipped" {{#if source.system.equipped}}checked{{/if}} /> {{localize "MGNE.Common.Equipped"}}</label> <label><input type="checkbox" name="system.equipped" {{#if source.system.equipped}}checked{{/if}} /> {{localize "MGNE.Common.Equipped"}}</label>
+27 -2
View File
@@ -23,8 +23,20 @@
<input type="text" name="system.range" value="{{source.system.range}}" /> <input type="text" name="system.range" value="{{source.system.range}}" />
</div> </div>
<div class="item-form-row"> <div class="item-form-row">
<label>{{localize "MGNE.Common.Properties"}}</label> <label>{{localize "MGNE.Common.Weight"}}</label>
<input type="text" name="system.properties" value="{{source.system.properties}}" /> <select name="system.weight">
{{#each selectOptions.weightCategories}}
<option value="{{value}}" {{#if (isEqual ../source.system.weight value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
<div class="item-form-row">
<label>{{localize "MGNE.Common.DurabilityDie"}}</label>
<select name="system.durabilityDie">
{{#each selectOptions.usageDice}}
<option value="{{value}}" {{#if (isEqual ../source.system.durabilityDie value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div> </div>
<div class="item-form-row"> <div class="item-form-row">
<label>{{localize "MGNE.Common.UsageDie"}}</label> <label>{{localize "MGNE.Common.UsageDie"}}</label>
@@ -43,6 +55,19 @@
<label><input type="checkbox" name="system.equipped" {{#if source.system.equipped}}checked{{/if}} /> {{localize "MGNE.Common.Equipped"}}</label> <label><input type="checkbox" name="system.equipped" {{#if source.system.equipped}}checked{{/if}} /> {{localize "MGNE.Common.Equipped"}}</label>
<label><input type="checkbox" name="system.broken" {{#if source.system.broken}}checked{{/if}} /> {{localize "MGNE.Common.Broken"}}</label> <label><input type="checkbox" name="system.broken" {{#if source.system.broken}}checked{{/if}} /> {{localize "MGNE.Common.Broken"}}</label>
</div> </div>
<div class="inventory-header">
<h3>{{localize "MGNE.Common.Properties"}}</h3>
</div>
<div class="weapon-properties-grid">
<input type="hidden" name="system.properties" value="" />
{{#each selectOptions.weaponProperties}}
<label class="property-check" title="{{hint}}">
<input type="checkbox" name="system.properties" value="{{value}}"
{{#if (includes ../source.system.properties value)}}checked{{/if}} />
{{label}}
</label>
{{/each}}
</div>
<div class="inventory-header"> <div class="inventory-header">
<h3>{{localize "MGNE.Common.Description"}}</h3> <h3>{{localize "MGNE.Common.Description"}}</h3>
</div> </div>