Compare commits

..

No commits in common. "5dbcd9448064f97d202abb541cd5b4c5072fe544" and "c8d0df87c8a59c3261a49742cd533e719c49fcad" have entirely different histories.

20 changed files with 62 additions and 218 deletions

View file

@ -446,33 +446,3 @@ Hooks.on('canvasTearDown', canvas => {
Hooks.on('canvasReady', canas => {
game.system.registeredTriggers.registerSceneTriggers(canvas.scene);
});
/** Make the user to select a document type, instead of having a default doc type for them to accidentally keep */
Hooks.on('renderDialogV2', (_dialog, html) => {
if (!html.classList.contains('dialog')) return;
const cls = html.classList.contains('item-create')
? documents.DHItem.implementation
: html.classList.contains('actor-create')
? documents.DhpActor.implementation
: null;
if (!cls) return;
const form = html.querySelector('form');
const submit = html.querySelector('button[type=submit]');
const select = html.querySelector('select[name=type]');
const nameInput = html.querySelector('input[name=name]');
if (!form || !select || !submit || !nameInput) return;
nameInput.placeholder = cls.defaultName({});
const emptyOption = document.createElement('option');
emptyOption.value = '';
emptyOption.selected = true;
select.required = true;
select.prepend(emptyOption);
submit.addEventListener('click', event => {
if (!form.reportValidity()) {
event.preventDefault();
event.stopPropagation();
}
});
});

View file

@ -711,9 +711,9 @@
},
"PendingReactionsDialog": {
"title": "Pending Reaction Rolls Found",
"unfinishedRolls": "Some Tokens have not finished their Reaction Rolls.",
"warning": "Unfinished reaction rolls will be considered as failed.",
"confirmation": "Are you sure you want to continue?"
"unfinishedRolls": "Some Tokens still need to roll their Reaction Roll.",
"confirmation": "Are you sure you want to continue ?",
"warning": "Undone reaction rolls will be considered as failed"
},
"ReactionRoll": {
"title": "Reaction Roll: {trait}"

View file

@ -31,16 +31,6 @@ export default class AdversarySheet extends DHBaseActorSheet {
dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]',
dropSelector: null
}
],
contextMenus: [
{
handler: DHBaseActorSheet.getBaseAttackContextOptions,
selector: '[data-item-uuid][data-type="attack"]',
options: {
parentClassHooks: false,
fixed: true
}
}
]
};

View file

@ -65,14 +65,6 @@ export default class CharacterSheet extends DHBaseActorSheet {
fixed: true
}
},
{
handler: DHBaseActorSheet.getBaseAttackContextOptions,
selector: '[data-item-uuid][data-type="attack"]',
options: {
parentClassHooks: false,
fixed: true
}
},
{
handler: CharacterSheet.#getDomainCardContextOptions,
selector: '[data-item-uuid][data-type="domainCard"]',
@ -1053,7 +1045,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
game.tooltip.activate(target, {
html,
locked: true,
cssClass: 'bordered-tooltip dh-style',
cssClass: 'bordered-tooltip',
direction: 'DOWN'
});
@ -1149,7 +1141,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
game.tooltip.activate(target, {
html,
locked: true,
cssClass: 'bordered-tooltip dh-style',
cssClass: 'bordered-tooltip',
direction: 'DOWN',
noOffset: true
});

View file

@ -11,17 +11,7 @@ export default class DhCompanionSheet extends DHBaseActorSheet {
toggleStress: DhCompanionSheet.#toggleStress,
actionRoll: DhCompanionSheet.#actionRoll,
levelManagement: DhCompanionSheet.#levelManagement
},
contextMenus: [
{
handler: DHBaseActorSheet.getBaseAttackContextOptions,
selector: '[data-item-uuid][data-type="attack"]',
options: {
parentClassHooks: false,
fixed: true
}
}
]
}
};
static PARTS = {

View file

@ -47,6 +47,11 @@ export default class Party extends DHBaseActorSheet {
template: 'systems/daggerheart/templates/sheets/actors/party/party-members.hbs',
scrollable: ['']
},
/* NOT YET IMPLEMENTED */
// projects: {
// template: 'systems/daggerheart/templates/sheets/actors/party/projects.hbs',
// scrollable: ['']
// },
inventory: {
template: 'systems/daggerheart/templates/sheets/actors/party/inventory.hbs',
scrollable: ['.tab.inventory .items-section']
@ -57,13 +62,19 @@ export default class Party extends DHBaseActorSheet {
/** @inheritdoc */
static TABS = {
primary: {
tabs: [{ id: 'partyMembers' }, { id: 'inventory' }, { id: 'notes' }],
tabs: [
{ id: 'partyMembers' },
/* NOT YET IMPLEMENTED */
// { id: 'projects' },
{ id: 'inventory' },
{ id: 'notes' }
],
initial: 'partyMembers',
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
}
};
static ALLOWED_ACTOR_TYPES = ['character', 'companion', 'adversary', 'npc'];
static ALLOWED_ACTOR_TYPES = ['character', 'companion', 'adversary'];
static DICE_ROLL_ACTOR_TYPES = ['character'];
async _onRender(context, options) {

View file

@ -189,43 +189,6 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true });
}
/**
* Get the set of ContextMenu options for the base attack.
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
* @this {CharacterSheet}
* @protected
*/
static getBaseAttackContextOptions() {
/**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */
return [
{
label: 'DAGGERHEART.CONFIG.RollTypes.attack.name',
icon: 'fa-solid fa-burst',
onClick: async (event, target) => (await getDocFromElement(target)).use(event)
},
{
label: 'DAGGERHEART.GENERAL.damage',
icon: 'fa-solid fa-explosion',
onClick: async (event, target) => {
const doc = await getDocFromElement(target),
action = doc?.system?.attack ?? doc;
const config = action.prepareConfig(event);
config.effects = await game.system.api.data.actions.actionsTypes.base.getEffects(
this.document,
doc
);
config.hasRoll = false;
return action && action.workflow.get('damage').execute(config, null, true);
}
},
{
label: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
icon: 'fa-solid fa-message',
onClick: async (_, target) => (await getDocFromElement(target)).toChat(this.document.uuid)
}
];
}
/* -------------------------------------------- */
/* Application Listener Actions */
/* -------------------------------------------- */

View file

@ -84,49 +84,19 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
});
}
/**
* Open the dialog used to edit the name of the currently viewed Combat encounter.
* @this {CombatTracker}
* @returns {Promise<void>}
*/
static async #onEditName() {
const combat = this.viewed;
if (!combat || !game.user.isGM) return null;
const field = combat.schema.fields.name;
const inputHTML = field.toFormGroup({}, { name: 'name', value: combat.name, autofocus: true }).outerHTML;
const formData = await foundry.applications.api.DialogV2.input({
window: { icon: 'fa-solid fa-tag', title: 'COMBAT.ACTIONS.EditNameTitle' },
position: { width: 480 },
content: inputHTML
});
await combat.update({ name: formData.name || '' });
}
_getCombatContextOptions() {
return [
{
label: 'COMBAT.ACTIONS.EditName',
icon: 'fa-solid fa-tag',
label: 'COMBAT.ClearMovementHistories',
icon: '<i class="fa-solid fa-shoe-prints"></i>',
visible: () => game.user.isGM && this.viewed?.combatants.size > 0,
callback: () => this.viewed.clearMovementHistories()
},
{
label: 'COMBAT.Delete',
icon: '<i class="fa-solid fa-trash"></i>',
visible: () => game.user.isGM && !!this.viewed,
onClick: () => DhCombatTracker.#onEditName.call(this)
},
{
label: 'COMBAT.ACTIONS.LinkToScene',
icon: '<i class="fa-solid fa-link"></i>',
visible: () => game.user.isGM && !this.scene,
onClick: () => this.viewed.toggleSceneLink()
},
{
label: 'COMBAT.ACTIONS.UnlinkFromScene',
icon: '<i class="fa-solid fa-unlink"></i>',
visible: () => game.user.isGM && !!this.scene,
onClick: () => this.viewed.toggleSceneLink()
},
{
label: 'COMBAT.End',
icon: 'fa-solid fa-xmark',
visible: () => game.user.isGM && !!this.viewed,
onClick: () => this.viewed.endCombat()
callback: () => this.viewed.endCombat()
}
];
}
@ -163,7 +133,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'),
type: combatant.actor?.system?.type,
img: await this._getCombatantThumbnail(combatant),
disposition: combatant.token?.disposition
disposition: combatant.token.disposition
};
turn.css = [turn.active ? 'active' : null, hidden ? 'hide' : null, isDefeated ? 'defeated' : null].filterJoin(

View file

@ -75,12 +75,7 @@ export default class DHAttackAction extends DHDamageAction {
const useAltDamage = this.actor?.effects?.find(x => x.type === 'horde')?.active;
for (const { value, valueAlt, type } of damage.parts) {
const usedValue = useAltDamage ? valueAlt : value;
const damageString = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {});
const str = damageString
? damageString
: game.i18n.format('DAGGERHEART.GENERAL.missingX', {
x: game.i18n.localize('DAGGERHEART.GENERAL.damage')
});
const str = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {});
const icons = Array.from(type)
.map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon)

View file

@ -36,7 +36,7 @@ export default class DhCountdownAction extends DHBaseAction {
/** @inheritDoc */
static migrateData(source) {
for (const countdown of Object.values(source.countdown)) {
for (const countdown of source.countdown) {
if (countdown.progress.max) {
countdown.progress.startFormula = countdown.progress.max;
countdown.progress.start = 1;

View file

@ -65,11 +65,6 @@ export default class DhpActor extends Actor {
};
}
static createDialog(data, createOptions, options, renderOptions) {
options.classes = [options.classes ?? [], 'actor-create'].flat(); // handled in hook
return super.createDialog(data, createOptions, options, renderOptions);
}
/* -------------------------------------------- */
/** @inheritDoc */

View file

@ -183,11 +183,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
if (pendingingSaves.length) {
const confirm = await foundry.applications.api.DialogV2.confirm({
window: { title: game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.title') },
content: `
<p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}</p>
<p><i>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}</i></p>
<p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}</p>
`
content: `<p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}</p><p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}</p><p><i>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}</i></p>`
});
if (!confirm) return;
}
@ -251,24 +247,8 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
const targets = this.filterPermTargets(this.system.hitTargets),
config = foundry.utils.deepClone(this.system);
config.event = event;
if (targets.length === 0)
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
else if (config.hasSave) {
const pendingingSaves = targets.filter(t => t.saved.success === null);
if (pendingingSaves.length) {
const confirm = await foundry.applications.api.DialogV2.confirm({
window: { title: game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.title') },
content: `
<p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}</p>
<p><i>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}</i></p>
<p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}</p>
`
});
if (!confirm) return;
}
}
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
this.consumeOnSuccess();
this.system.action?.workflow.get('effects')?.execute(config, targets, true);
}

View file

@ -82,7 +82,6 @@ export default class DHItem extends foundry.documents.Item {
/** @inheritdoc */
static async createDialog(data = {}, createOptions = {}, options = {}) {
const { folders, types, template, context = {}, ...dialogOptions } = options;
dialogOptions.classes = [options.classes ?? [], 'item-create'].flat(); // handled in hook
if (types?.length === 0) {
throw new Error('The array of sub-types to restrict to must not be empty.');

View file

@ -3,6 +3,7 @@ import { AdversaryBPPerEncounter, BaseBPPerEncounter } from '../config/encounter
export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager {
#wide = false;
#bordered = false;
#active = false;
async activate(element, options = {}) {
const { TextEditor } = foundry.applications.ux;

View file

@ -38,9 +38,6 @@
}
.status-effects {
// TODO: Remove when the issue https://github.com/foundryvtt/foundryvtt/issues/14410 is resolved and Foundry handles it cleanly themselves.
grid-template-rows: min-content;
.effect-control-container {
position: relative;

View file

@ -5,7 +5,7 @@
.portrait {
cursor: pointer;
max-width: 275px;
width: 275px;
img {
height: 275px;

View file

@ -2,7 +2,7 @@
"id": "daggerheart",
"title": "Daggerheart",
"description": "An unofficial implementation of the Daggerheart system",
"version": "2.3.1",
"version": "2.3.0",
"compatibility": {
"minimum": "14.361",
"verified": "14.363",
@ -10,7 +10,7 @@
},
"url": "https://github.com/Foundryborne/daggerheart",
"manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json",
"download": "https://github.com/Foundryborne/daggerheart/releases/download/2.3.1/system.zip",
"download": "https://github.com/Foundryborne/daggerheart/releases/download/2.3.0/system.zip",
"authors": [
{
"name": "WBHarry"

View file

@ -31,12 +31,10 @@
<span class="description">
<i>{{{description}}}</i>
</span>
{{#if source.system.motives}}
<div class="motives-and-tactics">
<b>{{localize 'DAGGERHEART.ACTORS.NPC.FIELDS.motives.label'}}: </b>
{{source.system.motives}}
</div>
{{/if}}
<div class="motives-and-tactics">
<b>{{localize 'DAGGERHEART.ACTORS.NPC.FIELDS.motives.label'}}: </b>
{{source.system.motives}}
</div>
</div>
</div>
</header>

View file

@ -42,7 +42,7 @@
{{#if member.difficulty includeZero=true}}
<div class="evasion" data-tooltip="DAGGERHEART.GENERAL.difficulty">{{member.difficulty}}</div>
{{/if}}
{{#unless (or (eq member.type 'companion') (eq member.type 'npc'))}}
{{#unless (eq member.type 'companion')}}
<div class="threshold-section">
<h4 class="threshold-label">{{localize "DAGGERHEART.ACTORS.Party.Thresholds.minor"}}</h4>
<h4 class="threshold-value">{{member.damageThresholds.major}}</h4>
@ -58,7 +58,7 @@
<a class="delete-icon" data-action="deletePartyMember" data-uuid="{{member.uuid}}"><i class="fa-regular fa-times" inert></i></a>
</h2>
<div>
{{#unless (or (eq member.type 'companion') (eq member.type 'adversary') (eq member.type 'npc')) }}
{{#unless (or (eq member.type 'companion') (eq member.type 'adversary')) }}
<div class="hope-section">
<h4>{{localize "DAGGERHEART.GENERAL.hope"}}</h4>
{{#times member.resources.hope.max}}
@ -79,7 +79,7 @@
</header>
<section class="body">
<section class="resources">
{{#unless (or (eq member.type 'companion') (eq member.type 'npc')) }}
{{#unless (eq member.type 'companion') }}
<div class="slot-section">
<div class="slot-label" data-tooltip="DAGGERHEART.GENERAL.HitPoints.plural">
<span class="label">
@ -101,27 +101,25 @@
</div>
{{/unless}}
{{#unless (eq member.type 'npc')}}
<div class="slot-section">
<div class="slot-label" data-tooltip="DAGGERHEART.GENERAL.stress">
<span class="label">
<i class="fa-solid fa-bolt" inert></i>
</span>
<span class="value">
<span class="current">{{member.resources.stress.value}}</span>
/
<span class="max">{{member.resources.stress.max}}</span>
</span>
</div>
<div class="slot-bar">
{{#times member.resources.stress.max}}
<span class='slot {{#if (gte member.resources.stress.value (add this 1))}}filled{{/if}}'
data-action='toggleStress' data-actor-id="{{member.uuid}}" data-value="{{add this 1}}">
</span>
{{/times}}
</div>
<div class="slot-section">
<div class="slot-label" data-tooltip="DAGGERHEART.GENERAL.stress">
<span class="label">
<i class="fa-solid fa-bolt" inert></i>
</span>
<span class="value">
<span class="current">{{member.resources.stress.value}}</span>
/
<span class="max">{{member.resources.stress.max}}</span>
</span>
</div>
{{/unless}}
<div class="slot-bar">
{{#times member.resources.stress.max}}
<span class='slot {{#if (gte member.resources.stress.value (add this 1))}}filled{{/if}}'
data-action='toggleStress' data-actor-id="{{member.uuid}}" data-value="{{add this 1}}">
</span>
{{/times}}
</div>
</div>
{{#if member.armorScore.max}}
<div class="slot-section">

View file

@ -50,11 +50,6 @@
</nav>
{{/if}}
{{!-- Encounter Name --}}
{{#if combat.name}}
<h2 class="encounter-name">{{ combat.name }}</h2>
{{/if}}
<div class="encounter-controls {{#if hasCombat}}combat{{/if}}">
{{!-- Combat Status --}}
<strong class="encounter-title">