mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
Initial commit
This commit is contained in:
commit
aa4021d1a2
163 changed files with 26530 additions and 0 deletions
103
module/ui/chatLog.mjs
Normal file
103
module/ui/chatLog.mjs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog {
|
||||
constructor(){
|
||||
super();
|
||||
|
||||
this.targetTemplate = {
|
||||
activeLayer: undefined,
|
||||
document: undefined,
|
||||
object: undefined,
|
||||
minimizedSheets: [],
|
||||
config: undefined,
|
||||
targets: undefined
|
||||
}
|
||||
this.setupHooks();
|
||||
}
|
||||
|
||||
addChatListeners = async (app, html, data) => {
|
||||
html.querySelectorAll('.roll-damage-button').forEach(element => element.addEventListener('click', event => this.onRollDamage(event, data.message)));
|
||||
html.querySelectorAll('.target-container').forEach(element => element.addEventListener('hover', hover(this.hoverTarget, this.unhoverTarget))); // ????
|
||||
// html.find('.target-container').mouseout(this.unhoverTarget);
|
||||
html.querySelectorAll('.damage-button').forEach(element => element.addEventListener('click', this.onDamage));
|
||||
html.querySelectorAll('.healing-button').forEach(element => element.addEventListener('click', this.onHealing));
|
||||
html.querySelectorAll('.target-indicator').forEach(element => element.addEventListener('click', this.onToggleTargets));
|
||||
html.querySelectorAll('.advantage').forEach(element => element.hover(this.hoverAdvantage)); // ??
|
||||
html.querySelectorAll('.advantage').forEach(element => element.addEventListener('click', event => this.selectAdvantage.bind(this)(event, data.message)));
|
||||
html.querySelectorAll('.ability-use-button').forEach(element => element.addEventListener('click', this.abilityUseButton.bind(this)(event, data.message)));
|
||||
}
|
||||
|
||||
setupHooks(){
|
||||
Hooks.on('renderChatMessageHTML', this.addChatListeners.bind());
|
||||
}
|
||||
|
||||
close(options){
|
||||
Hooks.off('renderChatMessageHTML', this.addChatListeners);
|
||||
super.close(options);
|
||||
}
|
||||
|
||||
onRollDamage = async (event, message) => {
|
||||
event.stopPropagation();
|
||||
|
||||
await game.user.character.damageRoll(message.system.damage, event.shiftKey);
|
||||
};
|
||||
|
||||
hoverTarget = (event) => {
|
||||
event.stopPropagation();
|
||||
const token = canvas.tokens.get(event.currentTarget.dataset.token);
|
||||
if ( !token.controlled ) token._onHoverIn(event, {hoverOutOthers: true});
|
||||
}
|
||||
|
||||
unhoverTarget = (event) => {
|
||||
const token = canvas.tokens.get(event.currentTarget.dataset.token);
|
||||
if ( !token.controlled ) token._onHoverOut(event);
|
||||
};
|
||||
|
||||
onDamage = async (event) => {
|
||||
event.stopPropagation();
|
||||
const damage = Number.parseInt(event.currentTarget.dataset.value);
|
||||
const targets = Array.from(game.user.targets);
|
||||
|
||||
if(targets.length === 0) ui.notifications.info(game.i18n.localize("DAGGERHEART.Notification.Info.NoTargetsSelected"));
|
||||
|
||||
for(var target of targets){
|
||||
await target.actor.takeDamage(damage, event.currentTarget.dataset.type);
|
||||
}
|
||||
};
|
||||
|
||||
onHealing = async (event) => {
|
||||
event.stopPropagation();
|
||||
const healing = Number.parseInt(event.currentTarget.dataset.value);
|
||||
const targets = Array.from(game.user.targets);
|
||||
|
||||
if(targets.length === 0) ui.notifications.info(game.i18n.localize("DAGGERHEART.Notification.Info.NoTargetsSelected"));
|
||||
|
||||
for(var target of targets){
|
||||
await target.actor.takeHealing(healing, event.currentTarget.dataset.type);
|
||||
}
|
||||
}
|
||||
|
||||
onToggleTargets = async (event) => {
|
||||
event.stopPropagation();
|
||||
$($(event.currentTarget).parent()).find('.target-container').toggleClass('hidden');
|
||||
};
|
||||
|
||||
hoverAdvantage = (event) => {
|
||||
$(event.currentTarget).siblings('.advantage').toggleClass('unused');
|
||||
};
|
||||
|
||||
selectAdvantage = async (event, message) => {
|
||||
event.stopPropagation();
|
||||
|
||||
const updateMessage = game.messages.get(message._id);
|
||||
await updateMessage.update({ system: { advantageSelected: event.currentTarget.id === 'hope' ? 1 : 2 }});
|
||||
|
||||
$(event.currentTarget).siblings('.advantage').off('click');
|
||||
$(event.currentTarget).off('click');
|
||||
}
|
||||
|
||||
abilityUseButton = async (event, message) => {
|
||||
event.stopPropagation();
|
||||
|
||||
const action = message.system.actions[Number.parseInt(event.currentTarget.dataset.index)];
|
||||
await game.user.character.useAction(action);
|
||||
}
|
||||
}
|
||||
200
module/ui/combatTracker.mjs
Normal file
200
module/ui/combatTracker.mjs
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
import { GMUpdateEvent, socketEvent } from "../helpers/socket.mjs";
|
||||
|
||||
export default class DhpCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
||||
constructor(data, context) {
|
||||
super(data, context);
|
||||
|
||||
Hooks.on(socketEvent.DhpFearUpdate, this.onFearUpdate);
|
||||
}
|
||||
|
||||
get template(){
|
||||
return 'systems/daggerheart/templates/ui/combatTracker.hbs';
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
html.on("click", ".token-action-tokens .use-action-token", this.useActionToken.bind(this));
|
||||
html.on("click", ".encounter-gm-resources .trade-actions", this.tradeActions.bind(this));
|
||||
html.on("click", ".encounter-gm-resources .trade-fear", this.tradeFear.bind(this));
|
||||
html.on("click", ".encounter-gm-resources .icon-button.up", this.increaseResource.bind(this));
|
||||
html.on("click", ".encounter-gm-resources .icon-button.down", this.decreaseResource.bind(this));
|
||||
}
|
||||
|
||||
async useActionToken(event){
|
||||
event.stopPropagation();
|
||||
const combatant = event.currentTarget.dataset.combatant;
|
||||
await game.combat.useActionToken(combatant);
|
||||
}
|
||||
|
||||
async tradeActions(event){
|
||||
if(event.currentTarget.classList.contains('disabled')) return;
|
||||
|
||||
const currentFear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
||||
const value = currentFear+1;
|
||||
|
||||
if(value <= 6){
|
||||
Hooks.callAll(socketEvent.GMUpdate,GMUpdateEvent.UpdateFear, null, value);
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: { action: GMUpdateEvent.UpdateFear, update: value },
|
||||
});
|
||||
await game.combat.update({ "system.actions": game.combat.system.actions-2 });
|
||||
}
|
||||
}
|
||||
|
||||
async tradeFear(){
|
||||
if(event.currentTarget.classList.contains('disabled')) return;
|
||||
|
||||
const currentFear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
||||
const value = currentFear-1;
|
||||
if(value >= 0){
|
||||
Hooks.callAll(socketEvent.GMUpdate,GMUpdateEvent.UpdateFear, null, value);
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: { action: GMUpdateEvent.UpdateFear, update: value },
|
||||
});
|
||||
await game.combat.update({ "system.actions": game.combat.system.actions+2 });
|
||||
}
|
||||
}
|
||||
|
||||
async increaseResource(event) {
|
||||
if(event.currentTarget.dataset.type === 'action'){
|
||||
await game.combat.update({ "system.actions": game.combat.system.actions+1 });
|
||||
}
|
||||
|
||||
const currentFear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
||||
const value = currentFear+1;
|
||||
if(event.currentTarget.dataset.type === 'fear' && value <= 6){
|
||||
Hooks.callAll(socketEvent.GMUpdate,GMUpdateEvent.UpdateFear, null, value);
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: { action: GMUpdateEvent.UpdateFear, update: value },
|
||||
});
|
||||
}
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
async decreaseResource(event) {
|
||||
if(event.currentTarget.dataset.type === 'action' && game.combat.system.actions-1 >= 0){
|
||||
await game.combat.update({ "system.actions": game.combat.system.actions-1 });
|
||||
}
|
||||
|
||||
const currentFear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
||||
const value = currentFear-1;
|
||||
if(event.currentTarget.dataset.type === 'fear' && value >= 0){
|
||||
Hooks.callAll(socketEvent.GMUpdate,GMUpdateEvent.UpdateFear, null, value);
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: { action: GMUpdateEvent.UpdateFear, update: value },
|
||||
});
|
||||
}
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
async getData(options={}) {
|
||||
let context = await super.getData(options);
|
||||
|
||||
// Get the combat encounters possible for the viewed Scene
|
||||
const combat = this.viewed;
|
||||
const hasCombat = combat !== null;
|
||||
const combats = this.combats;
|
||||
const currentIdx = combats.findIndex(c => c === combat);
|
||||
const previousId = currentIdx > 0 ? combats[currentIdx-1].id : null;
|
||||
const nextId = currentIdx < combats.length - 1 ? combats[currentIdx+1].id : null;
|
||||
const settings = game.settings.get("core", Combat.CONFIG_SETTING);
|
||||
|
||||
// Prepare rendering data
|
||||
context = foundry.utils.mergeObject(context, {
|
||||
combats: combats,
|
||||
currentIndex: currentIdx + 1,
|
||||
combatCount: combats.length,
|
||||
hasCombat: hasCombat,
|
||||
combat,
|
||||
turns: [],
|
||||
previousId,
|
||||
nextId,
|
||||
started: this.started,
|
||||
control: false,
|
||||
settings,
|
||||
linked: combat?.scene !== null,
|
||||
labels: {}
|
||||
});
|
||||
context.labels.scope = game.i18n.localize(`COMBAT.${context.linked ? "Linked" : "Unlinked"}`);
|
||||
if ( !hasCombat ) return context;
|
||||
|
||||
// Format information about each combatant in the encounter
|
||||
let hasDecimals = false;
|
||||
const turns = [];
|
||||
for ( let [i, combatant] of combat.turns.entries() ) {
|
||||
if ( !combatant.visible ) continue;
|
||||
|
||||
// Prepare turn data
|
||||
const resource = combatant.permission >= CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER ? combatant.resource : null;
|
||||
const turn = {
|
||||
id: combatant.id,
|
||||
name: combatant.name,
|
||||
img: await this._getCombatantThumbnail(combatant),
|
||||
active: combatant.id === combat.system.activeCombatant,
|
||||
owner: combatant.isOwner,
|
||||
defeated: combatant.isDefeated,
|
||||
hidden: combatant.hidden,
|
||||
initiative: combatant.initiative,
|
||||
hasRolled: combatant.initiative !== null,
|
||||
hasResource: resource !== null,
|
||||
resource: resource,
|
||||
canPing: (combatant.sceneId === canvas.scene?.id) && game.user.hasPermission("PING_CANVAS"),
|
||||
playerCharacter: game.user?.character?.id === combatant.actor.id,
|
||||
ownedByPlayer: combatant.hasPlayerOwner,
|
||||
};
|
||||
if ( (turn.initiative !== null) && !Number.isInteger(turn.initiative) ) hasDecimals = true;
|
||||
turn.css = [
|
||||
turn.active ? "active" : "",
|
||||
turn.hidden ? "hidden" : "",
|
||||
turn.defeated ? "defeated" : ""
|
||||
].join(" ").trim();
|
||||
|
||||
// Actor and Token status effects
|
||||
turn.effects = new Set();
|
||||
if ( combatant.token ) {
|
||||
combatant.token.effects.forEach(e => turn.effects.add(e));
|
||||
if ( combatant.token.overlayEffect ) turn.effects.add(combatant.token.overlayEffect);
|
||||
}
|
||||
if ( combatant.actor ) {
|
||||
for ( const effect of combatant.actor.temporaryEffects ) {
|
||||
if ( effect.statuses.has(CONFIG.specialStatusEffects.DEFEATED) ) turn.defeated = true;
|
||||
else if ( effect.icon ) turn.effects.add(effect.icon);
|
||||
}
|
||||
}
|
||||
turns.push(turn);
|
||||
}
|
||||
|
||||
// Format initiative numeric precision
|
||||
const precision = CONFIG.Combat.initiative.decimals;
|
||||
turns.forEach(t => {
|
||||
if ( t.initiative !== null ) t.initiative = t.initiative.toFixed(hasDecimals ? precision : 0);
|
||||
});
|
||||
|
||||
const fear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
||||
|
||||
// Merge update data for rendering
|
||||
return foundry.utils.mergeObject(context, {
|
||||
round: combat.round,
|
||||
turn: combat.turn,
|
||||
turns: turns,
|
||||
control: combat.combatant?.players?.includes(game.user),
|
||||
fear: fear,
|
||||
});
|
||||
}
|
||||
|
||||
onFearUpdate = async () => {
|
||||
this.render(true);
|
||||
}
|
||||
|
||||
async close(options){
|
||||
Hooks.off(socketEvent.DhpFearUpdate, this.onFearUpdate);
|
||||
|
||||
return super.close(options);
|
||||
}
|
||||
}
|
||||
53
module/ui/players.mjs
Normal file
53
module/ui/players.mjs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { GMUpdateEvent, socketEvent } from "../helpers/socket.mjs";
|
||||
|
||||
export default class DhpPlayers extends foundry.applications.ui.Players {
|
||||
constructor(data, context) {
|
||||
super(data, context);
|
||||
|
||||
Hooks.on(socketEvent.DhpFearUpdate, this.onFearUpdate);
|
||||
}
|
||||
|
||||
get template(){
|
||||
return 'systems/daggerheart/templates/ui/players.hbs';
|
||||
}
|
||||
|
||||
async getData(options={}) {
|
||||
const context = super.getData(options);
|
||||
context.fear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
||||
context.user = game.user;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
// Toggle online/offline
|
||||
html.find(".players-mode").click(this._onToggleOfflinePlayers.bind(this));
|
||||
html.find(".fear-control.up").click(async event => await this.updateFear(event, 1));
|
||||
html.find(".fear-control.down").click(async event => await this.updateFear(event, -1));
|
||||
|
||||
// Context menu
|
||||
const contextOptions = this._getUserContextOptions();
|
||||
Hooks.call("getUserContextOptions", html, contextOptions);
|
||||
new ContextMenu(html, ".player", contextOptions);
|
||||
}
|
||||
|
||||
async updateFear(_, change){
|
||||
const fear = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear);
|
||||
const value = Math.max(Math.min(fear+change, 6), 0);
|
||||
Hooks.callAll(socketEvent.GMUpdate,GMUpdateEvent.UpdateFear, null, value);
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: { action: GMUpdateEvent.UpdateFear, update: value },
|
||||
});
|
||||
}
|
||||
|
||||
onFearUpdate = async () => {
|
||||
this.render(true);
|
||||
}
|
||||
|
||||
async close(options){
|
||||
Hooks.off(socketEvent.DhpFearUpdate, this.onFearUpdate);
|
||||
|
||||
return super.close(options);
|
||||
}
|
||||
}
|
||||
29
module/ui/ruler.mjs
Normal file
29
module/ui/ruler.mjs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
export default class DhpRuler extends foundry.canvas.interaction.Ruler {
|
||||
_getSegmentLabel(segment, totalDistance) {
|
||||
const range = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.RangeMeasurement);
|
||||
if(!range.enabled) return super._getSegmentLabel(segment, totalDistance);
|
||||
|
||||
const segmentDistance = Math.round(segment.distance * 100) / 100;
|
||||
const totalDistanceValue = Math.round(totalDistance * 100) / 100;
|
||||
|
||||
return `${this.#getRangeLabel(segmentDistance, range)} [${this.#getRangeLabel(totalDistanceValue, range)}]`;
|
||||
}
|
||||
|
||||
#getRangeLabel(distance, settings){
|
||||
if(distance <= settings.melee){
|
||||
return game.i18n.localize("DAGGERHEART.Range.Melee.Name");
|
||||
}
|
||||
if(distance <= settings.veryClose){
|
||||
return game.i18n.localize("DAGGERHEART.Range.VeryClose.Name");
|
||||
}
|
||||
if(distance <= settings.close){
|
||||
return game.i18n.localize("DAGGERHEART.Range.Close.Name");
|
||||
}
|
||||
if(distance <= settings.far){
|
||||
return game.i18n.localize("DAGGERHEART.Range.Far.Name");
|
||||
}
|
||||
if(distance <= settings.veryFar){
|
||||
return game.i18n.localize("DAGGERHEART.Range.VeryFar.Name");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue