mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
Merge branch 'main' into feature/313-preset-measured-templates
This commit is contained in:
commit
7d2de14709
81 changed files with 611 additions and 446 deletions
|
|
@ -178,10 +178,17 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
}
|
||||
|
||||
static async takeDowntime() {
|
||||
const moves = Object.values(this.moveData).flatMap(category => {
|
||||
return Object.values(category.moves)
|
||||
.filter(x => x.selected)
|
||||
.flatMap(move => [...Array(move.selected).keys()].map(_ => move));
|
||||
const moves = Object.keys(this.moveData).flatMap(categoryKey => {
|
||||
const category = this.moveData[categoryKey];
|
||||
return Object.keys(category.moves)
|
||||
.filter(x => category.moves[x].selected)
|
||||
.flatMap(key => {
|
||||
const move = category.moves[key];
|
||||
return [...Array(move.selected).keys()].map(_ => ({
|
||||
...move,
|
||||
movePath: `${categoryKey}.moves.${key}`
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export default class ImageSelectDialog extends HandlebarsApplicationMixin(Applic
|
|||
tag: 'form',
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'image-select'],
|
||||
position: {
|
||||
width: 600,
|
||||
width: 612,
|
||||
height: 'auto'
|
||||
},
|
||||
window: {
|
||||
|
|
@ -30,7 +30,10 @@ export default class ImageSelectDialog extends HandlebarsApplicationMixin(Applic
|
|||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: { template: 'systems/daggerheart/templates/dialogs/image-select/main.hbs' },
|
||||
main: {
|
||||
template: 'systems/daggerheart/templates/dialogs/image-select/main.hbs',
|
||||
scrollable: ['.images-container']
|
||||
},
|
||||
footer: { template: 'systems/daggerheart/templates/dialogs/image-select/footer.hbs' }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
CONFIG.DH.SETTINGS.gameSettings.appearance
|
||||
).showGenericStatusEffects;
|
||||
if (!useGeneric) {
|
||||
partContext.statuses = Object.values(CONFIG.DH.GENERAL.conditions).map(status => ({
|
||||
partContext.statuses = Object.values(CONFIG.DH.GENERAL.conditions()).map(status => ({
|
||||
value: status.id,
|
||||
label: game.i18n.localize(status.name)
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
toggleEquipItem: CharacterSheet.#toggleEquipItem,
|
||||
toggleResourceDice: CharacterSheet.#toggleResourceDice,
|
||||
handleResourceDice: CharacterSheet.#handleResourceDice,
|
||||
cancelBeastform: CharacterSheet.#cancelBeastform,
|
||||
useDowntime: this.useDowntime
|
||||
},
|
||||
window: {
|
||||
|
|
@ -209,24 +210,33 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
context.resources.stress.emptyPips =
|
||||
context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0;
|
||||
|
||||
context.inventory = {
|
||||
currency: {
|
||||
title: game.i18n.localize('DAGGERHEART.CONFIG.Gold.title'),
|
||||
coins: game.i18n.localize('DAGGERHEART.CONFIG.Gold.coins'),
|
||||
handfuls: game.i18n.localize('DAGGERHEART.CONFIG.Gold.handfuls'),
|
||||
bags: game.i18n.localize('DAGGERHEART.CONFIG.Gold.bags'),
|
||||
chests: game.i18n.localize('DAGGERHEART.CONFIG.Gold.chests')
|
||||
}
|
||||
};
|
||||
|
||||
const homebrewCurrency = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).currency;
|
||||
if (homebrewCurrency.enabled) {
|
||||
context.inventory.currency = homebrewCurrency;
|
||||
context.inventory = { currencies: {} };
|
||||
const { title, ...currencies } = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.Homebrew
|
||||
).currency;
|
||||
for (let key in currencies) {
|
||||
context.inventory.currencies[key] = {
|
||||
...currencies[key],
|
||||
field: context.systemFields.gold.fields[key],
|
||||
value: context.source.system.gold[key]
|
||||
};
|
||||
}
|
||||
// context.inventory = {
|
||||
// currency: {
|
||||
// title: game.i18n.localize('DAGGERHEART.CONFIG.Gold.title'),
|
||||
// coins: game.i18n.localize('DAGGERHEART.CONFIG.Gold.coins'),
|
||||
// handfuls: game.i18n.localize('DAGGERHEART.CONFIG.Gold.handfuls'),
|
||||
// bags: game.i18n.localize('DAGGERHEART.CONFIG.Gold.bags'),
|
||||
// chests: game.i18n.localize('DAGGERHEART.CONFIG.Gold.chests')
|
||||
// }
|
||||
// };
|
||||
|
||||
if (context.inventory.length === 0) {
|
||||
context.inventory = Array(1).fill(Array(5).fill([]));
|
||||
}
|
||||
context.beastformActive = this.document.effects.find(x => x.type === 'beastform');
|
||||
|
||||
// if (context.inventory.length === 0) {
|
||||
// context.inventory = Array(1).fill(Array(5).fill([]));
|
||||
// }
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
@ -848,6 +858,15 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static async #cancelBeastform(_, target) {
|
||||
const item = await getDocFromElement(target);
|
||||
if (!item) return;
|
||||
game.system.api.fields.ActionFields.BeastformField.handleActiveTransformations.call(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the downtime application.
|
||||
* @type {ApplicationClickAction}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { socketEvent } from '../../../systemRegistration/socket.mjs';
|
|||
import GroupRollDialog from '../../dialogs/group-roll-dialog.mjs';
|
||||
import DhpActor from '../../../documents/actor.mjs';
|
||||
import DHItem from '../../../documents/item.mjs';
|
||||
import DhParty from '../../../data/actor/party.mjs';
|
||||
|
||||
export default class Party extends DHBaseActorSheet {
|
||||
constructor(options) {
|
||||
|
|
@ -19,7 +20,8 @@ export default class Party extends DHBaseActorSheet {
|
|||
static DEFAULT_OPTIONS = {
|
||||
classes: ['party'],
|
||||
position: {
|
||||
width: 550
|
||||
width: 550,
|
||||
height: 900,
|
||||
},
|
||||
window: {
|
||||
resizable: true
|
||||
|
|
@ -79,6 +81,9 @@ export default class Party extends DHBaseActorSheet {
|
|||
}
|
||||
};
|
||||
|
||||
static ALLOWED_ACTOR_TYPES = ['character', 'companion', 'adversary'];
|
||||
static DICE_ROLL_ACTOR_TYPES = ['character'];
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options);
|
||||
this._createFilterMenus();
|
||||
|
|
@ -92,23 +97,17 @@ export default class Party extends DHBaseActorSheet {
|
|||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
|
||||
context.inventory = {
|
||||
currency: {
|
||||
title: game.i18n.localize('DAGGERHEART.CONFIG.Gold.title'),
|
||||
coins: game.i18n.localize('DAGGERHEART.CONFIG.Gold.coins'),
|
||||
handfuls: game.i18n.localize('DAGGERHEART.CONFIG.Gold.handfuls'),
|
||||
bags: game.i18n.localize('DAGGERHEART.CONFIG.Gold.bags'),
|
||||
chests: game.i18n.localize('DAGGERHEART.CONFIG.Gold.chests')
|
||||
}
|
||||
};
|
||||
|
||||
const homebrewCurrency = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).currency;
|
||||
if (homebrewCurrency.enabled) {
|
||||
context.inventory.currency = homebrewCurrency;
|
||||
}
|
||||
|
||||
if (context.inventory.length === 0) {
|
||||
context.inventory = Array(1).fill(Array(5).fill([]));
|
||||
context.inventory = { currencies: {} };
|
||||
const { title, ...currencies } = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.Homebrew
|
||||
).currency;
|
||||
for (let key in currencies) {
|
||||
context.inventory.currencies[key] = {
|
||||
...currencies[key],
|
||||
field: context.systemFields.gold.fields[key],
|
||||
value: context.source.system.gold[key]
|
||||
};
|
||||
}
|
||||
|
||||
return context;
|
||||
|
|
@ -277,13 +276,17 @@ export default class Party extends DHBaseActorSheet {
|
|||
}
|
||||
|
||||
static async #tagTeamRoll() {
|
||||
new game.system.api.applications.dialogs.TagTeamDialog(this.document.system.partyMembers).render({
|
||||
new game.system.api.applications.dialogs.TagTeamDialog(
|
||||
this.document.system.partyMembers.filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type))
|
||||
).render({
|
||||
force: true
|
||||
});
|
||||
}
|
||||
|
||||
static async #groupRoll(params) {
|
||||
new GroupRollDialog(this.document.system.partyMembers).render({ force: true });
|
||||
static async #groupRoll(_params) {
|
||||
new GroupRollDialog(
|
||||
this.document.system.partyMembers.filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type))
|
||||
).render({ force: true });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -453,17 +456,17 @@ export default class Party extends DHBaseActorSheet {
|
|||
event.stopPropagation();
|
||||
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
const item = await foundry.utils.fromUuid(data.uuid);
|
||||
const document = await foundry.utils.fromUuid(data.uuid);
|
||||
|
||||
if (item instanceof DhpActor) {
|
||||
if (document instanceof DhpActor && Party.ALLOWED_ACTOR_TYPES.includes(document.type)) {
|
||||
const currentMembers = this.document.system.partyMembers.map(x => x.uuid);
|
||||
if (currentMembers.includes(data.uuid)) {
|
||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.duplicateCharacter'));
|
||||
}
|
||||
|
||||
await this.document.update({ 'system.partyMembers': [...currentMembers, item.uuid] });
|
||||
} else if (item instanceof DHItem) {
|
||||
this.document.createEmbeddedDocuments('Item', [item.toObject()]);
|
||||
await this.document.update({ 'system.partyMembers': [...currentMembers, document.uuid] });
|
||||
} else if (document instanceof DHItem) {
|
||||
this.document.createEmbeddedDocuments('Item', [document.toObject()]);
|
||||
} else {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.onlyCharactersInPartySheet'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -420,19 +420,6 @@ export default function DHApplicationMixin(Base) {
|
|||
];
|
||||
|
||||
if (usable) {
|
||||
options.unshift({
|
||||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.cancelBeastform',
|
||||
icon: 'fa-solid fa-ban',
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && doc.system?.actions?.some(a => a.type === 'beastform');
|
||||
},
|
||||
callback: async target =>
|
||||
game.system.api.fields.ActionFields.BeastformField.handleActiveTransformations.call(
|
||||
await getDocFromElement(target)
|
||||
)
|
||||
});
|
||||
|
||||
options.unshift({
|
||||
name: 'DAGGERHEART.GENERAL.damage',
|
||||
icon: 'fa-solid fa-explosion',
|
||||
|
|
|
|||
|
|
@ -132,12 +132,21 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
}
|
||||
|
||||
async actionUseButton(event, message) {
|
||||
const { moveIndex, actionIndex } = event.currentTarget.dataset;
|
||||
const { moveIndex, actionIndex, movePath } = event.currentTarget.dataset;
|
||||
const parent = await foundry.utils.fromUuid(message.system.actor);
|
||||
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) },
|
||||
{
|
||||
...actionType,
|
||||
_id: foundry.utils.randomID(),
|
||||
name: game.i18n.localize(actionType.name),
|
||||
originItem: {
|
||||
type: CONFIG.DH.ITEM.originItemType.restMove,
|
||||
itemPath: movePath,
|
||||
actionIndex: actionIndex
|
||||
}
|
||||
},
|
||||
{ parent: parent.system }
|
||||
);
|
||||
|
||||
|
|
@ -192,9 +201,18 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
|
||||
async groupRollButton(event, message) {
|
||||
const path = event.currentTarget.dataset.path;
|
||||
const isLeader = path === 'leader';
|
||||
const { actor: actorData, trait } = foundry.utils.getProperty(message.system, path);
|
||||
const actor = game.actors.get(actorData._id);
|
||||
|
||||
if (!actor) {
|
||||
return ui.notifications.error(
|
||||
game.i18n.format('DAGGERHEART.UI.Notifications.documentIsMissing', {
|
||||
documentType: game.i18n.localize('TYPES.Actor.character')
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (!actor.testUserPermission(game.user, 'OWNER')) {
|
||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noActorOwnership'));
|
||||
}
|
||||
|
|
@ -214,7 +232,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
hasRoll: true,
|
||||
skips: {
|
||||
createMessage: true,
|
||||
resources: true
|
||||
resources: !isLeader
|
||||
}
|
||||
};
|
||||
const result = await actor.diceRoll({
|
||||
|
|
@ -225,6 +243,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
})
|
||||
});
|
||||
|
||||
if (!result) return;
|
||||
await game.system.api.fields.ActionFields.CostField.execute.call({ actor }, result);
|
||||
|
||||
const newMessageData = foundry.utils.deepClone(message.system);
|
||||
foundry.utils.setProperty(newMessageData, `${path}.result`, result.roll);
|
||||
const renderData = { system: new game.system.api.models.chatMessages.config.groupRoll(newMessageData) };
|
||||
|
|
|
|||
|
|
@ -88,6 +88,17 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
|||
this.toggleCollapsedPosition(undefined, !ui.sidebar.expanded);
|
||||
}
|
||||
|
||||
/** Returns countdown data filtered by ownership */
|
||||
#getCountdowns() {
|
||||
const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
|
||||
const values = Object.entries(setting.countdowns).map(([key, countdown]) => ({
|
||||
key,
|
||||
countdown,
|
||||
ownership: DhCountdowns.#getPlayerOwnership(game.user, setting, countdown)
|
||||
}));
|
||||
return values.filter((v) => v.ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext(options) {
|
||||
const context = await super._prepareContext(options);
|
||||
|
|
@ -98,11 +109,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
|||
CONFIG.DH.GENERAL.countdownAppMode.iconOnly;
|
||||
|
||||
const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
|
||||
context.countdowns = Object.keys(setting.countdowns).reduce((acc, key) => {
|
||||
const countdown = setting.countdowns[key];
|
||||
const ownership = DhCountdowns.#getPlayerOwnership(game.user, setting, countdown);
|
||||
if (ownership === CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) return acc;
|
||||
|
||||
context.countdowns = this.#getCountdowns().reduce((acc, { key, countdown, ownership }) => {
|
||||
const playersWithAccess = game.users.reduce((acc, user) => {
|
||||
const ownership = DhCountdowns.#getPlayerOwnership(user, setting, countdown);
|
||||
if (!user.isGM && ownership && ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) {
|
||||
|
|
@ -238,4 +245,9 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
|||
});
|
||||
Hooks.callAll(socketEvent.Refresh, data);
|
||||
}
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options);
|
||||
this.element.hidden = !game.user.isGM && this.#getCountdowns().length === 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
const property = foundry.utils.getProperty(item, field.key);
|
||||
if (Array.isArray(property)) property.join(', ');
|
||||
if (typeof field.format !== 'function') return property ?? '-';
|
||||
return field.format(property);
|
||||
return game.i18n.localize(field.format(property));
|
||||
}
|
||||
|
||||
formatChoices(data) {
|
||||
|
|
@ -430,7 +430,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
if (matchesMenu) this.#filteredItems.browser.input.add(item.id);
|
||||
|
||||
const { search } = this.#filteredItems.browser;
|
||||
li.hidden = !(search.has(item.id) && matchesMenu);
|
||||
li.hidden = !((this.#search.browser.query.length === 0 || search.has(item.id)) && matchesMenu);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -469,6 +469,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
|
||||
static resetFilters() {
|
||||
this.render({ force: true });
|
||||
this.loadItems();
|
||||
}
|
||||
|
||||
static getFolderConfig(folder, property = 'columns') {
|
||||
|
|
|
|||
|
|
@ -363,7 +363,7 @@ export const typeConfig = {
|
|||
{
|
||||
key: 'system.linkedClass',
|
||||
label: 'Class',
|
||||
format: linkedClass => linkedClass.name
|
||||
format: linkedClass => linkedClass?.name ?? 'DAGGERHEART.UI.ItemBrowser.missing'
|
||||
},
|
||||
{
|
||||
key: 'system.spellcastingTrait',
|
||||
|
|
@ -375,10 +375,12 @@ export const typeConfig = {
|
|||
key: 'system.linkedClass.uuid',
|
||||
label: 'Class',
|
||||
choices: items => {
|
||||
const list = items.map(item => ({
|
||||
value: item.system.linkedClass.uuid,
|
||||
label: item.system.linkedClass.name
|
||||
}));
|
||||
const list = items
|
||||
.filter(item => item.system.linkedClass)
|
||||
.map(item => ({
|
||||
value: item.system.linkedClass.uuid,
|
||||
label: item.system.linkedClass.name
|
||||
}));
|
||||
return list.reduce((a, c) => {
|
||||
if (!a.find(i => i.value === c.value)) a.push(c);
|
||||
return a;
|
||||
|
|
|
|||
|
|
@ -1547,3 +1547,8 @@ export const beastformTypes = {
|
|||
label: 'DAGGERHEART.CONFIG.BeastformType.hybrid'
|
||||
}
|
||||
};
|
||||
|
||||
export const originItemType = {
|
||||
itemCollection: 'itemCollection',
|
||||
restMove: 'restMove'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import DhpActor from '../../documents/actor.mjs';
|
||||
import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs';
|
||||
import { ActionMixin } from '../fields/actionField.mjs';
|
||||
import { originItemField } from '../chat-message/actorRoll.mjs';
|
||||
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
|
|
@ -25,6 +26,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
description: new fields.HTMLField(),
|
||||
img: new fields.FilePathField({ initial: undefined, categories: ['IMAGE'], base64: false }),
|
||||
chatDisplay: new fields.BooleanField({ initial: true, label: 'DAGGERHEART.ACTIONS.Config.displayInChat' }),
|
||||
originItem: originItemField(),
|
||||
actionType: new fields.StringField({
|
||||
choices: CONFIG.DH.ITEM.actionTypes,
|
||||
initial: 'action',
|
||||
|
|
@ -215,6 +217,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
title: `${this.item instanceof CONFIG.Actor.documentClass ? '' : `${this.item.name}: `}${game.i18n.localize(this.name)}`,
|
||||
source: {
|
||||
item: this.item._id,
|
||||
originItem: this.originItem,
|
||||
action: this._id,
|
||||
actor: this.actor.uuid
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ export default class BeastformEffect extends BaseEffect {
|
|||
const fields = foundry.data.fields;
|
||||
return {
|
||||
characterTokenData: new fields.SchemaField({
|
||||
usesDynamicToken: new fields.BooleanField({ initial: false }),
|
||||
tokenImg: new fields.FilePathField({
|
||||
categories: ['IMAGE'],
|
||||
base64: false,
|
||||
nullable: true
|
||||
nullable: true,
|
||||
wildcard: true
|
||||
}),
|
||||
tokenRingImg: new fields.FilePathField({
|
||||
initial: 'icons/svg/mystery-man.svg',
|
||||
|
|
@ -38,20 +40,39 @@ export default class BeastformEffect extends BaseEffect {
|
|||
|
||||
async _preDelete() {
|
||||
if (this.parent.parent.type === 'character') {
|
||||
const update = {
|
||||
const baseUpdate = {
|
||||
height: this.characterTokenData.tokenSize.height,
|
||||
width: this.characterTokenData.tokenSize.width,
|
||||
width: this.characterTokenData.tokenSize.width
|
||||
};
|
||||
const update = {
|
||||
...baseUpdate,
|
||||
texture: {
|
||||
src: this.characterTokenData.tokenImg
|
||||
},
|
||||
ring: {
|
||||
enabled: this.characterTokenData.usesDynamicToken,
|
||||
subject: {
|
||||
texture: this.characterTokenData.tokenRingImg
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await updateActorTokens(this.parent.parent, update);
|
||||
const updateToken = token => ({
|
||||
...baseUpdate,
|
||||
'texture': {
|
||||
enabled: this.characterTokenData.usesDynamicToken,
|
||||
src: token.flags.daggerheart?.beastformTokenImg ?? this.characterTokenData.tokenImg
|
||||
},
|
||||
'ring': {
|
||||
subject: {
|
||||
texture:
|
||||
token.flags.daggerheart?.beastformSubjectTexture ?? this.characterTokenData.tokenRingImg
|
||||
}
|
||||
},
|
||||
'flags.daggerheart': { '-=beastformTokenImg': null, '-=beastformSubjectTexture': null }
|
||||
});
|
||||
|
||||
await updateActorTokens(this.parent.parent, update, updateToken);
|
||||
|
||||
await this.parent.parent.deleteEmbeddedDocuments('Item', this.featureIds);
|
||||
await this.parent.parent.deleteEmbeddedDocuments('ActiveEffect', this.effectIds);
|
||||
|
|
|
|||
|
|
@ -675,6 +675,8 @@ export default class DhCharacter extends BaseDataActor {
|
|||
}
|
||||
|
||||
_getTags() {
|
||||
return [this.class.value?.name, this.class.subclass?.name, this.community?.name, this.ancestry?.name].filter((t) => !!t);
|
||||
return [this.class.value?.name, this.class.subclass?.name, this.community?.name, this.ancestry?.name].filter(
|
||||
t => !!t
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export default class DhParty extends BaseDataActor {
|
|||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
partyMembers: new ForeignDocumentUUIDArrayField({ type: 'Actor' }),
|
||||
partyMembers: new ForeignDocumentUUIDArrayField({ type: 'Actor' }, { prune: true }),
|
||||
notes: new fields.HTMLField(),
|
||||
gold: new fields.SchemaField({
|
||||
coins: new fields.NumberField({ initial: 0, integer: true }),
|
||||
|
|
@ -27,7 +27,6 @@ export default class DhParty extends BaseDataActor {
|
|||
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
this.partyMembers = this.partyMembers.filter(p => !!p);
|
||||
|
||||
// Register this party to all members
|
||||
if (game.actors.get(this.parent.id) === this.parent) {
|
||||
|
|
@ -42,7 +41,7 @@ export default class DhParty extends BaseDataActor {
|
|||
|
||||
// Clear this party from all members that aren't deleted
|
||||
for (const member of this.partyMembers) {
|
||||
member.parties?.delete(this.parent);
|
||||
member?.parties?.delete(this.parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,16 @@ const targetsField = () =>
|
|||
})
|
||||
);
|
||||
|
||||
export const originItemField = () =>
|
||||
new fields.SchemaField({
|
||||
type: new fields.StringField({
|
||||
choices: CONFIG.DH.ITEM.originItemType,
|
||||
initial: CONFIG.DH.ITEM.originItemType.itemCollection
|
||||
}),
|
||||
itemPath: new fields.StringField(),
|
||||
actionIndex: new fields.StringField()
|
||||
});
|
||||
|
||||
export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
return {
|
||||
|
|
@ -35,6 +45,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
|||
source: new fields.SchemaField({
|
||||
actor: new fields.StringField(),
|
||||
item: new fields.StringField(),
|
||||
originItem: originItemField(),
|
||||
action: new fields.StringField()
|
||||
}),
|
||||
damage: new fields.ObjectField(),
|
||||
|
|
@ -51,14 +62,23 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
|||
get actionItem() {
|
||||
const actionActor = this.actionActor;
|
||||
if (!actionActor || !this.source.item) return null;
|
||||
return actionActor.items.get(this.source.item);
|
||||
|
||||
switch (this.source.originItem.type) {
|
||||
case CONFIG.DH.ITEM.originItemType.restMove:
|
||||
const restMoves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves;
|
||||
return Array.from(foundry.utils.getProperty(restMoves, `${this.source.originItem.itemPath}`).actions)[
|
||||
this.source.originItem.actionIndex
|
||||
];
|
||||
default:
|
||||
const item = actionActor.items.get(this.source.item);
|
||||
return item ? item.system.actionsList?.find(a => a.id === this.source.action) : null;
|
||||
}
|
||||
}
|
||||
|
||||
get action() {
|
||||
const actionActor = this.actionActor,
|
||||
actionItem = this.actionItem;
|
||||
const { actionActor, actionItem: itemAction } = this;
|
||||
if (!this.source.action) return null;
|
||||
if (actionItem) return actionItem.system.actionsList?.find(a => a.id === this.source.action);
|
||||
if (itemAction) return itemAction;
|
||||
else if (actionActor?.system.attack?._id === this.source.action) return actionActor.system.attack;
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,9 +41,6 @@ export default class BeastformField extends fields.SchemaField {
|
|||
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
|
||||
*/
|
||||
static async execute(config) {
|
||||
// Should not be useful anymore here
|
||||
await BeastformField.handleActiveTransformations.call(this);
|
||||
|
||||
const { selected, evolved, hybrid } = await BeastformDialog.configure(config, this.item);
|
||||
if (!selected) return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,9 @@ export default class DamageField extends fields.SchemaField {
|
|||
});
|
||||
}
|
||||
|
||||
const token = game.scenes.find(x => x.active).tokens.find(x => x.id === target.id);
|
||||
const token = target.id
|
||||
? game.scenes.find(x => x.active).tokens.find(x => x.id === target.id)
|
||||
: actor.prototypeToken;
|
||||
if (config.hasHealing)
|
||||
damagePromises.push(
|
||||
actor.takeHealing(config.damage).then(updates => targetDamage.push({ token, updates }))
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export default class TargetField extends fields.SchemaField {
|
|||
if (!this.target?.type) return (config.targets = []);
|
||||
config.hasTarget = true;
|
||||
let targets;
|
||||
// If the Action is configured as self-targeted, set targets as the owner.
|
||||
// If the Action is configured as self-targeted, set targets as the owner. Probably better way than to fallback to getDependentTokens
|
||||
if (this.target?.type === CONFIG.DH.GENERAL.targetTypes.self.id)
|
||||
targets = [this.actor.token ?? this.actor.prototypeToken];
|
||||
else {
|
||||
|
|
@ -72,17 +72,17 @@ export default class TargetField extends fields.SchemaField {
|
|||
|
||||
/**
|
||||
* Format actor to useful datas for Action roll workflow.
|
||||
* @param {*} actor Actor object to format.
|
||||
* @param {*} token Token object to format.
|
||||
* @returns {*} Formatted Actor.
|
||||
*/
|
||||
static formatTarget(actor) {
|
||||
static formatTarget(token) {
|
||||
return {
|
||||
id: actor.id,
|
||||
actorId: actor.actor.uuid,
|
||||
name: actor.actor.name,
|
||||
img: actor.actor.img,
|
||||
difficulty: actor.actor.system.difficulty,
|
||||
evasion: actor.actor.system.evasion,
|
||||
id: token.id,
|
||||
actorId: token.actor.uuid,
|
||||
name: token.actor.name,
|
||||
img: token.actor.img,
|
||||
difficulty: token.actor.system.difficulty,
|
||||
evasion: token.actor.system.evasion,
|
||||
saved: {
|
||||
value: null,
|
||||
success: null
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ export default class ForeignDocumentUUIDArrayField extends foundry.data.fields.A
|
|||
/** @inheritdoc */
|
||||
initialize(value, model, options = {}) {
|
||||
const v = super.initialize(value, model, options);
|
||||
return () => v.map(entry => (typeof entry === 'function' ? entry() : entry));
|
||||
return () => {
|
||||
const data = v.map(entry => (typeof entry === 'function' ? entry() : entry));
|
||||
return this.options.prune ? data.filter(d => !!d) : data;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,7 +160,6 @@ export default class DHBeastform extends BaseDataItem {
|
|||
this.parent.effects.filter(x => x.type !== 'beastform').map(x => x.toObject())
|
||||
);
|
||||
|
||||
const tokenImages = await this.parent.parent.getTokenImages();
|
||||
const beastformEffect = this.parent.effects.find(x => x.type === 'beastform');
|
||||
await beastformEffect.updateSource({
|
||||
changes: [
|
||||
|
|
@ -175,7 +174,8 @@ export default class DHBeastform extends BaseDataItem {
|
|||
],
|
||||
system: {
|
||||
characterTokenData: {
|
||||
tokenImg: tokenImages[0],
|
||||
usesDynamicToken: this.parent.parent.prototypeToken.ring.enabled,
|
||||
tokenImg: this.parent.parent.prototypeToken.texture.src,
|
||||
tokenRingImg: this.parent.parent.prototypeToken.ring.subject.texture,
|
||||
tokenSize: {
|
||||
height: this.parent.parent.prototypeToken.height,
|
||||
|
|
@ -190,7 +190,7 @@ export default class DHBeastform extends BaseDataItem {
|
|||
|
||||
await this.parent.parent.createEmbeddedDocuments('ActiveEffect', [beastformEffect.toObject()]);
|
||||
|
||||
await updateActorTokens(this.parent.parent, {
|
||||
const prototypeTokenUpdate = {
|
||||
height: this.tokenSize.height,
|
||||
width: this.tokenSize.width,
|
||||
texture: {
|
||||
|
|
@ -201,22 +201,20 @@ export default class DHBeastform extends BaseDataItem {
|
|||
texture: this.tokenRingImg
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const tokenUpdate = token => ({
|
||||
...prototypeTokenUpdate,
|
||||
flags: {
|
||||
daggerheart: {
|
||||
beastformTokenImg: token.texture.src,
|
||||
beastformSubjectTexture: token.ring.subject.texture
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await updateActorTokens(this.parent.parent, prototypeTokenUpdate, tokenUpdate);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_onCreate(_data, _options, userId) {
|
||||
if (userId !== game.user.id) return;
|
||||
|
||||
if (!this.parent.effects.find(x => x.type === 'beastform')) {
|
||||
this.parent.createEmbeddedDocuments('ActiveEffect', [
|
||||
{
|
||||
type: 'beastform',
|
||||
name: game.i18n.localize('DAGGERHEART.ITEMS.Beastform.beastformEffect'),
|
||||
img: 'icons/creatures/abilities/paw-print-pair-purple.webp'
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
import { defaultRestOptions } from '../../config/generalConfig.mjs';
|
||||
import { ActionsField } from '../fields/actionField.mjs';
|
||||
|
||||
const currencyField = (initial, label) =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
enabled: new foundry.data.fields.BooleanField({ required: true, initial: true }),
|
||||
label: new foundry.data.fields.StringField({
|
||||
required: true,
|
||||
initial,
|
||||
label
|
||||
})
|
||||
});
|
||||
|
||||
export default class DhHomebrew extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
|
@ -30,36 +40,15 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
|||
initial: () => [2, 1, 1, 0, 0, -1]
|
||||
}),
|
||||
currency: new fields.SchemaField({
|
||||
enabled: new fields.BooleanField({
|
||||
required: true,
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.enabled'
|
||||
}),
|
||||
title: new fields.StringField({
|
||||
required: true,
|
||||
initial: 'Gold',
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.currencyName'
|
||||
}),
|
||||
coins: new fields.StringField({
|
||||
required: true,
|
||||
initial: 'Coins',
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.coinName'
|
||||
}),
|
||||
handfuls: new fields.StringField({
|
||||
required: true,
|
||||
initial: 'Handfuls',
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.handfulName'
|
||||
}),
|
||||
bags: new fields.StringField({
|
||||
required: true,
|
||||
initial: 'Bags',
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.bagName'
|
||||
}),
|
||||
chests: new fields.StringField({
|
||||
required: true,
|
||||
initial: 'Chests',
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.chestName'
|
||||
})
|
||||
coins: currencyField('Coins', 'DAGGERHEART.SETTINGS.Homebrew.currency.coinName'),
|
||||
handfuls: currencyField('Handfuls', 'DAGGERHEART.SETTINGS.Homebrew.currency.handfulName'),
|
||||
bags: currencyField('Bags', 'DAGGERHEART.SETTINGS.Homebrew.currency.bagName'),
|
||||
chests: currencyField('Chests', 'DAGGERHEART.SETTINGS.Homebrew.currency.chestName')
|
||||
}),
|
||||
restMoves: new fields.SchemaField({
|
||||
longRest: new fields.SchemaField({
|
||||
|
|
@ -146,4 +135,26 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
|||
})
|
||||
};
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
_initializeSource(source, options = {}) {
|
||||
source = super._initializeSource(source, options);
|
||||
source.currency.coins = {
|
||||
enabled: source.currency.coins.enabled ?? true,
|
||||
label: source.currency.coins.label || source.currency.coins
|
||||
};
|
||||
source.currency.handfuls = {
|
||||
enabled: source.currency.handfuls.enabled ?? true,
|
||||
label: source.currency.handfuls.label || source.currency.handfuls
|
||||
};
|
||||
source.currency.bags = {
|
||||
enabled: source.currency.bags.enabled ?? true,
|
||||
label: source.currency.bags.label || source.currency.bags
|
||||
};
|
||||
source.currency.chests = {
|
||||
enabled: source.currency.chests.enabled ?? true,
|
||||
label: source.currency.chests.label || source.currency.chests
|
||||
};
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,9 +29,11 @@ export default class DHRoll extends Roll {
|
|||
config.hooks = [...this.getHooks(), ''];
|
||||
config.dialog ??= {};
|
||||
|
||||
const actorIdSplit = config.source.actor.split('.');
|
||||
const tagTeamSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);
|
||||
config.tagTeamSelected = tagTeamSettings.members[actorIdSplit[actorIdSplit.length - 1]];
|
||||
const actorIdSplit = config.source?.actor?.split('.');
|
||||
if (actorIdSplit) {
|
||||
const tagTeamSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);
|
||||
config.tagTeamSelected = tagTeamSettings.members[actorIdSplit[actorIdSplit.length - 1]];
|
||||
}
|
||||
|
||||
for (const hook of config.hooks) {
|
||||
if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null;
|
||||
|
|
|
|||
|
|
@ -145,9 +145,11 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
});
|
||||
|
||||
html.querySelectorAll('.token-target-container').forEach(element => {
|
||||
element.addEventListener('pointerover', this.hoverTarget);
|
||||
element.addEventListener('pointerout', this.unhoverTarget);
|
||||
element.addEventListener('click', this.clickTarget);
|
||||
if (element.dataset.token) {
|
||||
element.addEventListener('pointerover', this.hoverTarget);
|
||||
element.addEventListener('pointerout', this.unhoverTarget);
|
||||
element.addEventListener('click', this.clickTarget);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -251,15 +251,22 @@ export const adjustRange = (rangeVal, decrease) => {
|
|||
return range[rangeKeys[newIndex]];
|
||||
};
|
||||
|
||||
export const updateActorTokens = async (actor, update) => {
|
||||
/**
|
||||
*
|
||||
* @param {DhActor} actor - The actor for which all tokens will run a data update.
|
||||
* @param {string} update - The data update to be applied to all tokens.
|
||||
* @param {func} updateToken - Optional, specific data update for the non-prototype tokens as a function using the token data. Useful to handle wildcard images where each token has a different image but the prototype has a wildcard path.
|
||||
*/
|
||||
export const updateActorTokens = async (actor, update, updateToken) => {
|
||||
await actor.prototypeToken.update({ ...update });
|
||||
|
||||
/* Update the tokens in all scenes belonging to Actor */
|
||||
for (let token of actor.getDependentTokens()) {
|
||||
const tokenActor = token.baseActor ?? token.actor;
|
||||
if (tokenActor?.id === actor.id) {
|
||||
if (token.id && tokenActor?.id === actor.id) {
|
||||
await token.update({
|
||||
...update
|
||||
...(updateToken ? updateToken(token) : update),
|
||||
_id: token.id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ export async function runMigrations() {
|
|||
let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion);
|
||||
if (!lastMigrationVersion) lastMigrationVersion = game.system.version;
|
||||
|
||||
//#region old migrations
|
||||
if (foundry.utils.isNewerVersion('1.1.0', lastMigrationVersion)) {
|
||||
const lockedPacks = [];
|
||||
const compendiumActors = [];
|
||||
|
|
@ -190,6 +191,7 @@ export async function runMigrations() {
|
|||
|
||||
lastMigrationVersion = '1.2.0';
|
||||
}
|
||||
//#endregion
|
||||
|
||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue