diff --git a/css/fvtt-oath-hammer.css b/css/fvtt-oath-hammer.css
index bbd0e60..c9809cb 100644
--- a/css/fvtt-oath-hammer.css
+++ b/css/fvtt-oath-hammer.css
@@ -111,7 +111,7 @@
.oathhammer .skills-container .skills-header,
.oathhammer .skills-container .skill-row {
display: grid;
- grid-template-columns: 1fr 3rem 3rem;
+ grid-template-columns: 1fr 2.5rem 2.5rem 2.5rem 5.5rem;
align-items: center;
gap: 4px;
}
@@ -124,14 +124,59 @@
margin-bottom: 2px;
padding-bottom: 2px;
}
+.oathhammer .skills-container .skills-header span {
+ text-align: center;
+}
+.oathhammer .skills-container .skills-header .skill-name-col {
+ text-align: left;
+}
.oathhammer .skills-container .skill-row {
margin-bottom: 2px;
}
.oathhammer .skills-container .skill-row label.skill-name-col {
font-size: calc(0.82rem * 0.85);
}
-.oathhammer .skills-container .skill-row .skill-rank-col input {
+.oathhammer .skills-container .skill-row .skill-rank-col select,
+.oathhammer .skills-container .skill-row .skill-modifier-col input {
+ width: 100%;
text-align: center;
+ font-size: calc(0.82rem * 0.85);
+ padding: 1px 2px;
+}
+.oathhammer .skills-container .skill-row .skill-color-col {
+ display: flex;
+ align-items: center;
+ gap: 2px;
+}
+.oathhammer .skills-container .skill-row .skill-color-col .color-dice-dot {
+ display: inline-block;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ flex-shrink: 0;
+ border: 1px solid rgba(0, 0, 0, 0.3);
+}
+.oathhammer .skills-container .skill-row .skill-color-col .color-dice-dot.color-dice-white {
+ background: #f0f0f0;
+}
+.oathhammer .skills-container .skill-row .skill-color-col .color-dice-dot.color-dice-red {
+ background: #d9534f;
+}
+.oathhammer .skills-container .skill-row .skill-color-col .color-dice-dot.color-dice-black {
+ background: #222;
+}
+.oathhammer .skills-container .skill-row .skill-color-col .color-dice-select {
+ flex: 1;
+ font-size: calc(0.82rem * 0.85);
+ padding: 1px 1px;
+ min-width: 0;
+}
+.oathhammer .skills-container .skill-row .skill-color-col input[type="number"] {
+ width: 2.2rem;
+ text-align: center;
+ font-size: calc(0.82rem * 0.85);
+ padding: 1px 2px;
+ flex-shrink: 0;
}
.oathhammer .skills-container .skill-row .skill-total {
text-align: center;
@@ -193,15 +238,105 @@
flex: 1;
display: flex;
flex-direction: column;
- gap: 4px;
+ gap: 6px;
+ min-width: 0;
}
.oathhammer .character-main .character-name {
display: flex;
align-items: center;
gap: 4px;
+ border-bottom: 1px solid #535128;
+ padding-bottom: 4px;
}
.oathhammer .character-main .character-name input {
flex: 1;
+ font-family: "Sherwood", "Palatino Linotype", serif;
+ font-size: calc(0.82rem * 1.1);
+}
+.oathhammer .character-main .character-identity-bar {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 6px;
+ flex-wrap: wrap;
+}
+.oathhammer .character-main .character-identity-bar .identity-slot {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ padding: 2px 6px;
+ border: 1px solid #535128;
+ border-radius: 3px;
+ background: rgba(0, 0, 0, 0.15);
+ font-size: calc(0.82rem * 0.85);
+ min-width: 6rem;
+ min-height: 1.6rem;
+}
+.oathhammer .character-main .character-identity-bar .identity-slot.empty {
+ opacity: 0.6;
+ font-style: italic;
+ border-style: dashed;
+}
+.oathhammer .character-main .character-identity-bar .identity-slot .identity-img {
+ width: 18px;
+ height: 18px;
+ -o-object-fit: cover;
+ object-fit: cover;
+ border-radius: 2px;
+ border: none;
+}
+.oathhammer .character-main .character-identity-bar .identity-slot .identity-name {
+ flex: 1;
+ font-family: "BlueDragon", "Palatino Linotype", serif;
+ font-size: calc(0.82rem * 0.85);
+}
+.oathhammer .character-main .character-identity-bar .identity-slot .slot-icon {
+ font-size: calc(0.82rem * 0.85);
+ opacity: 0.6;
+}
+.oathhammer .character-main .character-identity-bar .identity-slot .slot-placeholder {
+ font-size: calc(0.82rem * 0.85);
+}
+.oathhammer .character-main .character-identity-bar .identity-slot a {
+ font-size: calc(0.82rem * 0.85);
+ opacity: 0.7;
+}
+.oathhammer .character-main .character-identity-bar .identity-slot a:hover {
+ opacity: 1;
+}
+.oathhammer .character-main .character-identity-bar .identity-xp {
+ display: flex;
+ align-items: center;
+ gap: 3px;
+ margin-left: auto;
+ font-size: calc(0.82rem * 0.85);
+}
+.oathhammer .character-main .character-identity-bar .identity-xp .xp-label {
+ font-family: "BlueDragon", "Palatino Linotype", serif;
+ font-size: calc(0.82rem * 0.85);
+ color: #535128;
+ margin-left: 4px;
+}
+.oathhammer .character-main .character-identity-bar .identity-xp .xp-sep {
+ opacity: 0.6;
+}
+.oathhammer .character-main .character-identity-bar .identity-xp input {
+ width: 3rem;
+ text-align: center;
+ font-size: calc(0.82rem * 0.85);
+ padding: 1px 2px;
+}
+.oathhammer .attributes-stress-row {
+ display: flex;
+ align-items: flex-start;
+ gap: 6px;
+}
+.oathhammer .attributes-stress-row .character-attributes {
+ flex: 1;
+}
+.oathhammer .attributes-stress-row .character-arcane-stress {
+ flex-shrink: 0;
+ width: auto;
}
.oathhammer .attributes-grid {
display: grid;
@@ -294,15 +429,22 @@
flex-direction: column;
gap: 2px;
}
-.oathhammer .character-arcane-stress .flexrow {
+.oathhammer .character-arcane-stress .stress-inputs {
+ display: flex;
align-items: center;
+ justify-content: center;
gap: 4px;
}
+.oathhammer .character-arcane-stress div.form-group {
+ display: contents;
+}
.oathhammer .character-arcane-stress input {
- min-width: 3rem;
- max-width: 3rem;
+ width: 2.8rem;
text-align: center;
}
+.oathhammer .character-arcane-stress span {
+ opacity: 0.6;
+}
.oathhammer .defense-display {
min-width: 3rem;
max-width: 3rem;
@@ -328,50 +470,136 @@
margin: 0;
padding: 0;
}
-.oathhammer .item-entry {
- display: flex;
+.oathhammer .item-list-header {
+ display: grid;
align-items: center;
- gap: 6px;
- padding: 3px 4px;
+ gap: 4px;
+ padding: 2px 4px 3px;
+ border-bottom: 1px solid #535128;
+ margin-bottom: 2px;
+ font-family: "BlueDragon", "Palatino Linotype", serif;
+ font-size: calc(0.82rem * 0.9);
+ color: #084a74;
+}
+.oathhammer .item-list-header span {
+ text-align: center;
+}
+.oathhammer .item-list-header .col-name {
+ text-align: left;
+}
+.oathhammer .item-entry {
+ display: grid;
+ align-items: center;
+ gap: 4px;
+ padding: 2px 4px;
border-bottom: 1px solid rgba(83, 81, 40, 0.2);
}
.oathhammer .item-entry:hover {
background-color: rgba(8, 74, 116, 0.08);
}
.oathhammer .item-entry .item-img {
- height: 24px;
width: 24px;
+ height: 24px;
border: 1px solid #535128;
border-radius: 2px;
-o-object-fit: cover;
object-fit: cover;
+ align-self: center;
+ justify-self: center;
+ flex-shrink: 0;
}
.oathhammer .item-entry .item-name {
- flex: 1;
font-family: "Calibri", "Segoe UI", sans-serif;
font-size: 0.82rem;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ min-width: 0;
}
.oathhammer .item-entry .item-detail {
font-size: calc(0.82rem * 0.9);
color: #535128;
- min-width: 4rem;
text-align: center;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.oathhammer .item-entry .item-type {
- font-size: calc(0.82rem * 0.85);
+ font-size: calc(0.82rem * 0.9);
color: #084a74;
- min-width: 6rem;
+ text-align: center;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
-.oathhammer .item-entry a {
+.oathhammer .item-entry .item-equipped {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.oathhammer .item-entry .item-equipped input[type="checkbox"] {
+ width: 0.95rem;
+ height: 0.95rem;
+ cursor: pointer;
+ margin: 0;
+}
+.oathhammer .item-entry .item-actions {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 4px;
+}
+.oathhammer .item-entry .item-actions a {
opacity: 0.6;
transition: opacity 0.2s;
+ font-size: calc(0.82rem * 0.85);
}
-.oathhammer .item-entry a:hover {
+.oathhammer .item-entry .item-actions a:hover {
opacity: 1;
}
-.oathhammer .item-entry a:hover {
+.oathhammer .item-entry .item-actions a:hover {
color: #084a74;
}
+.oathhammer .item-list--weapon .item-list-header,
+.oathhammer .item-list--weapon .item-entry {
+ grid-template-columns: 24px 1fr 5.5rem 3rem 1.8rem 3.5rem;
+}
+.oathhammer .item-list--armor .item-list-header,
+.oathhammer .item-list--armor .item-entry {
+ grid-template-columns: 24px 1fr 3.5rem 5rem 1.8rem 3.5rem;
+}
+.oathhammer .item-list--ammo .item-list-header,
+.oathhammer .item-list--ammo .item-entry {
+ grid-template-columns: 24px 1fr 4rem 3.5rem;
+}
+.oathhammer .item-list--spell .item-list-header,
+.oathhammer .item-list--spell .item-entry {
+ grid-template-columns: 24px 1fr 3rem 6rem 3rem 3.5rem;
+}
+.oathhammer .item-list--miracle .item-list-header,
+.oathhammer .item-list--miracle .item-entry {
+ grid-template-columns: 24px 1fr 4.5rem 3.5rem;
+}
+.oathhammer .item-list--equipment .item-list-header,
+.oathhammer .item-list--equipment .item-entry {
+ grid-template-columns: 24px 1fr 5rem 3rem 3.5rem;
+}
+.oathhammer .item-list--magic-item .item-list-header,
+.oathhammer .item-list--magic-item .item-entry {
+ grid-template-columns: 24px 1fr 5rem 3.5rem;
+}
+.oathhammer .item-list--condition .item-list-header,
+.oathhammer .item-list--condition .item-entry {
+ grid-template-columns: 24px 1fr 5.5rem 3.5rem;
+}
+.oathhammer .item-list--ability .item-list-header,
+.oathhammer .item-list--ability .item-entry {
+ grid-template-columns: 24px 1fr 6rem 3.5rem;
+}
+.oathhammer .item-list--oath .item-list-header,
+.oathhammer .item-list--oath .item-entry {
+ grid-template-columns: 24px 1fr 5.5rem 3.5rem;
+}
.oathhammer .no-items {
color: var(--color-dark-5);
font-style: italic;
diff --git a/lang/en.json b/lang/en.json
index 5a44233..c314dcf 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -242,11 +242,28 @@
"Bane": "Bane",
"Skill": "Skill",
"SkillRank": "Rank",
- "TotalDice": "Total Dice",
+ "SkillModifier": "Mod",
+ "TotalDice": "Total",
+ "ColorDice": "Color",
"DropLineage": "Drop Lineage Here",
"DropClass": "Drop Class Here",
"Traits": "Traits",
- "Features": "Features"
+ "Features": "Features",
+ "Name": "Name",
+ "Type": "Type",
+ "Damage": "Damage",
+ "Tradition": "Tradition",
+ "Piety": "Piety",
+ "Quantity": "Qty",
+ "Rarity": "Rarity",
+ "Penalty": "Penalty",
+ "Equipped": "Eq.",
+ "XPCurrent": "Current XP"
+ },
+ "ColorDice": {
+ "White": "White (4+)",
+ "Red": "Red (3+)",
+ "Black": "Black (2+)"
},
"NewItem": {
"Weapon": "New Weapon",
diff --git a/less/actor-sheet.less b/less/actor-sheet.less
index a9e1212..76fe443 100644
--- a/less/actor-sheet.less
+++ b/less/actor-sheet.less
@@ -67,15 +67,106 @@
flex: 1;
display: flex;
flex-direction: column;
- gap: 4px;
+ gap: 6px;
+ min-width: 0;
}
.character-name {
display: flex;
align-items: center;
gap: 4px;
+ border-bottom: 1px solid @color-olive;
+ padding-bottom: 4px;
- input { flex: 1; }
+ input {
+ flex: 1;
+ font-family: @font-primary;
+ font-size: @font-size-lg;
+ }
+ }
+
+ .character-identity-bar {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 6px;
+ flex-wrap: wrap;
+
+ .identity-slot {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ padding: 2px 6px;
+ border: 1px solid @color-olive;
+ border-radius: 3px;
+ background: rgba(0,0,0,0.15);
+ font-size: @font-size-sm;
+ min-width: 6rem;
+ min-height: 1.6rem;
+
+ &.empty {
+ opacity: 0.6;
+ font-style: italic;
+ border-style: dashed;
+ }
+
+ .identity-img {
+ width: 18px;
+ height: 18px;
+ object-fit: cover;
+ border-radius: 2px;
+ border: none;
+ }
+
+ .identity-name {
+ flex: 1;
+ font-family: @font-secondary;
+ font-size: @font-size-sm;
+ }
+
+ .slot-icon { font-size: @font-size-sm; opacity: 0.6; }
+ .slot-placeholder { font-size: @font-size-sm; }
+
+ a { font-size: @font-size-sm; opacity: 0.7; &:hover { opacity: 1; } }
+ }
+
+ .identity-xp {
+ display: flex;
+ align-items: center;
+ gap: 3px;
+ margin-left: auto;
+ font-size: @font-size-sm;
+
+ .xp-label {
+ font-family: @font-secondary;
+ font-size: @font-size-sm;
+ color: @color-olive;
+ margin-left: 4px;
+ }
+
+ .xp-sep { opacity: 0.6; }
+
+ input {
+ width: 3rem;
+ text-align: center;
+ font-size: @font-size-sm;
+ padding: 1px 2px;
+ }
+ }
+ }
+ }
+
+ // Attributes + Arcane Stress side by side
+ .attributes-stress-row {
+ display: flex;
+ align-items: flex-start;
+ gap: 6px;
+
+ .character-attributes { flex: 1; }
+
+ .character-arcane-stress {
+ flex-shrink: 0;
+ width: auto;
}
}
@@ -186,17 +277,21 @@
gap: 2px;
}
- // Arcane Stress narrow inputs
+ // Arcane Stress compact
.character-arcane-stress {
- .flexrow {
+ .stress-inputs {
+ display: flex;
align-items: center;
+ justify-content: center;
gap: 4px;
}
+ // formInput renders a
— flatten it
+ div.form-group { display: contents; }
input {
- min-width: 3rem;
- max-width: 3rem;
+ width: 2.8rem;
text-align: center;
}
+ span { opacity: 0.6; }
}
// Defense display
diff --git a/less/base.less b/less/base.less
index f17193d..f44360e 100644
--- a/less/base.less
+++ b/less/base.less
@@ -122,7 +122,7 @@
.skills-header,
.skill-row {
display: grid;
- grid-template-columns: 1fr 3rem 3rem;
+ grid-template-columns: 1fr 2.5rem 2.5rem 2.5rem 5.5rem;
align-items: center;
gap: 4px;
}
@@ -135,6 +135,9 @@
border-bottom: 1px solid @color-olive;
margin-bottom: 2px;
padding-bottom: 2px;
+
+ span { text-align: center; }
+ .skill-name-col { text-align: left; }
}
.skill-row {
@@ -144,8 +147,46 @@
font-size: @font-size-sm;
}
- .skill-rank-col input {
+ .skill-rank-col select,
+ .skill-modifier-col input {
+ width: 100%;
text-align: center;
+ font-size: @font-size-sm;
+ padding: 1px 2px;
+ }
+
+ .skill-color-col {
+ display: flex;
+ align-items: center;
+ gap: 2px;
+
+ .color-dice-dot {
+ display: inline-block;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ flex-shrink: 0;
+ border: 1px solid rgba(0,0,0,0.3);
+
+ &.color-dice-white { background: #f0f0f0; }
+ &.color-dice-red { background: #d9534f; }
+ &.color-dice-black { background: #222; }
+ }
+
+ .color-dice-select {
+ flex: 1;
+ font-size: @font-size-sm;
+ padding: 1px 1px;
+ min-width: 0;
+ }
+
+ input[type="number"] {
+ width: 2.2rem;
+ text-align: center;
+ font-size: @font-size-sm;
+ padding: 1px 2px;
+ flex-shrink: 0;
+ }
}
.skill-total {
diff --git a/less/item-list.less b/less/item-list.less
index 648b680..ccd37b5 100644
--- a/less/item-list.less
+++ b/less/item-list.less
@@ -10,45 +10,153 @@
padding: 0;
}
- .item-entry {
- display: flex;
+ // Header row shared by all lists
+ .item-list-header {
+ display: grid;
align-items: center;
- gap: 6px;
- padding: 3px 4px;
- border-bottom: 1px solid @color-olive-faint;
+ gap: 4px;
+ padding: 2px 4px 3px;
+ border-bottom: 1px solid @color-olive;
+ margin-bottom: 2px;
+ font-family: @font-secondary;
+ font-size: @font-size-xs;
+ color: @color-blue;
+ span { text-align: center; }
+ .col-name { text-align: left; }
+ }
+ // Data row
+ .item-entry {
+ display: grid;
+ align-items: center;
+ gap: 4px;
+ padding: 2px 4px;
+ border-bottom: 1px solid @color-olive-faint;
&:hover { background-color: @color-blue-hover; }
.item-img {
- height: @item-img-size;
width: @item-img-size;
+ height: @item-img-size;
border: 1px solid @color-olive;
border-radius: 2px;
object-fit: cover;
+ align-self: center;
+ justify-self: center;
+ flex-shrink: 0;
}
.item-name {
- flex: 1;
font-family: @font-body;
font-size: @font-size-base;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ min-width: 0;
}
.item-detail {
font-size: @font-size-xs;
color: @color-olive;
- min-width: 4rem;
text-align: center;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.item-type {
- font-size: @font-size-sm;
+ font-size: @font-size-xs;
color: @color-blue;
- min-width: 6rem;
+ text-align: center;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
- a {
- .transition-opacity();
- &:hover { color: @color-blue; }
+ .item-equipped {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ input[type="checkbox"] {
+ width: 0.95rem;
+ height: 0.95rem;
+ cursor: pointer;
+ margin: 0;
+ }
+ }
+
+ .item-actions {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 4px;
+ a {
+ .transition-opacity();
+ font-size: @font-size-sm;
+ &:hover { color: @color-blue; }
+ }
+ }
+ }
+
+ // ── Per-list grid templates ──────────────────────────────
+ // Format: [img] [name] [detail cols...] [equipped?] [actions]
+
+ .item-list--weapon {
+ .item-list-header, .item-entry {
+ grid-template-columns: @item-img-size 1fr 5.5rem 3rem 1.8rem 3.5rem;
+ }
+ }
+
+ .item-list--armor {
+ .item-list-header, .item-entry {
+ grid-template-columns: @item-img-size 1fr 3.5rem 5rem 1.8rem 3.5rem;
+ }
+ }
+
+ .item-list--ammo {
+ .item-list-header, .item-entry {
+ grid-template-columns: @item-img-size 1fr 4rem 3.5rem;
+ }
+ }
+
+ .item-list--spell {
+ .item-list-header, .item-entry {
+ grid-template-columns: @item-img-size 1fr 3rem 6rem 3rem 3.5rem;
+ }
+ }
+
+ .item-list--miracle {
+ .item-list-header, .item-entry {
+ grid-template-columns: @item-img-size 1fr 4.5rem 3.5rem;
+ }
+ }
+
+ .item-list--equipment {
+ .item-list-header, .item-entry {
+ grid-template-columns: @item-img-size 1fr 5rem 3rem 3.5rem;
+ }
+ }
+
+ .item-list--magic-item {
+ .item-list-header, .item-entry {
+ grid-template-columns: @item-img-size 1fr 5rem 3.5rem;
+ }
+ }
+
+ .item-list--condition {
+ .item-list-header, .item-entry {
+ grid-template-columns: @item-img-size 1fr 5.5rem 3.5rem;
+ }
+ }
+
+ .item-list--ability {
+ .item-list-header, .item-entry {
+ grid-template-columns: @item-img-size 1fr 6rem 3.5rem;
+ }
+ }
+
+ .item-list--oath {
+ .item-list-header, .item-entry {
+ grid-template-columns: @item-img-size 1fr 5.5rem 3.5rem;
}
}
diff --git a/module/applications/sheets/character-sheet.mjs b/module/applications/sheets/character-sheet.mjs
index 1ca3c47..351b2ad 100644
--- a/module/applications/sheets/character-sheet.mjs
+++ b/module/applications/sheets/character-sheet.mjs
@@ -57,6 +57,10 @@ export default class OathHammerCharacterSheet extends OathHammerActorSheet {
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
+ // lineage/class/experience available to all parts (header + identity tab)
+ const doc = this.document
+ context.lineage = doc.itemTypes.lineage?.[0] ?? null
+ context.characterClass = doc.itemTypes["class"]?.[0] ?? null
return context
}
@@ -68,8 +72,6 @@ export default class OathHammerCharacterSheet extends OathHammerActorSheet {
break
case "identity":
context.tab = context.tabs.identity
- context.lineage = doc.itemTypes.lineage?.[0] ?? null
- context.characterClass = doc.itemTypes["class"]?.[0] ?? null
context.abilities = doc.itemTypes.ability
context.oaths = doc.itemTypes.oath
break
@@ -89,14 +91,26 @@ export default class OathHammerCharacterSheet extends OathHammerActorSheet {
attribute: attr,
label: `OATHHAMMER.Attribute.${attr.charAt(0).toUpperCase()}${attr.slice(1)}`,
attrRank: attrRanks[attr],
- skillData: skillKeys.map(skillKey => ({
- key: skillKey,
- label: SYSTEM.SKILLS[skillKey].label,
- rank: sys.skills[skillKey].rank,
- name: `system.skills.${skillKey}.rank`,
- total: attrRanks[attr] + sys.skills[skillKey].rank,
- field: skillSchemaFields[skillKey].fields.rank,
- }))
+ skillData: skillKeys.map(skillKey => {
+ const sk = sys.skills[skillKey]
+ return {
+ key: skillKey,
+ label: SYSTEM.SKILLS[skillKey].label,
+ rank: sk.rank,
+ modifier: sk.modifier,
+ colorDice: sk.colorDice,
+ colorDiceType: sk.colorDiceType,
+ rankName: `system.skills.${skillKey}.rank`,
+ modifierName: `system.skills.${skillKey}.modifier`,
+ colorDiceName: `system.skills.${skillKey}.colorDice`,
+ colorDiceTypeName: `system.skills.${skillKey}.colorDiceType`,
+ rankOptions: [0,1,2,3,4].map(v => ({ value: v, label: String(v), selected: v === sk.rank })),
+ total: attrRanks[attr] + sk.rank,
+ // legacy - kept for formInput compatibility
+ name: `system.skills.${skillKey}.rank`,
+ field: skillSchemaFields[skillKey].fields.rank,
+ }
+ })
}))
break
}
@@ -125,6 +139,36 @@ export default class OathHammerCharacterSheet extends OathHammerActorSheet {
return context
}
+ /** Auto-fill colorDice count when color type changes */
+ static #COLOR_THRESHOLDS = { white: 4, red: 3, black: 2 }
+
+ _onRender(context, options) {
+ super._onRender?.(context, options)
+
+ // Color dice auto-fill
+ this.element.querySelectorAll('select.color-dice-select').forEach(select => {
+ select.addEventListener('change', event => {
+ const threshold = OathHammerCharacterSheet.#COLOR_THRESHOLDS[event.target.value] ?? 4
+ const countInput = event.target.closest('.skill-color-col')?.querySelector('input[type="number"]')
+ if (countInput) {
+ countInput.value = threshold
+ countInput.dispatchEvent(new Event('change', { bubbles: true }))
+ }
+ const dot = event.target.closest('.skill-color-col')?.querySelector('.color-dice-dot')
+ if (dot) dot.className = `color-dice-dot color-dice-${event.target.value}`
+ })
+ })
+
+ // Equipped checkbox — directly updates the item
+ this.element.querySelectorAll('input.item-equipped-cb').forEach(cb => {
+ cb.addEventListener('change', event => {
+ const itemId = event.target.dataset.itemId
+ const item = this.document.items.get(itemId)
+ if (item) item.update({ 'system.equipped': event.target.checked })
+ })
+ })
+ }
+
async _onDrop(event) {
if (!this.isEditable) return
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
diff --git a/module/models/character.mjs b/module/models/character.mjs
index aa79368..0e02a42 100644
--- a/module/models/character.mjs
+++ b/module/models/character.mjs
@@ -20,9 +20,15 @@ export default class OathHammerCharacter extends foundry.abstract.TypeDataModel
fate: attributeField()
})
- // Skills: 26 skills each with a rank (0–4). Grit.max = resilience.rank + toughness.rank.
+ // Skills: 26 skills each with a rank (0–4), a bonus/penalty modifier, and color dice.
+ // Total dice = attr rank + skill rank. Modifier = bonus (+) or penalty (-) dice.
+ // Color dice: type (white 4+, red 3+, black 2+) + count of colored dice in the pool.
const skillField = () => new fields.SchemaField({
- rank: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 4 })
+ rank: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 4 }),
+ modifier: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
+ colorDiceType: new fields.StringField({ required: true, nullable: false, initial: "white",
+ choices: { white: "OATHHAMMER.ColorDice.White", red: "OATHHAMMER.ColorDice.Red", black: "OATHHAMMER.ColorDice.Black" } }),
+ colorDice: new fields.NumberField({ ...requiredInteger, initial: 4, min: 0 }),
})
schema.skills = new fields.SchemaField({
academics: skillField(),
diff --git a/templates/actor/character-combat.hbs b/templates/actor/character-combat.hbs
index 8cd8ed6..077756b 100644
--- a/templates/actor/character-combat.hbs
+++ b/templates/actor/character-combat.hbs
@@ -21,18 +21,30 @@
{{#unless isPlayMode}}
{{/unless}}
{{#if weapons.length}}
-
+
+
{{#each weapons as |weapon|}}
- -
+
-
{{weapon.name}}
{{weapon.system.damageLabel}}
- AP: {{weapon.system.ap}}
- {{formField weapon.system.schema.fields.equipped value=weapon.system.equipped name="system.equipped"}}
- {{#unless ../isPlayMode}}
-
-
- {{/unless}}
+ {{weapon.system.ap}}
+
+
+
+
+ {{#unless ../isPlayMode}}
+
+
+ {{/unless}}
+
{{/each}}
@@ -44,18 +56,30 @@
{{#if armors.length}}
-
+
+
{{#each armors as |armor|}}
- -
+
-
{{armor.name}}
- AV: {{armor.system.armorValue}}
- {{#if armor.system.penalty}}{{armor.system.penalty}}{{/if}}
- {{formField armor.system.schema.fields.equipped value=armor.system.equipped name="system.equipped"}}
- {{#unless ../isPlayMode}}
-
-
- {{/unless}}
+ {{armor.system.armorValue}}
+ {{#if armor.system.penalty}}{{armor.system.penalty}}{{else}}—{{/if}}
+
+
+
+
+ {{#unless ../isPlayMode}}
+
+
+ {{/unless}}
+
{{/each}}
@@ -67,16 +91,24 @@
{{#if ammunition.length}}
-
-
-
-
-
- {{formInput systemFields.experience.fields.level value=system.experience.level name="system.experience.level" disabled=isPlayMode}}
-
-
-
- {{formInput systemFields.experience.fields.current value=system.experience.current name="system.experience.current" disabled=isPlayMode}}
-
-
-
- {{formInput systemFields.experience.fields.total value=system.experience.total name="system.experience.total" disabled=isPlayMode}}
-
-
-
{{#if abilities.length}}
-
+
+
{{#each abilities as |ability|}}
- -
+
-
{{ability.name}}
{{localize ability.system.abilityType}}
- {{#unless ../isPlayMode}}
-
-
- {{/unless}}
+
+ {{#unless ../isPlayMode}}
+
+
+ {{/unless}}
+
{{/each}}
@@ -81,16 +44,24 @@
{{#if oaths.length}}
-
+
+
{{#each oaths as |oath|}}
- -
+
-
{{oath.name}}
{{localize oath.system.oathType}}
- {{#unless ../isPlayMode}}
-
-
- {{/unless}}
+
+ {{#unless ../isPlayMode}}
+
+
+ {{/unless}}
+
{{/each}}
diff --git a/templates/actor/character-magic.hbs b/templates/actor/character-magic.hbs
index f37751a..4699cf2 100644
--- a/templates/actor/character-magic.hbs
+++ b/templates/actor/character-magic.hbs
@@ -11,18 +11,28 @@
{{#unless isPlayMode}}{{/unless}}
{{#if spells.length}}
-
+
+
{{#each spells as |spell|}}
- -
+
-
{{spell.name}}
- Lv.{{spell.system.level}}
- {{localize spell.system.tradition}}
- AS: {{spell.system.arcaneStress}}
- {{#unless ../isPlayMode}}
-
-
- {{/unless}}
+ {{spell.system.level}}
+ {{localize spell.system.tradition}}
+ {{spell.system.arcaneStress}}
+
+ {{#unless ../isPlayMode}}
+
+
+ {{/unless}}
+
{{/each}}
@@ -35,16 +45,24 @@
{{#unless isPlayMode}}{{/unless}}
{{#if miracles.length}}
-