Fixed lethality display. Improved combat tab styling. Improved edit stats modal.

This commit is contained in:
Aaron Lutomski
2026-06-17 19:03:17 -07:00
parent 219ad971d9
commit cb5cfcfcd0
16 changed files with 330 additions and 104 deletions
+8 -1
View File
@@ -238,7 +238,7 @@
"DG.Gear.Armor": "ARMOR",
"DG.Gear.OtherGear": "OTHER GEAR",
"DG.Gear.Name": "Name",
"DG.Gear.DamageOrLethality": "Damage/Lethality",
"DG.Gear.DamageOrLethality": "Damage/ Lethality",
"DG.Gear.ArmorPiercing": "Armor Piercing",
"DG.Gear.ArmorRating": "Armor Rating",
"DG.Gear.Equipped": "Equipped",
@@ -329,6 +329,13 @@
"DG.ModifyDamageRollDialogue.Weapon": "Weapon:",
"DG.ModifyDamageRollDialogue.OuterModifierTooltip": "Use this field to modify the outer part of the formula, such as to double the result.",
"DG.ModifyDamageRollDialogue.InnerModifierTooltip": "Use this field to add a flat modifier such as '+1' or another dice term such as '+1d4'.",
"DG.DamageOrLethalityRoll.Title": "Roll Damage or Lethality",
"DG.DamageOrLethalityRoll.Intro": "This weapon has both damage and lethality. Choose which to roll.",
"DG.DamageOrLethalityRoll.RollDamage": "Roll Damage",
"DG.DamageOrLethalityRoll.RollLethality": "Roll Lethality",
"DG.EditStats.Title": "Edit Statistics",
"DG.EditStats.Button": "Edit Statistics",
"DG.EditStats.DistinguishingFeature": "Distinguishing Feature",
"DG.Roll.Inhuman": "Inhuman",
"DG.Tooltip.BreakPoint": "The current breaking point, reset when current sanity falls to or below this value. The starting breaking point is SAN - POW.",
"DG.Skills.ritual": "Ritual",
+15 -3
View File
@@ -1,4 +1,5 @@
import DG, { BASE_TEMPLATE_PATH } from "../config/index.js";
import { applyDialogTheme } from "./dg-dialog.js";
export default class ActorEditStatForm extends foundry.applications.api.HandlebarsApplicationMixin(
foundry.applications.api.ApplicationV2,
@@ -11,9 +12,13 @@ export default class ActorEditStatForm extends foundry.applications.api.Handleba
/** @override */
static DEFAULT_OPTIONS = {
tag: "form",
classes: [DG.ID, "edit-stat-form"],
window: { title: "Edit statistics form", resizable: true },
position: { width: 400, height: 200 },
classes: [DG.ID, "edit-stat-form", "dg-dialog-app"],
window: {
title: "DG.EditStats.Title",
resizable: true,
contentClasses: ["standard-form"],
},
position: { width: 600, height: "auto" },
actions: {},
form: {
handler: this.formHandler,
@@ -28,11 +33,18 @@ export default class ActorEditStatForm extends foundry.applications.api.Handleba
},
});
/** @override */
async _onRender(context, options) {
await super._onRender(context, options);
applyDialogTheme(this);
}
/** @override */
async _prepareContext(options) {
return {
...(await super._prepareContext(options)),
actor: this.actor,
statKeys: DG.statistics,
sourceStatistics:
this.actor._source?.system?.statistics ?? this.actor.system.statistics,
};
+40
View File
@@ -7,6 +7,8 @@ const PERCENTILE_ROLL_DIALOG_TEMPLATE =
"systems/deltagreen/templates/dialog/modify-percentile-roll.html";
const DAMAGE_ROLL_DIALOG_TEMPLATE =
"systems/deltagreen/templates/dialog/modify-damage-roll.html";
const DAMAGE_OR_LETHALITY_ROLL_DIALOG_TEMPLATE =
"systems/deltagreen/templates/dialog/damage-or-lethality-roll.html";
const QUICK_MODIFIER_PRESETS = [
{
@@ -215,3 +217,41 @@ export async function showDamageRollModifyDialog({ itemName, formula }) {
],
});
}
/**
* @param {object} options
* @param {string} options.itemName
* @returns {Promise<"damage"|"lethality"|void>}
*/
export async function showDamageOrLethalityChoiceDialog({ itemName }) {
const content = await renderTemplate(
DAMAGE_OR_LETHALITY_ROLL_DIALOG_TEMPLATE,
{
itemName,
},
);
return showDgDialog({
modifier: "damage-or-lethality",
content,
window: {
title: game.i18n.localize("DG.DamageOrLethalityRoll.Title"),
},
close: () => null,
buttons: [
{
default: true,
label: game.i18n.localize("DG.DamageOrLethalityRoll.RollDamage"),
action: "damage",
icon: "fas fa-dice",
callback: () => "damage",
},
{
label: game.i18n.localize("DG.DamageOrLethalityRoll.RollLethality"),
action: "lethality",
icon: "fas fa-skull-crossbones",
callback: () => "lethality",
},
],
});
}
+12 -1
View File
@@ -1,4 +1,5 @@
import { getDGRollToken } from "../../chat/dg-chat-card.js";
import { showDamageOrLethalityChoiceDialog } from "../../roll/roll-dialogs.js";
import { createDGRollFromDataset, processDGRoll } from "../../roll/roll.js";
/** @param {typeof foundry.applications.api.ApplicationV2} Base */
@@ -46,7 +47,17 @@ export default function RollSheetMixin(Base) {
}
const item = this.actor.items.get(target.dataset.iid);
const roll = createDGRollFromDataset(target.dataset, {
const rollDataset = { ...target.dataset };
if (rollDataset.rolltype === "damage-or-lethality") {
const choice = await showDamageOrLethalityChoiceDialog({
itemName: item?.name ?? "",
});
if (!choice) return;
rollDataset.rolltype = choice;
}
const roll = createDGRollFromDataset(rollDataset, {
actor: this.actor,
item,
element: target,
+20
View File
@@ -122,4 +122,24 @@ export default function registerHandlebarsHelpers() {
return result;
});
Handlebars.registerHelper("hasWeaponDamage", (damage) => {
const trimmed = String(damage ?? "").trim();
return trimmed !== "" && trimmed !== "0";
});
Handlebars.registerHelper("hasWeaponLethality", (lethality) => {
const value = Number(lethality);
return Number.isFinite(value) && value > 0;
});
Handlebars.registerHelper("hasWeaponDamageAndLethality", (item) => {
const damage = item?.system?.damage;
const lethality = item?.system?.lethality;
const trimmed = String(damage ?? "").trim();
const hasDamage = trimmed !== "" && trimmed !== "0";
const hasLethality =
Number.isFinite(Number(lethality)) && Number(lethality) > 0;
return hasDamage && hasLethality;
});
}
+21 -1
View File
@@ -177,6 +177,22 @@ export function appendRollTargetTooltipSection(
return `${existingTooltip}<br><br>${appendix}`;
}
/**
* @param {string} existingTooltip
* @param {string} [feature]
* @returns {string}
*/
export function appendDistinguishingFeatureSection(existingTooltip, feature) {
const trimmed = String(feature ?? "").trim();
if (!trimmed) return existingTooltip;
const label = game.i18n.localize("DG.EditStats.DistinguishingFeature");
const escaped = foundry.utils.escapeHTML(trimmed);
const section = `<br><br>${foundry.utils.escapeHTML(label)}: ${escaped}`;
if (!existingTooltip) return section;
return `${existingTooltip}${section}`;
}
/**
* Stat and sanity roll-target tooltips for the agent sheet left bar (not skills).
*
@@ -191,8 +207,12 @@ export function prepareAgentStatSanityTooltips(actor) {
for (const [key, stat] of Object.entries(system.statistics ?? {})) {
const base = Number(stat.x5) || 0;
const existing = game.i18n.localize(`DG.Attributes.Tooltip.${key}`);
stat.tooltip = appendRollTargetTooltipSection(
const withFeature = appendDistinguishingFeatureSection(
existing,
stat.distinguishing_feature,
);
stat.tooltip = appendRollTargetTooltipSection(
withFeature,
actor,
"system.rollTarget.statistics",
base,
+5 -1
View File
@@ -69,7 +69,9 @@
}
button.rollable.inline-roll {
display: inline;
display: inline-block;
width: max-content;
max-width: 100%;
margin: 0;
padding: 0;
border: none;
@@ -78,6 +80,8 @@ button.rollable.inline-roll {
color: var(--dg-ink);
line-height: inherit;
vertical-align: baseline;
text-align: inherit;
box-sizing: border-box;
> i {
color: inherit;
+64 -4
View File
@@ -1,6 +1,7 @@
.agent-sheet {
--dg-agent-section-gap: 8px;
--dg-agent-list-row-border: 1px solid rgba(33, 33, 33, 0.25);
--weapons-col-damage-trail: 60px;
.window-content {
display: grid;
@@ -794,10 +795,42 @@
.tab.combat .items-list .item:not(.item-header) {
display: grid;
grid-template-columns:
24px minmax(0, 1fr) minmax(4rem, auto) minmax(3rem, auto)
24px minmax(0, 1fr)
minmax(0, calc(4.25rem + var(--weapons-col-damage-trail)))
minmax(0, 3.5rem)
86px;
align-items: center;
column-gap: 5px;
column-gap: 10px;
}
.tab.combat .items-list .weapons-col-damage,
.tab.combat .items-list .weapons-col-armor-piercing {
display: flex;
justify-content: center;
align-items: center;
min-width: 0;
text-align: center;
white-space: normal;
line-height: 1.2;
overflow-wrap: break-word;
box-sizing: border-box;
}
.tab.combat .items-list .weapons-col-damage {
padding: 0 calc(2px + var(--weapons-col-damage-trail)) 0 2px;
}
.tab.combat .items-list .weapons-col-armor-piercing {
padding: 0 2px;
}
.tab.combat .items-list .weapons-col-damage[data-action="roll"],
.tab.gear .items-list .weapons-col-damage[data-action="roll"] {
cursor: pointer;
flex-wrap: nowrap;
white-space: nowrap;
overflow-wrap: normal;
gap: 0.15em;
}
.tab.combat .items-list .item:not(.item-header) > .flexrow {
@@ -840,8 +873,35 @@
margin-top: var(--dg-agent-section-gap);
}
.tab.gear .items-list .centered-item-property {
flex: 0 0 auto;
.tab.gear .items-list .weapons-col-damage {
flex: 0 0 calc(4.25rem + var(--weapons-col-damage-trail));
max-width: calc(4.25rem + var(--weapons-col-damage-trail));
}
.tab.gear .items-list .weapons-col-armor-piercing {
flex: 0 0 3.5rem;
max-width: 3.5rem;
}
.tab.gear .items-list .weapons-col-damage,
.tab.gear .items-list .weapons-col-armor-piercing {
display: flex;
justify-content: center;
align-items: center;
min-width: 0;
text-align: center;
white-space: normal;
line-height: 1.2;
overflow-wrap: break-word;
box-sizing: border-box;
}
.tab.gear .items-list .weapons-col-damage {
padding: 0 calc(2px + var(--weapons-col-damage-trail)) 0 2px;
}
.tab.gear .items-list .weapons-col-armor-piercing {
padding: 0 2px;
}
/* Personal tab */
+17
View File
@@ -205,6 +205,23 @@
}
}
.deltagreen[id^="dg-settings"] section.window-content {
color: var(--color-text-primary);
.form-group > label {
color: var(--color-form-label);
}
.hint {
color: var(--color-form-hint);
}
input[type="checkbox"]:checked::before,
input[type="radio"]:checked::before {
color: var(--color-text-primary);
}
}
a.btn-tiny {
height: 26px;
margin-left: 5px;
+4 -9
View File
@@ -5,8 +5,8 @@
/* Edit icon */
.stats-edit-btn {
position: absolute;
top: -10px;
right: 0;
top: 50%;
right: -12px;
width: 24px;
height: 24px;
@@ -26,12 +26,13 @@
transition:
opacity 0.15s ease,
transform 0.15s ease;
transform: translateY(-50%) scale(1);
}
/* Show on hover of whole stats block */
.stats-wrapper:hover .stats-edit-btn {
opacity: 1;
transform: scale(1.05);
transform: translateY(-50%) scale(1.05);
}
/* Hover feedback */
@@ -39,12 +40,6 @@
background: var(--color-border-highlight, #c9a227);
}
.edit-stat-form {
label {
color: var(--color-text-primary);
}
}
.physical-stats-outer-grid-2col {
display: grid;
grid-template-columns: 1fr 1fr;
+43
View File
@@ -885,3 +885,46 @@
padding: 0;
border: 0;
}
/* Edit statistics */
.dg-dialog--edit-stats .edit-stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.75rem 0.5rem;
}
.dg-dialog--edit-stats .edit-stats-stat-block {
display: flex;
flex-direction: column;
gap: 0.35rem;
min-width: 0;
}
.dg-dialog--edit-stats .edit-stats-value-row {
display: flex;
flex-direction: column;
gap: 0.25rem;
align-items: stretch;
}
.dg-dialog--edit-stats .edit-stats-value-row input {
width: 100%;
text-align: center;
}
.dg-dialog--edit-stats .edit-stats-feature-row {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.dg-dialog--edit-stats .edit-stats-feature-row textarea {
width: 100%;
min-height: 2.5rem;
resize: vertical;
}
/* Damage or lethality roll choice */
.dg-dialog--damage-or-lethality .damage-or-lethality-weapon {
margin: 0.5rem 0 0;
}
+20 -20
View File
@@ -1,26 +1,26 @@
<div class="grid grid-3col stats">
{{#each actor.system.statistics as |statistic key| }}
<div class="flex-group-center flex-mid-border">
<div>
<div class="physical-stats-outer-grid-2col">
<label data-tooltip="{{localize (concat 'DG.Attributes.Tooltip.' key)}}"
for="system.statistics.{{key}}.value">
{{localize (concat 'DG.Attributes.' key)}}
</label>
<div class="dg-dialog dg-dialog--edit-stats">
<div class="edit-stats-grid">
{{#each statKeys as |key| }}
{{#with (lookup @root.actor.system.statistics key) as |statistic| }}
<div class="edit-stats-stat-block">
<label class="edit-stats-value-row dg-dialog__field">
<span class="edit-stats-label dg-dialog__field-label">{{localize (concat 'DG.Attributes.' key)}}</span>
<input class="centered"
type="text"
type="number"
name="system.statistics.{{key}}.value"
value="{{lookup (lookup @root.sourceStatistics key) 'value'}}"
data-dtype="Number" />
</div>
<div>
<input type="text"
name="system.statistics.{{key}}.distinguishing_feature"
value="{{statistic.distinguishing_feature}}"
data-dtype="String"
title="{{localize 'DG.Tooltip.DistinguishingFeature'}}" />
</div>
data-dtype="Number"
min="0" />
</label>
<label class="edit-stats-feature-row dg-dialog__field">
<span class="dg-dialog__field-label">{{localize 'DG.EditStats.DistinguishingFeature'}}</span>
<textarea name="system.statistics.{{key}}.distinguishing_feature"
rows="2"
data-dtype="String"
title="{{localize 'DG.Tooltip.DistinguishingFeature'}}">{{statistic.distinguishing_feature}}</textarea>
</label>
</div>
{{/with}}
{{/each}}
</div>
{{/each}}
</div>
@@ -21,8 +21,8 @@
{{localize 'DG.Gear.Name'}}
<a data-action="browsePack" data-pack-type="weapon"><i class="fas fa-search"></i></a>
</span>
<span class="centered-item-property">{{localize 'DG.Gear.DamageOrLethality'}}</span>
<span class="centered-item-property">{{localize 'DG.Gear.ArmorPiercing'}}</span>
<span class="centered-item-property weapons-col-damage">{{localize 'DG.Gear.DamageOrLethality'}}</span>
<span class="centered-item-property weapons-col-armor-piercing">{{localize 'DG.Gear.ArmorPiercing'}}</span>
<div class="item-controls">
<a class="item-control action-pill"
data-action="itemAction"
@@ -48,65 +48,60 @@
data-rolltype="weapon"
data-key="{{item.system.skill}}"
title="{{localize 'DG.Tooltip.SkillLabel'}} ({{localizeWeaponSkill item.system.skill}})">
<i class="fas fa-dice" inert></i>
<i class="fas fa-dice" aria-hidden="true"></i>
</button>
</span>
</div>
<span class="centered-item-property">
{{#if item.system.lethality }}
{{#if item.system.isLethal }}
<a class="item-control dg-red"
data-action="toggle"
data-prop="system.isLethal"
data-item-id="{{item._id}}"
title="Toggle Lethality (Current Lethality: {{item.system.lethality}}%)">
<i class="fas fa-skull-crossbones"></i>
</a>
{{#if (hasWeaponDamageAndLethality item)}}
<span class="centered-item-property weapons-col-damage rollable"
data-action="roll"
data-rolltype="damage-or-lethality"
data-iid="{{item._id}}"
title="{{localize 'DG.Gear.DamageOrLethality'}}">
{{toUpperCase item.system.damage}}
{{#if_eq item.system.skill 'unarmed_combat' }}
{{actor.system.statistics.str.meleeDamageBonusFormula}}
{{/if_eq}}
{{#if_eq item.system.skill 'melee_weapons' }}
{{actor.system.statistics.str.meleeDamageBonusFormula}}
{{/if_eq}}
/ {{item.system.lethality}}%
<i class="fas fa-dice" aria-hidden="true"></i>
</span>
{{else}}
{{#if (hasWeaponDamage item.system.damage)}}
<span class="centered-item-property weapons-col-damage rollable"
data-action="roll"
data-rolltype="damage"
data-iid="{{item._id}}"
title="{{localize 'DG.Gear.DamageOrLethality'}}">
{{toUpperCase item.system.damage}}
{{#if_eq item.system.skill 'unarmed_combat' }}
{{actor.system.statistics.str.meleeDamageBonusFormula}}
{{/if_eq}}
{{#if_eq item.system.skill 'melee_weapons' }}
{{actor.system.statistics.str.meleeDamageBonusFormula}}
{{/if_eq}}
<i class="fas fa-dice" aria-hidden="true"></i>
</span>
{{else}}
{{#if (hasWeaponLethality item.system.lethality)}}
<span class="centered-item-property weapons-col-damage rollable"
data-action="roll"
data-rolltype="lethality"
data-iid="{{item._id}}"
title="{{localize 'DG.ItemWindow.Weapons.LethalityTooltip'}}">
{{item.system.lethality}}%
<i class="fas fa-dice" aria-hidden="true"></i>
</span>
{{else}}
<a class="item-control"
data-action="toggle"
data-prop="system.isLethal"
data-item-id="{{item._id}}"
title="Toggle Lethality (Currently No Lethality)">
<i class="fas fa-user-injured"></i>
</a>
<span class="centered-item-property weapons-col-damage"></span>
{{/if}}
{{/if}}
{{/if}}
{{#unless item.system.isLethal}}
<button type="button"
class="rollable inline-roll"
data-action="roll"
data-rolltype="damage"
data-iid="{{item._id}}"
title="{{localize 'DG.Gear.DamageOrLethality'}}">
{{toUpperCase item.system.damage}}
{{#if_eq item.system.skill 'unarmed_combat' }}
{{actor.system.statistics.str.meleeDamageBonusFormula}}
{{/if_eq}}
{{#if_eq item.system.skill 'melee_weapons' }}
{{actor.system.statistics.str.meleeDamageBonusFormula}}
{{/if_eq}}
<i class="fas fa-dice" inert></i>
</button>
{{/unless}}
{{#if item.system.isLethal }}
<button type="button"
class="rollable inline-roll"
data-action="roll"
data-rolltype="lethality"
data-iid="{{item._id}}"
title="{{localize 'DG.ItemWindow.Weapons.LethalityTooltip'}}">
{{item.system.lethality}}%
<i class="fas fa-dice" inert></i>
</button>
{{/if}}
</span>
<span class="centered-item-property">{{item.system.armorPiercing}}</span>
<span class="centered-item-property weapons-col-armor-piercing">{{item.system.armorPiercing}}</span>
<div class="item-controls">
<a class="item-control"
+1 -1
View File
@@ -16,7 +16,7 @@
</div>
<div class="stats-wrapper">
<a class="stats-edit-btn" title="{{localize 'Edit Stats'}}" data-action="openStatsEdit">
<a class="stats-edit-btn" title="{{localize 'DG.EditStats.Button'}}" data-action="openStatsEdit">
<i class="fas fa-pen"></i>
</a>
@@ -0,0 +1,6 @@
<div class="dg-dialog dg-dialog--damage-or-lethality">
<p class="dg-dialog__intro">{{localize "DG.DamageOrLethalityRoll.Intro"}}</p>
<p class="damage-or-lethality-weapon">
<strong>{{localize "DG.ModifyDamageRollDialogue.Weapon"}}</strong> {{itemName}}
</p>
</div>
+6 -10
View File
@@ -63,17 +63,13 @@
<span class="resource-label">{{localize 'DG.ItemWindow.Weapons.SkillModifier'}}:</span>
<input type="text" name="system.skillModifier" value="{{item.system.skillModifier}}" data-dtype="Number"/>
{{#unless item.system.isLethal}}
<span class="resource-label">{{localize 'DG.ItemWindow.Weapons.Damage'}}:</span>
<input type="text" name="system.damage" value="{{item.system.damage}}" data-dtype="String"/>
{{/unless}}
<span class="resource-label">{{localize 'DG.ItemWindow.Weapons.Damage'}}:</span>
<input type="text" name="system.damage" value="{{item.system.damage}}" data-dtype="String"/>
{{#if item.system.isLethal}}
<span class="resource-label">{{localize 'DG.ItemWindow.Weapons.Lethality'}} %:</span>
<input type="text" name="system.lethality" value="{{item.system.lethality}}" data-dtype="Number"
title="{{localize 'DG.ItemWindow.Weapons.LethalityTooltip'}}"
/>
{{/if}}
<span class="resource-label">{{localize 'DG.ItemWindow.Weapons.Lethality'}} %:</span>
<input type="text" name="system.lethality" value="{{item.system.lethality}}" data-dtype="Number"
title="{{localize 'DG.ItemWindow.Weapons.LethalityTooltip'}}"
/>
<span class="resource-label">{{localize 'DG.ItemWindow.Weapons.Ammo'}}:</span>
<input type="text" name="system.ammo" value="{{item.system.ammo}}" data-dtype="String"/>