Add party an army sheeets
This commit is contained in:
@@ -28,7 +28,10 @@
|
|||||||
}
|
}
|
||||||
.oathhammer .character-content,
|
.oathhammer .character-content,
|
||||||
.oathhammer .npc-content,
|
.oathhammer .npc-content,
|
||||||
.oathhammer .settlement-content {
|
.oathhammer .settlement-content,
|
||||||
|
.oathhammer .regiment-content,
|
||||||
|
.oathhammer .party-content,
|
||||||
|
.oathhammer .army-content {
|
||||||
font-family: "Calibri", "Segoe UI", sans-serif;
|
font-family: "Calibri", "Segoe UI", sans-serif;
|
||||||
font-size: 0.86rem;
|
font-size: 0.86rem;
|
||||||
color: #2a1a0a;
|
color: #2a1a0a;
|
||||||
@@ -40,20 +43,32 @@
|
|||||||
}
|
}
|
||||||
.oathhammer .character-content nav.tabs [data-tab],
|
.oathhammer .character-content nav.tabs [data-tab],
|
||||||
.oathhammer .npc-content nav.tabs [data-tab],
|
.oathhammer .npc-content nav.tabs [data-tab],
|
||||||
.oathhammer .settlement-content nav.tabs [data-tab] {
|
.oathhammer .settlement-content nav.tabs [data-tab],
|
||||||
|
.oathhammer .regiment-content nav.tabs [data-tab],
|
||||||
|
.oathhammer .party-content nav.tabs [data-tab],
|
||||||
|
.oathhammer .army-content nav.tabs [data-tab] {
|
||||||
color: #535128;
|
color: #535128;
|
||||||
}
|
}
|
||||||
.oathhammer .character-content nav.tabs [data-tab].active,
|
.oathhammer .character-content nav.tabs [data-tab].active,
|
||||||
.oathhammer .npc-content nav.tabs [data-tab].active,
|
.oathhammer .npc-content nav.tabs [data-tab].active,
|
||||||
.oathhammer .settlement-content nav.tabs [data-tab].active {
|
.oathhammer .settlement-content nav.tabs [data-tab].active,
|
||||||
|
.oathhammer .regiment-content nav.tabs [data-tab].active,
|
||||||
|
.oathhammer .party-content nav.tabs [data-tab].active,
|
||||||
|
.oathhammer .army-content nav.tabs [data-tab].active {
|
||||||
color: #084a74;
|
color: #084a74;
|
||||||
}
|
}
|
||||||
.oathhammer .character-content input:disabled,
|
.oathhammer .character-content input:disabled,
|
||||||
.oathhammer .npc-content input:disabled,
|
.oathhammer .npc-content input:disabled,
|
||||||
.oathhammer .settlement-content input:disabled,
|
.oathhammer .settlement-content input:disabled,
|
||||||
|
.oathhammer .regiment-content input:disabled,
|
||||||
|
.oathhammer .party-content input:disabled,
|
||||||
|
.oathhammer .army-content input:disabled,
|
||||||
.oathhammer .character-content select:disabled,
|
.oathhammer .character-content select:disabled,
|
||||||
.oathhammer .npc-content select:disabled,
|
.oathhammer .npc-content select:disabled,
|
||||||
.oathhammer .settlement-content select:disabled {
|
.oathhammer .settlement-content select:disabled,
|
||||||
|
.oathhammer .regiment-content select:disabled,
|
||||||
|
.oathhammer .party-content select:disabled,
|
||||||
|
.oathhammer .army-content select:disabled {
|
||||||
background-color: rgba(0, 0, 0, 0.08);
|
background-color: rgba(0, 0, 0, 0.08);
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
color: #2a1a0a;
|
color: #2a1a0a;
|
||||||
@@ -61,9 +76,15 @@
|
|||||||
.oathhammer .character-content input,
|
.oathhammer .character-content input,
|
||||||
.oathhammer .npc-content input,
|
.oathhammer .npc-content input,
|
||||||
.oathhammer .settlement-content input,
|
.oathhammer .settlement-content input,
|
||||||
|
.oathhammer .regiment-content input,
|
||||||
|
.oathhammer .party-content input,
|
||||||
|
.oathhammer .army-content input,
|
||||||
.oathhammer .character-content select,
|
.oathhammer .character-content select,
|
||||||
.oathhammer .npc-content select,
|
.oathhammer .npc-content select,
|
||||||
.oathhammer .settlement-content select {
|
.oathhammer .settlement-content select,
|
||||||
|
.oathhammer .regiment-content select,
|
||||||
|
.oathhammer .party-content select,
|
||||||
|
.oathhammer .army-content select {
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
background-color: rgba(255, 255, 255, 0.3);
|
background-color: rgba(255, 255, 255, 0.3);
|
||||||
border-color: #084a74;
|
border-color: #084a74;
|
||||||
@@ -71,7 +92,10 @@
|
|||||||
}
|
}
|
||||||
.oathhammer .character-content input[name="name"],
|
.oathhammer .character-content input[name="name"],
|
||||||
.oathhammer .npc-content input[name="name"],
|
.oathhammer .npc-content input[name="name"],
|
||||||
.oathhammer .settlement-content input[name="name"] {
|
.oathhammer .settlement-content input[name="name"],
|
||||||
|
.oathhammer .regiment-content input[name="name"],
|
||||||
|
.oathhammer .party-content input[name="name"],
|
||||||
|
.oathhammer .army-content input[name="name"] {
|
||||||
height: 2.5rem;
|
height: 2.5rem;
|
||||||
font-family: "Sherwood", "Palatino Linotype", serif;
|
font-family: "Sherwood", "Palatino Linotype", serif;
|
||||||
font-size: calc(0.86rem * 1.2);
|
font-size: calc(0.86rem * 1.2);
|
||||||
@@ -82,14 +106,20 @@
|
|||||||
}
|
}
|
||||||
.oathhammer .character-content fieldset,
|
.oathhammer .character-content fieldset,
|
||||||
.oathhammer .npc-content fieldset,
|
.oathhammer .npc-content fieldset,
|
||||||
.oathhammer .settlement-content fieldset {
|
.oathhammer .settlement-content fieldset,
|
||||||
|
.oathhammer .regiment-content fieldset,
|
||||||
|
.oathhammer .party-content fieldset,
|
||||||
|
.oathhammer .army-content fieldset {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border-color: #535128;
|
border-color: #535128;
|
||||||
}
|
}
|
||||||
.oathhammer .character-content legend,
|
.oathhammer .character-content legend,
|
||||||
.oathhammer .npc-content legend,
|
.oathhammer .npc-content legend,
|
||||||
.oathhammer .settlement-content legend {
|
.oathhammer .settlement-content legend,
|
||||||
|
.oathhammer .regiment-content legend,
|
||||||
|
.oathhammer .party-content legend,
|
||||||
|
.oathhammer .army-content legend {
|
||||||
font-family: "BlueDragon", "Palatino Linotype", serif;
|
font-family: "BlueDragon", "Palatino Linotype", serif;
|
||||||
font-size: calc(0.86rem * 1.1);
|
font-size: calc(0.86rem * 1.1);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -98,7 +128,10 @@
|
|||||||
}
|
}
|
||||||
.oathhammer .character-content label,
|
.oathhammer .character-content label,
|
||||||
.oathhammer .npc-content label,
|
.oathhammer .npc-content label,
|
||||||
.oathhammer .settlement-content label {
|
.oathhammer .settlement-content label,
|
||||||
|
.oathhammer .regiment-content label,
|
||||||
|
.oathhammer .party-content label,
|
||||||
|
.oathhammer .army-content label {
|
||||||
font-family: "BlueDragon", "Palatino Linotype", serif;
|
font-family: "BlueDragon", "Palatino Linotype", serif;
|
||||||
font-size: 0.86rem;
|
font-size: 0.86rem;
|
||||||
color: #2a1a0a;
|
color: #2a1a0a;
|
||||||
@@ -882,6 +915,93 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
|
.oathhammer .npc-main .regiment-vitals-grid.regiment-row1 {
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
}
|
||||||
|
.oathhammer .npc-main .regiment-vitals-grid.regiment-row2 {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
border-top: none;
|
||||||
|
margin-top: 4px;
|
||||||
|
padding-top: 4px;
|
||||||
|
border-top: 1px dashed rgba(83, 81, 40, 0.5);
|
||||||
|
}
|
||||||
|
.oathhammer .regiment-content .npc-left {
|
||||||
|
min-width: 94px;
|
||||||
|
max-width: 94px;
|
||||||
|
}
|
||||||
|
.oathhammer .regiment-content .npc-left .actor-img {
|
||||||
|
width: 94px;
|
||||||
|
height: 110px;
|
||||||
|
-o-object-fit: cover;
|
||||||
|
object-fit: cover;
|
||||||
|
-o-object-position: center top;
|
||||||
|
object-position: center top;
|
||||||
|
border: 2px solid rgba(42, 26, 10, 0.4);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .regiment-fieldset {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .regiment-leader-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
border: 1px dashed rgba(83, 81, 40, 0.6);
|
||||||
|
border-radius: 3px;
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
.oathhammer .regiment-leader-row .regiment-leader-label {
|
||||||
|
font-family: "BlueDragon", "Palatino Linotype", serif;
|
||||||
|
font-size: calc(0.86rem * 0.85);
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2a1a0a;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 5.5rem;
|
||||||
|
}
|
||||||
|
.oathhammer .regiment-leader-row .regiment-leader-img {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #535128;
|
||||||
|
-o-object-fit: cover;
|
||||||
|
object-fit: cover;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .regiment-leader-row .regiment-leader-name {
|
||||||
|
flex: 1;
|
||||||
|
font-family: "BlueDragon", "Palatino Linotype", serif;
|
||||||
|
font-size: calc(0.86rem * 0.85);
|
||||||
|
color: #084a74;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.oathhammer .regiment-leader-row .regiment-leader-name:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #c8a84b;
|
||||||
|
}
|
||||||
|
.oathhammer .regiment-leader-row .regiment-leader-empty {
|
||||||
|
flex: 1;
|
||||||
|
font-size: calc(0.86rem * 0.9);
|
||||||
|
color: rgba(42, 26, 10, 0.45);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.oathhammer .regiment-leader-row .regiment-leader-clear {
|
||||||
|
color: rgba(42, 26, 10, 0.4);
|
||||||
|
font-size: calc(0.86rem * 0.85);
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .regiment-leader-row .regiment-leader-clear:hover {
|
||||||
|
color: #cc3333;
|
||||||
|
}
|
||||||
.oathhammer .item-list {
|
.oathhammer .item-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -1012,11 +1132,11 @@
|
|||||||
}
|
}
|
||||||
.oathhammer .item-list--spell .item-list-header,
|
.oathhammer .item-list--spell .item-list-header,
|
||||||
.oathhammer .item-list--spell .item-entry {
|
.oathhammer .item-list--spell .item-entry {
|
||||||
grid-template-columns: 24px 1fr 3rem 6rem 3rem 5.5rem;
|
grid-template-columns: 24px 1fr 2.5rem 5.5rem 3.5rem 4.5rem 3.5rem 5rem;
|
||||||
}
|
}
|
||||||
.oathhammer .item-list--miracle .item-list-header,
|
.oathhammer .item-list--miracle .item-list-header,
|
||||||
.oathhammer .item-list--miracle .item-entry {
|
.oathhammer .item-list--miracle .item-entry {
|
||||||
grid-template-columns: 24px 1fr 4.5rem 5.5rem;
|
grid-template-columns: 24px 1fr 6rem 5rem;
|
||||||
}
|
}
|
||||||
.oathhammer .miracles-blocked {
|
.oathhammer .miracles-blocked {
|
||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
@@ -1333,7 +1453,7 @@
|
|||||||
}
|
}
|
||||||
.oathhammer .regiment-sheet .regiment-stats-row {
|
.oathhammer .regiment-sheet .regiment-stats-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
.oathhammer .regiment-sheet .regiment-stats-row .form-group > label {
|
.oathhammer .regiment-sheet .regiment-stats-row .form-group > label {
|
||||||
@@ -2328,3 +2448,307 @@
|
|||||||
.oathhammer .settlement-buildings-header .collect-taxes-btn i {
|
.oathhammer .settlement-buildings-header .collect-taxes-btn i {
|
||||||
color: #c8a84b;
|
color: #c8a84b;
|
||||||
}
|
}
|
||||||
|
.oathhammer .party-main .party-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-portrait-wrap {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-portrait-wrap .party-portrait {
|
||||||
|
width: 94px;
|
||||||
|
height: 110px;
|
||||||
|
-o-object-fit: cover;
|
||||||
|
object-fit: cover;
|
||||||
|
-o-object-position: center top;
|
||||||
|
object-position: center top;
|
||||||
|
border: 2px solid rgba(42, 26, 10, 0.4);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-header-body {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-header-body .character-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-bottom: 1px solid #535128;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-header-body .character-name input,
|
||||||
|
.oathhammer .party-main .party-header-body .character-name span {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
font-family: "Sherwood", "Palatino Linotype", serif;
|
||||||
|
font-size: calc(0.86rem * 1.1);
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-header-body .character-name > .control {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-header-fieldset {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-treasury {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
border: 1px solid #535128;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: rgba(0, 0, 0, 0.08);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-treasury .party-treasury-label {
|
||||||
|
font-family: "BlueDragon", "Palatino Linotype", serif;
|
||||||
|
font-size: calc(0.86rem * 0.85);
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2a1a0a;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 4rem;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-treasury .party-currency {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 7rem;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-treasury .party-currency .currency-label {
|
||||||
|
font-family: "BlueDragon", "Palatino Linotype", serif;
|
||||||
|
font-size: calc(0.86rem * 0.9);
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2a1a0a;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 1.8rem;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-treasury .party-currency .currency-stepper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-treasury .party-currency .currency-stepper input {
|
||||||
|
width: 3.5rem;
|
||||||
|
text-align: center;
|
||||||
|
font-size: calc(0.86rem * 0.85);
|
||||||
|
padding: 1px 2px;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-treasury .party-currency .currency-stepper .currency-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 1.1rem;
|
||||||
|
height: 1.1rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1;
|
||||||
|
border: 1px solid #535128;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: rgba(83, 81, 40, 0.2);
|
||||||
|
color: #2a1a0a;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-treasury .party-currency .currency-stepper .currency-btn:hover {
|
||||||
|
background: #c8a84b;
|
||||||
|
border-color: #c8a84b;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-treasury .party-currency-gp .currency-label {
|
||||||
|
color: #987d2e;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-treasury .party-currency-sp .currency-label {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
.oathhammer .party-main .party-treasury .party-currency-cp .currency-label {
|
||||||
|
color: #aa6633;
|
||||||
|
}
|
||||||
|
.oathhammer .item-list--party-member .item-list-header,
|
||||||
|
.oathhammer .item-list--party-member .item-entry {
|
||||||
|
grid-template-columns: 1.8rem 24px 1fr 7rem 3rem 5rem 5.5rem;
|
||||||
|
}
|
||||||
|
.oathhammer .item-list--party-member .party-member-order {
|
||||||
|
font-family: "BlueDragon", "Palatino Linotype", serif;
|
||||||
|
font-size: calc(0.86rem * 0.9);
|
||||||
|
font-weight: bold;
|
||||||
|
color: #535128;
|
||||||
|
text-align: center;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
.oathhammer .item-list--party-loot .item-list-header,
|
||||||
|
.oathhammer .item-list--party-loot .item-entry {
|
||||||
|
grid-template-columns: 24px 1fr 6rem 5.5rem 5rem;
|
||||||
|
}
|
||||||
|
.oathhammer .item-list--party-loot .item-qty {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 3px;
|
||||||
|
font-size: calc(0.86rem * 0.85);
|
||||||
|
}
|
||||||
|
.oathhammer .item-list--party-loot .item-qty span {
|
||||||
|
min-width: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.oathhammer .item-list--party-loot .item-qty .qty-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1;
|
||||||
|
border: 1px solid #535128;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: rgba(83, 81, 40, 0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.oathhammer .item-list--party-loot .item-qty .qty-btn:hover {
|
||||||
|
background: #c8a84b;
|
||||||
|
border-color: #c8a84b;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-portrait-wrap {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-portrait-wrap .army-portrait {
|
||||||
|
width: 94px;
|
||||||
|
height: 110px;
|
||||||
|
-o-object-fit: cover;
|
||||||
|
object-fit: cover;
|
||||||
|
-o-object-position: center top;
|
||||||
|
object-position: center top;
|
||||||
|
border: 2px solid rgba(42, 26, 10, 0.4);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-header-body {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 5px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-header-body .character-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-bottom: 1px solid #535128;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-header-body .character-name input,
|
||||||
|
.oathhammer .army-main .army-header-body .character-name span {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
font-family: "Sherwood", "Palatino Linotype", serif;
|
||||||
|
font-size: calc(0.86rem * 1.1);
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-header-body .character-name > .control {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-header-fieldset {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-leader-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 3px 5px;
|
||||||
|
border: 1px dashed #535128;
|
||||||
|
border-radius: 3px;
|
||||||
|
min-height: 2rem;
|
||||||
|
background: rgba(83, 81, 40, 0.05);
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-leader-row .army-field-label {
|
||||||
|
font-family: "BlueDragon", "Palatino Linotype", serif;
|
||||||
|
font-size: calc(0.86rem * 0.85);
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2a1a0a;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 6rem;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-leader-row .army-leader-img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
-o-object-fit: cover;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #535128;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-leader-row .army-leader-name {
|
||||||
|
flex: 1;
|
||||||
|
font-size: calc(0.86rem * 0.85);
|
||||||
|
color: #2a1a0a;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-leader-row .army-leader-name:hover {
|
||||||
|
color: #535128;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-leader-row .army-leader-clear {
|
||||||
|
color: #535128;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-leader-row .army-leader-clear:hover {
|
||||||
|
color: #aa3333;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-leader-row .army-field-empty {
|
||||||
|
flex: 1;
|
||||||
|
font-size: calc(0.86rem * 0.9);
|
||||||
|
color: rgba(42, 26, 10, 0.5);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-location-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-location-row .army-field-label {
|
||||||
|
font-family: "BlueDragon", "Palatino Linotype", serif;
|
||||||
|
font-size: calc(0.86rem * 0.85);
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2a1a0a;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 6rem;
|
||||||
|
}
|
||||||
|
.oathhammer .army-main .army-location-row input {
|
||||||
|
flex: 1;
|
||||||
|
font-size: calc(0.86rem * 0.85);
|
||||||
|
}
|
||||||
|
.oathhammer .item-list--army-regiment .item-list-header,
|
||||||
|
.oathhammer .item-list--army-regiment .item-entry {
|
||||||
|
grid-template-columns: 24px 1fr 4.5rem 4.5rem 4.5rem 4.5rem 3rem;
|
||||||
|
}
|
||||||
|
.oathhammer .item-list--army-regiment .army-total-row {
|
||||||
|
border-top: 2px solid #535128;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.oathhammer .item-list--army-regiment .army-total-row .army-total-label {
|
||||||
|
font-family: "BlueDragon", "Palatino Linotype", serif;
|
||||||
|
font-size: calc(0.86rem * 0.85);
|
||||||
|
color: #2a1a0a;
|
||||||
|
}
|
||||||
|
.oathhammer .item-list--army-regiment .army-total-row .army-total-value {
|
||||||
|
color: #987d2e;
|
||||||
|
font-family: "BlueDragon", "Palatino Linotype", serif;
|
||||||
|
}
|
||||||
|
|||||||
70
lang/en.json
70
lang/en.json
@@ -18,7 +18,9 @@
|
|||||||
"Settlement": "Oath Hammer Settlement Sheet",
|
"Settlement": "Oath Hammer Settlement Sheet",
|
||||||
"SkillNPC": "Oath Hammer NPC Skill Sheet",
|
"SkillNPC": "Oath Hammer NPC Skill Sheet",
|
||||||
"NpcAttack": "Oath Hammer NPC Attack Sheet",
|
"NpcAttack": "Oath Hammer NPC Attack Sheet",
|
||||||
"Regiment": "Oath Hammer Regiment Sheet"
|
"Regiment": "Oath Hammer Regiment Sheet",
|
||||||
|
"Party": "Party Sheet",
|
||||||
|
"Army": "Army Sheet"
|
||||||
},
|
},
|
||||||
"Tab": {
|
"Tab": {
|
||||||
"Identity": "Oaths / Traits",
|
"Identity": "Oaths / Traits",
|
||||||
@@ -31,7 +33,9 @@
|
|||||||
"Buildings": "Buildings",
|
"Buildings": "Buildings",
|
||||||
"Inventory": "Inventory",
|
"Inventory": "Inventory",
|
||||||
"Traits": "Traits",
|
"Traits": "Traits",
|
||||||
"Garrison": "Garrison"
|
"Garrison": "Garrison",
|
||||||
|
"Members": "Members",
|
||||||
|
"Loot": "Loot"
|
||||||
},
|
},
|
||||||
"Attribute": {
|
"Attribute": {
|
||||||
"Might": "Might",
|
"Might": "Might",
|
||||||
@@ -229,7 +233,7 @@
|
|||||||
"DefenseValue": "Defense Value",
|
"DefenseValue": "Defense Value",
|
||||||
"ArmorRating": "Armor Rating",
|
"ArmorRating": "Armor Rating",
|
||||||
"DefenseBonus": "Defense Bonus",
|
"DefenseBonus": "Defense Bonus",
|
||||||
"Movement": "Movement",
|
"Movement": "Move",
|
||||||
"ArcaneStress": "Arcane Stress",
|
"ArcaneStress": "Arcane Stress",
|
||||||
"StressValue": "Stress",
|
"StressValue": "Stress",
|
||||||
"ThresholdBonus": "Threshold Bonus",
|
"ThresholdBonus": "Threshold Bonus",
|
||||||
@@ -254,7 +258,7 @@
|
|||||||
"Conditions": "Conditions",
|
"Conditions": "Conditions",
|
||||||
"Description": "Description",
|
"Description": "Description",
|
||||||
"Notes": "Notes",
|
"Notes": "Notes",
|
||||||
"Stats": "Statistics",
|
"Stats": "Stats",
|
||||||
"CR": "Challenge Rating",
|
"CR": "Challenge Rating",
|
||||||
"AttackBonus": "Attack Bonus",
|
"AttackBonus": "Attack Bonus",
|
||||||
"DamageBonus": "Damage Bonus",
|
"DamageBonus": "Damage Bonus",
|
||||||
@@ -337,15 +341,34 @@
|
|||||||
"Dice": "Dice",
|
"Dice": "Dice",
|
||||||
"DiceColor": "Color",
|
"DiceColor": "Color",
|
||||||
"Special": "Special",
|
"Special": "Special",
|
||||||
"Movement": "Move",
|
"NoRegiments": "No regiments yet — drag regiment actors here.",
|
||||||
"Stats": "Stats",
|
"SupplyCost": "Supply Cost",
|
||||||
"NoRegiments": "No regiments. Add one with the + button.",
|
|
||||||
"SkillName": "Skill name",
|
"SkillName": "Skill name",
|
||||||
"AttackName": "Attack name",
|
"AttackName": "Attack name",
|
||||||
"TraitName": "Trait name",
|
"TraitName": "Trait name",
|
||||||
"Edit": "Edit",
|
"Edit": "Edit",
|
||||||
"Delete": "Delete",
|
"Delete": "Delete",
|
||||||
"Rank": "Rank"
|
"Rank": "Rank",
|
||||||
|
"RemoveFromGarrison": "Remove from Garrison",
|
||||||
|
"RecruitmentCost": "Recruitment Cost",
|
||||||
|
"UnitLeader": "Unit Leader",
|
||||||
|
"DropLeaderHint": "Drop a linked actor here",
|
||||||
|
"MarchingOrder": "Marching Order",
|
||||||
|
"NoMembers": "No members yet — drag characters here.",
|
||||||
|
"DropMemberHint": "Drag a character actor here to add them to the party.",
|
||||||
|
"Loot": "Loot",
|
||||||
|
"NoLoot": "No loot yet — drag items here.",
|
||||||
|
"DropLootHint": "Drag weapons, armor or equipment here to add party loot.",
|
||||||
|
"Qty": "Qty",
|
||||||
|
"GP": "GP",
|
||||||
|
"SP": "SP",
|
||||||
|
"CP": "CP",
|
||||||
|
"Class": "Class",
|
||||||
|
"Commander": "Commander",
|
||||||
|
"Location": "Location",
|
||||||
|
"Regiments": "Regiments",
|
||||||
|
"DropRegimentHint": "Drag a regiment actor (must be token-linked) to add it to this army.",
|
||||||
|
"TotalSupply": "Total Supply"
|
||||||
},
|
},
|
||||||
"ColorDice": {
|
"ColorDice": {
|
||||||
"White": "White (4+)",
|
"White": "White (4+)",
|
||||||
@@ -364,11 +387,18 @@
|
|||||||
"Regiment": "New Regiment",
|
"Regiment": "New Regiment",
|
||||||
"RegimentSkill": "Add Skill",
|
"RegimentSkill": "Add Skill",
|
||||||
"RegimentAttack": "Add Attack",
|
"RegimentAttack": "Add Attack",
|
||||||
"RegimentTrait": "Add Trait"
|
"RegimentTrait": "Add Trait",
|
||||||
|
"SkillNPC": "New Skill"
|
||||||
},
|
},
|
||||||
"ToggleSheet": "Toggle Edit/Play Mode",
|
"ToggleSheet": "Toggle Edit/Play Mode",
|
||||||
"Tooltip": {
|
"Tooltip": {
|
||||||
"RollArmor": "Roll Armor Dice"
|
"RollArmor": "Roll Armor Dice",
|
||||||
|
"OpenLeader": "Open leader sheet",
|
||||||
|
"ClearLeader": "Remove unit leader",
|
||||||
|
"MoveUp": "Move up (march forward)",
|
||||||
|
"MoveDown": "Move down (march back)",
|
||||||
|
"RemoveMember": "Remove from party",
|
||||||
|
"RemoveRegiment": "Remove regiment from army"
|
||||||
},
|
},
|
||||||
"Action": {
|
"Action": {
|
||||||
"CastSpell": "Cast Spell",
|
"CastSpell": "Cast Spell",
|
||||||
@@ -462,7 +492,9 @@
|
|||||||
"APPenalty": "AP (Attacker)",
|
"APPenalty": "AP (Attacker)",
|
||||||
"APHint": "attacker's Armor Piercing value",
|
"APHint": "attacker's Armor Piercing value",
|
||||||
"ReinforcedHint": "Reinforced — rolling red dice",
|
"ReinforcedHint": "Reinforced — rolling red dice",
|
||||||
"RollInitiative": "Roll Initiative"
|
"RollInitiative": "Roll Initiative",
|
||||||
|
"Default": "Default",
|
||||||
|
"DicePool": "Dice Pool"
|
||||||
},
|
},
|
||||||
"Enhancement": {
|
"Enhancement": {
|
||||||
"None": "None",
|
"None": "None",
|
||||||
@@ -1056,7 +1088,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Warning": {
|
"Warning": {
|
||||||
"MiracleBlocked": "Divine favour has been lost — you cannot invoke miracles until a new day."
|
"MiracleBlocked": "Divine favour has been lost — you cannot invoke miracles until a new day.",
|
||||||
|
"LeaderNotLinked": "This actor's token is not linked. Only actors with linked tokens can be unit leaders.",
|
||||||
|
"RegimentNotLinked": "This regiment actor is not linked to its token. Only token-linked regiment actors can be added to an army."
|
||||||
},
|
},
|
||||||
"SettlementArchetype": {
|
"SettlementArchetype": {
|
||||||
"CenterOfLearning": "Center of Learning",
|
"CenterOfLearning": "Center of Learning",
|
||||||
@@ -1076,7 +1110,8 @@
|
|||||||
"CollectTaxes": "Collect Taxes",
|
"CollectTaxes": "Collect Taxes",
|
||||||
"CollectTaxesTooltip": "Roll tax revenue for all constructed buildings and total the result.",
|
"CollectTaxesTooltip": "Roll tax revenue for all constructed buildings and total the result.",
|
||||||
"NoTaxRevenue": "No constructed buildings with tax revenue defined.",
|
"NoTaxRevenue": "No constructed buildings with tax revenue defined.",
|
||||||
"TotalRevenue": "Total Revenue"
|
"TotalRevenue": "Total Revenue",
|
||||||
|
"GarrisonHint": "Drag a regiment actor here to add it to the garrison."
|
||||||
},
|
},
|
||||||
"SkillNPC": {
|
"SkillNPC": {
|
||||||
"FIELDS": {
|
"FIELDS": {
|
||||||
@@ -1124,7 +1159,8 @@
|
|||||||
"NpcSubtype": {
|
"NpcSubtype": {
|
||||||
"Creature": "Creature",
|
"Creature": "Creature",
|
||||||
"Npc": "NPC"
|
"Npc": "NPC"
|
||||||
}
|
},
|
||||||
|
"Party": {}
|
||||||
},
|
},
|
||||||
"TYPES": {
|
"TYPES": {
|
||||||
"Item": {
|
"Item": {
|
||||||
@@ -1141,13 +1177,15 @@
|
|||||||
"class": "Class",
|
"class": "Class",
|
||||||
"skillnpc": "NPC Skill",
|
"skillnpc": "NPC Skill",
|
||||||
"npcattack": "NPC Attack",
|
"npcattack": "NPC Attack",
|
||||||
"regiment": "Regiment",
|
|
||||||
"building": "Building"
|
"building": "Building"
|
||||||
},
|
},
|
||||||
"Actor": {
|
"Actor": {
|
||||||
"character": "Character",
|
"character": "Character",
|
||||||
"npc": "NPC / Creature",
|
"npc": "NPC / Creature",
|
||||||
"settlement": "Settlement"
|
"settlement": "Settlement",
|
||||||
|
"regiment": "Regiment",
|
||||||
|
"party": "Party",
|
||||||
|
"army": "Army"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
152
less/army-sheet.less
Normal file
152
less/army-sheet.less
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
// ============================================================
|
||||||
|
// ARMY ACTOR SHEET
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
.oathhammer .army-main {
|
||||||
|
|
||||||
|
.army-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.army-portrait-wrap {
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.army-portrait {
|
||||||
|
width: 94px;
|
||||||
|
height: 110px;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center top;
|
||||||
|
border: 2px solid fade(@color-dark, 40%);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.army-header-body {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 5px;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.character-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-bottom: 1px solid @color-olive;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
|
||||||
|
input, span {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
font-family: @font-primary;
|
||||||
|
font-size: @font-size-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .control { flex-shrink: 0; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.army-header-fieldset {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Leader row ─────────────────────────────────────────────
|
||||||
|
.army-leader-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 3px 5px;
|
||||||
|
border: 1px dashed @color-olive;
|
||||||
|
border-radius: 3px;
|
||||||
|
min-height: 2rem;
|
||||||
|
background: fade(@color-olive, 5%);
|
||||||
|
|
||||||
|
.army-field-label {
|
||||||
|
font-family: @font-secondary;
|
||||||
|
font-size: @font-size-sm;
|
||||||
|
font-weight: bold;
|
||||||
|
color: @color-dark;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.army-leader-img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid @color-olive;
|
||||||
|
}
|
||||||
|
|
||||||
|
.army-leader-name {
|
||||||
|
flex: 1;
|
||||||
|
font-size: @font-size-sm;
|
||||||
|
color: @color-dark;
|
||||||
|
&:hover { color: @color-olive; text-decoration: underline; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.army-leader-clear {
|
||||||
|
color: @color-olive;
|
||||||
|
&:hover { color: #aa3333; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.army-field-empty {
|
||||||
|
flex: 1;
|
||||||
|
font-size: @font-size-xs;
|
||||||
|
color: fade(@color-dark, 50%);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Location row ───────────────────────────────────────────
|
||||||
|
.army-location-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.army-field-label {
|
||||||
|
font-family: @font-secondary;
|
||||||
|
font-size: @font-size-sm;
|
||||||
|
font-weight: bold;
|
||||||
|
color: @color-dark;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
flex: 1;
|
||||||
|
font-size: @font-size-sm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Regiment list ──────────────────────────────────────────────
|
||||||
|
.oathhammer .item-list--army-regiment {
|
||||||
|
.item-list-header, .item-entry {
|
||||||
|
// img | name | grit | armor | movement | supply | actions
|
||||||
|
grid-template-columns: @item-img-size 1fr 4.5rem 4.5rem 4.5rem 4.5rem 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.army-total-row {
|
||||||
|
border-top: 2px solid @color-olive;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
.army-total-label {
|
||||||
|
font-family: @font-secondary;
|
||||||
|
font-size: @font-size-sm;
|
||||||
|
color: @color-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
.army-total-value {
|
||||||
|
color: darken(@color-gold, 15%);
|
||||||
|
font-family: @font-secondary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,7 +37,10 @@
|
|||||||
// Shared actor content base
|
// Shared actor content base
|
||||||
.oathhammer .character-content,
|
.oathhammer .character-content,
|
||||||
.oathhammer .npc-content,
|
.oathhammer .npc-content,
|
||||||
.oathhammer .settlement-content {
|
.oathhammer .settlement-content,
|
||||||
|
.oathhammer .regiment-content,
|
||||||
|
.oathhammer .party-content,
|
||||||
|
.oathhammer .army-content {
|
||||||
font-family: @font-body; // Calibri — standard text per design_rules.md
|
font-family: @font-body; // Calibri — standard text per design_rules.md
|
||||||
font-size: @font-size-base;
|
font-size: @font-size-base;
|
||||||
color: @color-dark;
|
color: @color-dark;
|
||||||
|
|||||||
@@ -12,3 +12,5 @@
|
|||||||
@import "rolls";
|
@import "rolls";
|
||||||
@import "roll-dialog";
|
@import "roll-dialog";
|
||||||
@import "settlement-sheet";
|
@import "settlement-sheet";
|
||||||
|
@import "party-sheet";
|
||||||
|
@import "army-sheet";
|
||||||
|
|||||||
@@ -148,13 +148,15 @@
|
|||||||
|
|
||||||
.item-list--spell {
|
.item-list--spell {
|
||||||
.item-list-header, .item-entry {
|
.item-list-header, .item-entry {
|
||||||
grid-template-columns: @item-img-size 1fr 3rem 6rem 3rem 5.5rem;
|
// img | name | DV | Tradition | Range | Duration | SpellSave | actions
|
||||||
|
grid-template-columns: @item-img-size 1fr 2.5rem 5.5rem 3.5rem 4.5rem 3.5rem 5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-list--miracle {
|
.item-list--miracle {
|
||||||
.item-list-header, .item-entry {
|
.item-list-header, .item-entry {
|
||||||
grid-template-columns: @item-img-size 1fr 4.5rem 5.5rem;
|
// img | name | DivineTradition | actions
|
||||||
|
grid-template-columns: @item-img-size 1fr 6rem 5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -207,7 +207,7 @@
|
|||||||
|
|
||||||
.regiment-stats-row {
|
.regiment-stats-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
|
||||||
.form-group > label { flex: 0 0 6rem; }
|
.form-group > label { flex: 0 0 6rem; }
|
||||||
|
|||||||
@@ -207,3 +207,100 @@
|
|||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// REGIMENT ACTOR SHEET overrides
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
// Regiment uses the same .npc-main structure but split into 2 rows
|
||||||
|
.oathhammer .npc-main .regiment-vitals-grid {
|
||||||
|
&.regiment-row1 { grid-template-columns: 1fr 1fr 1fr; }
|
||||||
|
&.regiment-row2 {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
border-top: none;
|
||||||
|
margin-top: 4px;
|
||||||
|
padding-top: 4px;
|
||||||
|
border-top: 1px dashed fade(@color-olive, 50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regiment portrait — same dimensions as settlement sheet
|
||||||
|
.oathhammer .regiment-content .npc-left {
|
||||||
|
min-width: 94px;
|
||||||
|
max-width: 94px;
|
||||||
|
|
||||||
|
.actor-img {
|
||||||
|
width: 94px;
|
||||||
|
height: 110px;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center top;
|
||||||
|
border: 2px solid fade(@color-dark, 40%);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regiment fieldset — remove default fieldset border for clean header look
|
||||||
|
.oathhammer .regiment-fieldset {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Regiment leader row ───────────────────────────────────────
|
||||||
|
.oathhammer .regiment-leader-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
border: 1px dashed fade(@color-olive, 60%);
|
||||||
|
border-radius: 3px;
|
||||||
|
background: rgba(0,0,0,0.04);
|
||||||
|
min-height: 28px;
|
||||||
|
|
||||||
|
.regiment-leader-label {
|
||||||
|
font-family: @font-secondary;
|
||||||
|
font-size: @font-size-sm;
|
||||||
|
font-weight: bold;
|
||||||
|
color: @color-dark;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 5.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.regiment-leader-img {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid @color-olive;
|
||||||
|
object-fit: cover;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.regiment-leader-name {
|
||||||
|
flex: 1;
|
||||||
|
font-family: @font-secondary;
|
||||||
|
font-size: @font-size-sm;
|
||||||
|
color: @color-blue;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover { text-decoration: underline; color: @color-gold; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.regiment-leader-empty {
|
||||||
|
flex: 1;
|
||||||
|
font-size: @font-size-xs;
|
||||||
|
color: fade(@color-dark, 45%);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.regiment-leader-clear {
|
||||||
|
color: fade(@color-dark, 40%);
|
||||||
|
font-size: @font-size-sm;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
&:hover { color: #cc3333; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
186
less/party-sheet.less
Normal file
186
less/party-sheet.less
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
// ============================================================
|
||||||
|
// PARTY ACTOR SHEET
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
.oathhammer .party-main {
|
||||||
|
|
||||||
|
.party-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.party-portrait-wrap {
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.party-portrait {
|
||||||
|
width: 94px;
|
||||||
|
height: 110px;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center top;
|
||||||
|
border: 2px solid fade(@color-dark, 40%);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.party-header-body {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.character-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-bottom: 1px solid @color-olive;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
|
||||||
|
input, span {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
font-family: @font-primary;
|
||||||
|
font-size: @font-size-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .control {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.party-header-fieldset {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Treasury ───────────────────────────────────────────────
|
||||||
|
.party-treasury {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
border: 1px solid @color-olive;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: rgba(0,0,0,0.08);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.party-treasury-label {
|
||||||
|
font-family: @font-secondary;
|
||||||
|
font-size: @font-size-sm;
|
||||||
|
font-weight: bold;
|
||||||
|
color: @color-dark;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.party-currency {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 7rem;
|
||||||
|
|
||||||
|
.currency-label {
|
||||||
|
font-family: @font-secondary;
|
||||||
|
font-size: @font-size-xs;
|
||||||
|
font-weight: bold;
|
||||||
|
color: @color-dark;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.currency-stepper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 3.5rem;
|
||||||
|
text-align: center;
|
||||||
|
font-size: @font-size-sm;
|
||||||
|
padding: 1px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.currency-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 1.1rem;
|
||||||
|
height: 1.1rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1;
|
||||||
|
border: 1px solid @color-olive;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: @color-olive-faint;
|
||||||
|
color: @color-dark;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
flex-shrink: 0;
|
||||||
|
&:hover { background: @color-gold; border-color: @color-gold; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.party-currency-gp .currency-label { color: darken(@color-gold, 15%); }
|
||||||
|
.party-currency-sp .currency-label { color: #888; }
|
||||||
|
.party-currency-cp .currency-label { color: #aa6633; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Member list ────────────────────────────────────────────────
|
||||||
|
.oathhammer .item-list--party-member {
|
||||||
|
.item-list-header, .item-entry {
|
||||||
|
// order# | img | name | class | level | grit | actions
|
||||||
|
grid-template-columns: 1.8rem @item-img-size 1fr 7rem 3rem 5rem 5.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.party-member-order {
|
||||||
|
font-family: @font-secondary;
|
||||||
|
font-size: @font-size-xs;
|
||||||
|
font-weight: bold;
|
||||||
|
color: @color-olive;
|
||||||
|
text-align: center;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Loot list ──────────────────────────────────────────────────
|
||||||
|
.oathhammer .item-list--party-loot {
|
||||||
|
.item-list-header, .item-entry {
|
||||||
|
// img | name | type | qty | actions
|
||||||
|
grid-template-columns: @item-img-size 1fr 6rem 5.5rem 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-qty {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 3px;
|
||||||
|
font-size: @font-size-sm;
|
||||||
|
|
||||||
|
span { min-width: 1.5rem; text-align: center; }
|
||||||
|
|
||||||
|
.qty-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1;
|
||||||
|
border: 1px solid @color-olive;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: @color-olive-faint;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
&:hover { background: @color-gold; border-color: @color-gold; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,8 @@ export { default as OathHammerSettlementSheet } from "./sheets/settlement-sheet.
|
|||||||
export { default as OathHammerSkillNPCSheet } from "./sheets/skillnpc-sheet.mjs"
|
export { default as OathHammerSkillNPCSheet } from "./sheets/skillnpc-sheet.mjs"
|
||||||
export { default as OathHammerNpcAttackSheet } from "./sheets/npcattack-sheet.mjs"
|
export { default as OathHammerNpcAttackSheet } from "./sheets/npcattack-sheet.mjs"
|
||||||
export { default as OathHammerRegimentSheet } from "./sheets/regiment-sheet.mjs"
|
export { default as OathHammerRegimentSheet } from "./sheets/regiment-sheet.mjs"
|
||||||
|
export { default as OathHammerPartySheet } from "./sheets/party-sheet.mjs"
|
||||||
|
export { default as OathHammerArmySheet } from "./sheets/army-sheet.mjs"
|
||||||
export { default as OathHammerRollDialog } from "./roll-dialog.mjs"
|
export { default as OathHammerRollDialog } from "./roll-dialog.mjs"
|
||||||
export { default as OathHammerWeaponDialog } from "./weapon-dialog.mjs"
|
export { default as OathHammerWeaponDialog } from "./weapon-dialog.mjs"
|
||||||
export { default as OathHammerSpellDialog } from "./spell-dialog.mjs"
|
export { default as OathHammerSpellDialog } from "./spell-dialog.mjs"
|
||||||
|
|||||||
152
module/applications/sheets/army-sheet.mjs
Normal file
152
module/applications/sheets/army-sheet.mjs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import OathHammerActorSheet from "./base-actor-sheet.mjs"
|
||||||
|
|
||||||
|
export default class OathHammerArmySheet extends OathHammerActorSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["army"],
|
||||||
|
position: { width: 680, height: 560 },
|
||||||
|
window: { contentClasses: ["army-content"] },
|
||||||
|
actions: {
|
||||||
|
openRegiment: OathHammerArmySheet.#onOpenRegiment,
|
||||||
|
removeRegiment: OathHammerArmySheet.#onRemoveRegiment,
|
||||||
|
openLeader: OathHammerArmySheet.#onOpenLeader,
|
||||||
|
clearLeader: OathHammerArmySheet.#onClearLeader,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: { template: "systems/fvtt-oath-hammer/templates/actor/army-sheet.hbs" },
|
||||||
|
tabs: { template: "templates/generic/tab-navigation.hbs" },
|
||||||
|
overview: { template: "systems/fvtt-oath-hammer/templates/actor/army-overview.hbs" },
|
||||||
|
notes: { template: "systems/fvtt-oath-hammer/templates/actor/army-notes.hbs" },
|
||||||
|
}
|
||||||
|
|
||||||
|
tabGroups = { sheet: "overview" }
|
||||||
|
|
||||||
|
#getTabs() {
|
||||||
|
const tabs = {
|
||||||
|
overview: { id: "overview", group: "sheet", icon: "fa-solid fa-shield-halved", label: "OATHHAMMER.Tab.Overview" },
|
||||||
|
notes: { id: "notes", group: "sheet", icon: "fa-solid fa-book", label: "OATHHAMMER.Tab.Notes" },
|
||||||
|
}
|
||||||
|
for (const v of Object.values(tabs)) {
|
||||||
|
v.active = this.tabGroups[v.group] === v.id
|
||||||
|
v.cssClass = v.active ? "active" : ""
|
||||||
|
}
|
||||||
|
return tabs
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const context = await super._prepareContext()
|
||||||
|
const doc = this.document
|
||||||
|
context.tabs = this.#getTabs()
|
||||||
|
|
||||||
|
// Resolve leader
|
||||||
|
const leaderUuid = doc.system.leaderUuid
|
||||||
|
if (leaderUuid) {
|
||||||
|
const leader = await fromUuid(leaderUuid)
|
||||||
|
context.leader = leader ? { uuid: leaderUuid, name: leader.name, img: leader.img } : null
|
||||||
|
} else {
|
||||||
|
context.leader = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _preparePartContext(partId, context) {
|
||||||
|
context = await super._preparePartContext(partId, context)
|
||||||
|
const doc = this.document
|
||||||
|
|
||||||
|
switch (partId) {
|
||||||
|
case "overview": {
|
||||||
|
context.tab = context.tabs.overview
|
||||||
|
const refs = doc.system.regimentRefs ?? []
|
||||||
|
const regiments = []
|
||||||
|
let totalSupply = 0
|
||||||
|
for (const id of refs) {
|
||||||
|
const regiment = game.actors?.get(id)
|
||||||
|
if (!regiment) continue
|
||||||
|
totalSupply += regiment.system.supplyCost ?? 0
|
||||||
|
regiments.push({
|
||||||
|
id: regiment.id,
|
||||||
|
name: regiment.name,
|
||||||
|
img: regiment.img,
|
||||||
|
grit: regiment.system.grit?.value ?? 0,
|
||||||
|
gritMax: regiment.system.grit?.max ?? 0,
|
||||||
|
armor: regiment.system.armorDice?.value ?? 0,
|
||||||
|
movement: regiment.system.movement ?? 0,
|
||||||
|
supplyCost: regiment.system.supplyCost ?? 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
context.regiments = regiments
|
||||||
|
context.totalSupply = totalSupply
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case "notes":
|
||||||
|
context.tab = context.tabs.notes
|
||||||
|
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
|
doc.system.notes ?? "", { async: true }
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _onDrop(event) {
|
||||||
|
if (!this.isEditable || !this.isEditMode) return
|
||||||
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
|
|
||||||
|
if (data.type === "Actor") {
|
||||||
|
const actor = await fromUuid(data.uuid)
|
||||||
|
if (!actor) return
|
||||||
|
|
||||||
|
// Leader drop (on leader drop zone)
|
||||||
|
if (event.target.closest(".army-leader-row")) {
|
||||||
|
if (!actor.prototypeToken?.actorLink) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("OATHHAMMER.Warning.LeaderNotLinked"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return this.document.update({ "system.leaderUuid": actor.uuid })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regiment drop
|
||||||
|
if (actor.type !== "regiment") return
|
||||||
|
if (!actor.prototypeToken?.actorLink) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("OATHHAMMER.Warning.RegimentNotLinked"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const refs = foundry.utils.deepClone(this.document.system.regimentRefs ?? [])
|
||||||
|
if (refs.includes(actor.id)) return
|
||||||
|
refs.push(actor.id)
|
||||||
|
return this.document.update({ "system.regimentRefs": refs })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Actions ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
static async #onOpenRegiment(event, target) {
|
||||||
|
const actor = game.actors?.get(target.dataset.actorId)
|
||||||
|
if (actor) actor.sheet.render(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onRemoveRegiment(event, target) {
|
||||||
|
const id = target.dataset.actorId
|
||||||
|
const refs = (this.document.system.regimentRefs ?? []).filter(r => r !== id)
|
||||||
|
await this.document.update({ "system.regimentRefs": refs })
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onOpenLeader() {
|
||||||
|
const uuid = this.document.system.leaderUuid
|
||||||
|
if (!uuid) return
|
||||||
|
const leader = await fromUuid(uuid)
|
||||||
|
if (leader) leader.sheet.render(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onClearLeader() {
|
||||||
|
await this.document.update({ "system.leaderUuid": null })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -205,11 +205,13 @@ export default class OathHammerCharacterSheet extends OathHammerActorSheet {
|
|||||||
context.stressBlocked = doc.system.arcaneStress.value >= doc.system.arcaneStress.threshold
|
context.stressBlocked = doc.system.arcaneStress.value >= doc.system.arcaneStress.threshold
|
||||||
context.spells = doc.itemTypes.spell.map(s => ({
|
context.spells = doc.itemTypes.spell.map(s => ({
|
||||||
id: s.id, uuid: s.uuid, img: s.img, name: s.name, system: s.system,
|
id: s.id, uuid: s.uuid, img: s.img, name: s.name, system: s.system,
|
||||||
_descTooltip: _stripHtml(s.system.effect)
|
_descTooltip: _stripHtml(s.system.effect),
|
||||||
|
traditionLabel: game.i18n.localize(SYSTEM.SORCEROUS_TRADITIONS[s.system.tradition]?.label ?? s.system.tradition)
|
||||||
}))
|
}))
|
||||||
context.miracles = doc.itemTypes.miracle.map(m => ({
|
context.miracles = doc.itemTypes.miracle.map(m => ({
|
||||||
id: m.id, uuid: m.uuid, img: m.img, name: m.name, system: m.system,
|
id: m.id, uuid: m.uuid, img: m.img, name: m.name, system: m.system,
|
||||||
_descTooltip: _stripHtml(m.system.effect)
|
_descTooltip: _stripHtml(m.system.effect),
|
||||||
|
traditionLabel: game.i18n.localize(SYSTEM.DIVINE_TRADITIONS[m.system.divineTradition] ?? m.system.divineTradition)
|
||||||
}))
|
}))
|
||||||
break
|
break
|
||||||
case "equipment":
|
case "equipment":
|
||||||
|
|||||||
@@ -107,11 +107,13 @@ export default class OathHammerNPCSheet extends OathHammerActorSheet {
|
|||||||
context.tab = context.tabs.magic
|
context.tab = context.tabs.magic
|
||||||
context.spells = (doc.itemTypes.spell ?? []).map(s => ({
|
context.spells = (doc.itemTypes.spell ?? []).map(s => ({
|
||||||
id: s.id, uuid: s.uuid, img: s.img, name: s.name, system: s.system,
|
id: s.id, uuid: s.uuid, img: s.img, name: s.name, system: s.system,
|
||||||
_descTooltip: s.system.effect?.replace(/<[^>]+>/g, "").slice(0, 300) ?? ""
|
_descTooltip: s.system.effect?.replace(/<[^>]+>/g, "").slice(0, 300) ?? "",
|
||||||
|
traditionLabel: game.i18n.localize(SYSTEM.SORCEROUS_TRADITIONS[s.system.tradition]?.label ?? s.system.tradition)
|
||||||
}))
|
}))
|
||||||
context.miracles = (doc.itemTypes.miracle ?? []).map(m => ({
|
context.miracles = (doc.itemTypes.miracle ?? []).map(m => ({
|
||||||
id: m.id, uuid: m.uuid, img: m.img, name: m.name, system: m.system,
|
id: m.id, uuid: m.uuid, img: m.img, name: m.name, system: m.system,
|
||||||
_descTooltip: m.system.effect?.replace(/<[^>]+>/g, "").slice(0, 300) ?? ""
|
_descTooltip: m.system.effect?.replace(/<[^>]+>/g, "").slice(0, 300) ?? "",
|
||||||
|
traditionLabel: game.i18n.localize(SYSTEM.DIVINE_TRADITIONS[m.system.divineTradition] ?? m.system.divineTradition)
|
||||||
}))
|
}))
|
||||||
break
|
break
|
||||||
case "equipment":
|
case "equipment":
|
||||||
|
|||||||
170
module/applications/sheets/party-sheet.mjs
Normal file
170
module/applications/sheets/party-sheet.mjs
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import OathHammerActorSheet from "./base-actor-sheet.mjs"
|
||||||
|
|
||||||
|
const ALLOWED_LOOT_TYPES = new Set(["weapon", "armor", "ammunition", "equipment", "magic-item"])
|
||||||
|
|
||||||
|
export default class OathHammerPartySheet extends OathHammerActorSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["party"],
|
||||||
|
position: { width: 780, height: 600 },
|
||||||
|
window: { contentClasses: ["party-content"] },
|
||||||
|
actions: {
|
||||||
|
openMember: OathHammerPartySheet.#onOpenMember,
|
||||||
|
removeMember: OathHammerPartySheet.#onRemoveMember,
|
||||||
|
moveMemberUp: OathHammerPartySheet.#onMoveMemberUp,
|
||||||
|
moveMemberDown: OathHammerPartySheet.#onMoveMemberDown,
|
||||||
|
adjustCurrency: OathHammerPartySheet.#onAdjustCurrency,
|
||||||
|
adjustQty: OathHammerPartySheet.#onAdjustQty,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: { template: "systems/fvtt-oath-hammer/templates/actor/party-sheet.hbs" },
|
||||||
|
tabs: { template: "templates/generic/tab-navigation.hbs" },
|
||||||
|
members: { template: "systems/fvtt-oath-hammer/templates/actor/party-members.hbs" },
|
||||||
|
loot: { template: "systems/fvtt-oath-hammer/templates/actor/party-loot.hbs" },
|
||||||
|
notes: { template: "systems/fvtt-oath-hammer/templates/actor/party-notes.hbs" },
|
||||||
|
}
|
||||||
|
|
||||||
|
tabGroups = { sheet: "members" }
|
||||||
|
|
||||||
|
#getTabs() {
|
||||||
|
const tabs = {
|
||||||
|
members: { id: "members", group: "sheet", icon: "fa-solid fa-users", label: "OATHHAMMER.Tab.Members" },
|
||||||
|
loot: { id: "loot", group: "sheet", icon: "fa-solid fa-treasure-chest", label: "OATHHAMMER.Tab.Loot" },
|
||||||
|
notes: { id: "notes", group: "sheet", icon: "fa-solid fa-book", label: "OATHHAMMER.Tab.Notes" },
|
||||||
|
}
|
||||||
|
for (const v of Object.values(tabs)) {
|
||||||
|
v.active = this.tabGroups[v.group] === v.id
|
||||||
|
v.cssClass = v.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 "main":
|
||||||
|
break
|
||||||
|
|
||||||
|
case "members": {
|
||||||
|
context.tab = context.tabs.members
|
||||||
|
const refs = doc.system.memberRefs ?? []
|
||||||
|
context.members = refs.map((id, idx) => {
|
||||||
|
const actor = game.actors?.get(id)
|
||||||
|
if (!actor) return null
|
||||||
|
const sys = actor.system
|
||||||
|
const classItem = actor.items?.find(i => i.type === "class")
|
||||||
|
return {
|
||||||
|
id: actor.id,
|
||||||
|
name: actor.name,
|
||||||
|
img: actor.img,
|
||||||
|
idx,
|
||||||
|
position: idx + 1,
|
||||||
|
isFirst: idx === 0,
|
||||||
|
isLast: idx === refs.length - 1,
|
||||||
|
classLabel: classItem?.name ?? "—",
|
||||||
|
level: sys.level ?? "—",
|
||||||
|
grit: sys.grit ? `${sys.grit.value}/${sys.grit.max}` : "—",
|
||||||
|
}
|
||||||
|
}).filter(Boolean)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case "loot": {
|
||||||
|
context.tab = context.tabs.loot
|
||||||
|
const allItems = doc.items.contents.filter(i => ALLOWED_LOOT_TYPES.has(i.type))
|
||||||
|
context.lootItems = allItems.map(i => ({
|
||||||
|
id: i.id, uuid: i.uuid, img: i.img, name: i.name,
|
||||||
|
type: i.type,
|
||||||
|
typeLabel: game.i18n.localize(`TYPES.Item.${i.type}`),
|
||||||
|
system: i.system,
|
||||||
|
}))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case "notes":
|
||||||
|
context.tab = context.tabs.notes
|
||||||
|
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
|
doc.system.notes ?? "", { async: true }
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _onDrop(event) {
|
||||||
|
if (!this.isEditable || !this.isEditMode) return
|
||||||
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
|
|
||||||
|
if (data.type === "Actor") {
|
||||||
|
const actor = await fromUuid(data.uuid)
|
||||||
|
if (!actor || actor.type !== "character") return
|
||||||
|
const refs = foundry.utils.deepClone(this.document.system.memberRefs ?? [])
|
||||||
|
if (refs.includes(actor.id)) return
|
||||||
|
refs.push(actor.id)
|
||||||
|
return this.document.update({ "system.memberRefs": refs })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.type === "Item") {
|
||||||
|
const item = await fromUuid(data.uuid)
|
||||||
|
if (!item || !ALLOWED_LOOT_TYPES.has(item.type)) return
|
||||||
|
return this._onDropItem(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 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)
|
||||||
|
await this.document.update({ "system.memberRefs": refs })
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onMoveMemberUp(event, target) {
|
||||||
|
const idx = parseInt(target.dataset.idx, 10)
|
||||||
|
if (idx <= 0) return
|
||||||
|
const refs = foundry.utils.deepClone(this.document.system.memberRefs ?? []);
|
||||||
|
[refs[idx - 1], refs[idx]] = [refs[idx], refs[idx - 1]]
|
||||||
|
await this.document.update({ "system.memberRefs": refs })
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onMoveMemberDown(event, target) {
|
||||||
|
const idx = parseInt(target.dataset.idx, 10)
|
||||||
|
const refs = foundry.utils.deepClone(this.document.system.memberRefs ?? [])
|
||||||
|
if (idx >= refs.length - 1) return;
|
||||||
|
[refs[idx], refs[idx + 1]] = [refs[idx + 1], refs[idx]]
|
||||||
|
await this.document.update({ "system.memberRefs": refs })
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onAdjustCurrency(event, target) {
|
||||||
|
const field = target.dataset.field
|
||||||
|
const delta = parseInt(target.dataset.delta, 10)
|
||||||
|
const cur = foundry.utils.getProperty(this.document, field) ?? 0
|
||||||
|
await this.document.update({ [field]: Math.max(0, cur + delta) })
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onAdjustQty(event, target) {
|
||||||
|
const item = this.document.items.get(target.dataset.itemId)
|
||||||
|
const delta = parseInt(target.dataset.delta, 10)
|
||||||
|
if (!item) return
|
||||||
|
const qty = (item.system.quantity ?? 1) + delta
|
||||||
|
if (qty <= 0) return item.delete()
|
||||||
|
await item.update({ "system.quantity": qty })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,80 +1,266 @@
|
|||||||
import OathHammerItemSheet from "./base-item-sheet.mjs"
|
import OathHammerActorSheet from "./base-actor-sheet.mjs"
|
||||||
|
import { rollNPCSkill, rollNPCArmor, rollNPCAttackDamage } from "../../rolls.mjs"
|
||||||
import { SYSTEM } from "../../config/system.mjs"
|
import { SYSTEM } from "../../config/system.mjs"
|
||||||
|
|
||||||
export default class OathHammerRegimentSheet extends OathHammerItemSheet {
|
export default class OathHammerRegimentSheet extends OathHammerActorSheet {
|
||||||
/** @override */
|
/** @override */
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
classes: ["regiment"],
|
classes: ["regiment"],
|
||||||
position: { width: 560, height: "auto" },
|
position: { width: 680, height: 620 },
|
||||||
window: { contentClasses: ["regiment-content"] },
|
window: { contentClasses: ["regiment-content"] },
|
||||||
actions: {
|
actions: {
|
||||||
addSkill: OathHammerRegimentSheet.#onAddSkill,
|
adjustGrit: OathHammerRegimentSheet.#onAdjustGrit,
|
||||||
removeSkill: OathHammerRegimentSheet.#onRemoveSkill,
|
rollArmor: OathHammerRegimentSheet.#onRollArmor,
|
||||||
addAttack: OathHammerRegimentSheet.#onAddAttack,
|
rollSkillNPC: OathHammerRegimentSheet.#onRollSkillNPC,
|
||||||
removeAttack:OathHammerRegimentSheet.#onRemoveAttack,
|
createNpcAttack: OathHammerRegimentSheet.#onCreateNpcAttack,
|
||||||
addTrait: OathHammerRegimentSheet.#onAddTrait,
|
rollNpcAttack: OathHammerRegimentSheet.#onRollNpcAttack,
|
||||||
removeTrait: OathHammerRegimentSheet.#onRemoveTrait,
|
createSkill: OathHammerRegimentSheet.#onCreateSkill,
|
||||||
|
createTrait: OathHammerRegimentSheet.#onCreateTrait,
|
||||||
|
openLeader: OathHammerRegimentSheet.#onOpenLeader,
|
||||||
|
clearLeader: OathHammerRegimentSheet.#onClearLeader,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
main: { template: "systems/fvtt-oath-hammer/templates/item/regiment-sheet.hbs" },
|
main: { template: "systems/fvtt-oath-hammer/templates/actor/regiment-sheet.hbs" },
|
||||||
|
tabs: { template: "templates/generic/tab-navigation.hbs" },
|
||||||
|
skills: { template: "systems/fvtt-oath-hammer/templates/actor/npc-skills.hbs" },
|
||||||
|
combat: { template: "systems/fvtt-oath-hammer/templates/actor/regiment-combat.hbs" },
|
||||||
|
traits: { template: "systems/fvtt-oath-hammer/templates/actor/npc-traits.hbs" },
|
||||||
|
notes: { template: "systems/fvtt-oath-hammer/templates/actor/npc-notes.hbs" },
|
||||||
|
}
|
||||||
|
|
||||||
|
tabGroups = { sheet: "skills" }
|
||||||
|
|
||||||
|
#getTabs() {
|
||||||
|
const tabs = {
|
||||||
|
skills: { id: "skills", group: "sheet", icon: "fa-solid fa-dice-d6", label: "OATHHAMMER.Tab.Skills" },
|
||||||
|
combat: { id: "combat", group: "sheet", icon: "fa-solid fa-swords", label: "OATHHAMMER.Tab.Combat" },
|
||||||
|
traits: { id: "traits", group: "sheet", icon: "fa-solid fa-star", label: "OATHHAMMER.Tab.Traits" },
|
||||||
|
notes: { id: "notes", group: "sheet", icon: "fa-solid fa-book", label: "OATHHAMMER.Tab.Notes" },
|
||||||
|
}
|
||||||
|
for (const v of Object.values(tabs)) {
|
||||||
|
v.active = this.tabGroups[v.group] === v.id
|
||||||
|
v.cssClass = v.active ? "active" : ""
|
||||||
|
}
|
||||||
|
return tabs
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _prepareContext() {
|
async _prepareContext() {
|
||||||
const context = await super._prepareContext()
|
const context = await super._prepareContext()
|
||||||
|
context.tabs = this.#getTabs()
|
||||||
|
const armorColor = this.document.system.armorDice?.colorDiceType ?? "white"
|
||||||
|
context.armorDiceEmoji = armorColor === "black" ? "⬛" : armorColor === "red" ? "🔴" : "⬜"
|
||||||
context.colorChoices = Object.fromEntries(
|
context.colorChoices = Object.fromEntries(
|
||||||
Object.entries(SYSTEM.DICE_COLOR_TYPES).map(([k, v]) => [k, game.i18n.localize(v)])
|
Object.entries(SYSTEM.DICE_COLOR_TYPES).map(([k, v]) => [k, game.i18n.localize(v)])
|
||||||
)
|
)
|
||||||
context.dicePoolChoices = Object.fromEntries(
|
// Resolve leader actor
|
||||||
Array.from({ length: 21 }, (_, i) => [i, String(i)])
|
const leaderUuid = this.document.system.leaderUuid
|
||||||
)
|
if (leaderUuid) {
|
||||||
context.apChoices = Object.fromEntries(
|
const leader = await fromUuid(leaderUuid)
|
||||||
Array.from({ length: 7 }, (_, i) => [i, String(i)])
|
context.leader = leader ? { id: leader.id, uuid: leader.uuid, name: leader.name, img: leader.img } : null
|
||||||
)
|
} else {
|
||||||
|
context.leader = null
|
||||||
|
}
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Array helpers ────────────────────────────────────────────────────────────
|
/** @override */
|
||||||
|
async _preparePartContext(partId, context) {
|
||||||
static async #onAddSkill() {
|
const doc = this.document
|
||||||
const skills = foundry.utils.deepClone(this.document.system.skills ?? [])
|
switch (partId) {
|
||||||
skills.push({ name: "", value: 2, colorDiceType: "white" })
|
case "main":
|
||||||
await this.document.update({ "system.skills": skills })
|
break
|
||||||
|
case "skills":
|
||||||
|
context.tab = context.tabs.skills
|
||||||
|
context.skills = doc.itemTypes.skillnpc ?? []
|
||||||
|
break
|
||||||
|
case "combat":
|
||||||
|
context.tab = context.tabs.combat
|
||||||
|
context.npcAttacks = (doc.itemTypes.npcattack ?? []).map(a => ({
|
||||||
|
id: a.id, uuid: a.uuid, img: a.img, name: a.name, system: a.system,
|
||||||
|
_descTooltip: a.system.description?.replace(/<[^>]+>/g, "").slice(0, 300) ?? ""
|
||||||
|
}))
|
||||||
|
break
|
||||||
|
case "traits":
|
||||||
|
context.tab = context.tabs.traits
|
||||||
|
context.traits = (doc.itemTypes.trait ?? []).map(t => ({
|
||||||
|
id: t.id, uuid: t.uuid, img: t.img, name: t.name, system: t.system,
|
||||||
|
_descTooltip: t.system.description?.replace(/<[^>]+>/g, "").slice(0, 300) ?? ""
|
||||||
|
}))
|
||||||
|
break
|
||||||
|
case "notes":
|
||||||
|
context.tab = context.tabs.notes
|
||||||
|
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
|
doc.system.description ?? "", { async: true }
|
||||||
|
)
|
||||||
|
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
|
doc.system.notes ?? "", { async: true }
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onRemoveSkill(event, target) {
|
/** @override */
|
||||||
const idx = parseInt(target.dataset.idx, 10)
|
async _onDrop(event) {
|
||||||
const skills = foundry.utils.deepClone(this.document.system.skills ?? [])
|
if (!this.isEditable || !this.isEditMode) return
|
||||||
skills.splice(idx, 1)
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
await this.document.update({ "system.skills": skills })
|
|
||||||
|
// Actor drop → set as unit leader (must be token-linked)
|
||||||
|
if (data.type === "Actor") {
|
||||||
|
const actor = await fromUuid(data.uuid)
|
||||||
|
if (!actor) return
|
||||||
|
if (!actor.prototypeToken?.actorLink) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("OATHHAMMER.Warning.LeaderNotLinked"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return this.document.update({ "system.leaderUuid": actor.uuid })
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onAddAttack() {
|
if (data.type !== "Item") return
|
||||||
const attacks = foundry.utils.deepClone(this.document.system.attacks ?? [])
|
const item = await fromUuid(data.uuid)
|
||||||
attacks.push({ name: "", damageDice: 6, colorDiceType: "white", ap: 0, special: "" })
|
if (!item) return
|
||||||
await this.document.update({ "system.attacks": attacks })
|
const ALLOWED = new Set(["skillnpc", "npcattack", "trait"])
|
||||||
|
if (!ALLOWED.has(item.type)) return
|
||||||
|
return this._onDropItem(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onRemoveAttack(event, target) {
|
// ── Actions ────────────────────────────────────────────────────────────────
|
||||||
const idx = parseInt(target.dataset.idx, 10)
|
|
||||||
const attacks = foundry.utils.deepClone(this.document.system.attacks ?? [])
|
static async #onAdjustGrit(event, target) {
|
||||||
attacks.splice(idx, 1)
|
const delta = parseInt(target.dataset.delta, 10)
|
||||||
await this.document.update({ "system.attacks": attacks })
|
const current = this.document.system.grit?.value ?? 0
|
||||||
|
const max = this.document.system.grit?.max ?? current
|
||||||
|
await this.document.update({ "system.grit.value": Math.max(0, Math.min(max, current + delta)) })
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onAddTrait() {
|
static async #onRollArmor() {
|
||||||
const traits = foundry.utils.deepClone(this.document.system.traits ?? [])
|
const doc = this.document
|
||||||
traits.push({ name: "", description: "" })
|
const armorDice = doc.system.armorDice
|
||||||
await this.document.update({ "system.traits": traits })
|
if (!armorDice?.value) return ui.notifications.info("No armor dice to roll.")
|
||||||
|
const bonusOptions = Array.from({ length: 13 }, (_, i) => {
|
||||||
|
const v = i - 6
|
||||||
|
return { value: v, label: v > 0 ? `+${v}` : String(v), selected: v === 0 }
|
||||||
|
})
|
||||||
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
"systems/fvtt-oath-hammer/templates/npc-skill-dialog.hbs",
|
||||||
|
{
|
||||||
|
skillName: game.i18n.localize("OATHHAMMER.Label.ArmorDice"),
|
||||||
|
skillImg: doc.img, basePool: armorDice.value, bonusOptions,
|
||||||
|
colorChoices: { white: "⬜ White (4+)", red: "🔴 Red (3+)", black: "⬛ Black (2+)" },
|
||||||
|
selectedColor: armorDice.colorDiceType,
|
||||||
|
rollModes: foundry.utils.duplicate(CONFIG.Dice.rollModes),
|
||||||
|
visibility: game.settings.get("core", "rollMode")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const result = await foundry.applications.api.DialogV2.prompt({
|
||||||
|
window: { title: `${doc.name} — ${game.i18n.localize("OATHHAMMER.Roll.ArmorRoll")}`, resizable: true },
|
||||||
|
classes: ["fvtt-oath-hammer"], position: { width: 420 }, content,
|
||||||
|
ok: { label: game.i18n.localize("OATHHAMMER.Dialog.Roll"), icon: "fa-solid fa-dice-d6" },
|
||||||
|
})
|
||||||
|
if (!result) return
|
||||||
|
const form = new DOMParser().parseFromString(result, "text/html")
|
||||||
|
const getValue = n => form.querySelector(`[name="${n}"]`)?.value
|
||||||
|
await rollNPCArmor(doc, {
|
||||||
|
bonus: parseInt(getValue("bonus")) || 0,
|
||||||
|
colorOverride: getValue("colorOverride") || null,
|
||||||
|
visibility: getValue("visibility"),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onRemoveTrait(event, target) {
|
static async #onRollSkillNPC(event, target) {
|
||||||
const idx = parseInt(target.dataset.idx, 10)
|
const skill = this.document.items.get(target.dataset.itemId)
|
||||||
const traits = foundry.utils.deepClone(this.document.system.traits ?? [])
|
if (!skill) return
|
||||||
traits.splice(idx, 1)
|
const bonusOptions = Array.from({ length: 13 }, (_, i) => {
|
||||||
await this.document.update({ "system.traits": traits })
|
const v = i - 6
|
||||||
|
return { value: v, label: v > 0 ? `+${v}` : String(v), selected: v === 0 }
|
||||||
|
})
|
||||||
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
"systems/fvtt-oath-hammer/templates/npc-skill-dialog.hbs",
|
||||||
|
{
|
||||||
|
skillName: skill.name, skillImg: skill.img, basePool: skill.system.dicePool, bonusOptions,
|
||||||
|
colorChoices: { white: "⬜ White (4+)", red: "🔴 Red (3+)", black: "⬛ Black (2+)" },
|
||||||
|
selectedColor: skill.system.colorDiceType,
|
||||||
|
rollModes: foundry.utils.duplicate(CONFIG.Dice.rollModes),
|
||||||
|
visibility: game.settings.get("core", "rollMode")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const result = await foundry.applications.api.DialogV2.prompt({
|
||||||
|
window: { title: `${skill.name} — ${game.i18n.localize("OATHHAMMER.Tab.Skills")}`, resizable: true },
|
||||||
|
classes: ["fvtt-oath-hammer"], position: { width: 420 }, content,
|
||||||
|
ok: { label: game.i18n.localize("OATHHAMMER.Dialog.Roll"), icon: "fa-solid fa-dice-d6" },
|
||||||
|
})
|
||||||
|
if (!result) return
|
||||||
|
const form = new DOMParser().parseFromString(result, "text/html")
|
||||||
|
const getValue = n => form.querySelector(`[name="${n}"]`)?.value
|
||||||
|
await rollNPCSkill(this.document, skill, {
|
||||||
|
bonus: parseInt(getValue("bonus")) || 0,
|
||||||
|
colorOverride: getValue("colorOverride") || null,
|
||||||
|
visibility: getValue("visibility"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static #onCreateNpcAttack() {
|
||||||
|
this.document.createEmbeddedDocuments("Item", [{
|
||||||
|
name: game.i18n.localize("OATHHAMMER.NewItem.NpcAttack"), type: "npcattack"
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onRollNpcAttack(event, target) {
|
||||||
|
const attack = this.document.items.get(target.dataset.itemId)
|
||||||
|
if (!attack) return
|
||||||
|
const bonusOptions = Array.from({ length: 13 }, (_, i) => {
|
||||||
|
const v = i - 6
|
||||||
|
return { value: v, label: v > 0 ? `+${v}` : String(v), selected: v === 0 }
|
||||||
|
})
|
||||||
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
"systems/fvtt-oath-hammer/templates/npc-skill-dialog.hbs",
|
||||||
|
{
|
||||||
|
skillName: attack.name, skillImg: attack.img, basePool: attack.system.damageDice, bonusOptions,
|
||||||
|
colorChoices: { white: "⬜ White (4+)", red: "🔴 Red (3+)", black: "⬛ Black (2+)" },
|
||||||
|
selectedColor: attack.system.colorDiceType,
|
||||||
|
rollModes: foundry.utils.duplicate(CONFIG.Dice.rollModes),
|
||||||
|
visibility: game.settings.get("core", "rollMode")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const result = await foundry.applications.api.DialogV2.prompt({
|
||||||
|
window: { title: `${attack.name} — ${game.i18n.localize("OATHHAMMER.Dialog.Damage")}`, resizable: true },
|
||||||
|
classes: ["fvtt-oath-hammer"], position: { width: 420 }, content,
|
||||||
|
ok: { label: game.i18n.localize("OATHHAMMER.Dialog.Roll"), icon: "fa-solid fa-dice-d6" },
|
||||||
|
})
|
||||||
|
if (!result) return
|
||||||
|
const form = new DOMParser().parseFromString(result, "text/html")
|
||||||
|
const getValue = n => form.querySelector(`[name="${n}"]`)?.value
|
||||||
|
await rollNPCAttackDamage(this.document, attack, {
|
||||||
|
bonus: parseInt(getValue("bonus")) || 0,
|
||||||
|
colorOverride: getValue("colorOverride") || null,
|
||||||
|
visibility: getValue("visibility"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static #onCreateSkill() {
|
||||||
|
this.document.createEmbeddedDocuments("Item", [{
|
||||||
|
name: game.i18n.localize("OATHHAMMER.NewItem.SkillNPC"), type: "skillnpc"
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
|
||||||
|
static #onCreateTrait() {
|
||||||
|
this.document.createEmbeddedDocuments("Item", [{
|
||||||
|
name: game.i18n.localize("OATHHAMMER.NewItem.Trait"), type: "trait"
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onOpenLeader() {
|
||||||
|
const leaderUuid = this.document.system.leaderUuid
|
||||||
|
if (!leaderUuid) return
|
||||||
|
const leader = await fromUuid(leaderUuid)
|
||||||
|
if (leader) leader.sheet.render(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onClearLeader() {
|
||||||
|
await this.document.update({ "system.leaderUuid": null })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import OathHammerActorSheet from "./base-actor-sheet.mjs"
|
import OathHammerActorSheet from "./base-actor-sheet.mjs"
|
||||||
|
|
||||||
const ALLOWED_ITEM_TYPES = new Set(["building", "equipment", "weapon", "armor", "regiment"])
|
const ALLOWED_ITEM_TYPES = new Set(["building", "equipment", "weapon", "armor"])
|
||||||
|
|
||||||
export default class OathHammerSettlementSheet extends OathHammerActorSheet {
|
export default class OathHammerSettlementSheet extends OathHammerActorSheet {
|
||||||
/** @override */
|
/** @override */
|
||||||
@@ -17,8 +17,9 @@ export default class OathHammerSettlementSheet extends OathHammerActorSheet {
|
|||||||
adjustCurrency: OathHammerSettlementSheet.#onAdjustCurrency,
|
adjustCurrency: OathHammerSettlementSheet.#onAdjustCurrency,
|
||||||
adjustQty: OathHammerSettlementSheet.#onAdjustQty,
|
adjustQty: OathHammerSettlementSheet.#onAdjustQty,
|
||||||
toggleConstructed: OathHammerSettlementSheet.#onToggleConstructed,
|
toggleConstructed: OathHammerSettlementSheet.#onToggleConstructed,
|
||||||
createRegiment: OathHammerSettlementSheet.#onCreateRegiment,
|
|
||||||
collectTaxes: OathHammerSettlementSheet.#onCollectTaxes,
|
collectTaxes: OathHammerSettlementSheet.#onCollectTaxes,
|
||||||
|
openRegiment: OathHammerSettlementSheet.#onOpenRegiment,
|
||||||
|
removeRegiment: OathHammerSettlementSheet.#onRemoveRegiment,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +103,9 @@ export default class OathHammerSettlementSheet extends OathHammerActorSheet {
|
|||||||
}
|
}
|
||||||
case "garrison":
|
case "garrison":
|
||||||
context.tab = context.tabs.garrison
|
context.tab = context.tabs.garrison
|
||||||
context.regiments = doc.itemTypes.regiment ?? []
|
context.regiments = (doc.system.garrisonRefs ?? [])
|
||||||
|
.map(id => game.actors?.get(id))
|
||||||
|
.filter(Boolean)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
@@ -112,6 +115,17 @@ export default class OathHammerSettlementSheet extends OathHammerActorSheet {
|
|||||||
async _onDrop(event) {
|
async _onDrop(event) {
|
||||||
if (!this.isEditable || !this.isEditMode) return
|
if (!this.isEditable || !this.isEditMode) return
|
||||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
|
|
||||||
|
// Regiment actors dropped onto garrison tab
|
||||||
|
if (data.type === "Actor") {
|
||||||
|
const actor = await fromUuid(data.uuid)
|
||||||
|
if (!actor || actor.type !== "regiment") return
|
||||||
|
const refs = foundry.utils.deepClone(this.document.system.garrisonRefs ?? [])
|
||||||
|
if (refs.includes(actor.id)) return // already linked
|
||||||
|
refs.push(actor.id)
|
||||||
|
return this.document.update({ "system.garrisonRefs": refs })
|
||||||
|
}
|
||||||
|
|
||||||
if (data.type !== "Item") return
|
if (data.type !== "Item") return
|
||||||
const item = await fromUuid(data.uuid)
|
const item = await fromUuid(data.uuid)
|
||||||
if (!item || !ALLOWED_ITEM_TYPES.has(item.type)) return
|
if (!item || !ALLOWED_ITEM_TYPES.has(item.type)) return
|
||||||
@@ -144,11 +158,15 @@ export default class OathHammerSettlementSheet extends OathHammerActorSheet {
|
|||||||
await item.update({ "system.constructed": !item.system.constructed })
|
await item.update({ "system.constructed": !item.system.constructed })
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onCreateRegiment() {
|
static async #onOpenRegiment(event, target) {
|
||||||
await this.document.createEmbeddedDocuments("Item", [{
|
const actor = game.actors?.get(target.dataset.actorId)
|
||||||
name: game.i18n.localize("OATHHAMMER.NewItem.Regiment"),
|
if (actor) actor.sheet.render(true)
|
||||||
type: "regiment",
|
}
|
||||||
}])
|
|
||||||
|
static async #onRemoveRegiment(event, target) {
|
||||||
|
const actorId = target.dataset.actorId
|
||||||
|
const refs = (this.document.system.garrisonRefs ?? []).filter(id => id !== actorId)
|
||||||
|
await this.document.update({ "system.garrisonRefs": refs })
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onCollectTaxes() {
|
static async #onCollectTaxes() {
|
||||||
|
|||||||
@@ -15,3 +15,5 @@ export { default as OathHammerSettlement } from "./settlement.mjs"
|
|||||||
export { default as OathHammerSkillNPC } from "./skillnpc.mjs"
|
export { default as OathHammerSkillNPC } from "./skillnpc.mjs"
|
||||||
export { default as OathHammerNpcAttack } from "./npcattack.mjs"
|
export { default as OathHammerNpcAttack } from "./npcattack.mjs"
|
||||||
export { default as OathHammerRegiment } from "./regiment.mjs"
|
export { default as OathHammerRegiment } from "./regiment.mjs"
|
||||||
|
export { default as OathHammerParty } from "./party.mjs"
|
||||||
|
export { default as OathHammerArmy } from "./army.mjs"
|
||||||
|
|||||||
13
module/models/army.mjs
Normal file
13
module/models/army.mjs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export default class OathHammerArmy extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const { fields } = foundry.data
|
||||||
|
const schema = {}
|
||||||
|
|
||||||
|
schema.regimentRefs = new fields.ArrayField(new fields.StringField({ required: true, blank: false }))
|
||||||
|
schema.leaderUuid = new fields.StringField({ required: false, nullable: true, initial: null })
|
||||||
|
schema.location = new fields.StringField({ required: false, nullable: true, initial: "" })
|
||||||
|
schema.notes = new fields.HTMLField({ required: false, nullable: true, initial: "" })
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
}
|
||||||
24
module/models/party.mjs
Normal file
24
module/models/party.mjs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
export default class OathHammerParty extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const { fields } = foundry.data
|
||||||
|
const requiredInteger = { required: true, nullable: false, integer: true }
|
||||||
|
const schema = {}
|
||||||
|
|
||||||
|
schema.notes = new fields.HTMLField({ required: false, nullable: true, initial: "" })
|
||||||
|
|
||||||
|
// Ordered list of character actor IDs — position = marching order
|
||||||
|
schema.memberRefs = new fields.ArrayField(
|
||||||
|
new fields.StringField({ required: true, nullable: false, blank: false })
|
||||||
|
)
|
||||||
|
|
||||||
|
schema.treasury = new fields.SchemaField({
|
||||||
|
gp: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
|
||||||
|
sp: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
|
||||||
|
cp: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
|
||||||
|
})
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
|
||||||
|
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Party"]
|
||||||
|
}
|
||||||
@@ -7,10 +7,11 @@ export default class OathHammerRegiment extends foundry.abstract.TypeDataModel {
|
|||||||
const schema = {}
|
const schema = {}
|
||||||
|
|
||||||
schema.description = new fields.HTMLField({ required: false, nullable: true, initial: "" })
|
schema.description = new fields.HTMLField({ required: false, nullable: true, initial: "" })
|
||||||
schema.notes = new fields.StringField({ required: false, nullable: true, initial: "" })
|
schema.notes = new fields.HTMLField({ required: false, nullable: true, initial: "" })
|
||||||
|
|
||||||
schema.grit = new fields.SchemaField({
|
schema.grit = new fields.SchemaField({
|
||||||
max: new fields.NumberField({ ...requiredInteger, initial: 20, min: 0, max: 200 }),
|
value: new fields.NumberField({ ...requiredInteger, initial: 20, min: 0 }),
|
||||||
|
max: new fields.NumberField({ ...requiredInteger, initial: 20, min: 0 }),
|
||||||
})
|
})
|
||||||
|
|
||||||
schema.armorDice = new fields.SchemaField({
|
schema.armorDice = new fields.SchemaField({
|
||||||
@@ -19,34 +20,20 @@ export default class OathHammerRegiment extends foundry.abstract.TypeDataModel {
|
|||||||
})
|
})
|
||||||
|
|
||||||
schema.movement = new fields.NumberField({ ...requiredInteger, initial: 60, min: 0, max: 500 })
|
schema.movement = new fields.NumberField({ ...requiredInteger, initial: 60, min: 0, max: 500 })
|
||||||
|
schema.supplyCost = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
||||||
|
schema.recruitmentCost = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
||||||
|
|
||||||
// Embedded skill rows: [{name, value, colorDiceType}]
|
schema.leaderUuid = new fields.StringField({ required: false, nullable: true, initial: null })
|
||||||
schema.skills = new fields.ArrayField(new fields.SchemaField({
|
|
||||||
name: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
|
||||||
value: new fields.NumberField({ ...requiredInteger, initial: 2, min: 0, max: 6 }),
|
|
||||||
colorDiceType: new fields.StringField({ required: true, nullable: false, initial: "white", choices: SYSTEM.DICE_COLOR_TYPES }),
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Embedded attack rows: [{name, damageDice, colorDiceType, ap, special}]
|
|
||||||
schema.attacks = new fields.ArrayField(new fields.SchemaField({
|
|
||||||
name: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
|
||||||
damageDice: new fields.NumberField({ ...requiredInteger, initial: 6, min: 0, max: 20 }),
|
|
||||||
colorDiceType: new fields.StringField({ required: true, nullable: false, initial: "white", choices: SYSTEM.DICE_COLOR_TYPES }),
|
|
||||||
ap: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 6 }),
|
|
||||||
special: new fields.StringField({ required: false, nullable: true, initial: "" }),
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Embedded trait rows: [{name, description}]
|
|
||||||
schema.traits = new fields.ArrayField(new fields.SchemaField({
|
|
||||||
name: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
|
||||||
description: new fields.StringField({ required: false, nullable: true, initial: "" }),
|
|
||||||
}))
|
|
||||||
|
|
||||||
return schema
|
return schema
|
||||||
}
|
}
|
||||||
|
|
||||||
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Regiment"]
|
static LOCALIZATION_PREFIXES = ["OATHHAMMER.Regiment"]
|
||||||
|
|
||||||
|
get threshold() {
|
||||||
|
return { white: 4, red: 3, black: 2 }[this.armorDice.colorDiceType] ?? 4
|
||||||
|
}
|
||||||
|
|
||||||
get colorEmoji() {
|
get colorEmoji() {
|
||||||
return { white: "⬜", red: "🔴", black: "⬛" }[this.armorDice.colorDiceType] ?? "⬜"
|
return { white: "⬜", red: "🔴", black: "⬛" }[this.armorDice.colorDiceType] ?? "⬜"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ export default class OathHammerSettlement extends foundry.abstract.TypeDataModel
|
|||||||
schema.isCapital = new fields.BooleanField({ required: true, initial: false })
|
schema.isCapital = new fields.BooleanField({ required: true, initial: false })
|
||||||
schema.taxNotes = new fields.StringField({ required: true, nullable: false, initial: "" })
|
schema.taxNotes = new fields.StringField({ required: true, nullable: false, initial: "" })
|
||||||
|
|
||||||
|
// Linked regiment actor IDs
|
||||||
|
schema.garrisonRefs = new fields.ArrayField(
|
||||||
|
new fields.StringField({ required: true, nullable: false, blank: false })
|
||||||
|
)
|
||||||
|
|
||||||
return schema
|
return schema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ Hooks.once("init", function () {
|
|||||||
CONFIG.Actor.dataModels = {
|
CONFIG.Actor.dataModels = {
|
||||||
character: models.OathHammerCharacter,
|
character: models.OathHammerCharacter,
|
||||||
npc: models.OathHammerNPC,
|
npc: models.OathHammerNPC,
|
||||||
settlement: models.OathHammerSettlement
|
settlement: models.OathHammerSettlement,
|
||||||
|
regiment: models.OathHammerRegiment,
|
||||||
|
party: models.OathHammerParty,
|
||||||
|
army: models.OathHammerArmy,
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG.Item.documentClass = documents.OathHammerItem
|
CONFIG.Item.documentClass = documents.OathHammerItem
|
||||||
@@ -42,7 +45,6 @@ Hooks.once("init", function () {
|
|||||||
building: models.OathHammerBuilding,
|
building: models.OathHammerBuilding,
|
||||||
skillnpc: models.OathHammerSkillNPC,
|
skillnpc: models.OathHammerSkillNPC,
|
||||||
npcattack: models.OathHammerNpcAttack,
|
npcattack: models.OathHammerNpcAttack,
|
||||||
regiment: models.OathHammerRegiment,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet)
|
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet)
|
||||||
@@ -61,6 +63,21 @@ Hooks.once("init", function () {
|
|||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: "OATHHAMMER.Sheet.Settlement"
|
label: "OATHHAMMER.Sheet.Settlement"
|
||||||
})
|
})
|
||||||
|
foundry.documents.collections.Actors.registerSheet("fvtt-oath-hammer", applications.OathHammerRegimentSheet, {
|
||||||
|
types: ["regiment"],
|
||||||
|
makeDefault: true,
|
||||||
|
label: "OATHHAMMER.Sheet.Regiment"
|
||||||
|
})
|
||||||
|
foundry.documents.collections.Actors.registerSheet("fvtt-oath-hammer", applications.OathHammerPartySheet, {
|
||||||
|
types: ["party"],
|
||||||
|
makeDefault: true,
|
||||||
|
label: "OATHHAMMER.Sheet.Party"
|
||||||
|
})
|
||||||
|
foundry.documents.collections.Actors.registerSheet("fvtt-oath-hammer", applications.OathHammerArmySheet, {
|
||||||
|
types: ["army"],
|
||||||
|
makeDefault: true,
|
||||||
|
label: "OATHHAMMER.Sheet.Army"
|
||||||
|
})
|
||||||
|
|
||||||
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet)
|
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet)
|
||||||
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerWeaponSheet, { types: ["weapon"], makeDefault: true, label: "OATHHAMMER.Sheet.Weapon" })
|
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerWeaponSheet, { types: ["weapon"], makeDefault: true, label: "OATHHAMMER.Sheet.Weapon" })
|
||||||
@@ -76,7 +93,6 @@ Hooks.once("init", function () {
|
|||||||
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerBuildingSheet, { types: ["building"], makeDefault: true, label: "OATHHAMMER.Sheet.Building" })
|
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerBuildingSheet, { types: ["building"], makeDefault: true, label: "OATHHAMMER.Sheet.Building" })
|
||||||
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerSkillNPCSheet, { types: ["skillnpc"], makeDefault: true, label: "OATHHAMMER.Sheet.SkillNPC" })
|
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerSkillNPCSheet, { types: ["skillnpc"], makeDefault: true, label: "OATHHAMMER.Sheet.SkillNPC" })
|
||||||
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerNpcAttackSheet, { types: ["npcattack"], makeDefault: true, label: "OATHHAMMER.Sheet.NpcAttack" })
|
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerNpcAttackSheet, { types: ["npcattack"], makeDefault: true, label: "OATHHAMMER.Sheet.NpcAttack" })
|
||||||
foundry.documents.collections.Items.registerSheet("fvtt-oath-hammer", applications.OathHammerRegimentSheet, { types: ["regiment"], makeDefault: true, label: "OATHHAMMER.Sheet.Regiment" })
|
|
||||||
|
|
||||||
CONFIG.statusEffects = STATUS_EFFECTS
|
CONFIG.statusEffects = STATUS_EFFECTS
|
||||||
|
|
||||||
|
|||||||
19
system.json
19
system.json
@@ -42,6 +42,22 @@
|
|||||||
"description",
|
"description",
|
||||||
"notes"
|
"notes"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"regiment": {
|
||||||
|
"htmlFields": [
|
||||||
|
"description",
|
||||||
|
"notes"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"party": {
|
||||||
|
"htmlFields": [
|
||||||
|
"notes"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"army": {
|
||||||
|
"htmlFields": [
|
||||||
|
"notes"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Item": {
|
"Item": {
|
||||||
@@ -107,8 +123,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"skillnpc": {},
|
"skillnpc": {},
|
||||||
"npcattack": {},
|
"npcattack": {}
|
||||||
"regiment": {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"grid": {
|
"grid": {
|
||||||
|
|||||||
6
templates/actor/army-notes.hbs
Normal file
6
templates/actor/army-notes.hbs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<section data-tab="notes" data-group="{{tab.group}}" class="tab {{tab.cssClass}}">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "OATHHAMMER.Label.Notes"}}</label>
|
||||||
|
{{formInput systemFields.notes enriched=enrichedNotes value=system.notes name="system.notes" toggled=true}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
55
templates/actor/army-overview.hbs
Normal file
55
templates/actor/army-overview.hbs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<section data-tab="overview" data-group="{{tab.group}}" class="tab {{tab.cssClass}}">
|
||||||
|
|
||||||
|
<fieldset class="army-regiments-fieldset">
|
||||||
|
<legend>{{localize "OATHHAMMER.Label.Regiments"}}</legend>
|
||||||
|
|
||||||
|
{{#if regiments.length}}
|
||||||
|
<ul class="item-list item-list--army-regiment">
|
||||||
|
<li class="item-list-header">
|
||||||
|
<span></span>
|
||||||
|
<span class="col-name">{{localize "OATHHAMMER.Label.Name"}}</span>
|
||||||
|
<span>{{localize "OATHHAMMER.Label.Grit"}}</span>
|
||||||
|
<span>{{localize "OATHHAMMER.Label.ArmorDice"}}</span>
|
||||||
|
<span>{{localize "OATHHAMMER.Label.Movement"}}</span>
|
||||||
|
<span>{{localize "OATHHAMMER.Label.SupplyCost"}}</span>
|
||||||
|
<span></span>
|
||||||
|
</li>
|
||||||
|
{{#each regiments as |regiment|}}
|
||||||
|
<li class="item-entry" data-actor-id="{{regiment.id}}">
|
||||||
|
<img src="{{regiment.img}}" class="item-img" />
|
||||||
|
<span class="item-name">
|
||||||
|
<a data-action="openRegiment" data-actor-id="{{regiment.id}}">{{regiment.name}}</a>
|
||||||
|
</span>
|
||||||
|
<span>{{regiment.grit}}/{{regiment.gritMax}}</span>
|
||||||
|
<span>{{regiment.armor}}d6</span>
|
||||||
|
<span>{{regiment.movement}}</span>
|
||||||
|
<span>{{regiment.supplyCost}} GP</span>
|
||||||
|
<div class="item-actions">
|
||||||
|
{{#unless ../isPlayMode}}
|
||||||
|
<a data-action="removeRegiment" data-actor-id="{{regiment.id}}" data-tooltip="{{localize 'OATHHAMMER.Tooltip.RemoveRegiment'}}">
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</a>
|
||||||
|
{{/unless}}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
<li class="item-entry army-total-row">
|
||||||
|
<span></span>
|
||||||
|
<span class="col-name army-total-label">{{localize "OATHHAMMER.Label.TotalSupply"}}</span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span class="army-total-value">{{totalSupply}} GP</span>
|
||||||
|
<span></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{{else}}
|
||||||
|
<p class="no-items">{{localize "OATHHAMMER.Label.NoRegiments"}}</p>
|
||||||
|
{{/if}}
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
{{#unless isPlayMode}}
|
||||||
|
<p class="settlement-hint">{{localize "OATHHAMMER.Label.DropRegimentHint"}}</p>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
</section>
|
||||||
45
templates/actor/army-sheet.hbs
Normal file
45
templates/actor/army-sheet.hbs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<section class="army-main army-main-{{ifThen isPlayMode 'play' 'edit'}}">
|
||||||
|
<fieldset class="army-header-fieldset">
|
||||||
|
<div class="army-header">
|
||||||
|
|
||||||
|
<!-- Portrait -->
|
||||||
|
<div class="army-portrait-wrap">
|
||||||
|
<img class="actor-img army-portrait" src="{{actor.img}}" data-edit="img" data-action="editImage" data-tooltip="{{actor.name}}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Name + Leader + Location -->
|
||||||
|
<div class="army-header-body">
|
||||||
|
|
||||||
|
<div class="character-name">
|
||||||
|
{{formInput fields.name value=source.name rootId=partId disabled=isPlayMode}}
|
||||||
|
<a class="control" data-action="toggleSheet" data-tooltip="OATHHAMMER.ToggleSheet" data-tooltip-direction="UP">
|
||||||
|
<i class="fa-solid {{#if isPlayMode}}fa-shield-halved{{else}}fa-user-pen{{/if}}"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Leader -->
|
||||||
|
<div class="army-leader-row" data-drop-target="leader">
|
||||||
|
<span class="army-field-label">{{localize "OATHHAMMER.Label.Commander"}}</span>
|
||||||
|
{{#if leader}}
|
||||||
|
<img src="{{leader.img}}" class="army-leader-img" />
|
||||||
|
<a class="army-leader-name" data-action="openLeader" data-tooltip="{{localize 'OATHHAMMER.Tooltip.OpenLeader'}}">{{leader.name}}</a>
|
||||||
|
{{#unless isPlayMode}}
|
||||||
|
<a class="army-leader-clear" data-action="clearLeader" data-tooltip="{{localize 'OATHHAMMER.Tooltip.ClearLeader'}}">
|
||||||
|
<i class="fa-solid fa-times"></i>
|
||||||
|
</a>
|
||||||
|
{{/unless}}
|
||||||
|
{{else}}
|
||||||
|
<span class="army-field-empty">{{localize "OATHHAMMER.Label.DropLeaderHint"}}</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Location -->
|
||||||
|
<div class="army-location-row">
|
||||||
|
<span class="army-field-label">{{localize "OATHHAMMER.Label.Location"}}</span>
|
||||||
|
{{formInput systemFields.location value=system.location name="system.location" placeholder="—" disabled=isPlayMode}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div><!-- /army-header-body -->
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<img src="{{spell.img}}" class="item-img" />
|
<img src="{{spell.img}}" class="item-img" />
|
||||||
<span class="item-name" {{#if spell._descTooltip}}data-tooltip="{{spell._descTooltip}}"{{/if}}>{{spell.name}}</span>
|
<span class="item-name" {{#if spell._descTooltip}}data-tooltip="{{spell._descTooltip}}"{{/if}}>{{spell.name}}</span>
|
||||||
<span class="item-detail">{{spell.system.difficultyValue}}</span>
|
<span class="item-detail">{{spell.system.difficultyValue}}</span>
|
||||||
<span class="item-type">{{localize spell.system.tradition}}</span>
|
<span class="item-type">{{spell.traditionLabel}}</span>
|
||||||
<span class="item-detail item-detail--small">{{#if spell.system.range}}{{spell.system.range}}{{else}}—{{/if}}</span>
|
<span class="item-detail item-detail--small">{{#if spell.system.range}}{{spell.system.range}}{{else}}—{{/if}}</span>
|
||||||
<span class="item-detail item-detail--small">{{#if spell.system.duration}}{{spell.system.duration}}{{else}}—{{/if}}</span>
|
<span class="item-detail item-detail--small">{{#if spell.system.duration}}{{spell.system.duration}}{{else}}—{{/if}}</span>
|
||||||
<span class="item-detail item-detail--small">{{#if spell.system.spellSave}}{{spell.system.spellSave}}{{else}}—{{/if}}</span>
|
<span class="item-detail item-detail--small">{{#if spell.system.spellSave}}{{spell.system.spellSave}}{{else}}—{{/if}}</span>
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
<li class="item-entry" data-item-id="{{miracle.id}}" data-item-uuid="{{miracle.uuid}}">
|
<li class="item-entry" data-item-id="{{miracle.id}}" data-item-uuid="{{miracle.uuid}}">
|
||||||
<img src="{{miracle.img}}" class="item-img" />
|
<img src="{{miracle.img}}" class="item-img" />
|
||||||
<span class="item-name" {{#if miracle._descTooltip}}data-tooltip="{{miracle._descTooltip}}"{{/if}}>{{miracle.name}}</span>
|
<span class="item-name" {{#if miracle._descTooltip}}data-tooltip="{{miracle._descTooltip}}"{{/if}}>{{miracle.name}}</span>
|
||||||
<span class="item-detail">{{miracle.system.divineTradition}}</span>
|
<span class="item-detail">{{miracle.traditionLabel}}</span>
|
||||||
<div class="item-actions">
|
<div class="item-actions">
|
||||||
<a data-action="castMiracle" data-item-id="{{miracle.id}}" title="{{localize 'OATHHAMMER.Action.InvokeMiracle'}}"><i class="fa-solid fa-hands-praying miracle-cast-icon"></i></a>
|
<a data-action="castMiracle" data-item-id="{{miracle.id}}" title="{{localize 'OATHHAMMER.Action.InvokeMiracle'}}"><i class="fa-solid fa-hands-praying miracle-cast-icon"></i></a>
|
||||||
<a data-action="edit" data-item-id="{{miracle.id}}" data-item-uuid="{{miracle.uuid}}"><i class="fa-solid fa-edit"></i></a>
|
<a data-action="edit" data-item-id="{{miracle.id}}" data-item-uuid="{{miracle.uuid}}"><i class="fa-solid fa-edit"></i></a>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
<span>{{localize "OATHHAMMER.Label.Tradition"}}</span>
|
<span>{{localize "OATHHAMMER.Label.Tradition"}}</span>
|
||||||
<span>{{localize "OATHHAMMER.Label.Range"}}</span>
|
<span>{{localize "OATHHAMMER.Label.Range"}}</span>
|
||||||
<span>{{localize "OATHHAMMER.Label.Duration"}}</span>
|
<span>{{localize "OATHHAMMER.Label.Duration"}}</span>
|
||||||
|
<span>{{localize "OATHHAMMER.Label.SpellSave"}}</span>
|
||||||
<span></span>
|
<span></span>
|
||||||
</li>
|
</li>
|
||||||
{{#each spells as |spell|}}
|
{{#each spells as |spell|}}
|
||||||
@@ -20,11 +21,12 @@
|
|||||||
<img src="{{spell.img}}" class="item-img" />
|
<img src="{{spell.img}}" class="item-img" />
|
||||||
<span class="item-name" {{#if spell._descTooltip}}data-tooltip="{{spell._descTooltip}}"{{/if}}>{{spell.name}}</span>
|
<span class="item-name" {{#if spell._descTooltip}}data-tooltip="{{spell._descTooltip}}"{{/if}}>{{spell.name}}</span>
|
||||||
<span class="item-detail">{{spell.system.difficultyValue}}</span>
|
<span class="item-detail">{{spell.system.difficultyValue}}</span>
|
||||||
<span class="item-type">{{localize spell.system.tradition}}</span>
|
<span class="item-type">{{spell.traditionLabel}}</span>
|
||||||
<span class="item-detail item-detail--small">{{#if spell.system.range}}{{spell.system.range}}{{else}}—{{/if}}</span>
|
<span class="item-detail item-detail--small">{{#if spell.system.range}}{{spell.system.range}}{{else}}—{{/if}}</span>
|
||||||
<span class="item-detail item-detail--small">{{#if spell.system.duration}}{{spell.system.duration}}{{else}}—{{/if}}</span>
|
<span class="item-detail item-detail--small">{{#if spell.system.duration}}{{spell.system.duration}}{{else}}—{{/if}}</span>
|
||||||
|
<span class="item-detail item-detail--small">{{#if spell.system.spellSave}}{{spell.system.spellSave}}{{else}}—{{/if}}</span>
|
||||||
<div class="item-actions">
|
<div class="item-actions">
|
||||||
<a data-action="castNPCSpell" data-item-id="{{spell.id}}" title="{{localize 'OATHHAMMER.Action.CastSpell'}}"><i class="fa-solid fa-wand-sparkles spell-cast-icon"></i></a>
|
<a data-action="castNPCSpell" data-item-id="{{spell.id}}" data-tooltip="{{localize 'OATHHAMMER.Action.CastSpell'}}"><i class="fa-solid fa-wand-sparkles spell-cast-icon"></i></a>
|
||||||
<a data-action="edit" data-item-id="{{spell.id}}" data-item-uuid="{{spell.uuid}}"><i class="fa-solid fa-edit"></i></a>
|
<a data-action="edit" data-item-id="{{spell.id}}" data-item-uuid="{{spell.uuid}}"><i class="fa-solid fa-edit"></i></a>
|
||||||
<a data-action="delete" data-item-id="{{spell.id}}" data-item-uuid="{{spell.uuid}}"><i class="fa-solid fa-trash"></i></a>
|
<a data-action="delete" data-item-id="{{spell.id}}" data-item-uuid="{{spell.uuid}}"><i class="fa-solid fa-trash"></i></a>
|
||||||
</div>
|
</div>
|
||||||
@@ -52,9 +54,9 @@
|
|||||||
<li class="item-entry" data-item-id="{{miracle.id}}" data-item-uuid="{{miracle.uuid}}">
|
<li class="item-entry" data-item-id="{{miracle.id}}" data-item-uuid="{{miracle.uuid}}">
|
||||||
<img src="{{miracle.img}}" class="item-img" />
|
<img src="{{miracle.img}}" class="item-img" />
|
||||||
<span class="item-name" {{#if miracle._descTooltip}}data-tooltip="{{miracle._descTooltip}}"{{/if}}>{{miracle.name}}</span>
|
<span class="item-name" {{#if miracle._descTooltip}}data-tooltip="{{miracle._descTooltip}}"{{/if}}>{{miracle.name}}</span>
|
||||||
<span class="item-detail">{{miracle.system.divineTradition}}</span>
|
<span class="item-detail">{{miracle.traditionLabel}}</span>
|
||||||
<div class="item-actions">
|
<div class="item-actions">
|
||||||
<a data-action="castNPCMiracle" data-item-id="{{miracle.id}}" title="{{localize 'OATHHAMMER.Action.InvokeMiracle'}}"><i class="fa-solid fa-hands-praying miracle-cast-icon"></i></a>
|
<a data-action="castNPCMiracle" data-item-id="{{miracle.id}}" data-tooltip="{{localize 'OATHHAMMER.Action.InvokeMiracle'}}"><i class="fa-solid fa-hands-praying miracle-cast-icon"></i></a>
|
||||||
<a data-action="edit" data-item-id="{{miracle.id}}" data-item-uuid="{{miracle.uuid}}"><i class="fa-solid fa-edit"></i></a>
|
<a data-action="edit" data-item-id="{{miracle.id}}" data-item-uuid="{{miracle.uuid}}"><i class="fa-solid fa-edit"></i></a>
|
||||||
<a data-action="delete" data-item-id="{{miracle.id}}" data-item-uuid="{{miracle.uuid}}"><i class="fa-solid fa-trash"></i></a>
|
<a data-action="delete" data-item-id="{{miracle.id}}" data-item-uuid="{{miracle.uuid}}"><i class="fa-solid fa-trash"></i></a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<span class="item-detail">{{skill.system.threshold}}</span>
|
<span class="item-detail">{{skill.system.threshold}}</span>
|
||||||
<a class="npc-skill-roll-btn" data-action="rollSkillNPC"
|
<a class="npc-skill-roll-btn" data-action="rollSkillNPC"
|
||||||
data-item-id="{{skill.id}}" data-item-uuid="{{skill.uuid}}"
|
data-item-id="{{skill.id}}" data-item-uuid="{{skill.uuid}}"
|
||||||
data-tooltip="{{localize 'OATHHAMMER.Roll.RollSkill'}}">
|
data-tooltip="{{localize 'OATHHAMMER.Dialog.RollSkill'}}">
|
||||||
<i class="fa-solid fa-dice-d6"></i>
|
<i class="fa-solid fa-dice-d6"></i>
|
||||||
</a>
|
</a>
|
||||||
<div class="item-actions">
|
<div class="item-actions">
|
||||||
|
|||||||
39
templates/actor/party-loot.hbs
Normal file
39
templates/actor/party-loot.hbs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<section data-tab="loot" data-group="{{tab.group}}" class="tab {{tab.cssClass}}">
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{localize "OATHHAMMER.Label.Loot"}}</legend>
|
||||||
|
|
||||||
|
{{#if lootItems.length}}
|
||||||
|
<ul class="item-list item-list--party-loot">
|
||||||
|
<li class="item-list-header">
|
||||||
|
<span></span>
|
||||||
|
<span class="col-name">{{localize "OATHHAMMER.Label.Name"}}</span>
|
||||||
|
<span>{{localize "OATHHAMMER.Label.Type"}}</span>
|
||||||
|
<span>{{localize "OATHHAMMER.Label.Qty"}}</span>
|
||||||
|
<span></span>
|
||||||
|
</li>
|
||||||
|
{{#each lootItems as |item|}}
|
||||||
|
<li class="item-entry" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
|
||||||
|
<img src="{{item.img}}" class="item-img" />
|
||||||
|
<span class="item-name">{{item.name}}</span>
|
||||||
|
<span class="item-type">{{item.typeLabel}}</span>
|
||||||
|
<div class="item-qty">
|
||||||
|
<a data-action="adjustQty" data-item-id="{{item.id}}" data-delta="-1" class="qty-btn">−</a>
|
||||||
|
<span>{{#if item.system.quantity}}{{item.system.quantity}}{{else}}1{{/if}}</span>
|
||||||
|
<a data-action="adjustQty" data-item-id="{{item.id}}" data-delta="1" class="qty-btn">+</a>
|
||||||
|
</div>
|
||||||
|
<div class="item-actions">
|
||||||
|
<a data-action="edit" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-tooltip="{{localize 'OATHHAMMER.Label.Edit'}}"><i class="fa-solid fa-edit"></i></a>
|
||||||
|
<a data-action="delete" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-tooltip="{{localize 'OATHHAMMER.Label.Delete'}}"><i class="fa-solid fa-trash"></i></a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{else}}
|
||||||
|
<p class="no-items">{{localize "OATHHAMMER.Label.NoLoot"}}</p>
|
||||||
|
{{/if}}
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<p class="settlement-hint">{{localize "OATHHAMMER.Label.DropLootHint"}}</p>
|
||||||
|
|
||||||
|
</section>
|
||||||
46
templates/actor/party-members.hbs
Normal file
46
templates/actor/party-members.hbs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<section data-tab="members" data-group="{{tab.group}}" class="tab {{tab.cssClass}}">
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{localize "OATHHAMMER.Label.MarchingOrder"}}</legend>
|
||||||
|
|
||||||
|
{{#if members.length}}
|
||||||
|
<ul class="item-list item-list--party-member">
|
||||||
|
<li class="item-list-header">
|
||||||
|
<span class="col-order">#</span>
|
||||||
|
<span></span>
|
||||||
|
<span class="col-name">{{localize "OATHHAMMER.Label.Name"}}</span>
|
||||||
|
<span>{{localize "OATHHAMMER.Label.Class"}}</span>
|
||||||
|
<span>{{localize "OATHHAMMER.Label.Level"}}</span>
|
||||||
|
<span>{{localize "OATHHAMMER.Label.Grit"}}</span>
|
||||||
|
<span></span>
|
||||||
|
</li>
|
||||||
|
{{#each members as |member|}}
|
||||||
|
<li class="item-entry" data-actor-id="{{member.id}}">
|
||||||
|
<span class="party-member-order">{{member.position}}</span>
|
||||||
|
<img src="{{member.img}}" class="item-img" />
|
||||||
|
<span class="item-name">
|
||||||
|
<a data-action="openMember" data-actor-id="{{member.id}}">{{member.name}}</a>
|
||||||
|
</span>
|
||||||
|
<span class="item-detail item-detail--small">{{member.classLabel}}</span>
|
||||||
|
<span class="item-detail">{{member.level}}</span>
|
||||||
|
<span class="item-detail">{{member.grit}}</span>
|
||||||
|
<div class="item-actions">
|
||||||
|
{{#unless member.isFirst}}
|
||||||
|
<a data-action="moveMemberUp" data-idx="{{member.idx}}" data-tooltip="{{localize 'OATHHAMMER.Tooltip.MoveUp'}}"><i class="fa-solid fa-chevron-up"></i></a>
|
||||||
|
{{/unless}}
|
||||||
|
{{#unless member.isLast}}
|
||||||
|
<a data-action="moveMemberDown" data-idx="{{member.idx}}" data-tooltip="{{localize 'OATHHAMMER.Tooltip.MoveDown'}}"><i class="fa-solid fa-chevron-down"></i></a>
|
||||||
|
{{/unless}}
|
||||||
|
<a data-action="removeMember" data-actor-id="{{member.id}}" data-tooltip="{{localize 'OATHHAMMER.Tooltip.RemoveMember'}}"><i class="fa-solid fa-times"></i></a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{else}}
|
||||||
|
<p class="no-items">{{localize "OATHHAMMER.Label.NoMembers"}}</p>
|
||||||
|
{{/if}}
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<p class="settlement-hint">{{localize "OATHHAMMER.Label.DropMemberHint"}}</p>
|
||||||
|
|
||||||
|
</section>
|
||||||
6
templates/actor/party-notes.hbs
Normal file
6
templates/actor/party-notes.hbs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<section data-tab="notes" data-group="{{tab.group}}" class="tab {{tab.cssClass}}">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "OATHHAMMER.Label.Notes"}}</label>
|
||||||
|
{{formInput systemFields.notes enriched=enrichedNotes value=system.notes name="system.notes" toggled=true}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
54
templates/actor/party-sheet.hbs
Normal file
54
templates/actor/party-sheet.hbs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<section class="party-main party-main-{{ifThen isPlayMode 'play' 'edit'}}">
|
||||||
|
<fieldset class="party-header-fieldset">
|
||||||
|
<div class="party-header">
|
||||||
|
|
||||||
|
<!-- Portrait -->
|
||||||
|
<div class="party-portrait-wrap">
|
||||||
|
<img class="actor-img party-portrait" src="{{actor.img}}" data-edit="img" data-action="editImage" data-tooltip="{{actor.name}}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Name + Treasury -->
|
||||||
|
<div class="party-header-body">
|
||||||
|
<div class="character-name">
|
||||||
|
{{formInput fields.name value=source.name rootId=partId disabled=isPlayMode}}
|
||||||
|
<a class="control" data-action="toggleSheet" data-tooltip="OATHHAMMER.ToggleSheet" data-tooltip-direction="UP">
|
||||||
|
<i class="fa-solid {{#if isPlayMode}}fa-users-viewfinder{{else}}fa-user-pen{{/if}}"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Treasury -->
|
||||||
|
<div class="party-treasury">
|
||||||
|
<span class="party-treasury-label">{{localize "OATHHAMMER.Label.Treasury"}}</span>
|
||||||
|
|
||||||
|
<div class="party-currency party-currency-gp">
|
||||||
|
<span class="currency-label">{{localize "OATHHAMMER.Label.GP"}}</span>
|
||||||
|
<div class="currency-stepper">
|
||||||
|
<a data-action="adjustCurrency" data-field="system.treasury.gp" data-delta="-1" class="currency-btn">−</a>
|
||||||
|
<input type="number" name="system.treasury.gp" value="{{system.treasury.gp}}" min="0" {{#if isPlayMode}}disabled{{/if}} />
|
||||||
|
<a data-action="adjustCurrency" data-field="system.treasury.gp" data-delta="1" class="currency-btn">+</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="party-currency party-currency-sp">
|
||||||
|
<span class="currency-label">{{localize "OATHHAMMER.Label.SP"}}</span>
|
||||||
|
<div class="currency-stepper">
|
||||||
|
<a data-action="adjustCurrency" data-field="system.treasury.sp" data-delta="-1" class="currency-btn">−</a>
|
||||||
|
<input type="number" name="system.treasury.sp" value="{{system.treasury.sp}}" min="0" {{#if isPlayMode}}disabled{{/if}} />
|
||||||
|
<a data-action="adjustCurrency" data-field="system.treasury.sp" data-delta="1" class="currency-btn">+</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="party-currency party-currency-cp">
|
||||||
|
<span class="currency-label">{{localize "OATHHAMMER.Label.CP"}}</span>
|
||||||
|
<div class="currency-stepper">
|
||||||
|
<a data-action="adjustCurrency" data-field="system.treasury.cp" data-delta="-1" class="currency-btn">−</a>
|
||||||
|
<input type="number" name="system.treasury.cp" value="{{system.treasury.cp}}" min="0" {{#if isPlayMode}}disabled{{/if}} />
|
||||||
|
<a data-action="adjustCurrency" data-field="system.treasury.cp" data-delta="1" class="currency-btn">+</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div><!-- /party-treasury -->
|
||||||
|
</div><!-- /party-header-body -->
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
33
templates/actor/regiment-combat.hbs
Normal file
33
templates/actor/regiment-combat.hbs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<section data-tab="combat" data-group="{{tab.group}}" class="tab {{tab.cssClass}}">
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{localize "OATHHAMMER.Label.Attacks"}}
|
||||||
|
{{#unless isPlayMode}}<a data-action="createNpcAttack" class="create-btn"><i class="fa-solid fa-plus"></i></a>{{/unless}}
|
||||||
|
</legend>
|
||||||
|
{{#if npcAttacks.length}}
|
||||||
|
<ul class="item-list item-list--npc-attack">
|
||||||
|
<li class="item-list-header">
|
||||||
|
<span></span>
|
||||||
|
<span class="col-name">{{localize "OATHHAMMER.Label.Name"}}</span>
|
||||||
|
<span>{{localize "OATHHAMMER.Label.Damage"}}</span>
|
||||||
|
<span title="Armor Penetration">AP</span>
|
||||||
|
<span></span>
|
||||||
|
</li>
|
||||||
|
{{#each npcAttacks as |attack|}}
|
||||||
|
<li class="item-entry" data-item-id="{{attack.id}}" data-item-uuid="{{attack.uuid}}">
|
||||||
|
<img src="{{attack.img}}" class="item-img" />
|
||||||
|
<span class="item-name" {{#if attack._descTooltip}}data-tooltip="{{attack._descTooltip}}"{{/if}}>{{attack.name}}</span>
|
||||||
|
<span class="item-detail">{{attack.system.damageLabel}}</span>
|
||||||
|
<span class="item-detail">{{#if attack.system.ap}}{{attack.system.ap}}{{else}}—{{/if}}</span>
|
||||||
|
<div class="item-actions">
|
||||||
|
<a data-action="rollNpcAttack" data-item-id="{{attack.id}}" data-tooltip="{{localize 'OATHHAMMER.Dialog.Damage'}}"><i class="fa-solid fa-burst"></i></a>
|
||||||
|
<a data-action="edit" data-item-id="{{attack.id}}" data-item-uuid="{{attack.uuid}}"><i class="fa-solid fa-edit"></i></a>
|
||||||
|
<a data-action="delete" data-item-id="{{attack.id}}" data-item-uuid="{{attack.uuid}}"><i class="fa-solid fa-trash"></i></a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{else}}
|
||||||
|
<p class="no-items">{{localize "OATHHAMMER.Label.NoAttacks"}}</p>
|
||||||
|
{{/if}}
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
107
templates/actor/regiment-sheet.hbs
Normal file
107
templates/actor/regiment-sheet.hbs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<section class="npc-main npc-main-{{ifThen isPlayMode 'play' 'edit'}}">
|
||||||
|
<fieldset class="regiment-fieldset">
|
||||||
|
|
||||||
|
<div class="npc-pc flexrow">
|
||||||
|
|
||||||
|
<!-- LEFT: portrait -->
|
||||||
|
<div class="npc-left">
|
||||||
|
<img class="actor-img" src="{{actor.img}}" data-edit="img" data-action="editImage" data-tooltip="{{actor.name}}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- RIGHT: name + vitals -->
|
||||||
|
<div class="npc-right">
|
||||||
|
<div class="character-name">
|
||||||
|
{{formInput fields.name value=source.name rootId=partId disabled=isPlayMode}}
|
||||||
|
<a class="control" data-action="toggleSheet" data-tooltip="OATHHAMMER.ToggleSheet" data-tooltip-direction="UP">
|
||||||
|
<i class="fa-solid fa-user-{{ifThen isPlayMode 'lock' 'pen'}}"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Row 1: combat stats -->
|
||||||
|
<div class="npc-vitals-grid regiment-vitals-grid regiment-row1">
|
||||||
|
|
||||||
|
<!-- Grit -->
|
||||||
|
<div class="npc-vital npc-vital-grit">
|
||||||
|
<span class="vital-label">{{localize "OATHHAMMER.Label.Grit"}}</span>
|
||||||
|
<span class="vital-value">
|
||||||
|
<a class="grit-btn" data-action="adjustGrit" data-delta="-1" data-tooltip="−1">−</a>
|
||||||
|
<input type="number" class="npc-num-input" name="system.grit.value" value="{{system.grit.value}}" min="0" />
|
||||||
|
<span class="res-sep">/</span>
|
||||||
|
{{formInput systemFields.grit.fields.max value=system.grit.max name="system.grit.max" disabled=isPlayMode}}
|
||||||
|
<a class="grit-btn" data-action="adjustGrit" data-delta="1" data-tooltip="+1">+</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Armor Dice -->
|
||||||
|
<div class="npc-vital">
|
||||||
|
<span class="vital-label{{#if isPlayMode}} vital-roll-label{{/if}}"
|
||||||
|
{{#if isPlayMode}}data-action="rollArmor" data-tooltip="OATHHAMMER.Tooltip.RollArmor"{{/if}}>
|
||||||
|
{{#if isPlayMode}}<i class="fa-solid fa-dice-d6"></i>{{/if}}
|
||||||
|
{{localize "OATHHAMMER.Label.ArmorDice"}}
|
||||||
|
</span>
|
||||||
|
<span class="vital-value">
|
||||||
|
<input type="number" class="npc-num-input" name="system.armorDice.value" value="{{system.armorDice.value}}" min="0" {{#if isPlayMode}}disabled{{/if}} />
|
||||||
|
{{#if isPlayMode}}
|
||||||
|
<span class="npc-color-badge">{{armorDiceEmoji}}</span>
|
||||||
|
{{else}}
|
||||||
|
<select name="system.armorDice.colorDiceType" class="npc-color-select">
|
||||||
|
{{selectOptions colorChoices selected=system.armorDice.colorDiceType}}
|
||||||
|
</select>
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Movement -->
|
||||||
|
<div class="npc-vital">
|
||||||
|
<span class="vital-label">{{localize "OATHHAMMER.Label.Movement"}}</span>
|
||||||
|
<span class="vital-value">
|
||||||
|
<input type="number" class="npc-num-input" name="system.movement" value="{{system.movement}}" min="0" {{#if isPlayMode}}disabled{{/if}} />
|
||||||
|
<span class="res-sep">ft</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div><!-- /row1 -->
|
||||||
|
|
||||||
|
<!-- Row 2: cost stats -->
|
||||||
|
<div class="npc-vitals-grid regiment-vitals-grid regiment-row2">
|
||||||
|
|
||||||
|
<!-- Supply Cost -->
|
||||||
|
<div class="npc-vital">
|
||||||
|
<span class="vital-label">{{localize "OATHHAMMER.Label.SupplyCost"}}</span>
|
||||||
|
<span class="vital-value">
|
||||||
|
<input type="number" class="npc-num-input" name="system.supplyCost" value="{{system.supplyCost}}" min="0" {{#if isPlayMode}}disabled{{/if}} />
|
||||||
|
<span class="res-sep">gp / month</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Recruitment Cost -->
|
||||||
|
<div class="npc-vital">
|
||||||
|
<span class="vital-label">{{localize "OATHHAMMER.Label.RecruitmentCost"}}</span>
|
||||||
|
<span class="vital-value">
|
||||||
|
<input type="number" class="npc-num-input" name="system.recruitmentCost" value="{{system.recruitmentCost}}" min="0" {{#if isPlayMode}}disabled{{/if}} />
|
||||||
|
<span class="res-sep">gp</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div><!-- /row2 -->
|
||||||
|
|
||||||
|
<!-- Leader -->
|
||||||
|
<div class="regiment-leader-row" data-drop-target="leader">
|
||||||
|
<span class="regiment-leader-label">{{localize "OATHHAMMER.Label.UnitLeader"}}</span>
|
||||||
|
{{#if leader}}
|
||||||
|
<img src="{{leader.img}}" class="regiment-leader-img" />
|
||||||
|
<a class="regiment-leader-name" data-action="openLeader" data-tooltip="{{localize 'OATHHAMMER.Tooltip.OpenLeader'}}">{{leader.name}}</a>
|
||||||
|
{{#unless isPlayMode}}
|
||||||
|
<a class="regiment-leader-clear" data-action="clearLeader" data-tooltip="{{localize 'OATHHAMMER.Tooltip.ClearLeader'}}">
|
||||||
|
<i class="fa-solid fa-times"></i>
|
||||||
|
</a>
|
||||||
|
{{/unless}}
|
||||||
|
{{else}}
|
||||||
|
<span class="regiment-leader-empty">{{localize "OATHHAMMER.Label.DropLeaderHint"}}</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div><!-- /npc-right -->
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{{localize "OATHHAMMER.Label.Garrison"}}
|
<legend>{{localize "OATHHAMMER.Label.Garrison"}}
|
||||||
{{#unless isPlayMode}}<a data-action="createRegiment" class="create-btn"><i class="fa-solid fa-plus"></i></a>{{/unless}}
|
|
||||||
</legend>
|
</legend>
|
||||||
{{#if regiments.length}}
|
{{#if regiments.length}}
|
||||||
<ul class="item-list item-list--regiment">
|
<ul class="item-list item-list--regiment">
|
||||||
@@ -15,15 +14,15 @@
|
|||||||
<span></span>
|
<span></span>
|
||||||
</li>
|
</li>
|
||||||
{{#each regiments as |regiment|}}
|
{{#each regiments as |regiment|}}
|
||||||
<li class="item-entry" data-item-id="{{regiment.id}}" data-item-uuid="{{regiment.uuid}}">
|
<li class="item-entry" data-actor-id="{{regiment.id}}">
|
||||||
<img src="{{regiment.img}}" class="item-img" />
|
<img src="{{regiment.img}}" class="item-img" />
|
||||||
<span class="item-name">{{regiment.name}}</span>
|
<span class="item-name"><a data-action="openRegiment" data-actor-id="{{regiment.id}}">{{regiment.name}}</a></span>
|
||||||
<span class="item-detail">{{regiment.system.grit.max}}</span>
|
<span class="item-detail">{{regiment.system.grit.max}}</span>
|
||||||
<span class="item-detail">{{regiment.system.armorLabel}}</span>
|
<span class="item-detail">{{regiment.system.armorLabel}}</span>
|
||||||
<span class="item-detail">{{regiment.system.movement}} ft</span>
|
<span class="item-detail">{{regiment.system.movement}} ft</span>
|
||||||
<div class="item-actions">
|
<div class="item-actions">
|
||||||
<a data-action="edit" data-item-id="{{regiment.id}}" data-item-uuid="{{regiment.uuid}}" data-tooltip="{{localize 'OATHHAMMER.Label.Edit'}}"><i class="fa-solid fa-edit"></i></a>
|
<a data-action="openRegiment" data-actor-id="{{regiment.id}}" data-tooltip="{{localize 'OATHHAMMER.Label.Edit'}}"><i class="fa-solid fa-edit"></i></a>
|
||||||
<a data-action="delete" data-item-id="{{regiment.id}}" data-item-uuid="{{regiment.uuid}}" data-tooltip="{{localize 'OATHHAMMER.Label.Delete'}}"><i class="fa-solid fa-trash"></i></a>
|
<a data-action="removeRegiment" data-actor-id="{{regiment.id}}" data-tooltip="{{localize 'OATHHAMMER.Label.RemoveFromGarrison'}}"><i class="fa-solid fa-minus-circle"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
@@ -33,4 +32,6 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<p class="settlement-hint">{{localize "OATHHAMMER.Settlement.GarrisonHint"}}</p>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,122 +0,0 @@
|
|||||||
<section class="item-sheet-common regiment-sheet">
|
|
||||||
|
|
||||||
<div class="header">
|
|
||||||
<img class="item-img" src="{{item.img}}" data-edit="img" data-action="editImage" data-tooltip="{{item.name}}" />
|
|
||||||
{{formInput fields.name value=source.name}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<fieldset class="regiment-stats">
|
|
||||||
<legend>{{localize "OATHHAMMER.Label.Stats"}}</legend>
|
|
||||||
<div class="regiment-stats-row">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "OATHHAMMER.Label.GritMax"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="number" name="system.grit.max" value="{{system.grit.max}}" min="0" max="200" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "OATHHAMMER.Label.ArmorDice"}}</label>
|
|
||||||
<div class="form-fields regiment-armor-fields">
|
|
||||||
<input type="number" name="system.armorDice.value" value="{{system.armorDice.value}}" min="0" max="20" />
|
|
||||||
<select name="system.armorDice.colorDiceType">
|
|
||||||
{{selectOptions colorChoices selected=system.armorDice.colorDiceType}}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "OATHHAMMER.Label.Movement"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="number" name="system.movement" value="{{system.movement}}" min="0" max="500" /> ft
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset class="regiment-skills">
|
|
||||||
<legend>
|
|
||||||
{{localize "OATHHAMMER.Tab.Skills"}}
|
|
||||||
<a data-action="addSkill" class="create-btn" data-tooltip="{{localize 'OATHHAMMER.NewItem.RegimentSkill'}}"><i class="fa-solid fa-plus"></i></a>
|
|
||||||
</legend>
|
|
||||||
{{#if system.skills.length}}
|
|
||||||
<div class="regiment-skill-header regiment-skill-row">
|
|
||||||
<span>{{localize "OATHHAMMER.Label.Name"}}</span>
|
|
||||||
<span>{{localize "OATHHAMMER.Label.Rank"}}</span>
|
|
||||||
<span>{{localize "OATHHAMMER.Label.DiceColor"}}</span>
|
|
||||||
<span></span>
|
|
||||||
</div>
|
|
||||||
{{#each system.skills as |skill idx|}}
|
|
||||||
<div class="regiment-skill-row" data-idx="{{idx}}">
|
|
||||||
<input type="text" name="system.skills.{{idx}}.name" value="{{skill.name}}" placeholder="{{localize 'OATHHAMMER.Label.SkillName'}}" />
|
|
||||||
<input type="number" name="system.skills.{{idx}}.value" value="{{skill.value}}" min="1" max="6" />
|
|
||||||
<select name="system.skills.{{idx}}.colorDiceType">
|
|
||||||
{{selectOptions ../colorChoices selected=skill.colorDiceType}}
|
|
||||||
</select>
|
|
||||||
<a data-action="removeSkill" data-idx="{{idx}}" class="item-delete"><i class="fa-solid fa-times"></i></a>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
{{else}}
|
|
||||||
<p class="no-items">{{localize "OATHHAMMER.Label.NoSkills"}}</p>
|
|
||||||
{{/if}}
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset class="regiment-attacks">
|
|
||||||
<legend>
|
|
||||||
{{localize "OATHHAMMER.Label.Attacks"}}
|
|
||||||
<a data-action="addAttack" class="create-btn" data-tooltip="{{localize 'OATHHAMMER.NewItem.RegimentAttack'}}"><i class="fa-solid fa-plus"></i></a>
|
|
||||||
</legend>
|
|
||||||
{{#if system.attacks.length}}
|
|
||||||
<div class="regiment-attack-header regiment-attack-row">
|
|
||||||
<span>{{localize "OATHHAMMER.Label.Name"}}</span>
|
|
||||||
<span>{{localize "OATHHAMMER.Label.Dice"}}</span>
|
|
||||||
<span>{{localize "OATHHAMMER.Label.DiceColor"}}</span>
|
|
||||||
<span>AP</span>
|
|
||||||
<span>{{localize "OATHHAMMER.Label.Special"}}</span>
|
|
||||||
<span></span>
|
|
||||||
</div>
|
|
||||||
{{#each system.attacks as |attack idx|}}
|
|
||||||
<div class="regiment-attack-row" data-idx="{{idx}}">
|
|
||||||
<input type="text" name="system.attacks.{{idx}}.name" value="{{attack.name}}" placeholder="{{localize 'OATHHAMMER.Label.AttackName'}}" />
|
|
||||||
<select name="system.attacks.{{idx}}.damageDice">
|
|
||||||
{{selectOptions ../dicePoolChoices selected=attack.damageDice}}
|
|
||||||
</select>
|
|
||||||
<select name="system.attacks.{{idx}}.colorDiceType">
|
|
||||||
{{selectOptions ../colorChoices selected=attack.colorDiceType}}
|
|
||||||
</select>
|
|
||||||
<select name="system.attacks.{{idx}}.ap">
|
|
||||||
{{selectOptions ../apChoices selected=attack.ap}}
|
|
||||||
</select>
|
|
||||||
<input type="text" name="system.attacks.{{idx}}.special" value="{{attack.special}}" placeholder="—" />
|
|
||||||
<a data-action="removeAttack" data-idx="{{idx}}" class="item-delete"><i class="fa-solid fa-times"></i></a>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
{{else}}
|
|
||||||
<p class="no-items">{{localize "OATHHAMMER.Label.NoAttacks"}}</p>
|
|
||||||
{{/if}}
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset class="regiment-traits">
|
|
||||||
<legend>
|
|
||||||
{{localize "OATHHAMMER.Tab.Traits"}}
|
|
||||||
<a data-action="addTrait" class="create-btn" data-tooltip="{{localize 'OATHHAMMER.NewItem.RegimentTrait'}}"><i class="fa-solid fa-plus"></i></a>
|
|
||||||
</legend>
|
|
||||||
{{#if system.traits.length}}
|
|
||||||
{{#each system.traits as |trait idx|}}
|
|
||||||
<div class="regiment-trait-row" data-idx="{{idx}}">
|
|
||||||
<input type="text" name="system.traits.{{idx}}.name" value="{{trait.name}}" placeholder="{{localize 'OATHHAMMER.Label.TraitName'}}" />
|
|
||||||
<input type="text" name="system.traits.{{idx}}.description" value="{{trait.description}}" placeholder="{{localize 'OATHHAMMER.Label.Description'}}" />
|
|
||||||
<a data-action="removeTrait" data-idx="{{idx}}" class="item-delete"><i class="fa-solid fa-times"></i></a>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
{{else}}
|
|
||||||
<p class="no-items">{{localize "OATHHAMMER.Label.NoTraits"}}</p>
|
|
||||||
{{/if}}
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend>{{localize "OATHHAMMER.Label.Description"}}</legend>
|
|
||||||
<prose-mirror name="system.description" toggled="false" collaborate="false">
|
|
||||||
{{{system.description}}}
|
|
||||||
</prose-mirror>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
Reference in New Issue
Block a user