From 41c3484904a761b76aedcc9db21074481133390c Mon Sep 17 00:00:00 2001 From: Ocame Date: Mon, 2 Mar 2026 12:02:33 +0100 Subject: [PATCH] Inital --- lang/de.json | 10 +++ lang/en.json | 9 +++ module.json | 39 +++++++++++ scripts/combat/Combat2d20.js | 87 +++++++++++++++++++++++ scripts/combat/CombatTracker2d20V2.js | 99 +++++++++++++++++++++++++++ scripts/dune.js | 10 +++ templates/tracker.hbs | 69 +++++++++++++++++++ 7 files changed, 323 insertions(+) create mode 100644 lang/de.json create mode 100644 lang/en.json create mode 100644 module.json create mode 100644 scripts/combat/Combat2d20.js create mode 100644 scripts/combat/CombatTracker2d20V2.js create mode 100644 scripts/dune.js create mode 100644 templates/tracker.hbs diff --git a/lang/de.json b/lang/de.json new file mode 100644 index 0000000..e7e4672 --- /dev/null +++ b/lang/de.json @@ -0,0 +1,10 @@ +{ + "duneinichecker": { + "combat": { + "ToggleCombatantsTurnDone": "Toggle Turn Completed", + "CombatantsTurnDone": "Turn Completed", + "CombatantsTurnNotDone": "Turn Not Completed" + } + } + +} diff --git a/lang/en.json b/lang/en.json new file mode 100644 index 0000000..42ad11d --- /dev/null +++ b/lang/en.json @@ -0,0 +1,9 @@ +{ + "duneinichecker": { + "combat": { + "ToggleCombatantsTurnDone": "Toggle Turn Completed", + "CombatantsTurnDone": "Turn Completed", + "CombatantsTurnNotDone": "Turn Not Completed" + } + } +} \ No newline at end of file diff --git a/module.json b/module.json new file mode 100644 index 0000000..094e032 --- /dev/null +++ b/module.json @@ -0,0 +1,39 @@ +{ + "id": "duneinichecker", + "title": "Dune - Ini Checker", + "version": "0.0.1", + "description": "", + "compatibility": { + "minimum": "13", + "verified": "13" + }, + "flags": { + "canUpload": true + }, + "relationships": { + "systems": [ + { + "id": "dune", + "type": "system", + "compatibility": {} + } + ] + }, + "esmodules": [ + "scripts/dune.js" + ], + "languages": [ + { + "lang": "en", + "name": "English", + "path": "lang/en.json", + "flags": {} + }, + { + "lang": "de", + "name": "Deutsch", + "path": "lang/de.json", + "flags": {} + } + ] +} \ No newline at end of file diff --git a/scripts/combat/Combat2d20.js b/scripts/combat/Combat2d20.js new file mode 100644 index 0000000..4adcc86 --- /dev/null +++ b/scripts/combat/Combat2d20.js @@ -0,0 +1,87 @@ +const KEY = "dune"; + +export default class Combat2d20 extends Combat { + + get combatantsTurnDone() { + return this.getFlag(KEY, "combatantsTurnDone") ?? []; + } + + + get combatantsTurnsDoneThisRound() { + const combatantsTurnDone = this.combatantsTurnDone; + return combatantsTurnDone[this.round] ?? {}; + } + + async rollInitiative() { + return this; + } + + async setTurn(newTurn) { + this.turn = newTurn; + + // Update the document, passing data through a hook first + const updateData = {round: this.round, turn: newTurn}; + const updateOptions = {advanceTime: CONFIG.time.turnTime, direction: 1}; + Hooks.callAll("combatTurn", this, updateData, updateOptions); + return this.update(updateData, updateOptions); + } + + + setupTurns() { + // Determine the turn order and the current turn + const turns = this.combatants.contents; + + // Sort alphabetically by name first + turns.sort((a, b) => { + if (a.name < b.name) { + return -1; + } + if (a.name > b.name) { + return 1; + } + return 0; + }); + + // Then make sure the player characters are first + turns.sort( + (a, b) => Number(b.hasPlayerOwner) - Number(a.hasPlayerOwner) + ); + + if (this.turn !== null) this.turn = + Math.clamp(this.turn, 0, turns.length - 1); + + // Update state tracking + let c = turns[this.turn]; + this.current = { + round: this.round, + turn: this.turn, + combatantId: c ? c.id : null, + tokenId: c ? c.tokenId : null, + }; + + // One-time initialization of the previous state + if (!this.previous) this.previous = this.current; + + // Return the array of prepared turns + return this.turns = turns; + } + + + async toggleTurnDone(combatantId) { + if (!game.user.isGM) return; + if (!this.started) return; + + const combatantsTurnsDoneThisRound = this.combatantsTurnsDoneThisRound; + + const turnDone = !(combatantsTurnsDoneThisRound[combatantId] ?? false); + combatantsTurnsDoneThisRound[combatantId] = turnDone; + + const combatantsTurnDone = this.combatantsTurnDone; + combatantsTurnDone[this.round] = combatantsTurnsDoneThisRound; + + this.setFlag(KEY, "combatantsTurnDone", combatantsTurnDone); + + + } + +} diff --git a/scripts/combat/CombatTracker2d20V2.js b/scripts/combat/CombatTracker2d20V2.js new file mode 100644 index 0000000..83ab74b --- /dev/null +++ b/scripts/combat/CombatTracker2d20V2.js @@ -0,0 +1,99 @@ +export default class CombatTracker2d20V2 + extends foundry.applications.sidebar.tabs.CombatTracker { + + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + actions: { + toggleCombatantTurnDone: CombatTracker2d20V2._onDuneCombatantControl, + }, + }; + + + /** @override */ + static PARTS = { + header: { + // We're still using the default Foundry template for this part + template: "templates/sidebar/tabs/combat/header.hbs", + }, + tracker: { + template: "modules/duneinichecker/templates/tracker.hbs", + }, + footer: { + // We're still using the default Foundry template for this part + template: "templates/sidebar/tabs/combat/footer.hbs", + }, + }; + + _onCombatantMouseDown(event, target) { + super._onCombatantMouseDown(event, target); + + const isInputElement = (event.target instanceof HTMLInputElement); + const isButtonElement = (event.target instanceof HTMLButtonElement); + + if (isInputElement || isButtonElement) return; + + if (game.user.isGM && this.viewed.started) { + const { combatantId } = target?.dataset ?? {}; + + const combat = this.viewed; + + const currentTurn = combat.turn ?? -1; + + let newTurn = currentTurn; + + for (let [i, turn] of combat.turns.entries() ) { + if (turn.isDefeated) continue; + if (turn.id === combatantId) { + newTurn = i; + break; + } + } + + if (newTurn !== currentTurn) { + combat.setTurn(newTurn); + } + } + } + + static async _onDuneCombatantControl(event, target) { + event.preventDefault(); + event.stopPropagation(); + + if (!game.user.isGM) return; + + if (!this.viewed.started) { + ui.notifications.warn( + game.i18n.localize("COMBAT.NotStarted") + ); + return; + } + + const { combatantId } = target.closest("[data-combatant-id]")?.dataset ?? {}; + const combatant = this.viewed?.combatants.get(combatantId); + if ( !combatant ) return; + + if (combatant.isOwner) { + this.viewed.toggleTurnDone(combatant.id); + } + } + + + /** + * Prepare render context for the tracker part. + * @param {ApplicationRenderContext} context + * @param {HandlebarsRenderOptions} options + * @returns {Promise} + * @protected + */ + async _prepareTrackerContext(context, options) { + await super._prepareTrackerContext(context, options); + + const combat = this.viewed; + if ( !combat ) return; + + const combatantsTurnDone = combat.combatantsTurnsDoneThisRound; + for (const turn of context.turns) { + turn.turnDone = combatantsTurnDone[turn.id] ?? false; + } + } +} diff --git a/scripts/dune.js b/scripts/dune.js new file mode 100644 index 0000000..3f4d1ec --- /dev/null +++ b/scripts/dune.js @@ -0,0 +1,10 @@ +// Wird ausgeführt, wenn Foundry geladen wurde, aber bevor das Spiel bereit ist + import Combat2d20 from "./combat/Combat2d20.js"; + import CombatTracker2d20V2 from "./combat/CombatTracker2d20V2.js"; + + +Hooks.once('init', () => { + + CONFIG.ui.combat = CombatTracker2d20V2; + CONFIG.Combat.documentClass = Combat2d20; +}); diff --git a/templates/tracker.hbs b/templates/tracker.hbs new file mode 100644 index 0000000..d07f1f2 --- /dev/null +++ b/templates/tracker.hbs @@ -0,0 +1,69 @@ +
    + {{#each turns}} +
  1. + {{!-- TODO: Targets --}} + + {{!-- Image --}} + {{ name }} + + {{!-- Name & Controls --}} +
    + {{ name }} +
    + {{#if @root.user.isGM}} + + + {{/if}} + {{#if canPing}} + + {{/if}} + {{#unless @root.user.isGM}} + + {{/unless}} + {{!-- TODO: Target Control --}} +
    + {{#each effects.icons}} + {{ name }} + {{/each}} +
    +
    +
    + + {{!-- Resource --}} + {{#if resource includeZero=true}} +
    + {{ resource }} +
    + {{/if}} + + {{!-- Turn Completed Toggle Button --}} +
    + + + +
    +
  2. + {{/each}} +