Inital
This commit is contained in:
10
lang/de.json
Normal file
10
lang/de.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"duneinichecker": {
|
||||||
|
"combat": {
|
||||||
|
"ToggleCombatantsTurnDone": "Toggle Turn Completed",
|
||||||
|
"CombatantsTurnDone": "Turn Completed",
|
||||||
|
"CombatantsTurnNotDone": "Turn Not Completed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
9
lang/en.json
Normal file
9
lang/en.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"duneinichecker": {
|
||||||
|
"combat": {
|
||||||
|
"ToggleCombatantsTurnDone": "Toggle Turn Completed",
|
||||||
|
"CombatantsTurnDone": "Turn Completed",
|
||||||
|
"CombatantsTurnNotDone": "Turn Not Completed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
module.json
Normal file
39
module.json
Normal file
@ -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": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
87
scripts/combat/Combat2d20.js
Normal file
87
scripts/combat/Combat2d20.js
Normal file
@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
99
scripts/combat/CombatTracker2d20V2.js
Normal file
99
scripts/combat/CombatTracker2d20V2.js
Normal file
@ -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<void>}
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
scripts/dune.js
Normal file
10
scripts/dune.js
Normal file
@ -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;
|
||||||
|
});
|
||||||
69
templates/tracker.hbs
Normal file
69
templates/tracker.hbs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<ol class="combat-tracker plain">
|
||||||
|
{{#each turns}}
|
||||||
|
<li class="combatant {{ css }}" data-combatant-id="{{ id }}" data-action="activateCombatant">
|
||||||
|
{{!-- TODO: Targets --}}
|
||||||
|
|
||||||
|
{{!-- Image --}}
|
||||||
|
<img class="token-image" src="{{ img }}" alt="{{ name }}" loading="lazy">
|
||||||
|
|
||||||
|
{{!-- Name & Controls --}}
|
||||||
|
<div class="token-name">
|
||||||
|
<strong class="name">{{ name }}</strong>
|
||||||
|
<div class="combatant-controls">
|
||||||
|
{{#if @root.user.isGM}}
|
||||||
|
<button type="button" class="inline-control combatant-control icon fa-solid fa-eye-slash {{#if hidden}}active{{/if}}"
|
||||||
|
data-action="toggleHidden" data-tooltip aria-label="{{ localize "COMBAT.ToggleVis" }}"></button>
|
||||||
|
<button type="button" class="inline-control combatant-control icon fa-solid fa-skull {{#if isDefeated}}active{{/if}}"
|
||||||
|
data-action="toggleDefeated" data-tooltip
|
||||||
|
aria-label="{{ localize "COMBAT.ToggleDead" }}"></button>
|
||||||
|
{{/if}}
|
||||||
|
{{#if canPing}}
|
||||||
|
<button type="button" class="inline-control combatant-control icon fa-solid fa-bullseye-arrow"
|
||||||
|
data-action="pingCombatant" data-tooltip
|
||||||
|
aria-label="{{ localize "COMBAT.PingCombatant" }}"></button>
|
||||||
|
{{/if}}
|
||||||
|
{{#unless @root.user.isGM}}
|
||||||
|
<button type="button" class="inline-control combatant-control icon fa-solid fa-arrows-to-eye"
|
||||||
|
data-action="panToCombatant" data-tooltip
|
||||||
|
aria-label="{{ localize "COMBAT.PanToCombatant" }}"></button>
|
||||||
|
{{/unless}}
|
||||||
|
{{!-- TODO: Target Control --}}
|
||||||
|
<div class="token-effects" data-tooltip-html="{{ effects.tooltip }}">
|
||||||
|
{{#each effects.icons}}
|
||||||
|
<img class="token-effect" src="{{ img }}" alt="{{ name }}">
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Resource --}}
|
||||||
|
{{#if resource includeZero=true}}
|
||||||
|
<div class="token-resource">
|
||||||
|
<span class="resource">{{ resource }}</span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{!-- Turn Completed Toggle Button --}}
|
||||||
|
<div class="token-turn-completed">
|
||||||
|
<a
|
||||||
|
class="combatant-control"
|
||||||
|
{{#if turnDone}}
|
||||||
|
style="color: var(--color-text-subtle);"
|
||||||
|
{{/if}}
|
||||||
|
{{#if @root.user.isGM}}
|
||||||
|
data-tooltip="{{localize 'DUNE.combat.ToggleCombatantsTurnDone'}}"
|
||||||
|
{{else}}
|
||||||
|
{{#if turnDone}}
|
||||||
|
data-tooltip="{{localize 'DUNE.combat.CombatantsTurnDone'}}"
|
||||||
|
{{else}}
|
||||||
|
data-tooltip="{{localize 'DUNE.combat.CombatantsTurnNotDone'}}"
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
data-action="toggleCombatantTurnDone"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-circle-check fa-xl"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ol>
|
||||||
Reference in New Issue
Block a user