Merged with main

This commit is contained in:
WBHarry 2025-07-22 14:36:35 +02:00
commit 480d04fee5
784 changed files with 13985 additions and 27621 deletions

View file

@ -2,3 +2,4 @@ export { default as DhChatLog } from './chatLog.mjs';
export { default as DhCombatTracker } from './combatTracker.mjs';
export * as DhCountdowns from './countdowns.mjs';
export { default as DhFearTracker } from './fearTracker.mjs';
export { default as DhHotbar } from './hotbar.mjs';

View file

@ -1,6 +1,6 @@
export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog {
constructor() {
super();
constructor(options) {
super(options);
this.targetTemplate = {
activeLayer: undefined,
@ -83,15 +83,15 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
actor.system.attack?._id === actionId
? actor.system.attack
: item.system.attack?._id === actionId
? item.system.attack
: item?.system?.actions?.find(a => a._id === actionId);
? item.system.attack
: item?.system?.actions?.find(a => a._id === actionId);
return action;
}
onRollDamage = async (event, message) => {
event.stopPropagation();
const actor = await this.getActor(message.system.source.actor);
if (!actor || !game.user.isGM) return true;
if (game.user.character?.id !== actor.id && !game.user.isGM) return true;
if (message.system.source.item && message.system.source.action) {
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
if (!action || !action?.rollDamage) return;
@ -190,7 +190,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.attackTargetDoesNotExist'));
return;
}
game.canvas.pan(token);
};
@ -212,13 +211,25 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
}
if (targets.length === 0)
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
for (let target of targets) {
let damage = message.system.roll.total;
if (message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true)
damage = Math.ceil(damage * (CONFIG.DH.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1));
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
await target.actor.takeDamage(damage, message.system.roll.type);
for (let target of targets) {
let damages = foundry.utils.deepClone(message.system.damage?.roll ?? message.system.roll);
if (
message.system.onSave &&
message.system.targets.find(t => t.id === target.id)?.saved?.success === true
) {
const mod = CONFIG.DH.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1;
Object.entries(damages).forEach(([k, v]) => {
v.total = 0;
v.parts.forEach(part => {
part.total = Math.ceil(part.total * mod);
v.total += part.total;
});
});
}
target.actor.takeDamage(damages);
}
};
@ -227,10 +238,10 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
const targets = Array.from(game.user.targets);
if (targets.length === 0)
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
for (var target of targets) {
await target.actor.takeHealing([{ value: message.system.roll.total, type: message.system.roll.type }]);
target.actor.takeHealing(message.system.roll);
}
};
@ -289,53 +300,54 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
await actor.useAction(action);
};
actionUseButton = async (_, message) => {
actionUseButton = async (event, message) => {
const { moveIndex, actionIndex } = event.currentTarget.dataset;
const parent = await foundry.utils.fromUuid(message.system.actor);
const actionType = Object.values(message.system.moves)[0].actions[0];
const cls = CONFIG.DH.ACTIONS.actionTypes[actionType.type];
const actionType = message.system.moves[moveIndex].actions[actionIndex];
const cls = game.system.api.models.actions.actionsTypes[actionType.type];
const action = new cls(
{ ...actionType, _id: foundry.utils.randomID(), name: game.i18n.localize(actionType.name) },
{ parent: parent }
{ parent: parent.system }
);
action.use();
action.use(event);
};
//Reroll Functionality
rerollEvent = async(event,message)=> {
let DieTerm=foundry.dice.terms.Die;
rerollEvent = async (event, message) => {
let DieTerm = foundry.dice.terms.Die;
let dicetype = event.target.value;
let originalRoll_parsed=message.rolls.map(roll => JSON.parse(roll))[0];
console.log("Parsed Map:",originalRoll_parsed);
let originalRoll=Roll.fromData(originalRoll_parsed);
let originalRoll_parsed = message.rolls.map(roll => JSON.parse(roll))[0];
console.log('Parsed Map:', originalRoll_parsed);
let originalRoll = Roll.fromData(originalRoll_parsed);
let diceIndex;
console.log("Dice to reroll is:",dicetype,", and the message id is:",message._id,originalRoll_parsed);
console.log("Original Roll Terms:",originalRoll.terms);
switch(dicetype){
case "hope": {
diceIndex=0; //Hope Die
console.log('Dice to reroll is:', dicetype, ', and the message id is:', message._id, originalRoll_parsed);
console.log('Original Roll Terms:', originalRoll.terms);
switch (dicetype) {
case 'hope': {
diceIndex = 0; //Hope Die
break;
};
case "fear" :{
diceIndex=2; //Fear Die
}
case 'fear': {
diceIndex = 2; //Fear Die
break;
};
default:
ui.notifications.warn("Invalid Dice type selected.");
}
default:
ui.notifications.warn('Invalid Dice type selected.');
break;
}
let rollClone=originalRoll.clone();
let rerolledTerm=originalRoll.terms[diceIndex];
console.log("originalRoll:",originalRoll,"rerolledTerm",rerolledTerm);
let rollClone = originalRoll.clone();
let rerolledTerm = originalRoll.terms[diceIndex];
console.log('originalRoll:', originalRoll, 'rerolledTerm', rerolledTerm);
if (!(rerolledTerm instanceof DieTerm)) {
ui.notifications.error("Selected term is not a die.");
ui.notifications.error('Selected term is not a die.');
return;
}
await rollClone.reroll({allowStrings:true})[diceIndex];
await rollClone.reroll({ allowStrings: true })[diceIndex];
console.log(rollClone);
await rollClone.evaluate({allowStrings:true});
await rollClone.evaluate({ allowStrings: true });
console.log(rollClone.result);
/*
/*
const confirm = await foundry.applications.api.DialogV2.confirm({
window: { title: 'Confirm Reroll' },
content: `<p>You have rerolled your <strong>${dicetype}</strong> die to <strong>${rollClone.result}</strong>.</p><p>Apply this new roll?</p>`
@ -343,5 +355,5 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
if (!confirm) return;
rollClone.toMessage({flavor: 'Selective reroll applied for ${dicetype}.'});
console.log("Updated Roll",rollClone);*/
}
};
}

View file

@ -66,6 +66,11 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
}
async setCombatantSpotlight(combatantId) {
const update = {
system: {
'spotlight.requesting': false
}
};
const combatant = this.viewed.combatants.get(combatantId);
const toggleTurn = this.viewed.combatants.contents
@ -73,10 +78,18 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
.map(x => x.id)
.indexOf(combatantId);
if (this.viewed.turn !== toggleTurn) Hooks.callAll(CONFIG.DH.HOOKS.spotlight, {});
if (this.viewed.turn !== toggleTurn) {
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
await updateCountdowns(CONFIG.DH.GENERAL.countdownTypes.spotlight.id);
const autoPoints = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).actionPoints;
if (autoPoints) {
update.system.actionTokens = Math.max(combatant.system.actionTokens - 1, 0);
}
}
await this.viewed.update({ turn: this.viewed.turn === toggleTurn ? null : toggleTurn });
await combatant.update({ 'system.spotlight.requesting': false });
await combatant.update(update);
}
static async requestSpotlight(_, target) {

View file

@ -1,4 +1,3 @@
import { countdownTypes } from '../../config/generalConfig.mjs';
import { GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
import constructHTMLButton from '../../helpers/utils.mjs';
import OwnershipSelection from '../dialogs/ownershipSelection.mjs';
@ -328,43 +327,29 @@ export class EncounterCountdowns extends Countdowns {
};
}
export const registerCountdownApplicationHooks = () => {
const updateCountdowns = async shouldProgress => {
if (game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).countdowns) {
const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
for (let countdownCategoryKey in countdownSetting) {
const countdownCategory = countdownSetting[countdownCategoryKey];
for (let countdownKey in countdownCategory.countdowns) {
const countdown = countdownCategory.countdowns[countdownKey];
if (shouldProgress(countdown)) {
await countdownSetting.updateSource({
[`${countdownCategoryKey}.countdowns.${countdownKey}.progress.current`]:
countdown.progress.current - 1
});
await game.settings.set(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.Countdowns,
countdownSetting
);
foundry.applications.instances.get(`${countdownCategoryKey}-countdowns`)?.render();
}
export async function updateCountdowns(progressType) {
const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
const update = Object.keys(countdownSetting).reduce((update, typeKey) => {
return foundry.utils.mergeObject(
update,
Object.keys(countdownSetting[typeKey].countdowns).reduce((acc, countdownKey) => {
const countdown = countdownSetting[typeKey].countdowns[countdownKey];
if (countdown.progress.current > 0 && countdown.progress.type.value === progressType) {
acc[`${typeKey}.countdowns.${countdownKey}.progress.current`] = countdown.progress.current - 1;
}
}
}
};
Hooks.on(CONFIG.DH.HOOKS.characterAttack, async () => {
updateCountdowns(countdown => {
return (
countdown.progress.type.value === countdownTypes.characterAttack.id && countdown.progress.current > 0
);
});
});
return acc;
}, {})
);
}, {});
Hooks.on(CONFIG.DH.HOOKS.spotlight, async () => {
updateCountdowns(countdown => {
return countdown.progress.type.value === countdownTypes.spotlight.id && countdown.progress.current > 0;
});
await countdownSetting.updateSource(update);
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, countdownSetting);
const data = { refreshType: RefreshType.Countdown };
await game.socket.emit(`system.${CONFIG.DH.id}`, {
action: socketEvent.Refresh,
data
});
};
Hooks.callAll(socketEvent.Refresh, data);
}

View file

@ -0,0 +1,129 @@
export default class DhHotbar extends foundry.applications.ui.Hotbar {
constructor(options) {
super(options);
this.setupHooks();
}
static async useItem(uuid) {
const item = await fromUuid(uuid);
if (!item) {
return ui.notifications.warn('WARNING.ObjectDoesNotExist', {
format: {
name: game.i18n.localize('Document'),
identifier: uuid
}
});
}
await item.use({});
}
static async useAction(itemUuid, actionId) {
const item = await foundry.utils.fromUuid(itemUuid);
if (!item) {
return ui.notifications.warn('WARNING.ObjectDoesNotExist', {
format: {
name: game.i18n.localize('Document'),
identifier: itemUuid
}
});
}
const action = item.system.actions.find(x => x.id === actionId);
if (!action) {
return ui.notifications.warn('DAGGERHEART.UI.Notifications.actionIsMissing');
}
await action.use({});
}
static async useAttack(actorUuid) {
const actor = await foundry.utils.fromUuid(actorUuid);
if (!actor) {
return ui.notifications.warn('WARNING.ObjectDoesNotExist', {
format: {
name: game.i18n.localize('Document'),
identifier: actorUuid
}
});
}
const attack = actor.system.attack;
if (!attack) {
return ui.notifications.warn('DAGGERHEART.UI.Notifications.attackIsMissing');
}
await attack.use({});
}
setupHooks() {
Hooks.on('hotbarDrop', (bar, data, slot) => {
if (data.type === 'Item') {
const item = foundry.utils.fromUuidSync(data.uuid);
if (item.uuid.startsWith('Compendium') || !item.isOwned || !item.isOwner) return true;
switch (item.type) {
case 'ancestry':
case 'community':
case 'class':
case 'subclass':
return true;
default:
this.createItemMacro(item, slot);
return false;
}
} else if (data.type === 'Action') {
const item = foundry.utils.fromUuidSync(data.data.itemUuid);
if (item.uuid.startsWith('Compendium')) return true;
if (!item.isOwned || !item.isOwner) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.unownedActionMacro'));
return false;
}
this.createActionMacro(data, slot);
return false;
} else if (data.type === 'Attack') {
const actor = foundry.utils.fromUuidSync(data.actorUuid);
if (actor.uuid.startsWith('Compendium')) return true;
if (!actor.isOwner) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.unownedAttackMacro'));
return false;
}
this.createAttackMacro(data, slot);
return false;
}
});
}
async createItemMacro(data, slot) {
const macro = await Macro.implementation.create({
name: data.name,
type: CONST.MACRO_TYPES.SCRIPT,
img: data.img,
command: `await game.system.api.applications.ui.DhHotbar.useItem("${data.uuid}");`
});
await game.user.assignHotbarMacro(macro, slot);
}
async createActionMacro(data, slot) {
const macro = await Macro.implementation.create({
name: data.data.name,
type: CONST.MACRO_TYPES.SCRIPT,
img: data.data.img,
command: `await game.system.api.applications.ui.DhHotbar.useAction("${data.data.itemUuid}", "${data.data.id}");`
});
await game.user.assignHotbarMacro(macro, slot);
}
async createAttackMacro(data, slot) {
const macro = await Macro.implementation.create({
name: data.name,
type: CONST.MACRO_TYPES.SCRIPT,
img: data.img,
command: `await game.system.api.applications.ui.DhHotbar.useAttack("${data.actorUuid}");`
});
await game.user.assignHotbarMacro(macro, slot);
}
}