feat: add color-coded movement ruler for tactical gameplay
This commit is contained in:
parent
9e4dcee3c3
commit
7faadf6583
8 changed files with 145 additions and 1 deletions
49
scss/components/shared/_ruler.scss
Normal file
49
scss/components/shared/_ruler.scss
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Alexander Minges
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* Token Ruler Waypoint Styling with Color-Coded Movement Ranges */
|
||||
|
||||
:root {
|
||||
--ds4-movement-normal: inherit;
|
||||
--ds4-movement-dash: #ffcc00;
|
||||
--ds4-movement-impossible: #e83031;
|
||||
}
|
||||
|
||||
.system-ds4 .waypoint-label {
|
||||
.distance {
|
||||
&.move-range {
|
||||
color: var(--ds4-movement-normal);
|
||||
}
|
||||
|
||||
&.dash-range {
|
||||
color: var(--ds4-movement-dash);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&.out-of-range {
|
||||
color: var(--ds4-movement-impossible);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.delta {
|
||||
opacity: 0.8;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.elevation {
|
||||
font-style: italic;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark mode support */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--ds4-movement-dash: #ffd700;
|
||||
--ds4-movement-impossible: #ff4444;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
@import "components/shared/embedded_document_list";
|
||||
@import "components/shared/form_group";
|
||||
@import "components/shared/rollable_image";
|
||||
@import "components/shared/ruler";
|
||||
@import "components/shared/sheet_body";
|
||||
@import "components/shared/sheet_form";
|
||||
@import "components/shared/sheet_tab_nav";
|
||||
|
|
47
src/apps/ruler/token-ruler.js
Normal file
47
src/apps/ruler/token-ruler.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
// SPDX-FileCopyrightText: 2025 Alexander Minges
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* DS4 Token Ruler implementation with color-coded movement ranges
|
||||
* Based on actor movement combat value
|
||||
*/
|
||||
export class DS4TokenRuler extends foundry.canvas.placeables.tokens.TokenRuler {
|
||||
|
||||
static WAYPOINT_LABEL_TEMPLATE = "systems/ds4/templates/partials/waypoint-label.hbs";
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Enhance waypoint label context with movement range information
|
||||
* @param {object} waypoint - The waypoint data
|
||||
* @param {object} state - The current ruler state
|
||||
* @returns {object} Enhanced context with range class
|
||||
*/
|
||||
_getWaypointLabelContext(waypoint, state) {
|
||||
const context = super._getWaypointLabelContext(waypoint, state);
|
||||
|
||||
// Only apply movement coloring for distance measurements in meters
|
||||
if (context?.cost?.units === "m" || context?.distance?.units === "m") {
|
||||
const movement = this.token?.actor?.system?.combatValues?.movement?.total;
|
||||
|
||||
if (movement) {
|
||||
const total = Number(context.cost?.total || context.distance?.total || 0);
|
||||
|
||||
// DS4 movement rules:
|
||||
// - Normal movement: up to movement value
|
||||
// - Dash: up to 2x movement value (requires action)
|
||||
// - Beyond 2x: impossible in single turn
|
||||
if (total > 2 * movement) {
|
||||
context.rangeClass = "out-of-range";
|
||||
} else if (total <= movement) {
|
||||
context.rangeClass = "move-range";
|
||||
} else {
|
||||
context.rangeClass = "dash-range";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
|
@ -492,7 +492,7 @@ export class DS4Actor extends Actor {
|
|||
}),
|
||||
ok: {
|
||||
label: getGame().i18n.localize("DS4.GenericOkButton"),
|
||||
callback: (_event, button, _dialog) => {
|
||||
callback: (_event, button) => {
|
||||
const selectedAttribute = button.form.elements[attributeIdentifier].value;
|
||||
if (!isAttribute(selectedAttribute)) {
|
||||
throw new Error(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Johannes Loher
|
||||
// SPDX-FileCopyrightText: 2021 Oliver Rümpelein
|
||||
// SPDX-FileCopyrightText: 2021 Gesina Schwalbe
|
||||
// SPDX-FileCopyrightText: 2025 Alexander Minges
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
|
@ -55,6 +56,7 @@ export async function registerHandlebarsPartials() {
|
|||
"systems/ds4/templates/sheets/shared/components/add-button.hbs",
|
||||
"systems/ds4/templates/sheets/shared/components/control-button-group.hbs",
|
||||
"systems/ds4/templates/sheets/shared/components/rollable-image.hbs",
|
||||
"systems/ds4/templates/partials/waypoint-label.hbs",
|
||||
];
|
||||
await foundry.applications.handlebars.loadTemplates(templatePaths);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import { registerForReadyHook } from "./ready";
|
|||
import { registerForRenderHooks } from "./render";
|
||||
import { registerForSetupHook } from "./setup";
|
||||
|
||||
|
||||
|
||||
export function registerForHooks(): void {
|
||||
registerForHotbarDropHook();
|
||||
registerForPreCreateItemHook();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Johannes Loher
|
||||
// SPDX-FileCopyrightText: 2021 Oliver Rümpelein
|
||||
// SPDX-FileCopyrightText: 2021 Gesina Schwalbe
|
||||
// SPDX-FileCopyrightText: 2025 Alexander Minges
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
|
@ -8,6 +9,7 @@ import { DS4ActiveEffectConfig } from "../apps/active-effect-config";
|
|||
import { DS4CharacterActorSheet } from "../apps/actor/character-sheet";
|
||||
import { DS4CreatureActorSheet } from "../apps/actor/creature-sheet";
|
||||
import { DS4ItemSheet } from "../apps/item-sheet";
|
||||
import { DS4TokenRuler } from "../apps/ruler/token-ruler";
|
||||
import { DS4 } from "../config";
|
||||
import { DS4Check } from "../dice/check";
|
||||
import { createCheckRoll } from "../dice/check-factory";
|
||||
|
@ -51,6 +53,8 @@ async function init() {
|
|||
CONFIG.ChatMessage.documentClass = DS4ChatMessage;
|
||||
CONFIG.Token.documentClass = DS4TokenDocument;
|
||||
|
||||
CONFIG.Token.rulerClass = DS4TokenRuler;
|
||||
|
||||
CONFIG.ActiveEffect.legacyTransferral = false;
|
||||
|
||||
CONFIG.Actor.typeLabels = DS4.i18n.actorTypes;
|
||||
|
|
39
templates/partials/waypoint-label.hbs
Normal file
39
templates/partials/waypoint-label.hbs
Normal file
|
@ -0,0 +1,39 @@
|
|||
{{!--
|
||||
SPDX-FileCopyrightText: 2025 Alexander Minges
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
--}}
|
||||
|
||||
{{!--
|
||||
!-- Waypoint label template with color-coded movement ranges
|
||||
!-- Based on DS4 movement combat values
|
||||
--}}
|
||||
<div class="waypoint-label {{rangeClass}}">
|
||||
{{#if action.icon}}
|
||||
<i class="{{action.icon}}"></i>
|
||||
{{else if action.label}}
|
||||
{{localize action.label}}
|
||||
{{/if}}
|
||||
{{#if cost}}
|
||||
<span class="distance {{rangeClass}}">{{cost.total}} {{cost.units}}</span>
|
||||
{{#if cost.delta}}
|
||||
<span class="delta">({{cost.delta}})</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<span class="distance {{rangeClass}}">{{distance.total}} {{units}}</span>
|
||||
{{#if distance.delta}}
|
||||
<span class="delta">({{distance.delta}})</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if (and elevation (not elevation.hidden))}}
|
||||
<span class="elevation">
|
||||
{{elevation.total}} {{units}}
|
||||
{{#if elevation.delta}}
|
||||
({{elevation.delta}})
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
{{#if secret}}
|
||||
<i class="fas fa-eye-slash"></i>
|
||||
{{/if}}
|
||||
</div>
|
Loading…
Add table
Add a link
Reference in a new issue