mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-04-23 07:53:39 +02:00
Merge branch 'main' into feature/-179-apply-items-filter-in-actors-sheet
This commit is contained in:
commit
09eaa0d771
143 changed files with 5790 additions and 2780 deletions
|
|
@ -1,5 +1,6 @@
|
|||
export { default as DhCharacterSheet } from './sheets/character.mjs';
|
||||
export { default as DhpAdversarySheet } from './sheets/adversary.mjs';
|
||||
export { default as DhCharacterSheet } from './sheets/actors/character.mjs';
|
||||
export { default as DhpAdversarySheet } from './sheets/actors/adversary.mjs';
|
||||
export { default as DhCompanionSheet } from './sheets/companion.mjs';
|
||||
export { default as DhpClassSheet } from './sheets/items/class.mjs';
|
||||
export { default as DhpSubclass } from './sheets/items/subclass.mjs';
|
||||
export { default as DhpFeatureSheet } from './sheets/items/feature.mjs';
|
||||
|
|
@ -11,9 +12,10 @@ export { default as DhpConsumable } from './sheets/items/consumable.mjs';
|
|||
export { default as DhpWeapon } from './sheets/items/weapon.mjs';
|
||||
export { default as DhpArmor } from './sheets/items/armor.mjs';
|
||||
export { default as DhpChatMessage } from './chatMessage.mjs';
|
||||
export { default as DhpEnvironment } from './sheets/environment.mjs';
|
||||
export { default as DhpEnvironment } from './sheets/actors/environment.mjs';
|
||||
export { default as DhActiveEffectConfig } from './sheets/activeEffectConfig.mjs';
|
||||
export { default as DhContextMenu } from './contextMenu.mjs';
|
||||
export { default as DhTooltipManager } from './tooltipManager.mjs';
|
||||
|
||||
export * as api from './sheets/api/_modules.mjs';
|
||||
export * as ux from "./ux/_module.mjs";
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ export default class AncestrySelectionDialog extends HandlebarsApplicationMixin(
|
|||
}
|
||||
|
||||
static _onEditImage() {
|
||||
const fp = new FilePicker({
|
||||
const fp = new foundry.applications.apps.FilePicker.implementation({
|
||||
current: this.data.ancestryInfo.img,
|
||||
type: 'image',
|
||||
redirectToRoot: ['icons/svg/mystery-man.svg'],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||
async renderHTML() {
|
||||
if(this.system.messageTemplate) this.content = await foundry.applications.handlebars.renderTemplate(this.system.messageTemplate, this.system);
|
||||
if (this.system.messageTemplate)
|
||||
this.content = await foundry.applications.handlebars.renderTemplate(
|
||||
this.system.messageTemplate,
|
||||
this.system
|
||||
);
|
||||
|
||||
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
||||
const html = await super.renderHTML();
|
||||
|
|
@ -14,7 +18,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
break;
|
||||
case -1:
|
||||
html.classList.add('fear');
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
html.classList.add('critical');
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export default class DhContextMenu extends ContextMenu {
|
||||
export default class DhContextMenu extends foundry.applications.ux.ContextMenu.implementation {
|
||||
constructor(container, selector, menuItems, options) {
|
||||
super(container, selector, menuItems, options);
|
||||
|
||||
|
|
@ -26,10 +26,16 @@ export default class DhContextMenu extends ContextMenu {
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const { clientX, clientY } = event;
|
||||
const selector = "[data-item-id]";
|
||||
const selector = '[data-item-id]';
|
||||
const target = event.target.closest(selector) ?? event.currentTarget.closest(selector);
|
||||
target?.dispatchEvent(new PointerEvent("contextmenu", {
|
||||
view: window, bubbles: true, cancelable: true, clientX, clientY
|
||||
}));
|
||||
target?.dispatchEvent(
|
||||
new PointerEvent('contextmenu', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clientX,
|
||||
clientY
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { countdownTypes } from '../config/generalConfig.mjs';
|
||||
import { GMUpdateEvent, RefreshType, socketEvent } from '../helpers/socket.mjs';
|
||||
import constructHTMLButton from '../helpers/utils.mjs';
|
||||
import OwnershipSelection from './ownershipSelection.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
|
@ -25,14 +26,15 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
frame: true,
|
||||
title: 'Countdowns',
|
||||
resizable: true,
|
||||
minimizable: true
|
||||
minimizable: false
|
||||
},
|
||||
actions: {
|
||||
addCountdown: this.addCountdown,
|
||||
removeCountdown: this.removeCountdown,
|
||||
editImage: this.onEditImage,
|
||||
openOwnership: this.openOwnership,
|
||||
openCountdownOwnership: this.openCountdownOwnership
|
||||
openCountdownOwnership: this.openCountdownOwnership,
|
||||
toggleSimpleView: this.toggleSimpleView
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true }
|
||||
};
|
||||
|
|
@ -53,11 +55,47 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
});
|
||||
}
|
||||
|
||||
async _onFirstRender(context, options) {
|
||||
super._onFirstRender(context, options);
|
||||
async _preFirstRender(context, options) {
|
||||
options.position =
|
||||
game.user.getFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].position) ??
|
||||
Countdowns.DEFAULT_OPTIONS.position;
|
||||
|
||||
this.element.querySelector('.expanded-view').classList.toggle('hidden');
|
||||
this.element.querySelector('.minimized-view').classList.toggle('hidden');
|
||||
const viewSetting =
|
||||
game.user.getFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].simple) ?? !game.user.isGM;
|
||||
this.simpleView =
|
||||
game.user.isGM || !this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER) ? viewSetting : true;
|
||||
context.simple = this.simpleView;
|
||||
}
|
||||
|
||||
_onPosition(position) {
|
||||
game.user.setFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].position, position);
|
||||
}
|
||||
|
||||
async _renderFrame(options) {
|
||||
const frame = await super._renderFrame(options);
|
||||
|
||||
if (this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER)) {
|
||||
const button = constructHTMLButton({
|
||||
label: '',
|
||||
classes: ['header-control', 'icon', 'fa-solid', 'fa-wrench'],
|
||||
dataset: { action: 'toggleSimpleView', tooltip: 'DAGGERHEART.Countdown.ToggleSimple' }
|
||||
});
|
||||
this.window.controls.after(button);
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
testUserPermission(level, exact, altSettings) {
|
||||
if (game.user.isGM) return true;
|
||||
|
||||
const settings =
|
||||
altSettings ?? game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||
const defaultAllowed = exact ? settings.ownership.default === level : settings.ownership.default >= level;
|
||||
const userAllowed = exact
|
||||
? settings.playerOwnership[game.user.id]?.value === level
|
||||
: settings.playerOwnership[game.user.id]?.value >= level;
|
||||
return defaultAllowed || userAllowed;
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
|
|
@ -67,15 +105,17 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
context.isGM = game.user.isGM;
|
||||
context.base = this.basePath;
|
||||
|
||||
context.canCreate = countdownData.playerOwnership[game.user.id].value === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
|
||||
context.canCreate = this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, true);
|
||||
context.source = {
|
||||
...countdownData,
|
||||
countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => {
|
||||
const countdown = countdownData.countdowns[key];
|
||||
|
||||
const ownershipValue = countdown.playerOwnership[game.user.id].value;
|
||||
if (ownershipValue > CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) {
|
||||
acc[key] = { ...countdown, canEdit: ownershipValue === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER };
|
||||
if (this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED, false, countdown)) {
|
||||
acc[key] = {
|
||||
...countdown,
|
||||
canEdit: this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, true, countdown)
|
||||
};
|
||||
}
|
||||
|
||||
return acc;
|
||||
|
|
@ -83,7 +123,7 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
};
|
||||
context.systemFields = countdownData.schema.fields;
|
||||
context.countdownFields = context.systemFields.countdowns.element.fields;
|
||||
context.minimized = this.minimized || _options.isFirstRender;
|
||||
context.simple = this.simpleView;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
@ -110,28 +150,6 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
}
|
||||
}
|
||||
|
||||
async minimize() {
|
||||
await super.minimize();
|
||||
|
||||
this.element.querySelector('.expanded-view').classList.toggle('hidden');
|
||||
this.element.querySelector('.minimized-view').classList.toggle('hidden');
|
||||
}
|
||||
|
||||
async maximize() {
|
||||
if (this.minimized) {
|
||||
const settings = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||
if (settings.playerOwnership[game.user.id].value <= CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED) {
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.Countdown.Notifications.LimitedOwnership'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.element.querySelector('.expanded-view').classList.toggle('hidden');
|
||||
this.element.querySelector('.minimized-view').classList.toggle('hidden');
|
||||
}
|
||||
|
||||
await super.maximize();
|
||||
}
|
||||
|
||||
async updateSetting(update) {
|
||||
if (game.user.isGM) {
|
||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, update);
|
||||
|
|
@ -160,7 +178,7 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
static onEditImage(_, target) {
|
||||
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||
const current = setting.countdowns[target.dataset.countdown].img;
|
||||
const fp = new FilePicker({
|
||||
const fp = new foundry.applications.apps.FilePicker.implementation({
|
||||
current,
|
||||
type: 'image',
|
||||
callback: async path => this.updateImage.bind(this)(path, target.dataset.countdown),
|
||||
|
|
@ -213,11 +231,17 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
});
|
||||
}
|
||||
|
||||
static async toggleSimpleView() {
|
||||
this.simpleView = !this.simpleView;
|
||||
await game.user.setFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].simple, this.simpleView);
|
||||
this.render();
|
||||
}
|
||||
|
||||
async updateCountdownValue(event, increase) {
|
||||
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||
const countdown = countdownSetting[this.basePath].countdowns[event.currentTarget.dataset.countdown];
|
||||
|
||||
if (countdown.playerOwnership[game.user.id] < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) {
|
||||
if (!this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
383
module/applications/levelup/characterLevelup.mjs
Normal file
383
module/applications/levelup/characterLevelup.mjs
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
import LevelUpBase from './levelup.mjs';
|
||||
import { DhLevelup } from '../../data/levelup.mjs';
|
||||
import { domains } from '../../config/domainConfig.mjs';
|
||||
import { abilities } from '../../config/actorConfig.mjs';
|
||||
|
||||
export default class DhCharacterLevelUp extends LevelUpBase {
|
||||
constructor(actor) {
|
||||
super(actor);
|
||||
|
||||
this.levelTiers = this.addBonusChoices(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers));
|
||||
const playerLevelupData = actor.system.levelData;
|
||||
this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData));
|
||||
}
|
||||
|
||||
async _preparePartContext(partId, context) {
|
||||
await super._preparePartContext(partId, context);
|
||||
|
||||
const currentLevel = this.levelup.levels[this.levelup.currentLevel];
|
||||
switch (partId) {
|
||||
case 'selections':
|
||||
const advancementChoices = Object.keys(currentLevel.choices).reduce((acc, choiceKey) => {
|
||||
Object.keys(currentLevel.choices[choiceKey]).forEach(checkboxNr => {
|
||||
const checkbox = currentLevel.choices[choiceKey][checkboxNr];
|
||||
const data = {
|
||||
...checkbox,
|
||||
path: `levels.${this.levelup.currentLevel}.choices.${choiceKey}.${checkboxNr}`,
|
||||
level: this.levelup.currentLevel
|
||||
};
|
||||
|
||||
if (!acc[choiceKey]) acc[choiceKey] = [];
|
||||
acc[choiceKey].push(data);
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const traits = Object.values(advancementChoices.trait ?? {});
|
||||
const traitValues = traits.filter(trait => trait.data.length > 0).flatMap(trait => trait.data);
|
||||
context.traits = {
|
||||
values: traitValues,
|
||||
active: traits.length > 0,
|
||||
progress: {
|
||||
selected: traitValues.length,
|
||||
max: traits.reduce((acc, exp) => acc + exp.amount, 0)
|
||||
}
|
||||
};
|
||||
|
||||
const experienceIncreases = Object.values(advancementChoices.experience ?? {});
|
||||
const experienceIncreaseValues = experienceIncreases
|
||||
.filter(exp => exp.data.length > 0)
|
||||
.flatMap(exp =>
|
||||
exp.data.map(data => {
|
||||
const experience = Object.keys(this.actor.system.experiences).find(x => x === data);
|
||||
return this.actor.system.experiences[experience].name;
|
||||
})
|
||||
);
|
||||
context.experienceIncreases = {
|
||||
values: experienceIncreaseValues,
|
||||
active: experienceIncreases.length > 0,
|
||||
progress: {
|
||||
selected: experienceIncreaseValues.length,
|
||||
max: experienceIncreases.reduce((acc, exp) => acc + exp.amount, 0)
|
||||
}
|
||||
};
|
||||
|
||||
context.newExperiences = Object.keys(currentLevel.achievements.experiences).map(key => {
|
||||
const experience = currentLevel.achievements.experiences[key];
|
||||
return {
|
||||
...experience,
|
||||
level: this.levelup.currentLevel,
|
||||
key: key
|
||||
};
|
||||
});
|
||||
|
||||
const allDomainCards = {
|
||||
...advancementChoices.domainCard,
|
||||
...currentLevel.achievements.domainCards
|
||||
};
|
||||
const allDomainCardKeys = Object.keys(allDomainCards);
|
||||
|
||||
const classDomainsData = this.actor.system.class.value.system.domains.map(domain => ({
|
||||
domain,
|
||||
multiclass: false
|
||||
}));
|
||||
const multiclassDomainsData = (this.actor.system.multiclass?.value?.system?.domains ?? []).map(
|
||||
domain => ({ domain, multiclass: true })
|
||||
);
|
||||
const domainsData = [...classDomainsData, ...multiclassDomainsData];
|
||||
const multiclassDomain = this.levelup.classUpgradeChoices?.multiclass?.domain;
|
||||
if (multiclassDomain) {
|
||||
if (!domainsData.some(x => x.domain === multiclassDomain))
|
||||
domainsData.push({ domain: multiclassDomain, multiclass: true });
|
||||
}
|
||||
|
||||
context.domainCards = [];
|
||||
for (var key of allDomainCardKeys) {
|
||||
const domainCard = allDomainCards[key];
|
||||
if (domainCard.level > this.levelup.endLevel) continue;
|
||||
|
||||
const uuid = domainCard.data?.length > 0 ? domainCard.data[0] : domainCard.uuid;
|
||||
const card = uuid ? await foundry.utils.fromUuid(uuid) : {};
|
||||
|
||||
context.domainCards.push({
|
||||
...(card.toObject?.() ?? card),
|
||||
emptySubtexts: domainsData.map(domain => {
|
||||
const levelBase = domain.multiclass
|
||||
? Math.ceil(this.levelup.currentLevel / 2)
|
||||
: this.levelup.currentLevel;
|
||||
const levelMax = domainCard.secondaryData?.limit
|
||||
? Math.min(domainCard.secondaryData.limit, levelBase)
|
||||
: levelBase;
|
||||
|
||||
return game.i18n.format('DAGGERHEART.Application.LevelUp.Selections.emptyDomainCardHint', {
|
||||
domain: game.i18n.localize(domains[domain.domain].label),
|
||||
level: levelMax
|
||||
});
|
||||
}),
|
||||
path: domainCard.data
|
||||
? `${domainCard.path}.data`
|
||||
: `levels.${domainCard.level}.achievements.domainCards.${key}.uuid`,
|
||||
limit: domainCard.secondaryData?.limit ?? null,
|
||||
compendium: 'domains'
|
||||
});
|
||||
}
|
||||
|
||||
const subclassSelections = advancementChoices.subclass?.flatMap(x => x.data) ?? [];
|
||||
const possibleSubclasses = [this.actor.system.class.subclass];
|
||||
if (this.actor.system.multiclass?.subclass) {
|
||||
possibleSubclasses.push(this.actor.system.multiclass.subclass);
|
||||
}
|
||||
|
||||
context.subclassCards = [];
|
||||
if (advancementChoices.subclass?.length > 0) {
|
||||
const featureStateIncrease = Object.values(this.levelup.levels).reduce((acc, level) => {
|
||||
acc += Object.values(level.choices).filter(choice => {
|
||||
return Object.values(choice).every(checkbox => checkbox.type === 'subclass');
|
||||
}).length;
|
||||
return acc;
|
||||
}, 0);
|
||||
|
||||
for (var subclass of possibleSubclasses) {
|
||||
const choice =
|
||||
advancementChoices.subclass.find(x => x.data[0] === subclass.uuid) ??
|
||||
advancementChoices.subclass.find(x => x.data.length === 0);
|
||||
const featureState = subclass.system.featureState + featureStateIncrease;
|
||||
const data = await foundry.utils.fromUuid(subclass.uuid);
|
||||
context.subclassCards.push({
|
||||
...data.toObject(),
|
||||
path: choice?.path,
|
||||
uuid: data.uuid,
|
||||
selected: subclassSelections.includes(subclass.uuid),
|
||||
featureState: featureState,
|
||||
featureLabel: game.i18n.localize(subclassFeatureLabels[featureState]),
|
||||
isMulticlass: subclass.system.isMulticlass ? 'true' : 'false'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const multiclasses = Object.values(advancementChoices.multiclass ?? {});
|
||||
if (multiclasses?.[0]) {
|
||||
const data = multiclasses[0];
|
||||
const multiclass = data.data.length > 0 ? await foundry.utils.fromUuid(data.data[0]) : {};
|
||||
|
||||
context.multiclass = {
|
||||
...data,
|
||||
...(multiclass.toObject?.() ?? multiclass),
|
||||
uuid: multiclass.uuid,
|
||||
domains:
|
||||
multiclass?.system?.domains.map(key => {
|
||||
const domain = domains[key];
|
||||
const alreadySelected = this.actor.system.class.value.system.domains.includes(key);
|
||||
|
||||
return {
|
||||
...domain,
|
||||
selected: key === data.secondaryData.domain,
|
||||
disabled:
|
||||
(data.secondaryData.domain && key !== data.secondaryData.domain) ||
|
||||
alreadySelected
|
||||
};
|
||||
}) ?? [],
|
||||
subclasses:
|
||||
multiclass?.system?.subclasses.map(subclass => ({
|
||||
...subclass,
|
||||
uuid: subclass.uuid,
|
||||
selected: data.secondaryData.subclass === subclass.uuid,
|
||||
disabled: data.secondaryData.subclass && data.secondaryData.subclass !== subclass.uuid
|
||||
})) ?? [],
|
||||
compendium: 'classes',
|
||||
limit: 1
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
case 'summary':
|
||||
const { current: currentActorLevel, changed: changedActorLevel } = this.actor.system.levelData.level;
|
||||
const actorArmor = this.actor.system.armor;
|
||||
const levelKeys = Object.keys(this.levelup.levels);
|
||||
let achivementProficiency = 0;
|
||||
const achievementCards = [];
|
||||
let achievementExperiences = [];
|
||||
for (var levelKey of levelKeys) {
|
||||
const level = this.levelup.levels[levelKey];
|
||||
if (Number(levelKey) < this.levelup.startLevel) continue;
|
||||
|
||||
achivementProficiency += level.achievements.proficiency ?? 0;
|
||||
const cards = level.achievements.domainCards ? Object.values(level.achievements.domainCards) : null;
|
||||
if (cards) {
|
||||
for (var card of cards) {
|
||||
const itemCard = await foundry.utils.fromUuid(card.uuid);
|
||||
achievementCards.push(itemCard);
|
||||
}
|
||||
}
|
||||
|
||||
achievementExperiences = level.achievements.experiences
|
||||
? Object.values(level.achievements.experiences).reduce((acc, experience) => {
|
||||
if (experience.name) acc.push(experience);
|
||||
return acc;
|
||||
}, [])
|
||||
: [];
|
||||
}
|
||||
|
||||
context.achievements = {
|
||||
proficiency: {
|
||||
old: this.actor.system.proficiency.total,
|
||||
new: this.actor.system.proficiency.total + achivementProficiency,
|
||||
shown: achivementProficiency > 0
|
||||
},
|
||||
damageThresholds: {
|
||||
major: {
|
||||
old: this.actor.system.damageThresholds.major,
|
||||
new: this.actor.system.damageThresholds.major + changedActorLevel - currentActorLevel
|
||||
},
|
||||
severe: {
|
||||
old: this.actor.system.damageThresholds.severe,
|
||||
new:
|
||||
this.actor.system.damageThresholds.severe +
|
||||
(actorArmor
|
||||
? changedActorLevel - currentActorLevel
|
||||
: (changedActorLevel - currentActorLevel) * 2)
|
||||
},
|
||||
unarmored: !actorArmor
|
||||
},
|
||||
domainCards: {
|
||||
values: achievementCards,
|
||||
shown: achievementCards.length > 0
|
||||
},
|
||||
experiences: {
|
||||
values: achievementExperiences,
|
||||
shown: achievementExperiences.length > 0
|
||||
}
|
||||
};
|
||||
|
||||
const advancement = {};
|
||||
for (var levelKey of levelKeys) {
|
||||
const level = this.levelup.levels[levelKey];
|
||||
if (Number(levelKey) < this.levelup.startLevel) continue;
|
||||
|
||||
for (var choiceKey of Object.keys(level.choices)) {
|
||||
const choice = level.choices[choiceKey];
|
||||
for (var checkbox of Object.values(choice)) {
|
||||
switch (choiceKey) {
|
||||
case 'proficiency':
|
||||
case 'hitPoint':
|
||||
case 'stress':
|
||||
case 'evasion':
|
||||
advancement[choiceKey] = advancement[choiceKey]
|
||||
? advancement[choiceKey] + Number(checkbox.value)
|
||||
: Number(checkbox.value);
|
||||
break;
|
||||
case 'trait':
|
||||
if (!advancement[choiceKey]) advancement[choiceKey] = {};
|
||||
for (var traitKey of checkbox.data) {
|
||||
if (!advancement[choiceKey][traitKey]) advancement[choiceKey][traitKey] = 0;
|
||||
advancement[choiceKey][traitKey] += 1;
|
||||
}
|
||||
break;
|
||||
case 'domainCard':
|
||||
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
||||
if (checkbox.data.length === 1) {
|
||||
const choiceItem = await foundry.utils.fromUuid(checkbox.data[0]);
|
||||
advancement[choiceKey].push(choiceItem.toObject());
|
||||
}
|
||||
break;
|
||||
case 'experience':
|
||||
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
||||
const data = checkbox.data.map(data => {
|
||||
const experience = Object.keys(this.actor.system.experiences).find(
|
||||
x => x === data
|
||||
);
|
||||
return this.actor.system.experiences[experience]?.description ?? '';
|
||||
});
|
||||
advancement[choiceKey].push({ data: data, value: checkbox.value });
|
||||
break;
|
||||
case 'subclass':
|
||||
if (checkbox.data[0]) {
|
||||
const subclassItem = await foundry.utils.fromUuid(checkbox.data[0]);
|
||||
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
||||
advancement[choiceKey].push({
|
||||
...subclassItem.toObject(),
|
||||
featureLabel: game.i18n.localize(
|
||||
subclassFeatureLabels[Number(checkbox.secondaryData.featureState)]
|
||||
)
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'multiclass':
|
||||
const multiclassItem = await foundry.utils.fromUuid(checkbox.data[0]);
|
||||
const subclass = multiclassItem
|
||||
? await foundry.utils.fromUuid(checkbox.secondaryData.subclass)
|
||||
: null;
|
||||
advancement[choiceKey] = multiclassItem
|
||||
? {
|
||||
...multiclassItem.toObject(),
|
||||
domain: checkbox.secondaryData.domain
|
||||
? game.i18n.localize(domains[checkbox.secondaryData.domain].label)
|
||||
: null,
|
||||
subclass: subclass ? subclass.name : null
|
||||
}
|
||||
: {};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.advancements = {
|
||||
statistics: {
|
||||
proficiency: {
|
||||
old: context.achievements.proficiency.new,
|
||||
new: context.achievements.proficiency.new + (advancement.proficiency ?? 0)
|
||||
},
|
||||
hitPoints: {
|
||||
old: this.actor.system.resources.hitPoints.maxTotal,
|
||||
new: this.actor.system.resources.hitPoints.maxTotal + (advancement.hitPoint ?? 0)
|
||||
},
|
||||
stress: {
|
||||
old: this.actor.system.resources.stress.maxTotal,
|
||||
new: this.actor.system.resources.stress.maxTotal + (advancement.stress ?? 0)
|
||||
},
|
||||
evasion: {
|
||||
old: this.actor.system.evasion.total,
|
||||
new: this.actor.system.evasion.total + (advancement.evasion ?? 0)
|
||||
}
|
||||
},
|
||||
traits: Object.keys(this.actor.system.traits).reduce((acc, traitKey) => {
|
||||
if (advancement.trait?.[traitKey]) {
|
||||
if (!acc) acc = {};
|
||||
acc[traitKey] = {
|
||||
label: game.i18n.localize(abilities[traitKey].label),
|
||||
old: this.actor.system.traits[traitKey].total,
|
||||
new: this.actor.system.traits[traitKey].total + advancement.trait[traitKey]
|
||||
};
|
||||
}
|
||||
return acc;
|
||||
}, null),
|
||||
domainCards: advancement.domainCard ?? [],
|
||||
experiences:
|
||||
advancement.experience?.flatMap(x => x.data.map(data => ({ name: data, modifier: x.value }))) ??
|
||||
[],
|
||||
multiclass: advancement.multiclass,
|
||||
subclass: advancement.subclass
|
||||
};
|
||||
|
||||
context.advancements.statistics.proficiency.shown =
|
||||
context.advancements.statistics.proficiency.new > context.advancements.statistics.proficiency.old;
|
||||
context.advancements.statistics.hitPoints.shown =
|
||||
context.advancements.statistics.hitPoints.new > context.advancements.statistics.hitPoints.old;
|
||||
context.advancements.statistics.stress.shown =
|
||||
context.advancements.statistics.stress.new > context.advancements.statistics.stress.old;
|
||||
context.advancements.statistics.evasion.shown =
|
||||
context.advancements.statistics.evasion.new > context.advancements.statistics.evasion.old;
|
||||
context.advancements.statistics.shown =
|
||||
context.advancements.statistics.proficiency.shown ||
|
||||
context.advancements.statistics.hitPoints.shown ||
|
||||
context.advancements.statistics.stress.shown ||
|
||||
context.advancements.statistics.evasion.shown;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
||||
163
module/applications/levelup/companionLevelup.mjs
Normal file
163
module/applications/levelup/companionLevelup.mjs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
import BaseLevelUp from './levelup.mjs';
|
||||
import { defaultCompanionTier, LevelOptionType } from '../../data/levelTier.mjs';
|
||||
import { DhLevelup } from '../../data/levelup.mjs';
|
||||
import { diceTypes, range } from '../../config/generalConfig.mjs';
|
||||
|
||||
export default class DhCompanionLevelUp extends BaseLevelUp {
|
||||
constructor(actor) {
|
||||
super(actor);
|
||||
|
||||
this.levelTiers = this.addBonusChoices(defaultCompanionTier);
|
||||
const playerLevelupData = actor.system.levelData;
|
||||
this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData));
|
||||
}
|
||||
|
||||
async _preparePartContext(partId, context) {
|
||||
await super._preparePartContext(partId, context);
|
||||
|
||||
const currentLevel = this.levelup.levels[this.levelup.currentLevel];
|
||||
switch (partId) {
|
||||
case 'selections':
|
||||
const advancementChoices = Object.keys(currentLevel.choices).reduce((acc, choiceKey) => {
|
||||
Object.keys(currentLevel.choices[choiceKey]).forEach(checkboxNr => {
|
||||
const checkbox = currentLevel.choices[choiceKey][checkboxNr];
|
||||
const data = {
|
||||
...checkbox,
|
||||
path: `levels.${this.levelup.currentLevel}.choices.${choiceKey}.${checkboxNr}`,
|
||||
level: this.levelup.currentLevel
|
||||
};
|
||||
|
||||
if (!acc[choiceKey]) acc[choiceKey] = [];
|
||||
acc[choiceKey].push(data);
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const experienceIncreases = Object.values(advancementChoices.experience ?? {});
|
||||
const experienceIncreaseValues = experienceIncreases
|
||||
.filter(exp => exp.data.length > 0)
|
||||
.flatMap(exp =>
|
||||
exp.data.map(data => {
|
||||
const experience = Object.keys(this.actor.system.experiences).find(x => x === data);
|
||||
return this.actor.system.experiences[experience].name;
|
||||
})
|
||||
);
|
||||
context.experienceIncreases = {
|
||||
values: experienceIncreaseValues,
|
||||
active: experienceIncreases.length > 0,
|
||||
progress: {
|
||||
selected: experienceIncreaseValues.length,
|
||||
max: experienceIncreases.reduce((acc, exp) => acc + exp.amount, 0)
|
||||
}
|
||||
};
|
||||
|
||||
context.newExperiences = Object.keys(currentLevel.achievements.experiences).map(key => {
|
||||
const experience = currentLevel.achievements.experiences[key];
|
||||
return {
|
||||
...experience,
|
||||
level: this.levelup.currentLevel,
|
||||
key: key
|
||||
};
|
||||
});
|
||||
|
||||
context.vicious = advancementChoices.vicious ? Object.values(advancementChoices.vicious) : null;
|
||||
context.viciousChoices = {
|
||||
damage: game.i18n.localize('DAGGERHEART.Application.LevelUp.Selections.viciousDamage'),
|
||||
range: game.i18n.localize('DAGGERHEART.Application.LevelUp.Selections.viciousRange')
|
||||
};
|
||||
|
||||
break;
|
||||
case 'summary':
|
||||
const levelKeys = Object.keys(this.levelup.levels);
|
||||
const actorDamageDice = this.actor.system.attack.damage.parts[0].value.dice;
|
||||
const actorRange = this.actor.system.attack.range;
|
||||
const advancement = {};
|
||||
for (var levelKey of levelKeys) {
|
||||
const level = this.levelup.levels[levelKey];
|
||||
if (Number(levelKey) < this.levelup.startLevel) continue;
|
||||
|
||||
for (var choiceKey of Object.keys(level.choices)) {
|
||||
const choice = level.choices[choiceKey];
|
||||
for (var checkbox of Object.values(choice)) {
|
||||
switch (choiceKey) {
|
||||
case 'stress':
|
||||
case 'evasion':
|
||||
advancement[choiceKey] = advancement[choiceKey]
|
||||
? advancement[choiceKey] + Number(checkbox.value)
|
||||
: Number(checkbox.value);
|
||||
break;
|
||||
case 'experience':
|
||||
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
||||
const data = checkbox.data.map(data => {
|
||||
const experience = Object.keys(this.actor.system.experiences).find(
|
||||
x => x === data
|
||||
);
|
||||
return this.actor.system.experiences[experience]?.name ?? '';
|
||||
});
|
||||
advancement[choiceKey].push({ data: data, value: checkbox.value });
|
||||
break;
|
||||
case 'vicious':
|
||||
if (!advancement[choiceKey]) advancement[choiceKey] = { damage: null, range: null };
|
||||
const isDamage = checkbox.data[0] === 'damage';
|
||||
const options = isDamage ? diceTypes : range;
|
||||
const keys = Object.keys(options);
|
||||
const actorKey = keys.indexOf(isDamage ? actorDamageDice : actorRange);
|
||||
const currentIndex = advancement[choiceKey][checkbox.data[0]]
|
||||
? keys.indexOf(advancement[choiceKey][checkbox.data[0]])
|
||||
: actorKey;
|
||||
advancement[choiceKey][checkbox.data[0]] =
|
||||
options[keys[Math.min(currentIndex + 1, keys.length - 1)]];
|
||||
default:
|
||||
if (!advancement.simple) advancement.simple = {};
|
||||
advancement.simple[choiceKey] = game.i18n.localize(
|
||||
LevelOptionType[checkbox.type].label
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.advancements = {
|
||||
statistics: {
|
||||
stress: {
|
||||
old: this.actor.system.resources.stress.maxTotal,
|
||||
new: this.actor.system.resources.stress.maxTotal + (advancement.stress ?? 0)
|
||||
},
|
||||
evasion: {
|
||||
old: this.actor.system.evasion.total,
|
||||
new: this.actor.system.evasion.total + (advancement.evasion ?? 0)
|
||||
}
|
||||
},
|
||||
experiences:
|
||||
advancement.experience?.flatMap(x => x.data.map(data => ({ name: data, modifier: x.value }))) ??
|
||||
[],
|
||||
vicious: {
|
||||
damage: advancement.vicious?.damage
|
||||
? {
|
||||
old: actorDamageDice,
|
||||
new: advancement.vicious.damage
|
||||
}
|
||||
: null,
|
||||
range: advancement.vicious?.range
|
||||
? {
|
||||
old: game.i18n.localize(`DAGGERHEART.Range.${actorRange}.name`),
|
||||
new: game.i18n.localize(advancement.vicious.range.label)
|
||||
}
|
||||
: null
|
||||
},
|
||||
simple: advancement.simple ?? {}
|
||||
};
|
||||
|
||||
context.advancements.statistics.stress.shown =
|
||||
context.advancements.statistics.stress.new > context.advancements.statistics.stress.old;
|
||||
context.advancements.statistics.evasion.shown =
|
||||
context.advancements.statistics.evasion.new > context.advancements.statistics.evasion.old;
|
||||
context.advancements.statistics.shown =
|
||||
context.advancements.statistics.stress.shown || context.advancements.statistics.evasion.shown;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import { abilities, subclassFeatureLabels } from '../config/actorConfig.mjs';
|
||||
import { domains } from '../config/domainConfig.mjs';
|
||||
import { DhLevelup } from '../data/levelup.mjs';
|
||||
import { getDeleteKeys, tagifyElement } from '../helpers/utils.mjs';
|
||||
import { abilities, subclassFeatureLabels } from '../../config/actorConfig.mjs';
|
||||
import { domains } from '../../config/domainConfig.mjs';
|
||||
import { getDeleteKeys, tagifyElement } from '../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
|
|
@ -10,10 +9,6 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
super({});
|
||||
|
||||
this.actor = actor;
|
||||
this.levelTiers = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers);
|
||||
|
||||
const playerLevelupData = actor.system.levelData;
|
||||
this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData, actor.system.level));
|
||||
|
||||
this._dragDrop = this._createDragDropHandlers();
|
||||
this.tabGroups.primary = 'advancements';
|
||||
|
|
@ -81,6 +76,21 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
}
|
||||
};
|
||||
|
||||
addBonusChoices(levelTiers) {
|
||||
for (var tierKey in levelTiers.tiers) {
|
||||
const tier = levelTiers.tiers[tierKey];
|
||||
tier.maxSelections = [...Array(tier.levels.end - tier.levels.start + 1).keys()].reduce((acc, index) => {
|
||||
const level = tier.levels.start + index;
|
||||
const bonus = this.actor.system.levelData.level.bonuses[level];
|
||||
acc[level] = tier.availableOptions + (bonus ?? 0);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
return levelTiers;
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.levelup = this.levelup;
|
||||
|
|
@ -118,181 +128,6 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
context.tabs.advancements.progress = { selected: selections, max: currentLevel.maxSelections };
|
||||
context.showTabs = this.tabGroups.primary !== 'summary';
|
||||
break;
|
||||
case 'selections':
|
||||
const advancementChoices = Object.keys(currentLevel.choices).reduce((acc, choiceKey) => {
|
||||
Object.keys(currentLevel.choices[choiceKey]).forEach(checkboxNr => {
|
||||
const checkbox = currentLevel.choices[choiceKey][checkboxNr];
|
||||
const data = {
|
||||
...checkbox,
|
||||
path: `levels.${this.levelup.currentLevel}.choices.${choiceKey}.${checkboxNr}`,
|
||||
level: this.levelup.currentLevel
|
||||
};
|
||||
|
||||
if (!acc[choiceKey]) acc[choiceKey] = [];
|
||||
acc[choiceKey].push(data);
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const traits = Object.values(advancementChoices.trait ?? {});
|
||||
const traitValues = traits.filter(trait => trait.data.length > 0).flatMap(trait => trait.data);
|
||||
context.traits = {
|
||||
values: traitValues,
|
||||
active: traits.length > 0,
|
||||
progress: {
|
||||
selected: traitValues.length,
|
||||
max: traits.reduce((acc, exp) => acc + exp.amount, 0)
|
||||
}
|
||||
};
|
||||
|
||||
const experienceIncreases = Object.values(advancementChoices.experience ?? {});
|
||||
const experienceIncreaseValues = experienceIncreases
|
||||
.filter(exp => exp.data.length > 0)
|
||||
.flatMap(exp =>
|
||||
exp.data.map(data => {
|
||||
const experience = Object.keys(this.actor.system.experiences).find(x => x === data);
|
||||
return this.actor.system.experiences[experience].description;
|
||||
})
|
||||
);
|
||||
context.experienceIncreases = {
|
||||
values: experienceIncreaseValues,
|
||||
active: experienceIncreases.length > 0,
|
||||
progress: {
|
||||
selected: experienceIncreaseValues.length,
|
||||
max: experienceIncreases.reduce((acc, exp) => acc + exp.amount, 0)
|
||||
}
|
||||
};
|
||||
|
||||
context.newExperiences = Object.keys(currentLevel.achievements.experiences).map(key => {
|
||||
const experience = currentLevel.achievements.experiences[key];
|
||||
return {
|
||||
...experience,
|
||||
level: this.levelup.currentLevel,
|
||||
key: key
|
||||
};
|
||||
});
|
||||
|
||||
const allDomainCards = {
|
||||
...advancementChoices.domainCard,
|
||||
...currentLevel.achievements.domainCards
|
||||
};
|
||||
const allDomainCardKeys = Object.keys(allDomainCards);
|
||||
|
||||
const classDomainsData = this.actor.system.class.value.system.domains.map(domain => ({
|
||||
domain,
|
||||
multiclass: false
|
||||
}));
|
||||
const multiclassDomainsData = (this.actor.system.multiclass?.value?.system?.domains ?? []).map(
|
||||
domain => ({ domain, multiclass: true })
|
||||
);
|
||||
const domainsData = [...classDomainsData, ...multiclassDomainsData];
|
||||
const multiclassDomain = this.levelup.classUpgradeChoices?.multiclass?.domain;
|
||||
if (multiclassDomain) {
|
||||
if (!domainsData.some(x => x.domain === multiclassDomain))
|
||||
domainsData.push({ domain: multiclassDomain, multiclass: true });
|
||||
}
|
||||
|
||||
context.domainCards = [];
|
||||
for (var key of allDomainCardKeys) {
|
||||
const domainCard = allDomainCards[key];
|
||||
if (domainCard.level > this.levelup.endLevel) continue;
|
||||
|
||||
const uuid = domainCard.data?.length > 0 ? domainCard.data[0] : domainCard.uuid;
|
||||
const card = uuid ? await foundry.utils.fromUuid(uuid) : {};
|
||||
|
||||
context.domainCards.push({
|
||||
...(card.toObject?.() ?? card),
|
||||
emptySubtexts: domainsData.map(domain => {
|
||||
const levelBase = domain.multiclass
|
||||
? Math.ceil(this.levelup.currentLevel / 2)
|
||||
: this.levelup.currentLevel;
|
||||
const levelMax = domainCard.secondaryData?.limit
|
||||
? Math.min(domainCard.secondaryData.limit, levelBase)
|
||||
: levelBase;
|
||||
|
||||
return game.i18n.format('DAGGERHEART.Application.LevelUp.Selections.emptyDomainCardHint', {
|
||||
domain: game.i18n.localize(domains[domain.domain].label),
|
||||
level: levelMax
|
||||
});
|
||||
}),
|
||||
path: domainCard.data
|
||||
? `${domainCard.path}.data`
|
||||
: `levels.${domainCard.level}.achievements.domainCards.${key}.uuid`,
|
||||
limit: domainCard.secondaryData?.limit ?? null,
|
||||
compendium: 'domains'
|
||||
});
|
||||
}
|
||||
|
||||
const subclassSelections = advancementChoices.subclass?.flatMap(x => x.data) ?? [];
|
||||
const possibleSubclasses = [this.actor.system.class.subclass];
|
||||
if (this.actor.system.multiclass?.subclass) {
|
||||
possibleSubclasses.push(this.actor.system.multiclass.subclass);
|
||||
}
|
||||
|
||||
context.subclassCards = [];
|
||||
if (advancementChoices.subclass?.length > 0) {
|
||||
const featureStateIncrease = Object.values(this.levelup.levels).reduce((acc, level) => {
|
||||
acc += Object.values(level.choices).filter(choice => {
|
||||
return Object.values(choice).every(checkbox => checkbox.type === 'subclass');
|
||||
}).length;
|
||||
return acc;
|
||||
}, 0);
|
||||
|
||||
for (var subclass of possibleSubclasses) {
|
||||
const choice =
|
||||
advancementChoices.subclass.find(x => x.data[0] === subclass.uuid) ??
|
||||
advancementChoices.subclass.find(x => x.data.length === 0);
|
||||
const featureState = subclass.system.featureState + featureStateIncrease;
|
||||
const data = await foundry.utils.fromUuid(subclass.uuid);
|
||||
context.subclassCards.push({
|
||||
...data.toObject(),
|
||||
path: choice?.path,
|
||||
uuid: data.uuid,
|
||||
selected: subclassSelections.includes(subclass.uuid),
|
||||
featureState: featureState,
|
||||
featureLabel: game.i18n.localize(subclassFeatureLabels[featureState]),
|
||||
isMulticlass: subclass.system.isMulticlass ? 'true' : 'false'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const multiclasses = Object.values(advancementChoices.multiclass ?? {});
|
||||
if (multiclasses?.[0]) {
|
||||
const data = multiclasses[0];
|
||||
const multiclass = data.data.length > 0 ? await foundry.utils.fromUuid(data.data[0]) : {};
|
||||
|
||||
context.multiclass = {
|
||||
...data,
|
||||
...(multiclass.toObject?.() ?? multiclass),
|
||||
uuid: multiclass.uuid,
|
||||
domains:
|
||||
multiclass?.system?.domains.map(key => {
|
||||
const domain = domains[key];
|
||||
const alreadySelected = this.actor.system.class.value.system.domains.includes(key);
|
||||
|
||||
return {
|
||||
...domain,
|
||||
selected: key === data.secondaryData.domain,
|
||||
disabled:
|
||||
(data.secondaryData.domain && key !== data.secondaryData.domain) ||
|
||||
alreadySelected
|
||||
};
|
||||
}) ?? [],
|
||||
subclasses:
|
||||
multiclass?.system?.subclasses.map(subclass => ({
|
||||
...subclass,
|
||||
uuid: subclass.uuid,
|
||||
selected: data.secondaryData.subclass === subclass.uuid,
|
||||
disabled: data.secondaryData.subclass && data.secondaryData.subclass !== subclass.uuid
|
||||
})) ?? [],
|
||||
compendium: 'classes',
|
||||
limit: 1
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
case 'summary':
|
||||
const { current: currentActorLevel, changed: changedActorLevel } = this.actor.system.levelData.level;
|
||||
const actorArmor = this.actor.system.armor;
|
||||
const levelKeys = Object.keys(this.levelup.levels);
|
||||
|
|
@ -516,7 +351,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
experienceIncreaseTagify,
|
||||
Object.keys(this.actor.system.experiences).reduce((acc, id) => {
|
||||
const experience = this.actor.system.experiences[id];
|
||||
acc[id] = { label: experience.description };
|
||||
acc[id] = { label: experience.name };
|
||||
|
||||
return acc;
|
||||
}, {}),
|
||||
|
|
@ -594,20 +429,20 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
Object.values(this.levelup.levels).some(level => {
|
||||
const achievementExists = Object.values(level.achievements.domainCards).some(
|
||||
card => card.uuid === item.uuid
|
||||
);
|
||||
const advancementExists = Object.keys(level.choices).some(choiceKey => {
|
||||
if (choiceKey !== 'domainCard') return false;
|
||||
const choice = level.choices[choiceKey];
|
||||
return Object.values(choice).some(checkbox => checkbox.data.includes(item.uuid));
|
||||
});
|
||||
const cardExistsInCharacter = this.actor.items.find(x => x.name === item.name); // Any other way to check? The item is a copy so different ids
|
||||
const cardExistsInLevelup = Object.values(this.levelup.levels).some(level => {
|
||||
const achievementExists = Object.values(level.achievements.domainCards).some(
|
||||
card => card.uuid === item.uuid
|
||||
);
|
||||
const advancementExists = Object.keys(level.choices).some(choiceKey => {
|
||||
if (choiceKey !== 'domainCard') return false;
|
||||
const choice = level.choices[choiceKey];
|
||||
return Object.values(choice).some(checkbox => checkbox.data.includes(item.uuid));
|
||||
});
|
||||
|
||||
return achievementExists || advancementExists;
|
||||
})
|
||||
) {
|
||||
return achievementExists || advancementExists;
|
||||
});
|
||||
if (cardExistsInCharacter || cardExistsInLevelup) {
|
||||
ui.notifications.error(
|
||||
game.i18n.localize('DAGGERHEART.Application.LevelUp.notifications.error.domainCardDuplicate')
|
||||
);
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
/** NOT USED ANYMORE - TO BE DELETED **/
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class NpcRollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import DHDamageRoll from '../data/chat-message/damageRoll.mjs';
|
||||
import D20RollDialog from '../dialogs/d20RollDialog.mjs';
|
||||
import DamageDialog from '../dialogs/damageDialog.mjs';
|
||||
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||
|
||||
/*
|
||||
- Damage & other resources roll
|
||||
|
|
@ -7,6 +9,7 @@ import DamageDialog from '../dialogs/damageDialog.mjs';
|
|||
*/
|
||||
|
||||
export class DHRoll extends Roll {
|
||||
baseTerms = [];
|
||||
constructor(formula, data, options) {
|
||||
super(formula, data, options);
|
||||
}
|
||||
|
|
@ -29,17 +32,18 @@ export class DHRoll extends Roll {
|
|||
for (const hook of config.hooks) {
|
||||
if (Hooks.call(`${SYSTEM.id}.preRoll${hook.capitalize()}`, config, message) === false) return null;
|
||||
}
|
||||
|
||||
|
||||
this.applyKeybindings(config);
|
||||
|
||||
let roll = new this(config.roll.formula, config.data, config);
|
||||
if (config.dialog.configure !== false) {
|
||||
// Open Roll Dialog
|
||||
const DialogClass = config.dialog?.class ?? this.DefaultDialog;
|
||||
console.log(roll, config);
|
||||
const configDialog = await DialogClass.configure(roll, config, message);
|
||||
if (!configDialog) return;
|
||||
}
|
||||
|
||||
|
||||
for (const hook of config.hooks) {
|
||||
if (Hooks.call(`${SYSTEM.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false)
|
||||
return [];
|
||||
|
|
@ -58,7 +62,7 @@ export class DHRoll extends Roll {
|
|||
}
|
||||
|
||||
// Create Chat Message
|
||||
if (message.data) {
|
||||
if (config.source?.message) {
|
||||
} else {
|
||||
const messageData = {};
|
||||
config.message = await this.toMessage(roll, config);
|
||||
|
|
@ -66,7 +70,7 @@ export class DHRoll extends Roll {
|
|||
}
|
||||
|
||||
static postEvaluate(roll, config = {}) {
|
||||
if(!config.roll) config.roll = {};
|
||||
if (!config.roll) config.roll = {};
|
||||
config.roll.total = roll.total;
|
||||
config.roll.formula = roll.formula;
|
||||
config.roll.dice = [];
|
||||
|
|
@ -93,12 +97,31 @@ export class DHRoll extends Roll {
|
|||
}
|
||||
|
||||
static applyKeybindings(config) {
|
||||
config.dialog.configure ??= !(config.event.shiftKey || config.event.altKey || config.event.ctrlKey);
|
||||
if (config.event)
|
||||
config.dialog.configure ??= !(config.event.shiftKey || config.event.altKey || config.event.ctrlKey);
|
||||
}
|
||||
|
||||
formatModifier(modifier) {
|
||||
const numTerm = modifier < 0 ? '-' : '+';
|
||||
return [
|
||||
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
|
||||
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) })
|
||||
];
|
||||
}
|
||||
|
||||
getFaces(faces) {
|
||||
return Number(faces.startsWith('d') ? faces.replace('d', '') : faces);
|
||||
}
|
||||
|
||||
constructFormula(config) {
|
||||
// const formula = Roll.replaceFormulaData(this.options.roll.formula, config.data);
|
||||
this.terms = Roll.parse(this.options.roll.formula, config.data)
|
||||
this.terms = Roll.parse(this.options.roll.formula, config.data);
|
||||
|
||||
if (this.options.extraFormula) {
|
||||
this.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
...this.constructor.parse(this.options.extraFormula, this.options.data)
|
||||
);
|
||||
}
|
||||
return (this._formula = this.constructor.getFormula(this.terms));
|
||||
}
|
||||
}
|
||||
|
|
@ -112,10 +135,6 @@ export class DualityDie extends foundry.dice.terms.Die {
|
|||
export class D20Roll extends DHRoll {
|
||||
constructor(formula, data = {}, options = {}) {
|
||||
super(formula, data, options);
|
||||
// this.createBaseDice();
|
||||
// this.configureModifiers();
|
||||
|
||||
// this._formula = this.resetFormula();
|
||||
this.constructFormula();
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +157,7 @@ export class D20Roll extends DHRoll {
|
|||
|
||||
set d20(faces) {
|
||||
if (!(this.terms[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
||||
this.terms[0].faces = faces;
|
||||
this.terms[0].faces = this.getFaces(faces);
|
||||
}
|
||||
|
||||
get dAdvantage() {
|
||||
|
|
@ -151,36 +170,82 @@ export class D20Roll extends DHRoll {
|
|||
}
|
||||
|
||||
get hasAdvantage() {
|
||||
return this.options.advantage === this.constructor.ADV_MODE.ADVANTAGE;
|
||||
return this.options.roll.advantage === this.constructor.ADV_MODE.ADVANTAGE;
|
||||
}
|
||||
|
||||
get hasDisadvantage() {
|
||||
return this.options.advantage === this.constructor.ADV_MODE.DISADVANTAGE;
|
||||
return this.options.roll.advantage === this.constructor.ADV_MODE.DISADVANTAGE;
|
||||
}
|
||||
|
||||
static applyKeybindings(config) {
|
||||
const keys = {
|
||||
normal: config.event.shiftKey || config.event.altKey || config.event.ctrlKey,
|
||||
advantage: config.event.altKey,
|
||||
disadvantage: config.event.ctrlKey
|
||||
let keys = {
|
||||
normal: true,
|
||||
advantage: false,
|
||||
disadvantage: false
|
||||
};
|
||||
|
||||
if (config.event) {
|
||||
keys = {
|
||||
normal: config.event.shiftKey || config.event.altKey || config.event.ctrlKey,
|
||||
advantage: config.event.altKey,
|
||||
disadvantage: config.event.ctrlKey
|
||||
};
|
||||
}
|
||||
|
||||
// Should the roll configuration dialog be displayed?
|
||||
config.dialog.configure ??= !Object.values(keys).some(k => k);
|
||||
|
||||
// Determine advantage mode
|
||||
const advantage = config.advantage || keys.advantage;
|
||||
const disadvantage = config.disadvantage || keys.disadvantage;
|
||||
if (advantage && !disadvantage) config.advantage = this.ADV_MODE.ADVANTAGE;
|
||||
else if (!advantage && disadvantage) config.advantage = this.ADV_MODE.DISADVANTAGE;
|
||||
else config.advantage = this.ADV_MODE.NORMAL;
|
||||
const advantage = config.roll.advantage === this.ADV_MODE.ADVANTAGE || keys.advantage || config.advantage;
|
||||
const disadvantage =
|
||||
config.roll.advantage === this.ADV_MODE.DISADVANTAGE || keys.disadvantage || config.disadvantage;
|
||||
if (advantage && !disadvantage) config.roll.advantage = this.ADV_MODE.ADVANTAGE;
|
||||
else if (!advantage && disadvantage) config.roll.advantage = this.ADV_MODE.DISADVANTAGE;
|
||||
else config.roll.advantage = this.ADV_MODE.NORMAL;
|
||||
}
|
||||
|
||||
constructFormula(config) {
|
||||
// this.terms = [];
|
||||
this.createBaseDice();
|
||||
this.configureModifiers();
|
||||
this.resetFormula();
|
||||
return this._formula;
|
||||
}
|
||||
|
||||
createBaseDice() {
|
||||
if (this.terms[0] instanceof foundry.dice.terms.Die) return;
|
||||
if (this.terms[0] instanceof foundry.dice.terms.Die) {
|
||||
this.terms = [this.terms[0]];
|
||||
return;
|
||||
}
|
||||
this.terms[0] = new foundry.dice.terms.Die({ faces: 20 });
|
||||
}
|
||||
|
||||
configureModifiers() {
|
||||
this.applyAdvantage();
|
||||
this.applyBaseBonus();
|
||||
|
||||
this.options.experiences?.forEach(m => {
|
||||
if (this.options.data.experiences?.[m])
|
||||
this.options.roll.modifiers.push({
|
||||
label: this.options.data.experiences[m].name,
|
||||
value: this.options.data.experiences[m].total ?? this.options.data.experiences[m].value
|
||||
});
|
||||
});
|
||||
|
||||
this.options.roll.modifiers?.forEach(m => {
|
||||
this.terms.push(...this.formatModifier(m.value));
|
||||
});
|
||||
|
||||
this.baseTerms = foundry.utils.deepClone(this.terms);
|
||||
|
||||
if (this.options.extraFormula) {
|
||||
this.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
...this.constructor.parse(this.options.extraFormula, this.options.data)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
applyAdvantage() {
|
||||
this.d20.modifiers.findSplice(m => ['kh', 'kl'].includes(m));
|
||||
if (!this.hasAdvantage && !this.hasDisadvantage) this.number = 1;
|
||||
|
|
@ -190,45 +255,26 @@ export class D20Roll extends DHRoll {
|
|||
}
|
||||
}
|
||||
|
||||
// Trait bonus != Adversary
|
||||
configureModifiers() {
|
||||
this.applyAdvantage();
|
||||
// this.options.roll.modifiers = [];
|
||||
this.applyBaseBonus();
|
||||
|
||||
this.options.experiences?.forEach(m => {
|
||||
if (this.options.data.experiences?.[m])
|
||||
this.options.roll.modifiers.push({
|
||||
label: this.options.data.experiences[m].name,
|
||||
value: this.options.data.experiences[m].total ?? this.options.data.experiences[m].value
|
||||
});
|
||||
});
|
||||
this.options.roll.modifiers?.forEach(m => {
|
||||
this.terms.push(...this.formatModifier(m.value));
|
||||
});
|
||||
|
||||
if (this.options.extraFormula) {
|
||||
this.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
...this.constructor.parse(this.options.extraFormula, this.getRollData())
|
||||
);
|
||||
}
|
||||
// this.resetFormula();
|
||||
}
|
||||
|
||||
constructFormula(config) {
|
||||
this.terms = [];
|
||||
this.createBaseDice();
|
||||
this.configureModifiers();
|
||||
this.resetFormula();
|
||||
return this._formula;
|
||||
}
|
||||
|
||||
applyBaseBonus() {
|
||||
this.options.roll.modifiers = [{
|
||||
label : 'Bonus to Hit',
|
||||
value: Roll.replaceFormulaData('@attackBonus', this.data)
|
||||
}];
|
||||
this.options.roll.modifiers = [];
|
||||
if (!this.options.roll.bonus) return;
|
||||
this.options.roll.modifiers.push({
|
||||
label: 'Bonus to Hit',
|
||||
value: this.options.roll.bonus
|
||||
// value: Roll.replaceFormulaData('@attackBonus', this.data)
|
||||
});
|
||||
}
|
||||
|
||||
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||
if (config.evaluate !== false) await roll.evaluate();
|
||||
const advantageState =
|
||||
config.roll.advantage == this.ADV_MODE.ADVANTAGE
|
||||
? true
|
||||
: config.roll.advantage == this.ADV_MODE.DISADVANTAGE
|
||||
? false
|
||||
: null;
|
||||
setDiceSoNiceForDualityRoll(roll, advantageState);
|
||||
this.postEvaluate(roll, config);
|
||||
}
|
||||
|
||||
static postEvaluate(roll, config = {}) {
|
||||
|
|
@ -238,25 +284,30 @@ export class D20Roll extends DHRoll {
|
|||
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
|
||||
target.hit = this.isCritical || roll.total >= difficulty;
|
||||
});
|
||||
} else if (config.roll.difficulty) config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty;
|
||||
} else if (config.roll.difficulty)
|
||||
config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty;
|
||||
config.roll.advantage = {
|
||||
type: config.advantage,
|
||||
type: config.roll.advantage,
|
||||
dice: roll.dAdvantage?.denomination,
|
||||
value: roll.dAdvantage?.total
|
||||
};
|
||||
config.roll.modifierTotal = config.roll.modifiers.reduce((a, c) => a + Number(c.value), 0);
|
||||
}
|
||||
|
||||
getRollData() {
|
||||
return this.options.data;
|
||||
}
|
||||
|
||||
formatModifier(modifier) {
|
||||
const numTerm = modifier < 0 ? '-' : '+';
|
||||
return [
|
||||
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
|
||||
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) })
|
||||
];
|
||||
config.roll.extra = roll.dice
|
||||
.filter(d => !roll.baseTerms.includes(d))
|
||||
.map(d => {
|
||||
return {
|
||||
dice: d.denomination,
|
||||
value: d.total
|
||||
};
|
||||
});
|
||||
config.roll.modifierTotal = 0;
|
||||
for (let i = 0; i < roll.terms.length; i++) {
|
||||
if (
|
||||
roll.terms[i] instanceof foundry.dice.terms.NumericTerm &&
|
||||
!!roll.terms[i - 1] &&
|
||||
roll.terms[i - 1] instanceof foundry.dice.terms.OperatorTerm
|
||||
)
|
||||
config.roll.modifierTotal += Number(`${roll.terms[i - 1].operator}${roll.terms[i].total}`);
|
||||
}
|
||||
}
|
||||
|
||||
resetFormula() {
|
||||
|
|
@ -265,6 +316,8 @@ export class D20Roll extends DHRoll {
|
|||
}
|
||||
|
||||
export class DualityRoll extends D20Roll {
|
||||
_advantageFaces = 6;
|
||||
|
||||
constructor(formula, data = {}, options = {}) {
|
||||
super(formula, data, options);
|
||||
}
|
||||
|
|
@ -282,7 +335,7 @@ export class DualityRoll extends D20Roll {
|
|||
|
||||
set dHope(faces) {
|
||||
if (!(this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice();
|
||||
this.terms[0].faces = faces;
|
||||
this.terms[0].faces = this.getFaces(faces);
|
||||
// this.#hopeDice = `d${face}`;
|
||||
}
|
||||
|
||||
|
|
@ -295,7 +348,7 @@ export class DualityRoll extends D20Roll {
|
|||
|
||||
set dFear(faces) {
|
||||
if (!(this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice();
|
||||
this.dice[1].faces = faces;
|
||||
this.dice[1].faces = this.getFaces(faces);
|
||||
// this.#fearDice = `d${face}`;
|
||||
}
|
||||
|
||||
|
|
@ -303,6 +356,14 @@ export class DualityRoll extends D20Roll {
|
|||
return this.dice[2];
|
||||
}
|
||||
|
||||
get advantageFaces() {
|
||||
return this._advantageFaces;
|
||||
}
|
||||
|
||||
set advantageFaces(faces) {
|
||||
this._advantageFaces = this.getFaces(faces);
|
||||
}
|
||||
|
||||
get isCritical() {
|
||||
if (!this.dHope._evaluated || !this.dFear._evaluated) return;
|
||||
return this.dHope.total === this.dFear.total;
|
||||
|
|
@ -332,25 +393,27 @@ export class DualityRoll extends D20Roll {
|
|||
return game.i18n.localize(label);
|
||||
}
|
||||
|
||||
updateFormula() {}
|
||||
|
||||
createBaseDice() {
|
||||
if (
|
||||
this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie &&
|
||||
this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie
|
||||
)
|
||||
) {
|
||||
this.terms = [this.terms[0], this.terms[1], this.terms[2]];
|
||||
return;
|
||||
if (!(this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie))
|
||||
this.terms[0] = new CONFIG.Dice.daggerheart.DualityDie();
|
||||
}
|
||||
this.terms[0] = new CONFIG.Dice.daggerheart.DualityDie();
|
||||
this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' });
|
||||
if (!(this.dice[2] instanceof CONFIG.Dice.daggerheart.DualityDie))
|
||||
this.terms[2] = new CONFIG.Dice.daggerheart.DualityDie();
|
||||
this.terms[2] = new CONFIG.Dice.daggerheart.DualityDie();
|
||||
}
|
||||
|
||||
applyAdvantage() {
|
||||
const dieFaces = 6,
|
||||
const dieFaces = this.advantageFaces,
|
||||
bardRallyFaces = this.hasBarRally,
|
||||
advDie = new foundry.dice.terms.Die({ faces: dieFaces });
|
||||
if (this.hasAdvantage || this.hasDisadvantage || bardRallyFaces)
|
||||
this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: (this.hasDisadvantage ? '-' : '+') }));
|
||||
this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }));
|
||||
if (bardRallyFaces) {
|
||||
const rallyDie = new foundry.dice.terms.Die({ faces: bardRallyFaces });
|
||||
if (this.hasAdvantage) {
|
||||
|
|
@ -367,10 +430,12 @@ export class DualityRoll extends D20Roll {
|
|||
}
|
||||
|
||||
applyBaseBonus() {
|
||||
this.options.roll.modifiers = [{
|
||||
label : `DAGGERHEART.Abilities.${this.options.roll.trait}.name`,
|
||||
this.options.roll.modifiers = [];
|
||||
if (!this.options.roll.trait) return;
|
||||
this.options.roll.modifiers.push({
|
||||
label: `DAGGERHEART.Abilities.${this.options.roll.trait}.name`,
|
||||
value: Roll.replaceFormulaData(`@traits.${this.options.roll.trait}.total`, this.data)
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
static postEvaluate(roll, config = {}) {
|
||||
|
|
@ -388,7 +453,6 @@ export class DualityRoll extends D20Roll {
|
|||
total: roll.dHope.total + roll.dFear.total,
|
||||
label: roll.totalLabel
|
||||
};
|
||||
console.log(roll, config)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -404,5 +468,9 @@ export class DamageRoll extends DHRoll {
|
|||
static async postEvaluate(roll, config = {}) {
|
||||
super.postEvaluate(roll, config);
|
||||
config.roll.type = config.type;
|
||||
if (config.source?.message) {
|
||||
const chatMessage = ui.chat.collection.get(config.source.message);
|
||||
chatMessage.update({ 'system.damage': config });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
/** NOT USED ANYMORE - TO BE DELETED **/
|
||||
|
||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class RollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ export default class DhSettingsActionView extends HandlebarsApplicationMixin(App
|
|||
}
|
||||
|
||||
static onEditImage() {
|
||||
const fp = new FilePicker({
|
||||
const fp = new foundry.applications.apps.FilePicker.implementation({
|
||||
current: this.img,
|
||||
type: 'image',
|
||||
callback: async path => {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
import DHActionConfig from '../config/Action.mjs';
|
||||
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
||||
import DHActionConfig from '../../config/Action.mjs';
|
||||
import DaggerheartSheet from '../daggerheart-sheet.mjs';
|
||||
import DHAdversarySettings from '../applications/adversary-settings.mjs';
|
||||
|
||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||
export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'adversary'],
|
||||
position: { width: 450, height: 1000 },
|
||||
position: { width: 660, height: 766 },
|
||||
actions: {
|
||||
reactionRoll: this.reactionRoll,
|
||||
attackRoll: this.attackRoll,
|
||||
useItem: this.useItem,
|
||||
toChat: this.toChat,
|
||||
attackConfigure: this.attackConfigure,
|
||||
addExperience: this.addExperience,
|
||||
removeExperience: this.removeExperience,
|
||||
toggleHP: this.toggleHP,
|
||||
toggleStress: this.toggleStress
|
||||
toggleStress: this.toggleStress,
|
||||
openSettings: this.openSettings
|
||||
},
|
||||
form: {
|
||||
handler: this.updateForm,
|
||||
|
|
@ -24,28 +27,37 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
};
|
||||
|
||||
static PARTS = {
|
||||
sidebar: { template: 'systems/daggerheart/templates/sheets/actors/adversary/sidebar.hbs' },
|
||||
header: { template: 'systems/daggerheart/templates/sheets/actors/adversary/header.hbs' },
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
main: { template: 'systems/daggerheart/templates/sheets/actors/adversary/main.hbs' },
|
||||
information: { template: 'systems/daggerheart/templates/sheets/actors/adversary/information.hbs' }
|
||||
actions: { template: 'systems/daggerheart/templates/sheets/actors/adversary/actions.hbs' },
|
||||
notes: { template: 'systems/daggerheart/templates/sheets/actors/adversary/notes.hbs' },
|
||||
effects: { template: 'systems/daggerheart/templates/sheets/actors/adversary/effects.hbs' }
|
||||
};
|
||||
|
||||
static TABS = {
|
||||
main: {
|
||||
actions: {
|
||||
active: true,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'main',
|
||||
id: 'actions',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.Sheets.Adversary.Tabs.Main'
|
||||
label: 'DAGGERHEART.General.tabs.actions'
|
||||
},
|
||||
information: {
|
||||
notes: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'information',
|
||||
id: 'notes',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.Sheets.Adversary.Tabs.Information'
|
||||
label: 'DAGGERHEART.Sheets.Adversary.Tabs.notes'
|
||||
},
|
||||
effects: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'effects',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.Sheets.Adversary.Tabs.effects'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -56,10 +68,15 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
context.systemFields.attack.fields = this.document.system.attack.schema.fields;
|
||||
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||
context.isNPC = true;
|
||||
console.log(context)
|
||||
return context;
|
||||
}
|
||||
|
||||
getAction(element) {
|
||||
const itemId = (element.target ?? element).closest('[data-item-id]').dataset.itemId,
|
||||
item = this.document.system.actions.find(x => x.id === itemId);
|
||||
return item;
|
||||
}
|
||||
|
||||
static async updateForm(event, _, formData) {
|
||||
await this.document.update(formData.object);
|
||||
this.render();
|
||||
|
|
@ -70,7 +87,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
event: event,
|
||||
title: `${this.actor.name} - Reaction Roll`,
|
||||
roll: {
|
||||
modifier: null,
|
||||
// modifier: null,
|
||||
type: 'reaction'
|
||||
},
|
||||
chatMessage: {
|
||||
|
|
@ -86,8 +103,40 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
return {};
|
||||
}
|
||||
|
||||
static async attackRoll(event) {
|
||||
this.actor.system.attack.use(event);
|
||||
static async openSettings() {
|
||||
await new DHAdversarySettings(this.document).render(true);
|
||||
}
|
||||
|
||||
static async useItem(event) {
|
||||
const action = this.getAction(event) ?? this.actor.system.attack;
|
||||
action.use(event);
|
||||
}
|
||||
|
||||
static async toChat(event, button) {
|
||||
if (button?.dataset?.type === 'experience') {
|
||||
const experience = this.document.system.experiences[button.dataset.uuid];
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = {
|
||||
name: game.i18n.localize('DAGGERHEART.General.Experience.Single'),
|
||||
description: `${experience.name} ${
|
||||
experience.modifier < 0 ? experience.modifier : `+${experience.modifier}`
|
||||
}`
|
||||
};
|
||||
const msg = new cls({
|
||||
type: 'abilityUse',
|
||||
user: game.user.id,
|
||||
system: systemData,
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||
systemData
|
||||
)
|
||||
});
|
||||
|
||||
cls.create(msg.toObject());
|
||||
} else {
|
||||
const item = this.getAction(event) ?? this.document.system.attack;
|
||||
item.toChat(this.document.id);
|
||||
}
|
||||
}
|
||||
|
||||
static async attackConfigure(event) {
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
import { capitalize } from '../../helpers/utils.mjs';
|
||||
import DhpDeathMove from '../deathMove.mjs';
|
||||
import DhpDowntime from '../downtime.mjs';
|
||||
import AncestrySelectionDialog from '../ancestrySelectionDialog.mjs';
|
||||
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
||||
import { abilities } from '../../config/actorConfig.mjs';
|
||||
import DhlevelUp from '../levelup.mjs';
|
||||
import DhCharacterCreation from '../characterCreation.mjs';
|
||||
import FilterMenu from '../ux/filter-menu.mjs';
|
||||
import { capitalize } from '../../../helpers/utils.mjs';
|
||||
import DhpDeathMove from '../../deathMove.mjs';
|
||||
import DhpDowntime from '../../downtime.mjs';
|
||||
import AncestrySelectionDialog from '../../ancestrySelectionDialog.mjs';
|
||||
import DaggerheartSheet from '.././daggerheart-sheet.mjs';
|
||||
import { abilities } from '../../../config/actorConfig.mjs';
|
||||
import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs';
|
||||
import DhCharacterCreation from '../../characterCreation.mjs';
|
||||
import FilterMenu from '../../ux/filter-menu.mjs';
|
||||
|
||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||
const { TextEditor } = foundry.applications.ux;
|
||||
|
|
@ -305,8 +305,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
}
|
||||
|
||||
getItem(element) {
|
||||
const itemId = (element.target ?? element).closest('[data-item-id]').dataset.itemId,
|
||||
item = this.document.items.get(itemId);
|
||||
const listElement = (element.target ?? element).closest('[data-item-id]');
|
||||
const document = listElement.dataset.companion ? this.document.system.companion : this.document;
|
||||
|
||||
const itemId = listElement.dataset.itemId,
|
||||
item = document.items.get(itemId);
|
||||
return item;
|
||||
}
|
||||
|
||||
|
|
@ -315,7 +318,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
}
|
||||
|
||||
static _onEditImage() {
|
||||
const fp = new FilePicker({
|
||||
const fp = new foundry.applications.apps.FilePicker.implementation({
|
||||
current: this.document.img,
|
||||
type: 'image',
|
||||
redirectToRoot: ['icons/svg/mystery-man.svg'],
|
||||
|
|
@ -330,25 +333,8 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
const context = await super._prepareContext(_options);
|
||||
context.document = this.document;
|
||||
context.tabs = super._getTabs(this.constructor.TABS);
|
||||
|
||||
context.config = SYSTEM;
|
||||
|
||||
const selectedAttributes = Object.values(this.document.system.traits).map(x => x.base);
|
||||
context.abilityScoreArray = await game.settings
|
||||
.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew)
|
||||
.traitArray.reduce((acc, x) => {
|
||||
const selectedIndex = selectedAttributes.indexOf(x);
|
||||
if (selectedIndex !== -1) {
|
||||
selectedAttributes.splice(selectedIndex, 1);
|
||||
} else {
|
||||
acc.push({ name: x, value: x });
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
if (!context.abilityScoreArray.includes(0)) context.abilityScoreArray.push({ name: 0, value: 0 });
|
||||
context.abilityScoresFinished = context.abilityScoreArray.every(x => x.value === 0);
|
||||
|
||||
context.attributes = Object.keys(this.document.system.traits).reduce((acc, key) => {
|
||||
acc[key] = {
|
||||
...this.document.system.traits[key],
|
||||
|
|
@ -359,67 +345,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
return acc;
|
||||
}, {});
|
||||
|
||||
const ancestry = await this.mapFeatureType(
|
||||
this.document.system.ancestry ? [this.document.system.ancestry] : [],
|
||||
SYSTEM.GENERAL.objectTypes
|
||||
);
|
||||
const community = await this.mapFeatureType(
|
||||
this.document.system.community ? [this.document.system.community] : [],
|
||||
SYSTEM.GENERAL.objectTypes
|
||||
);
|
||||
const foundation = {
|
||||
ancestry: ancestry[0],
|
||||
community: community[0],
|
||||
advancement: {}
|
||||
};
|
||||
|
||||
const nrLoadoutCards = this.document.system.domainCards.loadout.length;
|
||||
const loadout = await this.mapFeatureType(this.document.system.domainCards.loadout, SYSTEM.DOMAIN.cardTypes);
|
||||
const vault = await this.mapFeatureType(this.document.system.domainCards.vault, SYSTEM.DOMAIN.cardTypes);
|
||||
context.abilities = {
|
||||
foundation: foundation,
|
||||
loadout: {
|
||||
top: loadout.slice(0, Math.min(2, nrLoadoutCards)),
|
||||
bottom: nrLoadoutCards > 2 ? loadout.slice(2, Math.min(5, nrLoadoutCards)) : [],
|
||||
nrTotal: nrLoadoutCards,
|
||||
listView: game.user.getFlag(SYSTEM.id, SYSTEM.FLAGS.displayDomainCardsAsList)
|
||||
},
|
||||
vault: vault.map(x => ({
|
||||
...x,
|
||||
uuid: x.uuid,
|
||||
sendToLoadoutDisabled: this.document.system.domainCards.loadout.length >= 5
|
||||
}))
|
||||
};
|
||||
|
||||
context.inventory = {
|
||||
consumable: {
|
||||
titles: {
|
||||
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.ConsumableTitle'),
|
||||
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
||||
},
|
||||
items: this.document.items.filter(x => x.type === 'consumable')
|
||||
},
|
||||
miscellaneous: {
|
||||
titles: {
|
||||
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.MiscellaneousTitle'),
|
||||
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
||||
},
|
||||
items: this.document.items.filter(x => x.type === 'miscellaneous')
|
||||
},
|
||||
weapons: {
|
||||
titles: {
|
||||
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.WeaponsTitle'),
|
||||
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
||||
},
|
||||
items: this.document.items.filter(x => x.type === 'weapon')
|
||||
},
|
||||
armor: {
|
||||
titles: {
|
||||
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.ArmorsTitle'),
|
||||
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
||||
},
|
||||
items: this.document.items.filter(x => x.type === 'armor')
|
||||
},
|
||||
currency: {
|
||||
title: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Title'),
|
||||
coins: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Coins'),
|
||||
|
|
@ -557,102 +483,6 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Filter Menus */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
_createFilterMenus() {
|
||||
//Menus could be a application option if needed
|
||||
const menus = [
|
||||
{
|
||||
key: 'inventory',
|
||||
container: '[data-application-part="inventory"]',
|
||||
content: '.items-section',
|
||||
callback: this._onMenuFilterInventory.bind(this),
|
||||
target: '.filter-button',
|
||||
filters: FilterMenu.invetoryFilters
|
||||
},
|
||||
{
|
||||
key: 'loadout',
|
||||
container: '[data-application-part="loadout"]',
|
||||
content: '.items-section',
|
||||
callback: this._onMenuFilterLoadout.bind(this),
|
||||
target: '.filter-button',
|
||||
filters: FilterMenu.cardsFilters
|
||||
}
|
||||
];
|
||||
|
||||
menus.forEach(m => {
|
||||
const container = this.element.querySelector(m.container);
|
||||
this.#menu[m.key] = new FilterMenu(container, m.target, m.filters, m.callback, {
|
||||
contentSelector: m.content
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when filters change
|
||||
* @param {PointerEvent} event
|
||||
* @param {HTMLElement} html
|
||||
* @param {import('../ux/filter-menu.mjs').FilterItem[]} filters
|
||||
*/
|
||||
_onMenuFilterInventory(event, html, filters) {
|
||||
this.#filteredItems.inventory.menu.clear();
|
||||
|
||||
for (const li of html.querySelectorAll('.inventory-item')) {
|
||||
const item = this.document.items.get(li.dataset.itemId);
|
||||
|
||||
const matchesMenu =
|
||||
filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f));
|
||||
if (matchesMenu) this.#filteredItems.inventory.menu.add(item.id);
|
||||
|
||||
const { search } = this.#filteredItems.inventory;
|
||||
li.hidden = !(search.has(item.id) && matchesMenu);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when filters change
|
||||
* @param {PointerEvent} event
|
||||
* @param {HTMLElement} html
|
||||
* @param {import('../ux/filter-menu.mjs').FilterItem[]} filters
|
||||
*/
|
||||
_onMenuFilterLoadout(event, html, filters) {
|
||||
this.#filteredItems.loadout.menu.clear();
|
||||
|
||||
for (const li of html.querySelectorAll('.items-list .inventory-item, .card-list .card-item')) {
|
||||
const item = this.document.items.get(li.dataset.itemId);
|
||||
|
||||
const matchesMenu =
|
||||
filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f));
|
||||
if (matchesMenu) this.#filteredItems.loadout.menu.add(item.id);
|
||||
|
||||
const { search } = this.#filteredItems.loadout;
|
||||
li.hidden = !(search.has(item.id) && matchesMenu);
|
||||
}
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
|
||||
async mapFeatureType(data, configType) {
|
||||
return await Promise.all(
|
||||
data.map(async x => {
|
||||
const abilities = x.system.abilities
|
||||
? await Promise.all(x.system.abilities.map(async x => await fromUuid(x.uuid)))
|
||||
: [];
|
||||
|
||||
return {
|
||||
...x,
|
||||
uuid: x.uuid,
|
||||
system: {
|
||||
...x.system,
|
||||
abilities: abilities,
|
||||
type: game.i18n.localize(configType[x.system.type ?? x.type].label)
|
||||
}
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
static async rollAttribute(event, button) {
|
||||
const abilityLabel = game.i18n.localize(abilities[button.dataset.attribute].label);
|
||||
const config = {
|
||||
|
|
@ -738,7 +568,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
return;
|
||||
}
|
||||
|
||||
new DhlevelUp(this.document).render(true);
|
||||
new DhCharacterlevelUp(this.document).render(true);
|
||||
}
|
||||
|
||||
static async useDomainCard(event, button) {
|
||||
|
|
@ -804,15 +634,22 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
static async useItem(event, button) {
|
||||
const item = this.getItem(button);
|
||||
if (!item) return;
|
||||
const wasUsed = await item.use(event);
|
||||
if (wasUsed && item.type === 'weapon') {
|
||||
Hooks.callAll(SYSTEM.HOOKS.characterAttack, {});
|
||||
|
||||
// Should dandle its actions. Or maybe they'll be separate buttons as per an Issue on the board
|
||||
if (item.type === 'feature') {
|
||||
item.toChat();
|
||||
} else {
|
||||
const wasUsed = await item.use(event);
|
||||
if (wasUsed && item.type === 'weapon') {
|
||||
Hooks.callAll(SYSTEM.HOOKS.characterAttack, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async viewObject(event, button) {
|
||||
static async viewObject(event) {
|
||||
const item = this.getItem(event);
|
||||
if (!item) return;
|
||||
|
||||
item.sheet.render(true);
|
||||
}
|
||||
|
||||
|
|
@ -866,9 +703,10 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
this.render();
|
||||
}
|
||||
|
||||
static async deleteItem(event, button) {
|
||||
static async deleteItem(event) {
|
||||
const item = this.getItem(event);
|
||||
if (!item) return;
|
||||
|
||||
await item.delete();
|
||||
}
|
||||
|
||||
|
|
@ -910,9 +748,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = {
|
||||
name: game.i18n.localize('DAGGERHEART.General.Experience.Single'),
|
||||
description: `${experience.description} ${
|
||||
experience.total < 0 ? experience.total : `+${experience.total}`
|
||||
}`
|
||||
description: `${experience.name} ${experience.total < 0 ? experience.total : `+${experience.total}`}`
|
||||
};
|
||||
const msg = new cls({
|
||||
type: 'abilityUse',
|
||||
140
module/applications/sheets/actors/environment.mjs
Normal file
140
module/applications/sheets/actors/environment.mjs
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
import DaggerheartSheet from '../daggerheart-sheet.mjs';
|
||||
import DHEnvironmentSettings from '../applications/environment-settings.mjs';
|
||||
|
||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||
export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) {
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'environment'],
|
||||
position: {
|
||||
width: 500
|
||||
},
|
||||
actions: {
|
||||
addAdversary: this.addAdversary,
|
||||
deleteProperty: this.deleteProperty,
|
||||
openSettings: this.openSettings,
|
||||
useItem: this.useItem,
|
||||
toChat: this.toChat
|
||||
},
|
||||
form: {
|
||||
handler: this._updateForm,
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false
|
||||
},
|
||||
dragDrop: [{ dragSelector: '.action-section .inventory-item', dropSelector: null }]
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' },
|
||||
actions: { template: 'systems/daggerheart/templates/sheets/actors/environment/actions.hbs' },
|
||||
potentialAdversaries: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/environment/potentialAdversaries.hbs'
|
||||
},
|
||||
notes: { template: 'systems/daggerheart/templates/sheets/actors/environment/notes.hbs' }
|
||||
};
|
||||
|
||||
static TABS = {
|
||||
actions: {
|
||||
active: true,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'actions',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.General.tabs.actions'
|
||||
},
|
||||
potentialAdversaries: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'potentialAdversaries',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.General.tabs.potentialAdversaries'
|
||||
},
|
||||
notes: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'notes',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.Sheets.Adversary.Tabs.notes'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.document = this.document;
|
||||
context.tabs = super._getTabs(this.constructor.TABS);
|
||||
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
getAction(element) {
|
||||
const itemId = (element.target ?? element).closest('[data-item-id]').dataset.itemId,
|
||||
item = this.document.system.actions.find(x => x.id === itemId);
|
||||
return item;
|
||||
}
|
||||
|
||||
static async openSettings() {
|
||||
await new DHEnvironmentSettings(this.document).render(true);
|
||||
}
|
||||
|
||||
static async _updateForm(event, _, formData) {
|
||||
await this.document.update(formData.object);
|
||||
this.render();
|
||||
}
|
||||
|
||||
getEffectDetails(id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
static async addAdversary() {
|
||||
await this.document.update({
|
||||
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
|
||||
'DAGGERHEART.Sheets.Environment.newAdversary'
|
||||
)
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async deleteProperty(_, target) {
|
||||
await this.document.update({ [`${target.dataset.path}.-=${target.id}`]: null });
|
||||
this.render();
|
||||
}
|
||||
|
||||
async viewAdversary(_, button) {
|
||||
const target = button.closest('[data-item-uuid]');
|
||||
const adversary = await foundry.utils.fromUuid(target.dataset.itemUuid);
|
||||
if (!adversary) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.adversaryMissing'));
|
||||
return;
|
||||
}
|
||||
|
||||
adversary.sheet.render(true);
|
||||
}
|
||||
|
||||
static async useItem(event, button) {
|
||||
const action = this.getAction(event);
|
||||
if (!action) {
|
||||
await this.viewAdversary(event, button);
|
||||
} else {
|
||||
action.use(event);
|
||||
}
|
||||
}
|
||||
|
||||
static async toChat(event) {
|
||||
const item = this.getAction(event);
|
||||
item.toChat(this.document.id);
|
||||
}
|
||||
|
||||
async _onDragStart(event) {
|
||||
const item = event.currentTarget.closest('.inventory-item');
|
||||
|
||||
if (item) {
|
||||
const adversary = game.actors.find(x => x.type === 'adversary' && x.id === item.dataset.itemId);
|
||||
const adversaryData = { type: 'Actor', uuid: adversary.uuid };
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(adversaryData));
|
||||
event.dataTransfer.setDragImage(item, 60, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,19 +46,18 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
|
||||
switch (partId) {
|
||||
case 'description':
|
||||
const value = foundry.utils.getProperty(this.document, "system.description") ?? "";
|
||||
const value = foundry.utils.getProperty(this.document, 'system.description') ?? '';
|
||||
context.enrichedDescription = await TextEditor.enrichHTML(value, {
|
||||
relativeTo: this.item,
|
||||
rollData: this.item.getRollData(),
|
||||
secrets: this.item.isOwner
|
||||
})
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Application Clicks Actions */
|
||||
/* -------------------------------------------- */
|
||||
|
|
@ -70,11 +69,11 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
*/
|
||||
static async selectActionType() {
|
||||
const content = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/views/actionType.hbs',
|
||||
{ types: SYSTEM.ACTIONS.actionTypes }
|
||||
),
|
||||
title = 'Select Action Type'
|
||||
|
||||
'systems/daggerheart/templates/views/actionType.hbs',
|
||||
{ types: SYSTEM.ACTIONS.actionTypes }
|
||||
),
|
||||
title = 'Select Action Type';
|
||||
|
||||
return foundry.applications.api.DialogV2.prompt({
|
||||
window: { title },
|
||||
content,
|
||||
|
|
@ -92,7 +91,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
*/
|
||||
static async #addAction(_event, _button) {
|
||||
const actionType = await DHBaseItemSheet.selectActionType();
|
||||
if(!actionType) return;
|
||||
if (!actionType) return;
|
||||
try {
|
||||
const cls = actionsTypes[actionType] ?? actionsTypes.attack,
|
||||
action = new cls(
|
||||
|
|
@ -134,9 +133,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
event.stopPropagation();
|
||||
const actionIndex = button.closest('[data-index]').dataset.index;
|
||||
await this.document.update({
|
||||
'system.actions': this.document.system.actions.filter(
|
||||
(_, index) => index !== Number.parseInt(actionIndex)
|
||||
)
|
||||
'system.actions': this.document.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
181
module/applications/sheets/applications/adversary-settings.mjs
Normal file
181
module/applications/sheets/applications/adversary-settings.mjs
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
import DHActionConfig from '../../config/Action.mjs';
|
||||
import DHBaseItemSheet from '../api/base-item.mjs';
|
||||
import { actionsTypes } from '../../../data/_module.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DHAdversarySettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(actor) {
|
||||
super({});
|
||||
|
||||
this.actor = actor;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return `${game.i18n.localize('DAGGERHEART.Sheets.TABS.settings')}`;
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'dh-style', 'dialog', 'adversary-settings'],
|
||||
window: {
|
||||
icon: 'fa-solid fa-wrench',
|
||||
resizable: false
|
||||
},
|
||||
position: { width: 455, height: 'auto' },
|
||||
actions: {
|
||||
addExperience: this.#addExperience,
|
||||
removeExperience: this.#removeExperience,
|
||||
addAction: this.#addAction,
|
||||
editAction: this.#editAction,
|
||||
removeAction: this.#removeAction
|
||||
},
|
||||
form: {
|
||||
handler: this.updateForm,
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false
|
||||
}
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
header: {
|
||||
id: 'header',
|
||||
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/header.hbs'
|
||||
},
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
details: {
|
||||
id: 'details',
|
||||
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/details.hbs'
|
||||
},
|
||||
attack: {
|
||||
id: 'attack',
|
||||
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/attack.hbs'
|
||||
},
|
||||
experiences: {
|
||||
id: 'experiences',
|
||||
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/experiences.hbs'
|
||||
},
|
||||
actions: {
|
||||
id: 'actions',
|
||||
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/actions.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
static TABS = {
|
||||
details: {
|
||||
active: true,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'details',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.General.tabs.details'
|
||||
},
|
||||
attack: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'attack',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.General.tabs.attack'
|
||||
},
|
||||
experiences: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'experiences',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.General.tabs.experiences'
|
||||
},
|
||||
actions: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'actions',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.General.tabs.actions'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.document = this.actor;
|
||||
context.tabs = this._getTabs(this.constructor.TABS);
|
||||
context.systemFields = this.actor.system.schema.fields;
|
||||
context.systemFields.attack.fields = this.actor.system.attack.schema.fields;
|
||||
context.isNPC = true;
|
||||
console.log(context)
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
_getTabs(tabs) {
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active;
|
||||
v.cssClass = v.active ? 'active' : '';
|
||||
}
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
static async #addExperience() {
|
||||
const newExperience = {
|
||||
name: 'Experience',
|
||||
modifier: 0
|
||||
};
|
||||
await this.actor.update({ [`system.experiences.${foundry.utils.randomID()}`]: newExperience });
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #removeExperience(_, target) {
|
||||
await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null });
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #addAction(_event, _button) {
|
||||
const actionType = await DHBaseItemSheet.selectActionType();
|
||||
if (!actionType) return;
|
||||
try {
|
||||
const cls = actionsTypes[actionType] ?? actionsTypes.attack,
|
||||
action = new cls(
|
||||
{
|
||||
_id: foundry.utils.randomID(),
|
||||
type: actionType,
|
||||
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType].name),
|
||||
...cls.getSourceConfig(this.actor)
|
||||
},
|
||||
{
|
||||
parent: this.actor
|
||||
}
|
||||
);
|
||||
await this.actor.update({ 'system.actions': [...this.actor.system.actions, action] });
|
||||
await new DHActionConfig(this.actor.system.actions[this.actor.system.actions.length - 1]).render({
|
||||
force: true
|
||||
});
|
||||
this.render();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
static async #editAction(event, target) {
|
||||
event.stopPropagation();
|
||||
const actionIndex = target.dataset.index;
|
||||
await new DHActionConfig(this.actor.system.actions[actionIndex]).render({
|
||||
force: true
|
||||
});
|
||||
}
|
||||
|
||||
static async #removeAction(event, target) {
|
||||
event.stopPropagation();
|
||||
const actionIndex = target.dataset.index;
|
||||
await this.actor.update({
|
||||
'system.actions': this.actor.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex))
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async updateForm(event, _, formData) {
|
||||
await this.actor.update(formData.object);
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
220
module/applications/sheets/applications/environment-settings.mjs
Normal file
220
module/applications/sheets/applications/environment-settings.mjs
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
import DHActionConfig from '../../config/Action.mjs';
|
||||
import DHBaseItemSheet from '../api/base-item.mjs';
|
||||
import { actionsTypes } from '../../../data/_module.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DHEnvironmentSettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(actor) {
|
||||
super({});
|
||||
|
||||
this.actor = actor;
|
||||
this._dragDrop = this._createDragDropHandlers();
|
||||
}
|
||||
|
||||
get title() {
|
||||
return `${game.i18n.localize('DAGGERHEART.Sheets.TABS.settings')}`;
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'dh-style', 'dialog', 'environment-settings'],
|
||||
window: {
|
||||
icon: 'fa-solid fa-wrench',
|
||||
resizable: false
|
||||
},
|
||||
position: { width: 455, height: 'auto' },
|
||||
actions: {
|
||||
addAction: this.#addAction,
|
||||
editAction: this.#editAction,
|
||||
removeAction: this.#removeAction,
|
||||
addCategory: this.#addCategory,
|
||||
deleteProperty: this.#deleteProperty,
|
||||
viewAdversary: this.#viewAdversary,
|
||||
deleteAdversary: this.#deleteAdversary
|
||||
},
|
||||
form: {
|
||||
handler: this.updateForm,
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false
|
||||
},
|
||||
dragDrop: [{ dragSelector: null, dropSelector: '.category-container' }]
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
header: {
|
||||
id: 'header',
|
||||
template: 'systems/daggerheart/templates/sheets/applications/environment-settings/header.hbs'
|
||||
},
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
details: {
|
||||
id: 'details',
|
||||
template: 'systems/daggerheart/templates/sheets/applications/environment-settings/details.hbs'
|
||||
},
|
||||
actions: {
|
||||
id: 'actions',
|
||||
template: 'systems/daggerheart/templates/sheets/applications/environment-settings/actions.hbs'
|
||||
},
|
||||
adversaries: {
|
||||
id: 'adversaries',
|
||||
template: 'systems/daggerheart/templates/sheets/applications/environment-settings/adversaries.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
static TABS = {
|
||||
details: {
|
||||
active: true,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'details',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.General.tabs.details'
|
||||
},
|
||||
actions: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'actions',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.General.tabs.actions'
|
||||
},
|
||||
adversaries: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'adversaries',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.General.tabs.adversaries'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.document = this.actor;
|
||||
context.tabs = this._getTabs(this.constructor.TABS);
|
||||
context.systemFields = this.actor.system.schema.fields;
|
||||
context.isNPC = true;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
this._dragDrop.forEach(d => d.bind(htmlElement));
|
||||
}
|
||||
|
||||
_createDragDropHandlers() {
|
||||
return this.options.dragDrop.map(d => {
|
||||
d.callbacks = {
|
||||
drop: this._onDrop.bind(this)
|
||||
};
|
||||
return new foundry.applications.ux.DragDrop.implementation(d);
|
||||
});
|
||||
}
|
||||
|
||||
_getTabs(tabs) {
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active;
|
||||
v.cssClass = v.active ? 'active' : '';
|
||||
}
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
static async #addAction(_event, _button) {
|
||||
const actionType = await DHBaseItemSheet.selectActionType();
|
||||
if (!actionType) return;
|
||||
try {
|
||||
const cls = actionsTypes[actionType] ?? actionsTypes.attack,
|
||||
action = new cls(
|
||||
{
|
||||
_id: foundry.utils.randomID(),
|
||||
type: actionType,
|
||||
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType].name),
|
||||
...cls.getSourceConfig(this.actor)
|
||||
},
|
||||
{
|
||||
parent: this.actor
|
||||
}
|
||||
);
|
||||
await this.actor.update({ 'system.actions': [...this.actor.system.actions, action] });
|
||||
await new DHActionConfig(this.actor.system.actions[this.actor.system.actions.length - 1]).render({
|
||||
force: true
|
||||
});
|
||||
this.render();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
static async #editAction(event, target) {
|
||||
event.stopPropagation();
|
||||
const actionIndex = target.dataset.index;
|
||||
await new DHActionConfig(this.actor.system.actions[actionIndex]).render({
|
||||
force: true
|
||||
});
|
||||
}
|
||||
|
||||
static async #removeAction(event, target) {
|
||||
event.stopPropagation();
|
||||
const actionIndex = target.dataset.index;
|
||||
await this.actor.update({
|
||||
'system.actions': this.actor.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex))
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #addCategory() {
|
||||
await this.actor.update({
|
||||
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
|
||||
'DAGGERHEART.Sheets.Environment.newAdversary'
|
||||
)
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #deleteProperty(_, target) {
|
||||
await this.actor.update({ [`${target.dataset.path}.-=${target.id}`]: null });
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #viewAdversary(_, button) {
|
||||
const adversary = await foundry.utils.fromUuid(button.dataset.adversary);
|
||||
if (!adversary) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.adversaryMissing'));
|
||||
return;
|
||||
}
|
||||
|
||||
adversary.sheet.render(true);
|
||||
}
|
||||
|
||||
static async #deleteAdversary(event, target) {
|
||||
const adversaryKey = target.dataset.adversary;
|
||||
const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`;
|
||||
const newAdversaries = foundry.utils
|
||||
.getProperty(this.actor, path)
|
||||
.filter(x => x && (x?.uuid ?? x) !== adversaryKey);
|
||||
await this.actor.update({ [path]: newAdversaries });
|
||||
this.render();
|
||||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
const data = TextEditor.getDragEventData(event);
|
||||
const item = await fromUuid(data.uuid);
|
||||
if (item.type === 'adversary') {
|
||||
const target = event.target.closest('.category-container');
|
||||
const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`;
|
||||
const current = foundry.utils.getProperty(this.actor, path).map(x => x.uuid);
|
||||
await this.actor.update({
|
||||
[path]: [...current, item.uuid]
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
static async updateForm(event, _, formData) {
|
||||
await this.actor.update(formData.object);
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
86
module/applications/sheets/companion.mjs
Normal file
86
module/applications/sheets/companion.mjs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import { GMUpdateEvent, socketEvent } from '../../helpers/socket.mjs';
|
||||
import DhCompanionlevelUp from '../levelup/companionLevelup.mjs';
|
||||
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
||||
|
||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||
export default class DhCompanionSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'companion'],
|
||||
position: { width: 700, height: 1000 },
|
||||
actions: {
|
||||
attackRoll: this.attackRoll,
|
||||
levelUp: this.levelUp
|
||||
},
|
||||
form: {
|
||||
handler: this.updateForm,
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false
|
||||
}
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
sidebar: { template: 'systems/daggerheart/templates/sheets/actors/companion/tempMain.hbs' }
|
||||
};
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelector('.partner-value')?.addEventListener('change', this.onPartnerChange.bind(this));
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.document = this.document;
|
||||
context.playerCharacters = game.actors
|
||||
.filter(
|
||||
x =>
|
||||
x.type === 'character' &&
|
||||
(x.ownership.default === 3 ||
|
||||
x.ownership[game.user.id] === 3 ||
|
||||
this.document.system.partner?.uuid === x.uuid)
|
||||
)
|
||||
.map(x => ({ key: x.uuid, name: x.name }));
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateForm(event, _, formData) {
|
||||
await this.document.update(formData.object);
|
||||
this.render();
|
||||
}
|
||||
|
||||
async onPartnerChange(event) {
|
||||
const partnerDocument = event.target.value
|
||||
? await foundry.utils.fromUuid(event.target.value)
|
||||
: this.document.system.partner;
|
||||
const partnerUpdate = { 'system.companion': event.target.value ? this.document.uuid : null };
|
||||
|
||||
if (!partnerDocument.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)) {
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: {
|
||||
action: GMUpdateEvent.UpdateDocument,
|
||||
uuid: partnerDocument.uuid,
|
||||
update: update
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await partnerDocument.update(partnerUpdate);
|
||||
}
|
||||
|
||||
await this.document.update({ 'system.partner': event.target.value });
|
||||
|
||||
if (!event.target.value) {
|
||||
await this.document.updateLevel(1);
|
||||
}
|
||||
}
|
||||
|
||||
static async attackRoll(event) {
|
||||
this.actor.system.attack.use(event);
|
||||
}
|
||||
|
||||
static async levelUp() {
|
||||
new DhCompanionlevelUp(this.document).render(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ export default function DhpApplicationMixin(Base) {
|
|||
const attr = target.dataset.edit;
|
||||
const current = foundry.utils.getProperty(this.document, attr);
|
||||
const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {};
|
||||
const fp = new FilePicker({
|
||||
const fp = new foundry.applications.apps.FilePicker.implementation({
|
||||
current,
|
||||
type: 'image',
|
||||
redirectToRoot: img ? [img] : [],
|
||||
|
|
@ -60,7 +60,7 @@ export default function DhpApplicationMixin(Base) {
|
|||
// drop: this._canDragDrop.bind(this)
|
||||
// };
|
||||
d.callbacks = {
|
||||
// dragstart: this._onDragStart.bind(this),
|
||||
dragstart: this._onDragStart.bind(this),
|
||||
// dragover: this._onDragOver.bind(this),
|
||||
drop: this._onDrop.bind(this)
|
||||
};
|
||||
|
|
@ -68,6 +68,7 @@ export default function DhpApplicationMixin(Base) {
|
|||
});
|
||||
}
|
||||
|
||||
async _onDragStart(event) {}
|
||||
_onDrop(event) {}
|
||||
|
||||
_getTabs(tabs) {
|
||||
|
|
|
|||
|
|
@ -1,107 +0,0 @@
|
|||
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
||||
|
||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||
export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) {
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'environment'],
|
||||
position: {
|
||||
width: 450,
|
||||
height: 1000
|
||||
},
|
||||
actions: {
|
||||
addAdversary: this.addAdversary,
|
||||
addFeature: this.addFeature,
|
||||
deleteProperty: this.deleteProperty,
|
||||
viewAdversary: this.viewAdversary
|
||||
},
|
||||
form: {
|
||||
handler: this._updateForm,
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false
|
||||
},
|
||||
dragDrop: [{ dragSelector: null, dropSelector: '.adversary-container' }]
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' },
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
main: { template: 'systems/daggerheart/templates/sheets/actors/environment/main.hbs' },
|
||||
information: { template: 'systems/daggerheart/templates/sheets/actors/environment/information.hbs' }
|
||||
};
|
||||
|
||||
static TABS = {
|
||||
main: {
|
||||
active: true,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'main',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.Sheets.Environment.Tabs.Main'
|
||||
},
|
||||
information: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'information',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.Sheets.Environment.Tabs.Information'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.document = this.document;
|
||||
context.tabs = super._getTabs(this.constructor.TABS);
|
||||
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async _updateForm(event, _, formData) {
|
||||
await this.document.update(formData.object);
|
||||
this.render();
|
||||
}
|
||||
|
||||
getEffectDetails(id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
static async addAdversary() {
|
||||
await this.document.update({
|
||||
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
|
||||
'DAGGERHEART.Sheets.Environment.newAdversary'
|
||||
)
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async addFeature() {
|
||||
ui.notifications.error('Not Implemented yet. Awaiting datamodel rework');
|
||||
}
|
||||
|
||||
static async deleteProperty(_, target) {
|
||||
await this.document.update({ [`${target.dataset.path}.-=${target.id}`]: null });
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async viewAdversary(_, button) {
|
||||
const adversary = foundry.utils.getProperty(
|
||||
this.document.system.potentialAdversaries,
|
||||
`${button.dataset.potentialAdversary}.adversaries.${button.dataset.adversary}`
|
||||
);
|
||||
adversary.sheet.render(true);
|
||||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
const data = TextEditor.getDragEventData(event);
|
||||
const item = await fromUuid(data.uuid);
|
||||
if (item.type === 'adversary') {
|
||||
const target = event.target.closest('.adversary-container');
|
||||
const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries.${item.id}`;
|
||||
await this.document.update({
|
||||
[path]: item.uuid
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,11 +6,7 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
|||
id: 'daggerheart-feature',
|
||||
classes: ['feature'],
|
||||
position: { height: 600 },
|
||||
window: { resizable: true },
|
||||
actions: {
|
||||
addEffect: this.addEffect,
|
||||
removeEffect: this.removeEffect
|
||||
}
|
||||
window: { resizable: true }
|
||||
};
|
||||
|
||||
/**@override */
|
||||
|
|
@ -22,27 +18,16 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
|||
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs',
|
||||
scrollable: ['.actions']
|
||||
},
|
||||
settings: {
|
||||
template: 'systems/daggerheart/templates/sheets/items/feature/settings.hbs',
|
||||
scrollable: ['.settings']
|
||||
},
|
||||
effects: {
|
||||
template: 'systems/daggerheart/templates/sheets/items/feature/effects.hbs',
|
||||
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs',
|
||||
scrollable: ['.effects']
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internally tracks the selected effect type from the select.
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
_selectedEffectType;
|
||||
|
||||
/**@override */
|
||||
static TABS = {
|
||||
primary: {
|
||||
tabs: [{ id: 'description' }, { id: 'actions' }, { id: 'settings' }, { id: 'effects' }],
|
||||
tabs: [{ id: 'description' }, { id: 'actions' }, { id: 'effects' }],
|
||||
initial: 'description',
|
||||
labelPrefix: 'DAGGERHEART.Sheets.TABS'
|
||||
}
|
||||
|
|
@ -50,68 +35,10 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@inheritdoc*/
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
if (partId === 'effects')
|
||||
htmlElement.querySelector('.effect-select')?.addEventListener('change', this._effectSelect.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles selection of a new effect type.
|
||||
* @param {Event} event - Change Event
|
||||
*/
|
||||
_effectSelect(event) {
|
||||
const value = event.currentTarget.value;
|
||||
this._selectedEffectType = value;
|
||||
this.render({ parts: ['effects'] });
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@inheritdoc */
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.properties = CONFIG.daggerheart.ACTOR.featureProperties;
|
||||
context.dice = CONFIG.daggerheart.GENERAL.diceTypes;
|
||||
context.effectConfig = CONFIG.daggerheart.EFFECTS;
|
||||
|
||||
context.selectedEffectType = this._selectedEffectType;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Application Clicks Actions */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Adds a new effect to the item, based on the selected effect type.
|
||||
* @param {PointerEvent} _event - The originating click event
|
||||
* @param {HTMLElement} _target - The capturing HTML element which defines the [data-action]
|
||||
* @returns
|
||||
*/
|
||||
static async addEffect(_event, _target) {
|
||||
const type = this._selectedEffectType;
|
||||
if (!type) return;
|
||||
const { id, name, ...rest } = CONFIG.daggerheart.EFFECTS.effectTypes[type];
|
||||
await this.item.update({
|
||||
[`system.effects.${foundry.utils.randomID()}`]: {
|
||||
type,
|
||||
value: '',
|
||||
...rest
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an effect from the item.
|
||||
* @param {PointerEvent} _event - The originating click event
|
||||
* @param {HTMLElement} target - The capturing HTML element which defines the [data-action]
|
||||
* @returns
|
||||
*/
|
||||
static async removeEffect(_event, target) {
|
||||
const path = `system.effects.-=${target.dataset.effect}`;
|
||||
await this.item.update({ [path]: null });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
module/applications/tooltipManager.mjs
Normal file
16
module/applications/tooltipManager.mjs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
export default class DhTooltipManager extends TooltipManager {
|
||||
async activate(element, options = {}) {
|
||||
let html = options.html;
|
||||
if (element.dataset.tooltip.startsWith('#item#')) {
|
||||
const item = await foundry.utils.fromUuid(element.dataset.tooltip.slice(6));
|
||||
if (item) {
|
||||
html = await foundry.applications.handlebars.renderTemplate(
|
||||
`systems/daggerheart/templates/tooltip/${item.type}.hbs`,
|
||||
item
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
super.activate(element, { ...options, html: html });
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue