mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 11:41:08 +01:00
Merged with development
This commit is contained in:
commit
bd76e22e8d
1096 changed files with 11080 additions and 5102 deletions
|
|
@ -2,6 +2,7 @@ export * as characterCreation from './characterCreation/_module.mjs';
|
|||
export * as dialogs from './dialogs/_module.mjs';
|
||||
export * as hud from './hud/_module.mjs';
|
||||
export * as levelup from './levelup/_module.mjs';
|
||||
export * as scene from './scene/_module.mjs';
|
||||
export * as settings from './settings/_module.mjs';
|
||||
export * as sheets from './sheets/_module.mjs';
|
||||
export * as sheetConfigs from './sheets-configs/_module.mjs';
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
export { default as AttributionDialog } from './attributionDialog.mjs';
|
||||
export { default as BeastformDialog } from './beastformDialog.mjs';
|
||||
export { default as d20RollDialog } from './d20RollDialog.mjs';
|
||||
export { default as DamageDialog } from './damageDialog.mjs';
|
||||
|
|
|
|||
93
module/applications/dialogs/attributionDialog.mjs
Normal file
93
module/applications/dialogs/attributionDialog.mjs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import autocomplete from 'autocompleter';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class AttriubtionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(item) {
|
||||
super({});
|
||||
|
||||
this.item = item;
|
||||
this.sources = Object.keys(CONFIG.DH.GENERAL.attributionSources).flatMap(groupKey => {
|
||||
const group = CONFIG.DH.GENERAL.attributionSources[groupKey];
|
||||
return group.values.map(x => ({ group: group.label, ...x }));
|
||||
});
|
||||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.APPLICATIONS.Attribution.title');
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'dh-style', 'dialog', 'views', 'attribution'],
|
||||
position: { width: 'auto', height: 'auto' },
|
||||
window: { icon: 'fa-solid fa-signature' },
|
||||
form: { handler: this.updateData, submitOnChange: false, closeOnSubmit: true }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
main: { template: 'systems/daggerheart/templates/dialogs/attribution.hbs' }
|
||||
};
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
const sources = this.sources;
|
||||
|
||||
htmlElement.querySelectorAll('.attribution-input').forEach(element => {
|
||||
autocomplete({
|
||||
input: element,
|
||||
fetch: function (text, update) {
|
||||
if (!text) {
|
||||
update(sources);
|
||||
} else {
|
||||
text = text.toLowerCase();
|
||||
var suggestions = sources.filter(n => n.label.toLowerCase().includes(text));
|
||||
update(suggestions);
|
||||
}
|
||||
},
|
||||
render: function (item, search) {
|
||||
const label = game.i18n.localize(item.label);
|
||||
const matchIndex = label.toLowerCase().indexOf(search);
|
||||
|
||||
const beforeText = label.slice(0, matchIndex);
|
||||
const matchText = label.slice(matchIndex, matchIndex + search.length);
|
||||
const after = label.slice(matchIndex + search.length, label.length);
|
||||
|
||||
const element = document.createElement('li');
|
||||
element.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
|
||||
if (item.hint) {
|
||||
element.dataset.tooltip = game.i18n.localize(item.hint);
|
||||
}
|
||||
|
||||
return element;
|
||||
},
|
||||
renderGroup: function (label) {
|
||||
const itemElement = document.createElement('div');
|
||||
itemElement.textContent = game.i18n.localize(label);
|
||||
return itemElement;
|
||||
},
|
||||
onSelect: function (item) {
|
||||
element.value = item.label;
|
||||
},
|
||||
click: e => e.fetch(),
|
||||
customize: function (_input, _inputRect, container) {
|
||||
container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ;
|
||||
},
|
||||
minLength: 0
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.item = this.item;
|
||||
context.data = this.item.system.attribution;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateData(_event, _element, formData) {
|
||||
await this.item.update({ 'system.attribution': formData.object });
|
||||
this.item.sheet.refreshFrame();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
export { default as CharacterLevelup } from './characterLevelup.mjs';
|
||||
export { default as CompanionLevelup } from './companionLevelup.mjs';
|
||||
export { default as Levelup } from './levelup.mjs';
|
||||
export { default as LevelupViewMode } from './levelupViewMode.mjs';
|
||||
|
|
|
|||
95
module/applications/levelup/levelupViewMode.mjs
Normal file
95
module/applications/levelup/levelupViewMode.mjs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import { chunkify } from '../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhlevelUpViewMode extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(actor) {
|
||||
super({});
|
||||
|
||||
this.actor = actor;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.format('DAGGERHEART.APPLICATIONS.Levelup.viewModeTitle', { actor: this.actor.name });
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'levelup'],
|
||||
position: { width: 1000, height: 'auto' },
|
||||
window: {
|
||||
resizable: true,
|
||||
icon: 'fa-solid fa-arrow-turn-up'
|
||||
}
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
main: { template: 'systems/daggerheart/templates/levelup/tabs/viewMode.hbs' }
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
|
||||
const { tiers } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers);
|
||||
const tierKeys = Object.keys(tiers);
|
||||
const selections = Object.keys(this.actor.system.levelData.levelups).reduce(
|
||||
(acc, key) => {
|
||||
const level = this.actor.system.levelData.levelups[key];
|
||||
Object.keys(level.selections).forEach(optionKey => {
|
||||
const choice = level.selections[optionKey];
|
||||
if (!acc[choice.tier][choice.optionKey]) acc[choice.tier][choice.optionKey] = {};
|
||||
acc[choice.tier][choice.optionKey][choice.checkboxNr] = choice;
|
||||
});
|
||||
|
||||
return acc;
|
||||
},
|
||||
tierKeys.reduce((acc, key) => {
|
||||
acc[key] = {};
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
|
||||
context.tiers = tierKeys.map((tierKey, tierIndex) => {
|
||||
const tier = tiers[tierKey];
|
||||
|
||||
return {
|
||||
name: tier.name,
|
||||
active: true,
|
||||
groups: Object.keys(tier.options).map(optionKey => {
|
||||
const option = tier.options[optionKey];
|
||||
|
||||
const checkboxes = [...Array(option.checkboxSelections).keys()].flatMap(index => {
|
||||
const checkboxNr = index + 1;
|
||||
const checkboxData = selections[tierKey]?.[optionKey]?.[checkboxNr];
|
||||
const checkbox = { ...option, checkboxNr, tier: tierKey, disabled: true };
|
||||
|
||||
if (checkboxData) {
|
||||
checkbox.level = checkboxData.level;
|
||||
checkbox.selected = true;
|
||||
}
|
||||
|
||||
return checkbox;
|
||||
});
|
||||
|
||||
let label = game.i18n.localize(option.label);
|
||||
return {
|
||||
label: label,
|
||||
checkboxGroups: chunkify(checkboxes, option.minCost, chunkedBoxes => {
|
||||
const anySelected = chunkedBoxes.some(x => x.selected);
|
||||
const anyDisabled = chunkedBoxes.some(x => x.disabled);
|
||||
return {
|
||||
multi: option.minCost > 1,
|
||||
checkboxes: chunkedBoxes.map(x => ({
|
||||
...x,
|
||||
selected: anySelected,
|
||||
disabled: anyDisabled
|
||||
}))
|
||||
};
|
||||
})
|
||||
};
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
||||
1
module/applications/scene/_module.mjs
Normal file
1
module/applications/scene/_module.mjs
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default as DhSceneConfigSettings } from './sceneConfigSettings.mjs';
|
||||
24
module/applications/scene/sceneConfigSettings.mjs
Normal file
24
module/applications/scene/sceneConfigSettings.mjs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
export default class DhSceneConfigSettings extends foundry.applications.sheets.SceneConfig {
|
||||
constructor(options, ...args) {
|
||||
super(options, ...args);
|
||||
}
|
||||
|
||||
static buildParts() {
|
||||
const { footer, ...parts } = super.PARTS;
|
||||
const tmpParts = {
|
||||
...parts,
|
||||
dh: { template: 'systems/daggerheart/templates/scene/dh-config.hbs' },
|
||||
footer
|
||||
};
|
||||
return tmpParts;
|
||||
}
|
||||
|
||||
static PARTS = DhSceneConfigSettings.buildParts();
|
||||
|
||||
static buildTabs() {
|
||||
super.TABS.sheet.tabs.push({ id: 'dh', icon: 'fa-solid' });
|
||||
return super.TABS;
|
||||
}
|
||||
|
||||
static TABS = DhSceneConfigSettings.buildTabs();
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { DhHomebrew } from '../../data/settings/_module.mjs';
|
||||
import { slugify } from '../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhHomebrewSettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
|
|
@ -10,11 +11,14 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).toObject()
|
||||
);
|
||||
|
||||
this.selected = {
|
||||
domain: null
|
||||
};
|
||||
this.selected = this.#getDefaultAdversaryType();
|
||||
}
|
||||
|
||||
#getDefaultAdversaryType = () => ({
|
||||
domain: null,
|
||||
adversaryType: null
|
||||
});
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
|
||||
}
|
||||
|
|
@ -35,6 +39,9 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
addDomain: this.addDomain,
|
||||
toggleSelectedDomain: this.toggleSelectedDomain,
|
||||
deleteDomain: this.deleteDomain,
|
||||
addAdversaryType: this.addAdversaryType,
|
||||
deleteAdversaryType: this.deleteAdversaryType,
|
||||
selectAdversaryType: this.selectAdversaryType,
|
||||
save: this.save,
|
||||
reset: this.reset
|
||||
},
|
||||
|
|
@ -45,6 +52,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
settings: { template: 'systems/daggerheart/templates/settings/homebrew-settings/settings.hbs' },
|
||||
domains: { template: 'systems/daggerheart/templates/settings/homebrew-settings/domains.hbs' },
|
||||
types: { template: 'systems/daggerheart/templates/settings/homebrew-settings/types.hbs' },
|
||||
downtime: { template: 'systems/daggerheart/templates/settings/homebrew-settings/downtime.hbs' },
|
||||
footer: { template: 'systems/daggerheart/templates/settings/homebrew-settings/footer.hbs' }
|
||||
};
|
||||
|
|
@ -52,12 +60,19 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
/** @inheritdoc */
|
||||
static TABS = {
|
||||
main: {
|
||||
tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'downtime' }],
|
||||
tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'types' }, { id: 'downtime' }],
|
||||
initial: 'settings',
|
||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||
}
|
||||
};
|
||||
|
||||
changeTab(tab, group, options) {
|
||||
super.changeTab(tab, group, options);
|
||||
this.selected = this.#getDefaultAdversaryType();
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.settingFields = this.settings;
|
||||
|
|
@ -79,6 +94,11 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
context.configDomains = CONFIG.DH.DOMAIN.domains;
|
||||
context.homebrewDomains = this.settings.domains;
|
||||
break;
|
||||
case 'types':
|
||||
context.selectedAdversaryType = this.selected.adversaryType
|
||||
? { id: this.selected.adversaryType, ...this.settings.adversaryTypes[this.selected.adversaryType] }
|
||||
: null;
|
||||
break;
|
||||
}
|
||||
|
||||
return context;
|
||||
|
|
@ -301,6 +321,32 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
this.render();
|
||||
}
|
||||
|
||||
static async addAdversaryType(_, target) {
|
||||
const newId = foundry.utils.randomID();
|
||||
await this.settings.updateSource({
|
||||
[`adversaryTypes.${newId}`]: {
|
||||
id: newId,
|
||||
label: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.adversaryType.newType')
|
||||
}
|
||||
});
|
||||
|
||||
this.selected.adversaryType = newId;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async deleteAdversaryType(_, target) {
|
||||
const { key } = target.dataset;
|
||||
await this.settings.updateSource({ [`adversaryTypes.-=${key}`]: null });
|
||||
|
||||
this.selected.adversaryType = this.selected.adversaryType === key ? null : this.selected.adversaryType;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async selectAdversaryType(_, target) {
|
||||
this.selected.adversaryType = this.selected.adversaryType === target.dataset.type ? null : target.dataset.type;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async save() {
|
||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||
this.close();
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import DHBaseActorSheet from '../api/base-actor.mjs';
|
|||
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
||||
|
||||
export default class AdversarySheet extends DHBaseActorSheet {
|
||||
/** @inheritDoc */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['adversary'],
|
||||
position: { width: 660, height: 766 },
|
||||
|
|
@ -12,16 +13,34 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
|||
reactionRoll: AdversarySheet.#reactionRoll
|
||||
},
|
||||
window: {
|
||||
resizable: true
|
||||
resizable: true,
|
||||
controls: [
|
||||
{
|
||||
icon: 'fa-solid fa-signature',
|
||||
label: 'DAGGERHEART.UI.Tooltip.configureAttribution',
|
||||
action: 'editAttribution'
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
sidebar: { template: 'systems/daggerheart/templates/sheets/actors/adversary/sidebar.hbs' },
|
||||
sidebar: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/adversary/sidebar.hbs',
|
||||
scrollable: ['.shortcut-items-section']
|
||||
},
|
||||
header: { template: 'systems/daggerheart/templates/sheets/actors/adversary/header.hbs' },
|
||||
features: { template: 'systems/daggerheart/templates/sheets/actors/adversary/features.hbs' },
|
||||
notes: { template: 'systems/daggerheart/templates/sheets/actors/adversary/notes.hbs' },
|
||||
effects: { template: 'systems/daggerheart/templates/sheets/actors/adversary/effects.hbs' }
|
||||
features: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/adversary/features.hbs',
|
||||
scrollable: ['.feature-section']
|
||||
},
|
||||
notes: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/adversary/notes.hbs'
|
||||
},
|
||||
effects: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/adversary/effects.hbs',
|
||||
scrollable: ['.effects-sections']
|
||||
}
|
||||
};
|
||||
|
||||
/** @inheritdoc */
|
||||
|
|
@ -37,6 +56,7 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
|||
async _prepareContext(options) {
|
||||
const context = await super._prepareContext(options);
|
||||
context.systemFields.attack.fields = this.document.system.attack.schema.fields;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +66,9 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
|||
switch (partId) {
|
||||
case 'header':
|
||||
await this._prepareHeaderContext(context, options);
|
||||
|
||||
const adversaryTypes = CONFIG.DH.ACTOR.allAdversaryTypes();
|
||||
context.adversaryType = game.i18n.localize(adversaryTypes[this.document.system.type].label);
|
||||
break;
|
||||
case 'notes':
|
||||
await this._prepareNotesContext(context, options);
|
||||
|
|
@ -60,6 +83,7 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
|||
|
||||
htmlElement.querySelectorAll('.inventory-item-resource').forEach(element => {
|
||||
element.addEventListener('change', this.updateItemResource.bind(this));
|
||||
element.addEventListener('click', e => e.stopPropagation());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import DHBaseActorSheet from '../api/base-actor.mjs';
|
||||
import DhpDeathMove from '../../dialogs/deathMove.mjs';
|
||||
import { abilities } from '../../../config/actorConfig.mjs';
|
||||
import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs';
|
||||
import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs';
|
||||
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
|
||||
import FilterMenu from '../../ux/filter-menu.mjs';
|
||||
import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs';
|
||||
|
|
@ -23,6 +23,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
openPack: CharacterSheet.#openPack,
|
||||
makeDeathMove: CharacterSheet.#makeDeathMove,
|
||||
levelManagement: CharacterSheet.#levelManagement,
|
||||
viewLevelups: CharacterSheet.#viewLevelups,
|
||||
toggleEquipItem: CharacterSheet.#toggleEquipItem,
|
||||
toggleResourceDice: CharacterSheet.#toggleResourceDice,
|
||||
handleResourceDice: CharacterSheet.#handleResourceDice,
|
||||
|
|
@ -30,7 +31,14 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
tempBrowser: CharacterSheet.#tempBrowser
|
||||
},
|
||||
window: {
|
||||
resizable: true
|
||||
resizable: true,
|
||||
controls: [
|
||||
{
|
||||
icon: 'fa-solid fa-angles-up',
|
||||
label: 'DAGGERHEART.ACTORS.Character.viewLevelups',
|
||||
action: 'viewLevelups'
|
||||
}
|
||||
]
|
||||
},
|
||||
dragDrop: [
|
||||
{
|
||||
|
|
@ -70,6 +78,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
static PARTS = {
|
||||
sidebar: {
|
||||
id: 'sidebar',
|
||||
scrollable: ['.shortcut-items-section'],
|
||||
template: 'systems/daggerheart/templates/sheets/actors/character/sidebar.hbs'
|
||||
},
|
||||
header: {
|
||||
|
|
@ -78,22 +87,27 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
},
|
||||
features: {
|
||||
id: 'features',
|
||||
scrollable: ['.features-sections'],
|
||||
template: 'systems/daggerheart/templates/sheets/actors/character/features.hbs'
|
||||
},
|
||||
loadout: {
|
||||
id: 'loadout',
|
||||
scrollable: ['.items-section'],
|
||||
template: 'systems/daggerheart/templates/sheets/actors/character/loadout.hbs'
|
||||
},
|
||||
inventory: {
|
||||
id: 'inventory',
|
||||
scrollable: ['.items-section'],
|
||||
template: 'systems/daggerheart/templates/sheets/actors/character/inventory.hbs'
|
||||
},
|
||||
biography: {
|
||||
id: 'biography',
|
||||
scrollable: ['.items-section'],
|
||||
template: 'systems/daggerheart/templates/sheets/actors/character/biography.hbs'
|
||||
},
|
||||
effects: {
|
||||
id: 'effects',
|
||||
scrollable: ['.effects-sections'],
|
||||
template: 'systems/daggerheart/templates/sheets/actors/character/effects.hbs'
|
||||
}
|
||||
};
|
||||
|
|
@ -114,6 +128,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
|
||||
htmlElement.querySelectorAll('.inventory-item-resource').forEach(element => {
|
||||
element.addEventListener('change', this.updateItemResource.bind(this));
|
||||
element.addEventListener('click', e => e.stopPropagation());
|
||||
});
|
||||
htmlElement.querySelectorAll('.inventory-item-quantity').forEach(element => {
|
||||
element.addEventListener('change', this.updateItemQuantity.bind(this));
|
||||
|
|
@ -585,7 +600,14 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
if (!value || !subclass)
|
||||
return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.missingClassOrSubclass'));
|
||||
|
||||
new DhCharacterlevelUp(this.document).render({ force: true });
|
||||
new CharacterLevelup(this.document).render({ force: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the charater level management window in viewMode.
|
||||
*/
|
||||
static #viewLevelups() {
|
||||
new LevelupViewMode(this.document).render({ force: true });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -638,7 +660,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
ability: abilityLabel
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
this.consumeResource(result?.costs);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ export default class DhCompanionSheet extends DHBaseActorSheet {
|
|||
static PARTS = {
|
||||
header: { template: 'systems/daggerheart/templates/sheets/actors/companion/header.hbs' },
|
||||
details: { template: 'systems/daggerheart/templates/sheets/actors/companion/details.hbs' },
|
||||
effects: { template: 'systems/daggerheart/templates/sheets/actors/companion/effects.hbs' }
|
||||
effects: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/companion/effects.hbs',
|
||||
scrollable: ['.effects-sections']
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -8,10 +8,17 @@ export default class DhpEnvironment extends DHBaseActorSheet {
|
|||
classes: ['environment'],
|
||||
position: {
|
||||
width: 500,
|
||||
height: 725
|
||||
height: 740
|
||||
},
|
||||
window: {
|
||||
resizable: true
|
||||
resizable: true,
|
||||
controls: [
|
||||
{
|
||||
icon: 'fa-solid fa-signature',
|
||||
label: 'DAGGERHEART.UI.Tooltip.configureAttribution',
|
||||
action: 'editAttribution'
|
||||
}
|
||||
]
|
||||
},
|
||||
actions: {},
|
||||
dragDrop: [{ dragSelector: '.action-section .inventory-item', dropSelector: null }]
|
||||
|
|
@ -20,9 +27,13 @@ export default class DhpEnvironment extends DHBaseActorSheet {
|
|||
/**@override */
|
||||
static PARTS = {
|
||||
header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' },
|
||||
features: { template: 'systems/daggerheart/templates/sheets/actors/environment/features.hbs' },
|
||||
features: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/environment/features.hbs',
|
||||
scrollable: ['feature-section']
|
||||
},
|
||||
potentialAdversaries: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/environment/potentialAdversaries.hbs'
|
||||
template: 'systems/daggerheart/templates/sheets/actors/environment/potentialAdversaries.hbs',
|
||||
scrollable: ['items-sections']
|
||||
},
|
||||
notes: { template: 'systems/daggerheart/templates/sheets/actors/environment/notes.hbs' }
|
||||
};
|
||||
|
|
@ -42,6 +53,7 @@ export default class DhpEnvironment extends DHBaseActorSheet {
|
|||
switch (partId) {
|
||||
case 'header':
|
||||
await this._prepareHeaderContext(context, options);
|
||||
|
||||
break;
|
||||
case 'notes':
|
||||
await this._prepareNotesContext(context, options);
|
||||
|
|
|
|||
|
|
@ -85,6 +85,8 @@ export default function DHApplicationMixin(Base) {
|
|||
this._dragDrop = this._createDragDropHandlers();
|
||||
}
|
||||
|
||||
#nonHeaderAttribution = ['environment', 'ancestry', 'community', 'domainCard'];
|
||||
|
||||
/**
|
||||
* The default options for the sheet.
|
||||
* @type {DHSheetV2Configuration}
|
||||
|
|
@ -101,7 +103,8 @@ export default function DHApplicationMixin(Base) {
|
|||
toggleEffect: DHSheetV2.#toggleEffect,
|
||||
toggleExtended: DHSheetV2.#toggleExtended,
|
||||
addNewItem: DHSheetV2.#addNewItem,
|
||||
browseItem: DHSheetV2.#browseItem
|
||||
browseItem: DHSheetV2.#browseItem,
|
||||
editAttribution: DHSheetV2.#editAttribution
|
||||
},
|
||||
contextMenus: [
|
||||
{
|
||||
|
|
@ -121,10 +124,47 @@ export default function DHApplicationMixin(Base) {
|
|||
}
|
||||
}
|
||||
],
|
||||
dragDrop: [],
|
||||
dragDrop: [{ dragSelector: '.inventory-item[data-type="effect"]', dropSelector: null }],
|
||||
tagifyConfigs: []
|
||||
};
|
||||
|
||||
/**@inheritdoc */
|
||||
async _renderFrame(options) {
|
||||
const frame = await super._renderFrame(options);
|
||||
|
||||
const hideAttribution = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.appearance
|
||||
).hideAttribution;
|
||||
const headerAttribution = !this.#nonHeaderAttribution.includes(this.document.type);
|
||||
if (!hideAttribution && this.document.system.metadata.hasAttribution && headerAttribution) {
|
||||
const { source, page } = this.document.system.attribution;
|
||||
const attribution = [source, page ? `pg ${page}.` : null].filter(x => x).join('. ');
|
||||
const element = `<label class="attribution-header-label">${attribution}</label>`;
|
||||
this.window.controls.insertAdjacentHTML('beforebegin', element);
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the custom parts of the application frame
|
||||
*/
|
||||
refreshFrame() {
|
||||
const hideAttribution = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.appearance
|
||||
).hideAttribution;
|
||||
const headerAttribution = !this.#nonHeaderAttribution.includes(this.document.type);
|
||||
if (!hideAttribution && this.document.system.metadata.hasAttribution && headerAttribution) {
|
||||
const { source, page } = this.document.system.attribution;
|
||||
const attribution = [source, page ? `pg ${page}.` : null].filter(x => x).join('. ');
|
||||
|
||||
const label = this.window.header.querySelector('.attribution-header-label');
|
||||
label.innerHTML = attribution;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Related documents that should cause a rerender of this application when updated.
|
||||
*/
|
||||
|
|
@ -249,14 +289,37 @@ export default function DHApplicationMixin(Base) {
|
|||
* @param {DragEvent} event
|
||||
* @protected
|
||||
*/
|
||||
_onDragStart(event) {}
|
||||
async _onDragStart(event) {
|
||||
const inventoryItem = event.currentTarget.closest('.inventory-item');
|
||||
if (inventoryItem) {
|
||||
const { type, itemUuid } = inventoryItem.dataset;
|
||||
if (type === 'effect') {
|
||||
const effect = await foundry.utils.fromUuid(itemUuid);
|
||||
const effectData = {
|
||||
type: 'ActiveEffect',
|
||||
data: { ...effect.toObject(), _id: null },
|
||||
fromInternal: this.document.uuid
|
||||
};
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(effectData));
|
||||
event.dataTransfer.setDragImage(inventoryItem.querySelector('img'), 60, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle drop event.
|
||||
* @param {DragEvent} event
|
||||
* @protected
|
||||
*/
|
||||
_onDrop(event) {}
|
||||
_onDrop(event) {
|
||||
event.stopPropagation();
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
if (data.fromInternal === this.document.uuid) return;
|
||||
|
||||
if (data.type === 'ActiveEffect') {
|
||||
this.document.createEmbeddedDocuments('ActiveEffect', [data.data]);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Context Menu */
|
||||
|
|
@ -548,6 +611,14 @@ export default function DHApplicationMixin(Base) {
|
|||
return new ItemBrowser({ presets }).render({ force: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the attribution dialog
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #editAttribution() {
|
||||
new game.system.api.applications.dialogs.AttributionDialog(this.document).render({ force: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an embedded document.
|
||||
* @type {ApplicationClickAction}
|
||||
|
|
@ -568,7 +639,6 @@ export default function DHApplicationMixin(Base) {
|
|||
if (featureOnCharacter) {
|
||||
systemData = {
|
||||
originItemType: this.document.type,
|
||||
originId: this.document.id,
|
||||
identifier: this.document.system.isMulticlass ? 'multiclass' : null
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.isNPC = this.document.isNPC;
|
||||
context.showAttribution = !game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
|
||||
.hideAttribution;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
@ -195,6 +198,8 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
};
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(attackData));
|
||||
event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0);
|
||||
} else if (this.document.type !== 'environment') {
|
||||
super._onDragStart(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,16 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
static DEFAULT_OPTIONS = {
|
||||
classes: ['item'],
|
||||
position: { width: 600 },
|
||||
window: { resizable: true },
|
||||
window: {
|
||||
resizable: true,
|
||||
controls: [
|
||||
{
|
||||
icon: 'fa-solid fa-signature',
|
||||
label: 'DAGGERHEART.UI.Tooltip.configureAttribution',
|
||||
action: 'editAttribution'
|
||||
}
|
||||
]
|
||||
},
|
||||
form: {
|
||||
submitOnChange: true
|
||||
},
|
||||
|
|
@ -55,6 +64,15 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
/* Prepare Context */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@inheritdoc */
|
||||
async _prepareContext(options) {
|
||||
const context = super._prepareContext(options);
|
||||
context.showAttribution = !game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
|
||||
.hideAttribution;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
async _preparePartContext(partId, context, options) {
|
||||
await super._preparePartContext(partId, context, options);
|
||||
|
|
@ -149,12 +167,12 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
const { type } = target.dataset;
|
||||
const cls = foundry.documents.Item.implementation;
|
||||
|
||||
const multiclass = this.document.system.isMulticlass ? 'multiclass' : null;
|
||||
let systemData = {};
|
||||
if (this.document.parent?.type === 'character') {
|
||||
systemData = {
|
||||
originItemType: this.document.type,
|
||||
originId: this.document.id,
|
||||
identifier: this.document.system.isMulticlass ? 'multiclass' : null
|
||||
identifier: multiclass ?? type
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -252,6 +270,8 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
};
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(actionData));
|
||||
event.dataTransfer.setDragImage(actionItem.querySelector('img'), 60, 0);
|
||||
} else {
|
||||
super._onDragStart(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -261,6 +281,8 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
* @param {DragEvent} event - The drag event
|
||||
*/
|
||||
async _onDrop(event) {
|
||||
super._onDrop(event);
|
||||
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
if (data.fromInternal) return;
|
||||
|
||||
|
|
@ -271,14 +293,15 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
|
||||
if (this.document.parent?.type === 'character') {
|
||||
const itemData = item.toObject();
|
||||
const multiclass = this.document.system.isMulticlass ? 'multiclass' : null;
|
||||
item = await cls.create(
|
||||
{
|
||||
...itemData,
|
||||
_stats: { compendiumSource: this.document.uuid },
|
||||
system: {
|
||||
...itemData.system,
|
||||
originItemType: this.document.type,
|
||||
originId: this.document.id,
|
||||
identifier: this.document.system.isMulticlass ? 'multiclass' : null
|
||||
identifier: multiclass ?? target.dataset.type
|
||||
}
|
||||
},
|
||||
{ parent: this.document.parent }
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default function ItemAttachmentSheet(Base) {
|
|||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
const data = TextEditor.getDragEventData(event);
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
|
||||
const attachmentsSection = event.target.closest('.attachments-section');
|
||||
if (!attachmentsSection) return super._onDrop(event);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ export default class AncestrySheet extends DHHeritageSheet {
|
|||
* @param {DragEvent} event - The drag event
|
||||
*/
|
||||
async _onDrop(event) {
|
||||
const data = TextEditor.getDragEventData(event);
|
||||
if (data.type === 'ActiveEffect') return super._onDrop(event);
|
||||
|
||||
const target = event.target.closest('fieldset.drop-section');
|
||||
const typeField =
|
||||
this.document.system[target.dataset.type === 'primary' ? 'primaryFeature' : 'secondaryFeature'];
|
||||
|
|
|
|||
|
|
@ -115,16 +115,17 @@ export default class ClassSheet extends DHBaseItemSheet {
|
|||
async _onDrop(event) {
|
||||
event.stopPropagation();
|
||||
const data = TextEditor.getDragEventData(event);
|
||||
const item = await fromUuid(data.uuid);
|
||||
const item = data.data ?? (await fromUuid(data.uuid));
|
||||
const itemType = data.data ? data.type : item.type;
|
||||
const target = event.target.closest('fieldset.drop-section');
|
||||
if (item.type === 'subclass') {
|
||||
if (itemType === 'subclass') {
|
||||
await this.document.update({
|
||||
'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
} else if (item.type === 'feature') {
|
||||
} else if (['feature', 'ActiveEffect'].includes(itemType)) {
|
||||
super._onDrop(event);
|
||||
} else if (this.document.parent?.type !== 'character') {
|
||||
if (item.type === 'weapon') {
|
||||
if (itemType === 'weapon') {
|
||||
if (target.classList.contains('primary-weapon-section')) {
|
||||
if (!item.system.secondary)
|
||||
await this.document.update({
|
||||
|
|
@ -136,21 +137,21 @@ export default class ClassSheet extends DHBaseItemSheet {
|
|||
'system.characterGuide.suggestedSecondaryWeapon': item.uuid
|
||||
});
|
||||
}
|
||||
} else if (item.type === 'armor') {
|
||||
} else if (itemType === 'armor') {
|
||||
if (target.classList.contains('armor-section')) {
|
||||
await this.document.update({
|
||||
'system.characterGuide.suggestedArmor': item.uuid
|
||||
});
|
||||
}
|
||||
} else if (target.classList.contains('choice-a-section')) {
|
||||
if (item.type === 'loot' || item.type === 'consumable') {
|
||||
if (itemType === 'loot' || itemType === 'consumable') {
|
||||
const filteredChoiceA = this.document.system.inventory.choiceA;
|
||||
if (filteredChoiceA.length < 2)
|
||||
await this.document.update({
|
||||
'system.inventory.choiceA': [...filteredChoiceA.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
}
|
||||
} else if (item.type === 'loot') {
|
||||
} else if (itemType === 'loot') {
|
||||
if (target.classList.contains('take-section')) {
|
||||
const filteredTake = this.document.system.inventory.take.filter(x => x);
|
||||
if (filteredTake.length < 3)
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
async onRollDamage(event, message) {
|
||||
event.stopPropagation();
|
||||
const actor = await this.getActor(message.system.source.actor);
|
||||
if (game.user.character?.id !== actor.id && !game.user.isGM) return true;
|
||||
if (!actor.isOwner) 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;
|
||||
|
|
|
|||
|
|
@ -124,11 +124,11 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement
|
||||
.querySelectorAll('[data-action="selectFolder"]')
|
||||
.forEach(element => element.addEventListener("contextmenu", (event) => {
|
||||
htmlElement.querySelectorAll('[data-action="selectFolder"]').forEach(element =>
|
||||
element.addEventListener('contextmenu', event => {
|
||||
event.target.classList.toggle('expanded');
|
||||
}))
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
|
@ -154,7 +154,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
Object.values(config).forEach(c => {
|
||||
const folder = {
|
||||
id: c.id,
|
||||
label: c.label,
|
||||
label: game.i18n.localize(c.label),
|
||||
selected: (!parent || parent.selected) && this.selectedMenu.path[depth] === c.id
|
||||
};
|
||||
folder.folders = c.folders
|
||||
|
|
@ -173,11 +173,16 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
folderPath = `${compendium}.folders.${folderId}`,
|
||||
folderData = foundry.utils.getProperty(config, folderPath);
|
||||
|
||||
const columns = ItemBrowser.getFolderConfig(folderData).map(col => ({
|
||||
...col,
|
||||
label: game.i18n.localize(col.label)
|
||||
}));
|
||||
|
||||
this.selectedMenu = {
|
||||
path: folderPath.split('.'),
|
||||
data: {
|
||||
...folderData,
|
||||
columns: ItemBrowser.getFolderConfig(folderData)
|
||||
columns: columns
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -190,8 +195,11 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
|
||||
this.items = ItemBrowser.sortBy(items, 'name');
|
||||
|
||||
if(target) {
|
||||
target.closest('.compendium-sidebar').querySelectorAll('[data-action="selectFolder"]').forEach(element => element.classList.remove("is-selected"))
|
||||
if (target) {
|
||||
target
|
||||
.closest('.compendium-sidebar')
|
||||
.querySelectorAll('[data-action="selectFolder"]')
|
||||
.forEach(element => element.classList.remove('is-selected'));
|
||||
target.classList.add('is-selected');
|
||||
}
|
||||
|
||||
|
|
@ -199,7 +207,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
}
|
||||
|
||||
_replaceHTML(result, content, options) {
|
||||
if(!options.isFirstRender) delete result.sidebar;
|
||||
if (!options.isFirstRender) delete result.sidebar;
|
||||
super._replaceHTML(result, content, options);
|
||||
}
|
||||
|
||||
|
|
@ -237,6 +245,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
else if (typeof f.choices === 'function') {
|
||||
f.choices = f.choices();
|
||||
}
|
||||
|
||||
// Clear field label so template uses our custom label parameter
|
||||
if (f.field && f.label) {
|
||||
f.field.label = undefined;
|
||||
}
|
||||
|
||||
f.name ??= f.key;
|
||||
f.value = this.presets?.filter?.[f.name]?.value ?? null;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,29 +10,41 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur
|
|||
const splitRulerText = this.ruler.text.split(' ');
|
||||
if (splitRulerText.length > 0) {
|
||||
const rulerValue = Number(splitRulerText[0]);
|
||||
const vagueLabel = this.constructor.getDistanceLabel(rulerValue, rangeMeasurementSettings);
|
||||
this.ruler.text = vagueLabel;
|
||||
const result = DhMeasuredTemplate.getRangeLabels(rulerValue, rangeMeasurementSettings);
|
||||
this.ruler.text = result.distance + (result.units ? ' ' + result.units : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static getDistanceLabel(distance, settings) {
|
||||
if (distance <= settings.melee) {
|
||||
return game.i18n.localize('DAGGERHEART.CONFIG.Range.melee.name');
|
||||
static getRangeLabels(distanceValue, settings) {
|
||||
let result = { distance: distanceValue, units: '' };
|
||||
const rangeMeasurementOverride = canvas.scene.flags.daggerheart?.rangeMeasurementOverride;
|
||||
|
||||
if (rangeMeasurementOverride === true) {
|
||||
result.distance = distanceValue;
|
||||
result.units = canvas.scene?.grid?.units;
|
||||
return result;
|
||||
}
|
||||
if (distance <= settings.veryClose) {
|
||||
return game.i18n.localize('DAGGERHEART.CONFIG.Range.veryClose.name');
|
||||
if (distanceValue <= settings.melee) {
|
||||
result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.melee.name');
|
||||
return result;
|
||||
}
|
||||
if (distance <= settings.close) {
|
||||
return game.i18n.localize('DAGGERHEART.CONFIG.Range.close.name');
|
||||
if (distanceValue <= settings.veryClose) {
|
||||
result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.veryClose.name');
|
||||
return result;
|
||||
}
|
||||
if (distance <= settings.far) {
|
||||
return game.i18n.localize('DAGGERHEART.CONFIG.Range.far.name');
|
||||
if (distanceValue <= settings.close) {
|
||||
result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.close.name');
|
||||
return result;
|
||||
}
|
||||
if (distance > settings.far) {
|
||||
return game.i18n.localize('DAGGERHEART.CONFIG.Range.veryFar.name');
|
||||
if (distanceValue <= settings.far) {
|
||||
result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.far.name');
|
||||
return result;
|
||||
}
|
||||
if (distanceValue > settings.far) {
|
||||
result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.veryFar.name');
|
||||
}
|
||||
|
||||
return '';
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ export default class DhpRuler extends foundry.canvas.interaction.Ruler {
|
|||
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement;
|
||||
|
||||
if (range.enabled) {
|
||||
const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range);
|
||||
context.cost = { total: distance, units: null };
|
||||
context.distance = { total: distance, units: null };
|
||||
const result = DhMeasuredTemplate.getRangeLabels(waypoint.measurement.distance.toNearest(0.01), range);
|
||||
context.cost = { total: result.distance, units: result.units };
|
||||
context.distance = { total: result.distance, units: result.units };
|
||||
}
|
||||
|
||||
return context;
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ export default class DhpTokenRuler extends foundry.canvas.placeables.tokens.Toke
|
|||
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement;
|
||||
|
||||
if (range.enabled) {
|
||||
const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range);
|
||||
context.cost = { total: distance, units: null };
|
||||
context.distance = { total: distance, units: null };
|
||||
const result = DhMeasuredTemplate.getRangeLabels(waypoint.measurement.distance.toNearest(0.01), range);
|
||||
context.cost = { total: result.distance, units: result.units };
|
||||
context.distance = { total: result.distance, units: result.units };
|
||||
}
|
||||
|
||||
return context;
|
||||
|
|
|
|||
|
|
@ -157,6 +157,11 @@ export const adversaryTypes = {
|
|||
}
|
||||
};
|
||||
|
||||
export const allAdversaryTypes = () => ({
|
||||
...adversaryTypes,
|
||||
...game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).adversaryTypes
|
||||
});
|
||||
|
||||
export const environmentTypes = {
|
||||
exploration: {
|
||||
label: 'DAGGERHEART.CONFIG.EnvironmentType.exploration.label',
|
||||
|
|
|
|||
|
|
@ -624,6 +624,13 @@ export const rollTypes = {
|
|||
}
|
||||
};
|
||||
|
||||
export const attributionSources = {
|
||||
daggerheart: {
|
||||
label: 'Daggerheart',
|
||||
values: [{ label: 'Daggerheart SRD' }]
|
||||
}
|
||||
};
|
||||
|
||||
export const fearDisplay = {
|
||||
token: { value: 'token', label: 'DAGGERHEART.SETTINGS.Appearance.fearDisplay.token' },
|
||||
bar: { value: 'bar', label: 'DAGGERHEART.SETTINGS.Appearance.fearDisplay.bar' },
|
||||
|
|
|
|||
|
|
@ -2,270 +2,278 @@ export const typeConfig = {
|
|||
adversaries: {
|
||||
columns: [
|
||||
{
|
||||
key: "system.tier",
|
||||
label: "Tier"
|
||||
key: 'system.tier',
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.singular'
|
||||
},
|
||||
{
|
||||
key: "system.type",
|
||||
label: "Type"
|
||||
key: 'system.type',
|
||||
label: 'DAGGERHEART.GENERAL.type'
|
||||
}
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
key: "system.tier",
|
||||
label: "Tier",
|
||||
key: 'system.tier',
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.singular',
|
||||
field: 'system.api.models.actors.DhAdversary.schema.fields.tier'
|
||||
},
|
||||
{
|
||||
key: "system.type",
|
||||
label: "Type",
|
||||
key: 'system.type',
|
||||
label: 'DAGGERHEART.GENERAL.type',
|
||||
field: 'system.api.models.actors.DhAdversary.schema.fields.type'
|
||||
},
|
||||
{
|
||||
key: "system.difficulty",
|
||||
name: "difficulty.min",
|
||||
label: "Difficulty (Min)",
|
||||
key: 'system.difficulty',
|
||||
name: 'difficulty.min',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.difficultyMin',
|
||||
field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty',
|
||||
operator: "gte"
|
||||
operator: 'gte'
|
||||
},
|
||||
{
|
||||
key: "system.difficulty",
|
||||
name: "difficulty.max",
|
||||
label: "Difficulty (Max)",
|
||||
key: 'system.difficulty',
|
||||
name: 'difficulty.max',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.difficultyMax',
|
||||
field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty',
|
||||
operator: "lte"
|
||||
operator: 'lte'
|
||||
},
|
||||
{
|
||||
key: "system.resources.hitPoints.max",
|
||||
name: "hp.min",
|
||||
label: "Hit Points (Min)",
|
||||
key: 'system.resources.hitPoints.max',
|
||||
name: 'hp.min',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.hitPointsMin',
|
||||
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max',
|
||||
operator: "gte"
|
||||
operator: 'gte'
|
||||
},
|
||||
{
|
||||
key: "system.resources.hitPoints.max",
|
||||
name: "hp.max",
|
||||
label: "Hit Points (Max)",
|
||||
key: 'system.resources.hitPoints.max',
|
||||
name: 'hp.max',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.hitPointsMax',
|
||||
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max',
|
||||
operator: "lte"
|
||||
operator: 'lte'
|
||||
},
|
||||
{
|
||||
key: "system.resources.stress.max",
|
||||
name: "stress.min",
|
||||
label: "Stress (Min)",
|
||||
key: 'system.resources.stress.max',
|
||||
name: 'stress.min',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.stressMin',
|
||||
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max',
|
||||
operator: "gte"
|
||||
operator: 'gte'
|
||||
},
|
||||
{
|
||||
key: "system.resources.stress.max",
|
||||
name: "stress.max",
|
||||
label: "Stress (Max)",
|
||||
key: 'system.resources.stress.max',
|
||||
name: 'stress.max',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.stressMax',
|
||||
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max',
|
||||
operator: "lte"
|
||||
},
|
||||
operator: 'lte'
|
||||
}
|
||||
]
|
||||
},
|
||||
items: {
|
||||
columns: [
|
||||
{
|
||||
key: "type",
|
||||
label: "Type"
|
||||
key: 'type',
|
||||
label: 'DAGGERHEART.GENERAL.type'
|
||||
},
|
||||
{
|
||||
key: "system.secondary",
|
||||
label: "Subtype",
|
||||
format: (isSecondary) => isSecondary ? "secondary" : (isSecondary === false ? "primary" : '-')
|
||||
key: 'system.secondary',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.subtype',
|
||||
format: isSecondary => (isSecondary ? 'secondary' : isSecondary === false ? 'primary' : '-')
|
||||
},
|
||||
{
|
||||
key: "system.tier",
|
||||
label: "Tier"
|
||||
key: 'system.tier',
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.singular'
|
||||
}
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
key: "type",
|
||||
label: "Type",
|
||||
choices: () => CONFIG.Item.documentClass.TYPES.filter(t => ["armor", "weapon", "consumable", "loot"].includes(t)).map(t => ({ value: t, label: t }))
|
||||
key: 'type',
|
||||
label: 'DAGGERHEART.GENERAL.type',
|
||||
choices: () =>
|
||||
CONFIG.Item.documentClass.TYPES.filter(t =>
|
||||
['armor', 'weapon', 'consumable', 'loot'].includes(t)
|
||||
).map(t => ({ value: t, label: t }))
|
||||
},
|
||||
{
|
||||
key: "system.secondary",
|
||||
label: "Subtype",
|
||||
key: 'system.secondary',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.subtype',
|
||||
choices: [
|
||||
{ value: false, label: "Primary Weapon"},
|
||||
{ value: true, label: "Secondary Weapon"}
|
||||
{ value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon' },
|
||||
{ value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon' }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "system.tier",
|
||||
label: "Tier",
|
||||
choices: [{ value: "1", label: "1"}, { value: "2", label: "2"}, { value: "3", label: "3"}, { value: "4", label: "4"}]
|
||||
key: 'system.tier',
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.singular',
|
||||
choices: [
|
||||
{ value: '1', label: '1' },
|
||||
{ value: '2', label: '2' },
|
||||
{ value: '3', label: '3' },
|
||||
{ value: '4', label: '4' }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "system.burden",
|
||||
label: "Burden",
|
||||
key: 'system.burden',
|
||||
label: 'DAGGERHEART.GENERAL.burden',
|
||||
field: 'system.api.models.items.DHWeapon.schema.fields.burden'
|
||||
},
|
||||
{
|
||||
key: "system.attack.roll.trait",
|
||||
label: "Trait",
|
||||
key: 'system.attack.roll.trait',
|
||||
label: 'DAGGERHEART.GENERAL.Trait.single',
|
||||
field: 'system.api.models.actions.actionsTypes.attack.schema.fields.roll.fields.trait'
|
||||
},
|
||||
{
|
||||
key: "system.attack.range",
|
||||
label: "Range",
|
||||
key: 'system.attack.range',
|
||||
label: 'DAGGERHEART.GENERAL.range',
|
||||
field: 'system.api.models.actions.actionsTypes.attack.schema.fields.range'
|
||||
},
|
||||
{
|
||||
key: "system.baseScore",
|
||||
name: "armor.min",
|
||||
label: "Armor Score (Min)",
|
||||
key: 'system.baseScore',
|
||||
name: 'armor.min',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.armorScoreMin',
|
||||
field: 'system.api.models.items.DHArmor.schema.fields.baseScore',
|
||||
operator: "gte"
|
||||
operator: 'gte'
|
||||
},
|
||||
{
|
||||
key: "system.baseScore",
|
||||
name: "armor.max",
|
||||
label: "Armor Score (Max)",
|
||||
key: 'system.baseScore',
|
||||
name: 'armor.max',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.armorScoreMax',
|
||||
field: 'system.api.models.items.DHArmor.schema.fields.baseScore',
|
||||
operator: "lte"
|
||||
operator: 'lte'
|
||||
},
|
||||
{
|
||||
key: "system.itemFeatures",
|
||||
label: "Features",
|
||||
choices: () => [...Object.entries(CONFIG.DH.ITEM.weaponFeatures), ...Object.entries(CONFIG.DH.ITEM.armorFeatures)].map(([k,v]) => ({ value: k, label: v.label})),
|
||||
operator: "contains3"
|
||||
key: 'system.itemFeatures',
|
||||
label: 'DAGGERHEART.GENERAL.features',
|
||||
choices: () =>
|
||||
[
|
||||
...Object.entries(CONFIG.DH.ITEM.weaponFeatures),
|
||||
...Object.entries(CONFIG.DH.ITEM.armorFeatures)
|
||||
].map(([k, v]) => ({ value: k, label: v.label })),
|
||||
operator: 'contains3'
|
||||
}
|
||||
]
|
||||
},
|
||||
features: {
|
||||
columns: [
|
||||
|
||||
],
|
||||
filters: [
|
||||
|
||||
]
|
||||
columns: [],
|
||||
filters: []
|
||||
},
|
||||
cards: {
|
||||
columns: [
|
||||
{
|
||||
key: "system.type",
|
||||
label: "Type"
|
||||
key: 'system.type',
|
||||
label: 'DAGGERHEART.GENERAL.type'
|
||||
},
|
||||
{
|
||||
key: "system.domain",
|
||||
label: "Domain"
|
||||
key: 'system.domain',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.single'
|
||||
},
|
||||
{
|
||||
key: "system.level",
|
||||
label: "Level"
|
||||
key: 'system.level',
|
||||
label: 'DAGGERHEART.GENERAL.level'
|
||||
}
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
key: "system.type",
|
||||
label: "Type",
|
||||
key: 'system.type',
|
||||
label: 'DAGGERHEART.GENERAL.type',
|
||||
field: 'system.api.models.items.DHDomainCard.schema.fields.type'
|
||||
},
|
||||
{
|
||||
key: "system.domain",
|
||||
label: "Domain",
|
||||
key: 'system.domain',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.single',
|
||||
field: 'system.api.models.items.DHDomainCard.schema.fields.domain',
|
||||
operator: "contains2"
|
||||
operator: 'contains2'
|
||||
},
|
||||
{
|
||||
key: "system.level",
|
||||
name: "level.min",
|
||||
label: "Level (Min)",
|
||||
key: 'system.level',
|
||||
name: 'level.min',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.levelMin',
|
||||
field: 'system.api.models.items.DHDomainCard.schema.fields.level',
|
||||
operator: "gte"
|
||||
operator: 'gte'
|
||||
},
|
||||
{
|
||||
key: "system.level",
|
||||
name: "level.max",
|
||||
label: "Level (Max)",
|
||||
key: 'system.level',
|
||||
name: 'level.max',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.levelMax',
|
||||
field: 'system.api.models.items.DHDomainCard.schema.fields.level',
|
||||
operator: "lte"
|
||||
operator: 'lte'
|
||||
},
|
||||
{
|
||||
key: "system.recallCost",
|
||||
name: "recall.min",
|
||||
label: "Recall Cost (Min)",
|
||||
key: 'system.recallCost',
|
||||
name: 'recall.min',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.recallCostMin',
|
||||
field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost',
|
||||
operator: "gte"
|
||||
operator: 'gte'
|
||||
},
|
||||
{
|
||||
key: "system.recallCost",
|
||||
name: "recall.max",
|
||||
label: "Recall Cost (Max)",
|
||||
key: 'system.recallCost',
|
||||
name: 'recall.max',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.recallCostMax',
|
||||
field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost',
|
||||
operator: "lte"
|
||||
operator: 'lte'
|
||||
}
|
||||
]
|
||||
},
|
||||
classes: {
|
||||
columns: [
|
||||
{
|
||||
key: "system.evasion",
|
||||
label: "Evasion"
|
||||
key: 'system.evasion',
|
||||
label: 'DAGGERHEART.GENERAL.evasion'
|
||||
},
|
||||
{
|
||||
key: "system.hitPoints",
|
||||
label: "Hit Points"
|
||||
key: 'system.hitPoints',
|
||||
label: 'DAGGERHEART.GENERAL.HitPoints.plural'
|
||||
},
|
||||
{
|
||||
key: "system.domains",
|
||||
label: "Domains"
|
||||
key: 'system.domains',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.plural'
|
||||
}
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
key: "system.evasion",
|
||||
name: "evasion.min",
|
||||
label: "Evasion (Min)",
|
||||
key: 'system.evasion',
|
||||
name: 'evasion.min',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.evasionMin',
|
||||
field: 'system.api.models.items.DHClass.schema.fields.evasion',
|
||||
operator: "gte"
|
||||
operator: 'gte'
|
||||
},
|
||||
{
|
||||
key: "system.evasion",
|
||||
name: "evasion.max",
|
||||
label: "Evasion (Max)",
|
||||
key: 'system.evasion',
|
||||
name: 'evasion.max',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.evasionMax',
|
||||
field: 'system.api.models.items.DHClass.schema.fields.evasion',
|
||||
operator: "lte"
|
||||
operator: 'lte'
|
||||
},
|
||||
{
|
||||
key: "system.hitPoints",
|
||||
name: "hp.min",
|
||||
label: "Hit Points (Min)",
|
||||
key: 'system.hitPoints',
|
||||
name: 'hp.min',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.hitPointsMin',
|
||||
field: 'system.api.models.items.DHClass.schema.fields.hitPoints',
|
||||
operator: "gte"
|
||||
operator: 'gte'
|
||||
},
|
||||
{
|
||||
key: "system.hitPoints",
|
||||
name: "hp.max",
|
||||
label: "Hit Points (Max)",
|
||||
key: 'system.hitPoints',
|
||||
name: 'hp.max',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.hitPointsMax',
|
||||
field: 'system.api.models.items.DHClass.schema.fields.hitPoints',
|
||||
operator: "lte"
|
||||
operator: 'lte'
|
||||
},
|
||||
{
|
||||
key: "system.domains",
|
||||
label: "Domains",
|
||||
choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label})),
|
||||
operator: "contains2"
|
||||
key: 'system.domains',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.plural',
|
||||
choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label })),
|
||||
operator: 'contains2'
|
||||
}
|
||||
]
|
||||
},
|
||||
subclasses: {
|
||||
columns: [
|
||||
{
|
||||
key: "id",
|
||||
label: "Class",
|
||||
format: (id) => {
|
||||
return "";
|
||||
key: 'id',
|
||||
label: 'TYPES.Item.class',
|
||||
format: id => {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "system.spellcastingTrait",
|
||||
label: "Spellcasting Trait"
|
||||
key: 'system.spellcastingTrait',
|
||||
label: 'DAGGERHEART.ITEMS.Subclass.spellcastingTrait'
|
||||
}
|
||||
],
|
||||
filters: []
|
||||
|
|
@ -273,133 +281,133 @@ export const typeConfig = {
|
|||
beastforms: {
|
||||
columns: [
|
||||
{
|
||||
key: "system.tier",
|
||||
label: "Tier"
|
||||
key: 'system.tier',
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.singular'
|
||||
},
|
||||
{
|
||||
key: "system.mainTrait",
|
||||
label: "Main Trait"
|
||||
key: 'system.mainTrait',
|
||||
label: 'DAGGERHEART.GENERAL.Trait.single'
|
||||
}
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
key: "system.tier",
|
||||
label: "Tier",
|
||||
key: 'system.tier',
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.singular',
|
||||
field: 'system.api.models.items.DHBeastform.schema.fields.tier'
|
||||
},
|
||||
{
|
||||
key: "system.mainTrait",
|
||||
label: "Main Trait",
|
||||
key: 'system.mainTrait',
|
||||
label: 'DAGGERHEART.GENERAL.Trait.single',
|
||||
field: 'system.api.models.items.DHBeastform.schema.fields.mainTrait'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const compendiumConfig = {
|
||||
"daggerheart": {
|
||||
id: "daggerheart",
|
||||
label: "DAGGERHEART",
|
||||
daggerheart: {
|
||||
id: 'daggerheart',
|
||||
label: 'DAGGERHEART',
|
||||
folders: {
|
||||
"adversaries": {
|
||||
id: "adversaries",
|
||||
keys: ["adversaries"],
|
||||
label: "Adversaries",
|
||||
type: ["adversary"],
|
||||
listType: "adversaries"
|
||||
adversaries: {
|
||||
id: 'adversaries',
|
||||
keys: ['adversaries'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.adversaries',
|
||||
type: ['adversary'],
|
||||
listType: 'adversaries'
|
||||
},
|
||||
"ancestries": {
|
||||
id: "ancestries",
|
||||
keys: ["ancestries"],
|
||||
label: "Ancestries",
|
||||
type: ["ancestry"],
|
||||
ancestries: {
|
||||
id: 'ancestries',
|
||||
keys: ['ancestries'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.ancestries',
|
||||
type: ['ancestry'],
|
||||
folders: {
|
||||
"features": {
|
||||
id: "features",
|
||||
keys: ["ancestries"],
|
||||
label: "Features",
|
||||
type: ["feature"]
|
||||
features: {
|
||||
id: 'features',
|
||||
keys: ['ancestries'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.features',
|
||||
type: ['feature']
|
||||
}
|
||||
}
|
||||
},
|
||||
"equipments": {
|
||||
id: "equipments",
|
||||
keys: ["armors", "weapons", "consumables", "loot"],
|
||||
label: "Equipment",
|
||||
type: ["armor", "weapon", "consumable", "loot"],
|
||||
listType: "items"
|
||||
equipments: {
|
||||
id: 'equipments',
|
||||
keys: ['armors', 'weapons', 'consumables', 'loot'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.equipment',
|
||||
type: ['armor', 'weapon', 'consumable', 'loot'],
|
||||
listType: 'items'
|
||||
},
|
||||
"classes": {
|
||||
id: "classes",
|
||||
keys: ["classes"],
|
||||
label: "Classes",
|
||||
type: ["class"],
|
||||
classes: {
|
||||
id: 'classes',
|
||||
keys: ['classes'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.classes',
|
||||
type: ['class'],
|
||||
folders: {
|
||||
"features": {
|
||||
id: "features",
|
||||
keys: ["classes"],
|
||||
label: "Features",
|
||||
type: ["feature"]
|
||||
features: {
|
||||
id: 'features',
|
||||
keys: ['classes'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.features',
|
||||
type: ['feature']
|
||||
},
|
||||
"items": {
|
||||
id: "items",
|
||||
keys: ["classes"],
|
||||
label: "Items",
|
||||
type: ["armor", "weapon", "consumable", "loot"],
|
||||
listType: "items"
|
||||
items: {
|
||||
id: 'items',
|
||||
keys: ['classes'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.items',
|
||||
type: ['armor', 'weapon', 'consumable', 'loot'],
|
||||
listType: 'items'
|
||||
}
|
||||
},
|
||||
listType: "classes"
|
||||
listType: 'classes'
|
||||
},
|
||||
"subclasses": {
|
||||
id: "subclasses",
|
||||
keys: ["subclasses"],
|
||||
label: "Subclasses",
|
||||
type: ["subclass"],
|
||||
listType: "subclasses"
|
||||
subclasses: {
|
||||
id: 'subclasses',
|
||||
keys: ['subclasses'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.subclasses',
|
||||
type: ['subclass'],
|
||||
listType: 'subclasses'
|
||||
},
|
||||
"domains": {
|
||||
id: "domains",
|
||||
keys: ["domains"],
|
||||
label: "Domain Cards",
|
||||
type: ["domainCard"],
|
||||
listType: "cards"
|
||||
domains: {
|
||||
id: 'domains',
|
||||
keys: ['domains'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.domainCards',
|
||||
type: ['domainCard'],
|
||||
listType: 'cards'
|
||||
},
|
||||
"communities": {
|
||||
id: "communities",
|
||||
keys: ["communities"],
|
||||
label: "Communities",
|
||||
type: ["community"],
|
||||
communities: {
|
||||
id: 'communities',
|
||||
keys: ['communities'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.communities',
|
||||
type: ['community'],
|
||||
folders: {
|
||||
"features": {
|
||||
id: "features",
|
||||
keys: ["communities"],
|
||||
label: "Features",
|
||||
type: ["feature"]
|
||||
features: {
|
||||
id: 'features',
|
||||
keys: ['communities'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.features',
|
||||
type: ['feature']
|
||||
}
|
||||
}
|
||||
},
|
||||
"environments": {
|
||||
id: "environments",
|
||||
keys: ["environments"],
|
||||
label: "Environments",
|
||||
type: ["environment"]
|
||||
environments: {
|
||||
id: 'environments',
|
||||
keys: ['environments'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.environments',
|
||||
type: ['environment']
|
||||
},
|
||||
"beastforms": {
|
||||
id: "beastforms",
|
||||
keys: ["beastforms"],
|
||||
label: "Beastforms",
|
||||
type: ["beastform"],
|
||||
listType: "beastforms",
|
||||
beastforms: {
|
||||
id: 'beastforms',
|
||||
keys: ['beastforms'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.beastforms',
|
||||
type: ['beastform'],
|
||||
listType: 'beastforms',
|
||||
folders: {
|
||||
"features": {
|
||||
id: "features",
|
||||
keys: ["beastforms"],
|
||||
label: "Features",
|
||||
type: ["feature"]
|
||||
features: {
|
||||
id: 'features',
|
||||
keys: ['beastforms'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.features',
|
||||
type: ['feature']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,5 +26,6 @@ export const gameSettings = {
|
|||
Fear: 'ResourcesFear'
|
||||
},
|
||||
LevelTiers: 'LevelTiers',
|
||||
Countdowns: 'Countdowns'
|
||||
Countdowns: 'Countdowns',
|
||||
LastMigrationVersion: 'LastMigrationVersion'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
const hasRoll = this.getUseHasRoll(byPass);
|
||||
return {
|
||||
event,
|
||||
title: `${this.item.name}: ${this.name}`,
|
||||
title: `${this.item.name}: ${game.i18n.localize(this.name)}`,
|
||||
source: {
|
||||
item: this.item._id,
|
||||
action: this._id,
|
||||
|
|
@ -209,15 +209,15 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
}
|
||||
|
||||
async consume(config, successCost = false) {
|
||||
const actor= this.actor.system.partner ?? this.actor,
|
||||
const actor = this.actor.system.partner ?? this.actor,
|
||||
usefulResources = {
|
||||
...foundry.utils.deepClone(actor.system.resources),
|
||||
fear: {
|
||||
value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear),
|
||||
max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear,
|
||||
reversed: false
|
||||
}
|
||||
};
|
||||
...foundry.utils.deepClone(actor.system.resources),
|
||||
fear: {
|
||||
value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear),
|
||||
max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear,
|
||||
reversed: false
|
||||
}
|
||||
};
|
||||
|
||||
for (var cost of config.costs) {
|
||||
if (cost.keyIsID) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Actor.adversary',
|
||||
type: 'adversary',
|
||||
settingSheet: DHAdversarySettings
|
||||
settingSheet: DHAdversarySettings,
|
||||
hasAttribution: true
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -26,7 +27,7 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
}),
|
||||
type: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.ACTOR.adversaryTypes,
|
||||
choices: CONFIG.DH.ACTOR.allAdversaryTypes,
|
||||
initial: CONFIG.DH.ACTOR.adversaryTypes.standard.id
|
||||
}),
|
||||
motivesAndTactics: new fields.StringField(),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs';
|
||||
import { createScrollText, getScrollTextData } from '../../helpers/utils.mjs';
|
||||
import { getScrollTextData } from '../../helpers/utils.mjs';
|
||||
|
||||
const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
|
|
@ -39,7 +39,8 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
type: 'base',
|
||||
isNPC: true,
|
||||
settingSheet: null,
|
||||
hasResistances: true
|
||||
hasResistances: true,
|
||||
hasAttribution: false
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +54,13 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
const fields = foundry.data.fields;
|
||||
const schema = {};
|
||||
|
||||
if (this.metadata.hasAttribution) {
|
||||
schema.attribution = new fields.SchemaField({
|
||||
source: new fields.StringField(),
|
||||
page: new fields.NumberField(),
|
||||
artist: new fields.StringField()
|
||||
});
|
||||
}
|
||||
if (this.metadata.isNPC) schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||
if (this.metadata.hasResistances)
|
||||
schema.resistance = new fields.SchemaField({
|
||||
|
|
@ -78,6 +86,13 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
*/
|
||||
static DEFAULT_ICON = null;
|
||||
|
||||
get attributionLabel() {
|
||||
if (!this.attribution) return;
|
||||
|
||||
const { source, page } = this.attribution;
|
||||
return [source, page ? `pg ${page}.` : null].filter(x => x).join('. ');
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
|
|
@ -133,6 +148,6 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
_onUpdate(changes, options, userId) {
|
||||
super._onUpdate(changes, options, userId);
|
||||
|
||||
createScrollText(this.parent, options.scrollingTextData);
|
||||
if (options.scrollingTextData) this.parent.queueScrollText(options.scrollingTextData);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
}),
|
||||
attack: new ActionField({
|
||||
initial: {
|
||||
name: 'Unarmed Attack',
|
||||
name: 'DAGGERHEART.GENERAL.unarmedAttack',
|
||||
img: 'icons/skills/melee/unarmed-punch-fist-yellow-red.webp',
|
||||
_id: foundry.utils.randomID(),
|
||||
systemPath: 'attack',
|
||||
|
|
@ -444,16 +444,12 @@ export default class DhCharacter extends BaseDataActor {
|
|||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) {
|
||||
if (this.class.subclass) {
|
||||
const subclassState = this.class.subclass.system.featureState;
|
||||
const subclass =
|
||||
item.system.identifier === 'multiclass' ? this.multiclass.subclass : this.class.subclass;
|
||||
const featureType = subclass
|
||||
? (subclass.system.features.find(x => x.item?.uuid === item.uuid)?.type ?? null)
|
||||
: null;
|
||||
|
||||
if (
|
||||
featureType === CONFIG.DH.ITEM.featureSubTypes.foundation ||
|
||||
(featureType === CONFIG.DH.ITEM.featureSubTypes.specialization && subclassState >= 2) ||
|
||||
(featureType === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3)
|
||||
item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.foundation ||
|
||||
(item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.specialization &&
|
||||
subclassState >= 2) ||
|
||||
(item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3)
|
||||
) {
|
||||
subclassFeatures.push(item);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ export default class DhEnvironment extends BaseDataActor {
|
|||
label: 'TYPES.Actor.environment',
|
||||
type: 'environment',
|
||||
settingSheet: DHEnvironmentSettings,
|
||||
hasResistances: false
|
||||
hasResistances: false,
|
||||
hasAttribution: true
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export default class CostField extends fields.ArrayField {
|
|||
config.costs = CostField.calcCosts.call(this, costs);
|
||||
const hasCost = CostField.hasCost.call(this, config.costs);
|
||||
if (config.isFastForward && !hasCost)
|
||||
return ui.notifications.warn("You don't have the resources to use that action.");
|
||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.insufficientResources'));
|
||||
return hasCost;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,14 +23,22 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
|||
multiplier: new fields.StringField({
|
||||
choices: CONFIG.DH.GENERAL.multiplierTypes,
|
||||
initial: 'prof',
|
||||
label: 'Multiplier'
|
||||
label: 'DAGGERHEART.ACTIONS.Config.damage.multiplier'
|
||||
}),
|
||||
flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }),
|
||||
dice: new fields.StringField({ choices: CONFIG.DH.GENERAL.diceTypes, initial: 'd6', label: 'Dice' }),
|
||||
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }),
|
||||
flatMultiplier: new fields.NumberField({
|
||||
nullable: true,
|
||||
initial: 1,
|
||||
label: 'DAGGERHEART.ACTIONS.Config.damage.flatMultiplier'
|
||||
}),
|
||||
dice: new fields.StringField({
|
||||
choices: CONFIG.DH.GENERAL.diceTypes,
|
||||
initial: 'd6',
|
||||
label: 'DAGGERHEART.GENERAL.Dice.single'
|
||||
}),
|
||||
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'DAGGERHEART.GENERAL.bonus' }),
|
||||
custom: new fields.SchemaField({
|
||||
enabled: new fields.BooleanField({ label: 'Custom Formula' }),
|
||||
formula: new FormulaField({ label: 'Formula', initial: '' })
|
||||
enabled: new fields.BooleanField({ label: 'DAGGERHEART.ACTIONS.Config.general.customFormula' }),
|
||||
formula: new FormulaField({ label: 'DAGGERHEART.ACTIONS.Config.general.formula', initial: '' })
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ export default class UsesField extends fields.SchemaField {
|
|||
if (uses && !uses.value) uses.value = 0;
|
||||
config.uses = uses;
|
||||
const hasUses = UsesField.hasUses.call(this, config.uses);
|
||||
if (config.isFastForward && !hasUses) return ui.notifications.warn("That action doesn't have remaining uses.");
|
||||
if (config.isFastForward && !hasUses)
|
||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.actionNoUsesRemaining'));
|
||||
return hasUses;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
hasResource: false,
|
||||
isQuantifiable: false,
|
||||
isInventoryItem: false,
|
||||
hasActions: false
|
||||
hasActions: false,
|
||||
hasAttribution: true
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +38,13 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
|
||||
/** @inheritDoc */
|
||||
static defineSchema() {
|
||||
const schema = {};
|
||||
const schema = {
|
||||
attribution: new fields.SchemaField({
|
||||
source: new fields.StringField(),
|
||||
page: new fields.NumberField(),
|
||||
artist: new fields.StringField()
|
||||
})
|
||||
};
|
||||
|
||||
if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||
|
||||
|
|
@ -110,6 +117,13 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
return [];
|
||||
}
|
||||
|
||||
get attributionLabel() {
|
||||
if (!this.attribution) return;
|
||||
|
||||
const { source, page } = this.attribution;
|
||||
return [source, page ? `pg ${page}.` : null].filter(x => x).join('. ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
|
||||
* @param {object} [options] - Options which modify the getRollData method.
|
||||
|
|
@ -144,50 +158,30 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
}
|
||||
|
||||
if (this.actor && this.actor.type === 'character' && this.features) {
|
||||
const featureUpdates = {};
|
||||
const features = [];
|
||||
for (let f of this.features) {
|
||||
const fBase = f.item ?? f;
|
||||
const feature = fBase.system ? fBase : await foundry.utils.fromUuid(fBase.uuid);
|
||||
const createData = foundry.utils.mergeObject(
|
||||
feature.toObject(),
|
||||
{
|
||||
system: {
|
||||
originItemType: this.parent.type,
|
||||
originId: data._id,
|
||||
identifier: this.isMulticlass ? 'multiclass' : null
|
||||
}
|
||||
},
|
||||
{ inplace: false }
|
||||
const multiclass = this.isMulticlass ? 'multiclass' : null;
|
||||
features.push(
|
||||
foundry.utils.mergeObject(
|
||||
feature.toObject(),
|
||||
{
|
||||
_stats: { compendiumSource: fBase.uuid },
|
||||
system: {
|
||||
originItemType: this.parent.type,
|
||||
identifier: multiclass ?? (f.item ? f.type : null)
|
||||
}
|
||||
},
|
||||
{ inplace: false }
|
||||
)
|
||||
);
|
||||
const [doc] = await this.actor.createEmbeddedDocuments('Item', [createData]);
|
||||
|
||||
if (!featureUpdates.features)
|
||||
featureUpdates.features = this.features.map(x => (x.item ? { ...x, item: x.item.uuid } : x.uuid));
|
||||
|
||||
if (f.item) {
|
||||
const existingFeature = featureUpdates.features.find(x => x.item === f.item.uuid);
|
||||
existingFeature.item = doc.uuid;
|
||||
} else {
|
||||
const replaceIndex = featureUpdates.features.findIndex(x => x === f.uuid);
|
||||
featureUpdates.features.splice(replaceIndex, 1, doc.uuid);
|
||||
}
|
||||
}
|
||||
|
||||
await this.updateSource(featureUpdates);
|
||||
await this.actor.createEmbeddedDocuments('Item', features);
|
||||
}
|
||||
}
|
||||
|
||||
async _preDelete() {
|
||||
if (!this.actor || this.actor.type !== 'character') return;
|
||||
|
||||
const items = this.actor.items.filter(item => item.system.originId === this.parent.id);
|
||||
if (items.length > 0)
|
||||
await this.actor.deleteEmbeddedDocuments(
|
||||
'Item',
|
||||
items.map(x => x.id)
|
||||
);
|
||||
}
|
||||
|
||||
async _preUpdate(changed, options, userId) {
|
||||
const allowed = await super._preUpdate(changed, options, userId);
|
||||
if (allowed === false) return false;
|
||||
|
|
@ -207,6 +201,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
super._onUpdate(changed, options, userId);
|
||||
|
||||
updateLinkedItemApps(options, this.parent.sheet);
|
||||
createScrollText(this.parent?.parent, options.scrollingTextData);
|
||||
|
||||
if (this.parent?.parent && options.scrollingTextData)
|
||||
this.parent.parent.queueScrollText(options.scrollingTextData);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import BaseDataItem from './base.mjs';
|
||||
import { ActionField, ActionsField } from '../fields/actionField.mjs';
|
||||
|
||||
export default class DHFeature extends BaseDataItem {
|
||||
/** @inheritDoc */
|
||||
|
|
@ -30,24 +29,7 @@ export default class DHFeature extends BaseDataItem {
|
|||
nullable: true,
|
||||
initial: null
|
||||
}),
|
||||
originId: new fields.StringField({ nullable: true, initial: null }),
|
||||
identifier: new fields.StringField()
|
||||
};
|
||||
}
|
||||
|
||||
get spellcastingModifier() {
|
||||
let traitValue = 0;
|
||||
if (this.actor && this.originId && ['class', 'subclass'].includes(this.originItemType)) {
|
||||
if (this.originItemType === 'subclass') {
|
||||
traitValue =
|
||||
this.actor.system.traits[this.actor.items.get(this.originId).system.spellcastingTrait]?.value ?? 0;
|
||||
} else {
|
||||
const { value: multiclass, subclass } = this.actor.system.multiclass;
|
||||
const selectedSubclass = multiclass?.id === this.originId ? subclass : this.actor.system.class.subclass;
|
||||
traitValue = this.actor.system.traits[selectedSubclass.system.spellcastingTrait]?.value ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
return traitValue;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,8 +199,8 @@ export default class DHWeapon extends AttachableItem {
|
|||
];
|
||||
|
||||
for (const { value, type } of attack.damage.parts) {
|
||||
const parts = [value.dice];
|
||||
if (value.bonus) parts.push(value.bonus.signedString());
|
||||
const parts = value.custom.enabled ? [game.i18n.localize('DAGGERHEART.GENERAL.custom')] : [value.dice];
|
||||
if (!value.custom.enabled && value.bonus) parts.push(value.bonus.signedString());
|
||||
|
||||
if (type.size > 0) {
|
||||
const typeTags = Array.from(type)
|
||||
|
|
|
|||
|
|
@ -89,6 +89,11 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
|||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessageTarget.label'
|
||||
})
|
||||
}),
|
||||
hideAttribution: new fields.BooleanField({
|
||||
required: true,
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.hideAttribution.label'
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,13 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
|||
}),
|
||||
description: new fields.HTMLField()
|
||||
})
|
||||
),
|
||||
adversaryTypes: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
id: new fields.StringField({ required: true }),
|
||||
label: new fields.StringField({ required: true, label: 'DAGGERHEART.GENERAL.label' }),
|
||||
description: new fields.StringField()
|
||||
})
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
export { default as BaseRoll } from './baseRoll.mjs';
|
||||
export { default as D20Roll } from './d20Roll.mjs';
|
||||
export { default as DamageRoll } from './damageRoll.mjs';
|
||||
export { default as DHRoll } from './dhRoll.mjs';
|
||||
|
|
|
|||
7
module/dice/baseRoll.mjs
Normal file
7
module/dice/baseRoll.mjs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export default class BaseRoll extends Roll {
|
||||
/** @inheritdoc */
|
||||
static CHAT_TEMPLATE = 'systems/daggerheart/templates/ui/chat/foundryRoll.hbs';
|
||||
|
||||
/** @inheritdoc */
|
||||
static TOOLTIP_TEMPLATE = 'systems/daggerheart/templates/ui/chat/foundryRollTooltip.hbs';
|
||||
}
|
||||
|
|
@ -37,7 +37,13 @@ export default class DamageRoll extends DHRoll {
|
|||
Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll))
|
||||
),
|
||||
diceRoll = Roll.fromTerms([pool]);
|
||||
await game.dice3d.showForRoll(diceRoll, game.user, true, chatMessage.whisper, chatMessage.blind);
|
||||
await game.dice3d.showForRoll(
|
||||
diceRoll,
|
||||
game.user,
|
||||
true,
|
||||
chatMessage.whisper?.length > 0 ? chatMessage.whisper : null,
|
||||
chatMessage.blind
|
||||
);
|
||||
}
|
||||
await super.buildPost(roll, config, message);
|
||||
if (config.source?.message) {
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ export default class DHRoll extends Roll {
|
|||
|
||||
static async toMessage(roll, config) {
|
||||
const cls = getDocumentClass('ChatMessage'),
|
||||
msg = {
|
||||
msgData = {
|
||||
type: this.messageType,
|
||||
user: game.user.id,
|
||||
title: roll.title,
|
||||
|
|
@ -94,8 +94,16 @@ export default class DHRoll extends Roll {
|
|||
rolls: [roll]
|
||||
};
|
||||
config.selectedRollMode ??= game.settings.get('core', 'rollMode');
|
||||
if (roll._evaluated) return await cls.create(msg, { rollMode: config.selectedRollMode });
|
||||
return msg;
|
||||
|
||||
if (roll._evaluated) {
|
||||
const message = await cls.create(msgData, { rollMode: config.selectedRollMode });
|
||||
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
await game.dice3d.waitFor3DAnimationByMessageID(message.id);
|
||||
}
|
||||
|
||||
return message;
|
||||
} else return msgData;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
|
||||
import { LevelOptionType } from '../data/levelTier.mjs';
|
||||
import DHFeature from '../data/item/feature.mjs';
|
||||
import { damageKeyToNumber } from '../helpers/utils.mjs';
|
||||
import { createScrollText, damageKeyToNumber, versionCompare } from '../helpers/utils.mjs';
|
||||
import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs';
|
||||
|
||||
export default class DhpActor extends Actor {
|
||||
#scrollTextQueue = [];
|
||||
#scrollTextInterval;
|
||||
|
||||
/**
|
||||
* Return the first Actor active owner.
|
||||
*/
|
||||
|
|
@ -27,7 +30,7 @@ export default class DhpActor extends Actor {
|
|||
|
||||
/** @inheritDoc */
|
||||
static migrateData(source) {
|
||||
if(source.system?.attack && !source.system.attack.type) source.system.attack.type = "attack";
|
||||
if (source.system?.attack && !source.system.attack.type) source.system.attack.type = 'attack';
|
||||
return super.migrateData(source);
|
||||
}
|
||||
|
||||
|
|
@ -572,19 +575,15 @@ export default class DhpActor extends Actor {
|
|||
if (armorSlotResult) {
|
||||
const { modifiedDamage, armorSpent, stressSpent } = armorSlotResult;
|
||||
updates.find(u => u.key === 'hitPoints').value = modifiedDamage;
|
||||
if(armorSpent) {
|
||||
if (armorSpent) {
|
||||
const armorUpdate = updates.find(u => u.key === 'armor');
|
||||
if(armorUpdate)
|
||||
armorUpdate.value += armorSpent;
|
||||
else
|
||||
updates.push({ value: armorSpent, key: 'armor' });
|
||||
if (armorUpdate) armorUpdate.value += armorSpent;
|
||||
else updates.push({ value: armorSpent, key: 'armor' });
|
||||
}
|
||||
if(stressSpent) {
|
||||
if (stressSpent) {
|
||||
const stressUpdate = updates.find(u => u.key === 'stress');
|
||||
if(stressUpdate)
|
||||
stressUpdate.value += stressSpent;
|
||||
else
|
||||
updates.push({ value: stressSpent, key: 'stress' });
|
||||
if (stressUpdate) stressUpdate.value += stressSpent;
|
||||
else updates.push({ value: stressSpent, key: 'stress' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -754,4 +753,45 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
queueScrollText(scrollingTextData) {
|
||||
this.#scrollTextQueue.push(...scrollingTextData.map(data => () => createScrollText(this, data)));
|
||||
if (!this.#scrollTextInterval) {
|
||||
const scrollFunc = this.#scrollTextQueue.pop();
|
||||
scrollFunc?.();
|
||||
|
||||
const intervalFunc = () => {
|
||||
const scrollFunc = this.#scrollTextQueue.pop();
|
||||
scrollFunc?.();
|
||||
if (this.#scrollTextQueue.length === 0) {
|
||||
clearInterval(this.#scrollTextInterval);
|
||||
this.#scrollTextInterval = null;
|
||||
}
|
||||
};
|
||||
|
||||
this.#scrollTextInterval = setInterval(intervalFunc.bind(this), 600);
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
async importFromJSON(json) {
|
||||
if (!this.type === 'character') return await super.importFromJSON(json);
|
||||
|
||||
if (!CONST.WORLD_DOCUMENT_TYPES.includes(this.documentName)) {
|
||||
throw new Error('Only world Documents may be imported');
|
||||
}
|
||||
|
||||
const parsedJSON = JSON.parse(json);
|
||||
if (versionCompare(parsedJSON._stats.systemVersion, '1.1.0')) {
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: game.i18n.localize('DAGGERHEART.ACTORS.Character.InvalidOldCharacterImportTitle')
|
||||
},
|
||||
content: game.i18n.localize('DAGGERHEART.ACTORS.Character.InvalidOldCharacterImportText')
|
||||
});
|
||||
if (!confirmed) return;
|
||||
}
|
||||
|
||||
return await super.importFromJSON(json);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
||||
const html = await super.renderHTML({ actor: actorData, author: this.author });
|
||||
|
||||
if (this.flags.core?.RollTable) {
|
||||
html.querySelector('.roll-buttons.apply-buttons').remove();
|
||||
}
|
||||
|
||||
this.enrichChatMessage(html);
|
||||
this.addChatListeners(html);
|
||||
|
||||
|
|
@ -43,6 +47,18 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
return super._preDelete(options, user);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
_onUpdate(changes, options, userId) {
|
||||
super._onUpdate(changes, options, userId);
|
||||
|
||||
const lastMessage = Array.from(game.messages).sort((a, b) => b.timestamp - a.timestamp)[0];
|
||||
if (lastMessage.id === this.id && ui.chat.isAtBottom) {
|
||||
setTimeout(() => {
|
||||
ui.chat.scrollBottom();
|
||||
}, 5);
|
||||
}
|
||||
}
|
||||
|
||||
enrichChatMessage(html) {
|
||||
const elements = html.querySelectorAll('[data-perm-id]');
|
||||
elements.forEach(e => {
|
||||
|
|
@ -55,7 +71,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
});
|
||||
|
||||
if (this.isContentVisible) {
|
||||
if(this.type === 'dualityRoll') {
|
||||
if (this.type === 'dualityRoll') {
|
||||
html.classList.add('duality');
|
||||
switch (this.system.roll?.result?.duality) {
|
||||
case 1:
|
||||
|
|
@ -70,27 +86,28 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
}
|
||||
}
|
||||
|
||||
const autoExpandRoll = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).expandRollMessage,
|
||||
rollSections = html.querySelectorAll(".roll-part"),
|
||||
itemDesc = html.querySelector(".domain-card-move");
|
||||
const autoExpandRoll = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.appearance
|
||||
).expandRollMessage,
|
||||
rollSections = html.querySelectorAll('.roll-part'),
|
||||
itemDesc = html.querySelector('.domain-card-move');
|
||||
rollSections.forEach(s => {
|
||||
if(s.classList.contains("roll-section")) {
|
||||
if (s.classList.contains('roll-section')) {
|
||||
const toExpand = s.querySelector('[data-action="expandRoll"]');
|
||||
toExpand.classList.toggle("expanded", autoExpandRoll.roll);
|
||||
} else if(s.classList.contains("damage-section"))
|
||||
s.classList.toggle("expanded", autoExpandRoll.damage);
|
||||
else if(s.classList.contains("target-section"))
|
||||
s.classList.toggle("expanded", autoExpandRoll.target);
|
||||
toExpand.classList.toggle('expanded', autoExpandRoll.roll);
|
||||
} else if (s.classList.contains('damage-section'))
|
||||
s.classList.toggle('expanded', autoExpandRoll.damage);
|
||||
else if (s.classList.contains('target-section')) s.classList.toggle('expanded', autoExpandRoll.target);
|
||||
});
|
||||
if(itemDesc && autoExpandRoll.desc)
|
||||
itemDesc.setAttribute("open", "");
|
||||
if (itemDesc && autoExpandRoll.desc) itemDesc.setAttribute('open', '');
|
||||
}
|
||||
|
||||
if(!game.user.isGM) {
|
||||
const applyButtons = html.querySelector(".apply-buttons");
|
||||
|
||||
if (!game.user.isGM) {
|
||||
const applyButtons = html.querySelector('.apply-buttons');
|
||||
applyButtons?.remove();
|
||||
if(!this.isAuthor && !this.speakerActor?.isOwner) {
|
||||
const buttons = html.querySelectorAll(".ability-card-footer > .ability-use-button");
|
||||
if (!this.isAuthor && !this.speakerActor?.isOwner) {
|
||||
const buttons = html.querySelectorAll('.ability-card-footer > .ability-use-button');
|
||||
buttons.forEach(b => b.remove());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,14 @@ export default class DHItem extends foundry.documents.Item {
|
|||
return doc;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritDoc */
|
||||
static migrateData(source) {
|
||||
if (source.system?.attack && !source.system.attack.type) source.system.attack.type = 'attack';
|
||||
return super.migrateData(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @param {object} options - Options which modify the getRollData method.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ export default class RegisterHandlebarsHelpers {
|
|||
hasProperty: foundry.utils.hasProperty,
|
||||
getProperty: foundry.utils.getProperty,
|
||||
setVar: this.setVar,
|
||||
empty: this.empty
|
||||
empty: this.empty,
|
||||
pluralize: this.pluralize
|
||||
});
|
||||
}
|
||||
static add(a, b) {
|
||||
|
|
@ -64,7 +65,7 @@ export default class RegisterHandlebarsHelpers {
|
|||
return isNumerical ? (!result ? 0 : Number(result)) : result;
|
||||
}
|
||||
|
||||
static setVar(name, value, context) {
|
||||
static setVar(name, value) {
|
||||
this[name] = value;
|
||||
}
|
||||
|
||||
|
|
@ -72,4 +73,20 @@ export default class RegisterHandlebarsHelpers {
|
|||
if (!(typeof object === 'object')) return true;
|
||||
return Object.keys(object).length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pluralize helper that returns the appropriate localized string based on count
|
||||
* @param {number} count - The number to check for plurality
|
||||
* @param {string} baseKey - The base localization key (e.g., "DAGGERHEART.GENERAL.Target")
|
||||
* @returns {string} The localized singular or plural string
|
||||
*
|
||||
* Usage: {{pluralize currentTargets.length "DAGGERHEART.GENERAL.Target"}}
|
||||
* Returns: "Target" if count is exactly 1, "Targets" if count is 0, 2+, or invalid
|
||||
*/
|
||||
static pluralize(count, baseKey) {
|
||||
const numericCount = Number(count);
|
||||
const isSingular = !isNaN(numericCount) && numericCount === 1;
|
||||
const key = isSingular ? `${baseKey}.single` : `${baseKey}.plural`;
|
||||
return game.i18n.localize(key);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -371,17 +371,15 @@ export function getScrollTextData(resources, resource, key) {
|
|||
return { text, stroke, fill, direction };
|
||||
}
|
||||
|
||||
export function createScrollText(actor, optionsData) {
|
||||
if (actor && optionsData?.length) {
|
||||
export function createScrollText(actor, data) {
|
||||
if (actor) {
|
||||
actor.getActiveTokens().forEach(token => {
|
||||
optionsData.forEach(data => {
|
||||
const { text, ...options } = data;
|
||||
canvas.interface.createScrollingText(token.getCenterPoint(), data.text, {
|
||||
duration: 2000,
|
||||
distance: token.h,
|
||||
jitter: 0,
|
||||
...options
|
||||
});
|
||||
const { text, ...options } = data;
|
||||
canvas.interface.createScrollingText(token.getCenterPoint(), data.text, {
|
||||
duration: 2000,
|
||||
distance: token.h,
|
||||
jitter: 0,
|
||||
...options
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -420,3 +418,14 @@ export async function createEmbeddedItemsWithEffects(actor, baseData) {
|
|||
export const slugify = name => {
|
||||
return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', '');
|
||||
};
|
||||
|
||||
export const versionCompare = (current, target) => {
|
||||
const currentSplit = current.split('.').map(x => Number.parseInt(x));
|
||||
const targetSplit = target.split('.').map(x => Number.parseInt(x));
|
||||
for (var i = 0; i < currentSplit.length; i++) {
|
||||
if (currentSplit[i] < targetSplit[i]) return true;
|
||||
if (currentSplit[i] > targetSplit[i]) return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
export { preloadHandlebarsTemplates as handlebarsRegistration } from './handlebars.mjs';
|
||||
export * as settingsRegistration from './settings.mjs';
|
||||
export * as socketRegistration from './socket.mjs';
|
||||
export { runMigrations } from './migrations.mjs';
|
||||
|
|
|
|||
|
|
@ -30,10 +30,11 @@ export const preloadHandlebarsTemplates = async function () {
|
|||
'systems/daggerheart/templates/dialogs/downtime/activities.hbs',
|
||||
'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs',
|
||||
|
||||
|
||||
'systems/daggerheart/templates/ui/chat/parts/roll-part.hbs',
|
||||
'systems/daggerheart/templates/ui/chat/parts/damage-part.hbs',
|
||||
'systems/daggerheart/templates/ui/chat/parts/target-part.hbs',
|
||||
'systems/daggerheart/templates/ui/chat/parts/button-part.hbs',
|
||||
|
||||
'systems/daggerheart/templates/scene/dh-config.hbs'
|
||||
]);
|
||||
};
|
||||
|
|
|
|||
41
module/systemRegistration/migrations.mjs
Normal file
41
module/systemRegistration/migrations.mjs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { versionCompare } from '../helpers/utils.mjs';
|
||||
|
||||
export async function runMigrations() {
|
||||
let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion);
|
||||
if (!lastMigrationVersion) lastMigrationVersion = '1.0.6';
|
||||
|
||||
if (versionCompare(lastMigrationVersion, '1.1.0')) {
|
||||
const compendiumActors = [];
|
||||
for (let pack of game.packs) {
|
||||
const documents = await pack.getDocuments();
|
||||
compendiumActors.push(...documents.filter(x => x.type === 'character'));
|
||||
}
|
||||
|
||||
[...compendiumActors, ...game.actors].forEach(actor => {
|
||||
const items = actor.items.reduce((acc, item) => {
|
||||
if (item.type === 'feature') {
|
||||
const { originItemType, isMulticlass, identifier } = item.system;
|
||||
const base = originItemType
|
||||
? actor.items.find(
|
||||
x => x.type === originItemType && Boolean(isMulticlass) === Boolean(x.system.isMulticlass)
|
||||
)
|
||||
: null;
|
||||
if (base) {
|
||||
const feature = base.system.features.find(x => x.item && x.item.uuid === item.uuid);
|
||||
if (feature && identifier !== 'multiclass') {
|
||||
acc.push({ _id: item.id, system: { identifier: feature.type } });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
actor.updateEmbeddedDocuments('Item', items);
|
||||
});
|
||||
|
||||
lastMigrationVersion = '1.1.0';
|
||||
}
|
||||
|
||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion);
|
||||
}
|
||||
|
|
@ -91,6 +91,12 @@ const registerMenus = () => {
|
|||
};
|
||||
|
||||
const registerNonConfigSettings = () => {
|
||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: String
|
||||
});
|
||||
|
||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue