mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-18 07:59:03 +01:00
Merge branch 'main' into feature/336-damage-targeted-resources
This commit is contained in:
commit
31647d71ee
52 changed files with 701 additions and 290 deletions
|
|
@ -64,6 +64,13 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
context.rollConfig = this.config;
|
||||
context.hasRoll = !!this.config.roll;
|
||||
context.canRoll = true;
|
||||
context.selectedRollMode = this.config.selectedRollMode;
|
||||
context.rollModes = Object.entries(CONFIG.Dice.rollModes).map(([action, { label, icon }]) => ({
|
||||
action,
|
||||
label,
|
||||
icon
|
||||
}));
|
||||
|
||||
if (this.config.costs?.length) {
|
||||
const updatedCosts = this.action.calcCosts(this.config.costs);
|
||||
context.costs = updatedCosts.map(x => ({
|
||||
|
|
@ -99,6 +106,8 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
|
||||
static updateRollConfiguration(event, _, formData) {
|
||||
const { ...rest } = foundry.utils.expandObject(formData.object);
|
||||
this.config.selectedRollMode = rest.selectedRollMode;
|
||||
|
||||
if (this.config.costs) {
|
||||
this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs);
|
||||
}
|
||||
|
|
@ -122,11 +131,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
}
|
||||
|
||||
static selectExperience(_, button) {
|
||||
/* if (this.config.experiences.find(x => x === button.dataset.key)) {
|
||||
this.config.experiences = this.config.experiences.filter(x => x !== button.dataset.key);
|
||||
} else {
|
||||
this.config.experiences = [...this.config.experiences, button.dataset.key];
|
||||
} */
|
||||
this.config.experiences =
|
||||
this.config.experiences.indexOf(button.dataset.key) > -1
|
||||
? this.config.experiences.filter(x => x !== button.dataset.key)
|
||||
|
|
|
|||
|
|
@ -48,12 +48,22 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
: game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
|
||||
context.extraFormula = this.config.extraFormula;
|
||||
context.formula = this.roll.constructFormula(this.config);
|
||||
context.directDamage = this.config.directDamage;
|
||||
context.selectedRollMode = this.config.selectedRollMode;
|
||||
context.rollModes = Object.entries(CONFIG.Dice.rollModes).map(([action, { label, icon }]) => ({
|
||||
action,
|
||||
label,
|
||||
icon
|
||||
}));
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static updateRollConfiguration(event, _, formData) {
|
||||
static updateRollConfiguration(_event, _, formData) {
|
||||
const { ...rest } = foundry.utils.expandObject(formData.object);
|
||||
this.config.extraFormula = rest.extraFormula;
|
||||
this.config.selectedRollMode = rest.selectedRollMode;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
export { default as DHTokenHUD } from './tokenHud.mjs';
|
||||
export { default as DHTokenHUD } from './tokenHUD.mjs';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export default class DHTokenHUD extends TokenHUD {
|
||||
export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['daggerheart']
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,3 +3,5 @@ export { default as AdversarySettings } from './adversary-settings.mjs';
|
|||
export { default as CompanionSettings } from './companion-settings.mjs';
|
||||
export { default as EnvironmentSettings } from './environment-settings.mjs';
|
||||
export { default as ActiveEffectConfig } from './activeEffectConfig.mjs';
|
||||
export { default as DhTokenConfig } from './token-config.mjs';
|
||||
export { default as DhPrototypeTokenConfig } from './prototype-token-config.mjs';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,24 @@
|
|||
import autocomplete from 'autocompleter';
|
||||
|
||||
export default class DhActiveEffectConfig extends foundry.applications.sheets.ActiveEffectConfig {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
const ignoredActorKeys = ['config', 'DhEnvironment'];
|
||||
this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => {
|
||||
if (!ignoredActorKeys.includes(key)) {
|
||||
const model = game.system.api.models.actors[key];
|
||||
const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model);
|
||||
const group = game.i18n.localize(model.metadata.label);
|
||||
const choices = CONFIG.Token.documentClass
|
||||
.getTrackedAttributeChoices(attributes, model)
|
||||
.map(x => ({ ...x, group: group }));
|
||||
acc.push(...choices);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['daggerheart', 'sheet', 'dh-style']
|
||||
};
|
||||
|
|
@ -27,36 +47,59 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
}
|
||||
};
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
const changeChoices = this.changeChoices;
|
||||
|
||||
htmlElement.querySelectorAll('.effect-change-input').forEach(element => {
|
||||
autocomplete({
|
||||
input: element,
|
||||
fetch: function (text, update) {
|
||||
if (!text) {
|
||||
update(changeChoices);
|
||||
} else {
|
||||
text = text.toLowerCase();
|
||||
var suggestions = changeChoices.filter(n => n.label.toLowerCase().includes(text));
|
||||
update(suggestions);
|
||||
}
|
||||
},
|
||||
render: function (item, search) {
|
||||
const label = game.i18n.localize(item.label);
|
||||
const matchIndex = label.toLowerCase().indexOf(search);
|
||||
|
||||
const beforeText = label.slice(0, matchIndex);
|
||||
const matchText = label.slice(matchIndex, matchIndex + search.length);
|
||||
const after = label.slice(matchIndex + search.length, label.length);
|
||||
|
||||
const element = document.createElement('li');
|
||||
element.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
|
||||
if (item.hint) {
|
||||
element.dataset.tooltip = game.i18n.localize(item.hint);
|
||||
}
|
||||
|
||||
return element;
|
||||
},
|
||||
renderGroup: function (label) {
|
||||
const itemElement = document.createElement('div');
|
||||
itemElement.textContent = game.i18n.localize(label);
|
||||
return itemElement;
|
||||
},
|
||||
onSelect: function (item) {
|
||||
element.value = `system.${item.value}`;
|
||||
},
|
||||
click: e => e.fetch(),
|
||||
minLength: 0
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async _preparePartContext(partId, context) {
|
||||
const partContext = await super._preparePartContext(partId, context);
|
||||
switch (partId) {
|
||||
case 'changes':
|
||||
const fieldPaths = [];
|
||||
const validFieldPath = fieldPath => this.validFieldPath(fieldPath, this.#unapplicablePaths);
|
||||
context.document.parent.system.schema.apply(function () {
|
||||
if (!(this instanceof foundry.data.fields.SchemaField)) {
|
||||
if (validFieldPath(this.fieldPath)) {
|
||||
fieldPaths.push(this.fieldPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
context.fieldPaths = fieldPaths;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return partContext;
|
||||
}
|
||||
|
||||
#unapplicablePaths = ['story', 'pronouns', 'description'];
|
||||
validFieldPath(fieldPath, unapplicablePaths) {
|
||||
const splitPath = fieldPath.split('.');
|
||||
if (splitPath.length > 1 && unapplicablePaths.includes(splitPath[1])) return false;
|
||||
|
||||
/* The current value of a resource should not be modified */
|
||||
if (new RegExp(/resources.*\.value/).exec(fieldPath)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
export default class DhPrototypeTokenConfig extends foundry.applications.sheets.PrototypeTokenConfig {
|
||||
/** @inheritDoc */
|
||||
async _prepareResourcesTab() {
|
||||
const token = this.token;
|
||||
const usesTrackableAttributes = !foundry.utils.isEmpty(CONFIG.Actor.trackableAttributes);
|
||||
const attributeSource =
|
||||
this.actor?.system instanceof foundry.abstract.DataModel && usesTrackableAttributes
|
||||
? this.actor?.type
|
||||
: this.actor?.system;
|
||||
const TokenDocument = foundry.utils.getDocumentClass('Token');
|
||||
const attributes = TokenDocument.getTrackedAttributes(attributeSource);
|
||||
return {
|
||||
barAttributes: TokenDocument.getTrackedAttributeChoices(attributes, attributeSource),
|
||||
bar1: token.getBarAttribute?.('bar1'),
|
||||
bar2: token.getBarAttribute?.('bar2'),
|
||||
turnMarkerModes: DhPrototypeTokenConfig.TURN_MARKER_MODES,
|
||||
turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations
|
||||
};
|
||||
}
|
||||
}
|
||||
20
module/applications/sheets-configs/token-config.mjs
Normal file
20
module/applications/sheets-configs/token-config.mjs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
export default class DhTokenConfig extends foundry.applications.sheets.TokenConfig {
|
||||
/** @inheritDoc */
|
||||
async _prepareResourcesTab() {
|
||||
const token = this.token;
|
||||
const usesTrackableAttributes = !foundry.utils.isEmpty(CONFIG.Actor.trackableAttributes);
|
||||
const attributeSource =
|
||||
this.actor?.system instanceof foundry.abstract.DataModel && usesTrackableAttributes
|
||||
? this.actor?.type
|
||||
: this.actor?.system;
|
||||
const TokenDocument = foundry.utils.getDocumentClass('Token');
|
||||
const attributes = TokenDocument.getTrackedAttributes(attributeSource);
|
||||
return {
|
||||
barAttributes: TokenDocument.getTrackedAttributeChoices(attributes, attributeSource),
|
||||
bar1: token.getBarAttribute?.('bar1'),
|
||||
bar2: token.getBarAttribute?.('bar2'),
|
||||
turnMarkerModes: DhTokenConfig.TURN_MARKER_MODES,
|
||||
turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -103,7 +103,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
htmlElement.querySelectorAll('.inventory-item-quantity').forEach(element => {
|
||||
element.addEventListener('change', this.updateItemQuantity.bind(this));
|
||||
});
|
||||
|
||||
|
||||
// Add listener for armor marks input
|
||||
htmlElement.querySelectorAll('.armor-marks-input').forEach(element => {
|
||||
element.addEventListener('change', this.updateArmorMarks.bind(this));
|
||||
|
|
@ -669,10 +669,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
} else if (item instanceof ActiveEffect) {
|
||||
item.toChat(this);
|
||||
} else {
|
||||
const wasUsed = await item.use(event);
|
||||
if (wasUsed && item.type === 'weapon') {
|
||||
Hooks.callAll(CONFIG.DH.HOOKS.characterAttack, {});
|
||||
}
|
||||
item.use(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
onRollDamage = async (event, message) => {
|
||||
event.stopPropagation();
|
||||
const actor = await this.getActor(message.system.source.actor);
|
||||
if (!actor || !game.user.isGM) return true;
|
||||
if (game.user.character?.id !== actor.id && !game.user.isGM) return true;
|
||||
if (message.system.source.item && message.system.source.action) {
|
||||
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
|
||||
if (!action || !action?.rollDamage) return;
|
||||
|
|
|
|||
|
|
@ -66,6 +66,11 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
|||
}
|
||||
|
||||
async setCombatantSpotlight(combatantId) {
|
||||
const update = {
|
||||
system: {
|
||||
'spotlight.requesting': false
|
||||
}
|
||||
};
|
||||
const combatant = this.viewed.combatants.get(combatantId);
|
||||
|
||||
const toggleTurn = this.viewed.combatants.contents
|
||||
|
|
@ -73,10 +78,18 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
|||
.map(x => x.id)
|
||||
.indexOf(combatantId);
|
||||
|
||||
if (this.viewed.turn !== toggleTurn) Hooks.callAll(CONFIG.DH.HOOKS.spotlight, {});
|
||||
if (this.viewed.turn !== toggleTurn) {
|
||||
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
|
||||
await updateCountdowns(CONFIG.DH.GENERAL.countdownTypes.spotlight.id);
|
||||
|
||||
const autoPoints = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).actionPoints;
|
||||
if (autoPoints) {
|
||||
update.system.actionTokens = Math.max(combatant.system.actionTokens - 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
await this.viewed.update({ turn: this.viewed.turn === toggleTurn ? null : toggleTurn });
|
||||
await combatant.update({ 'system.spotlight.requesting': false });
|
||||
await combatant.update(update);
|
||||
}
|
||||
|
||||
static async requestSpotlight(_, target) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { countdownTypes } from '../../config/generalConfig.mjs';
|
||||
import { GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
import constructHTMLButton from '../../helpers/utils.mjs';
|
||||
import OwnershipSelection from '../dialogs/ownershipSelection.mjs';
|
||||
|
|
@ -328,43 +327,29 @@ export class EncounterCountdowns extends Countdowns {
|
|||
};
|
||||
}
|
||||
|
||||
export const registerCountdownApplicationHooks = () => {
|
||||
const updateCountdowns = async shouldProgress => {
|
||||
if (game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).countdowns) {
|
||||
const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
|
||||
for (let countdownCategoryKey in countdownSetting) {
|
||||
const countdownCategory = countdownSetting[countdownCategoryKey];
|
||||
for (let countdownKey in countdownCategory.countdowns) {
|
||||
const countdown = countdownCategory.countdowns[countdownKey];
|
||||
|
||||
if (shouldProgress(countdown)) {
|
||||
await countdownSetting.updateSource({
|
||||
[`${countdownCategoryKey}.countdowns.${countdownKey}.progress.current`]:
|
||||
countdown.progress.current - 1
|
||||
});
|
||||
await game.settings.set(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.Countdowns,
|
||||
countdownSetting
|
||||
);
|
||||
foundry.applications.instances.get(`${countdownCategoryKey}-countdowns`)?.render();
|
||||
}
|
||||
export async function updateCountdowns(progressType) {
|
||||
const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
|
||||
const update = Object.keys(countdownSetting).reduce((update, typeKey) => {
|
||||
return foundry.utils.mergeObject(
|
||||
update,
|
||||
Object.keys(countdownSetting[typeKey].countdowns).reduce((acc, countdownKey) => {
|
||||
const countdown = countdownSetting[typeKey].countdowns[countdownKey];
|
||||
if (countdown.progress.current > 0 && countdown.progress.type.value === progressType) {
|
||||
acc[`${typeKey}.countdowns.${countdownKey}.progress.current`] = countdown.progress.current - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Hooks.on(CONFIG.DH.HOOKS.characterAttack, async () => {
|
||||
updateCountdowns(countdown => {
|
||||
return (
|
||||
countdown.progress.type.value === countdownTypes.characterAttack.id && countdown.progress.current > 0
|
||||
);
|
||||
});
|
||||
});
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
}, {});
|
||||
|
||||
Hooks.on(CONFIG.DH.HOOKS.spotlight, async () => {
|
||||
updateCountdowns(countdown => {
|
||||
return countdown.progress.type.value === countdownTypes.spotlight.id && countdown.progress.current > 0;
|
||||
});
|
||||
await countdownSetting.updateSource(update);
|
||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, countdownSetting);
|
||||
|
||||
const data = { refreshType: RefreshType.Countdown };
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.Refresh,
|
||||
data
|
||||
});
|
||||
};
|
||||
Hooks.callAll(socketEvent.Refresh, data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ export * as domainConfig from './domainConfig.mjs';
|
|||
export * as effectConfig from './effectConfig.mjs';
|
||||
export * as flagsConfig from './flagsConfig.mjs';
|
||||
export * as generalConfig from './generalConfig.mjs';
|
||||
export * as hooksConfig from './hooksConfig.mjs';
|
||||
export * as itemConfig from './itemConfig.mjs';
|
||||
export * as settingsConfig from './settingsConfig.mjs';
|
||||
export * as systemConfig from './system.mjs';
|
||||
|
|
|
|||
|
|
@ -3,55 +3,55 @@ export const domains = {
|
|||
id: 'arcana',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.arcana.label',
|
||||
src: 'systems/daggerheart/assets/icons/domains/arcana.svg',
|
||||
description: 'DAGGERHEART.GENERAL.Domain.Arcana'
|
||||
description: 'DAGGERHEART.GENERAL.Domain.arcana.description'
|
||||
},
|
||||
blade: {
|
||||
id: 'blade',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.blade.label',
|
||||
src: 'systems/daggerheart/assets/icons/domains/blade.svg',
|
||||
description: 'DAGGERHEART.GENERAL.Domain.Blade'
|
||||
description: 'DAGGERHEART.GENERAL.Domain.blade.description'
|
||||
},
|
||||
bone: {
|
||||
id: 'bone',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.bone.label',
|
||||
src: 'systems/daggerheart/assets/icons/domains/bone.svg',
|
||||
description: 'DAGGERHEART.GENERAL.Domain.Bone'
|
||||
description: 'DAGGERHEART.GENERAL.Domain.bone.description'
|
||||
},
|
||||
codex: {
|
||||
id: 'codex',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.codex.label',
|
||||
src: 'systems/daggerheart/assets/icons/domains/codex.svg',
|
||||
description: 'DAGGERHEART.GENERAL.Domain.Codex'
|
||||
description: 'DAGGERHEART.GENERAL.Domain.codex.description'
|
||||
},
|
||||
grace: {
|
||||
id: 'grace',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.grace.label',
|
||||
src: 'systems/daggerheart/assets/icons/domains/grace.svg',
|
||||
description: 'DAGGERHEART.GENERAL.Domain.Grace'
|
||||
description: 'DAGGERHEART.GENERAL.Domain.grace.description'
|
||||
},
|
||||
midnight: {
|
||||
id: 'midnight',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.midnight.label',
|
||||
src: 'systems/daggerheart/assets/icons/domains/midnight.svg',
|
||||
description: 'DAGGERHEART.GENERAL.Domain.Midnight'
|
||||
description: 'DAGGERHEART.GENERAL.Domain.midnight.description'
|
||||
},
|
||||
sage: {
|
||||
id: 'sage',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.sage.label',
|
||||
src: 'systems/daggerheart/assets/icons/domains/sage.svg',
|
||||
description: 'DAGGERHEART.GENERAL.Domain.Sage'
|
||||
description: 'DAGGERHEART.GENERAL.Domain.sage.description'
|
||||
},
|
||||
splendor: {
|
||||
id: 'splendor',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.splendor.label',
|
||||
src: 'systems/daggerheart/assets/icons/domains/splendor.svg',
|
||||
description: 'DAGGERHEART.GENERAL.Domain.Splendor'
|
||||
description: 'DAGGERHEART.GENERAL.Domain.splendor.description'
|
||||
},
|
||||
valor: {
|
||||
id: 'valor',
|
||||
label: 'DAGGERHEART.GENERAL.Domain.valor.label',
|
||||
src: 'systems/daggerheart/assets/icons/domains/valor.svg',
|
||||
description: 'DAGGERHEART.GENERAL.Domain.Valor'
|
||||
description: 'DAGGERHEART.GENERAL.Domain.valor.description'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -376,15 +376,15 @@ export const abilityCosts = {
|
|||
export const countdownTypes = {
|
||||
spotlight: {
|
||||
id: 'spotlight',
|
||||
label: 'DAGGERHEART.CONFIG.CountdownTypes.Spotlight'
|
||||
label: 'DAGGERHEART.CONFIG.CountdownType.spotlight'
|
||||
},
|
||||
characterAttack: {
|
||||
id: 'characterAttack',
|
||||
label: 'DAGGERHEART.CONFIG.CountdownTypes.CharacterAttack'
|
||||
label: 'DAGGERHEART.CONFIG.CountdownType.characterAttack'
|
||||
},
|
||||
custom: {
|
||||
id: 'custom',
|
||||
label: 'DAGGERHEART.CONFIG.CountdownTypes.Custom'
|
||||
label: 'DAGGERHEART.CONFIG.CountdownType.custom'
|
||||
}
|
||||
};
|
||||
export const rollTypes = {
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
export const hooks = {
|
||||
characterAttack: 'characterAttackHook',
|
||||
spotlight: 'spotlightHook'
|
||||
};
|
||||
|
|
@ -3,7 +3,6 @@ import * as DOMAIN from './domainConfig.mjs';
|
|||
import * as ACTOR from './actorConfig.mjs';
|
||||
import * as ITEM from './itemConfig.mjs';
|
||||
import * as SETTINGS from './settingsConfig.mjs';
|
||||
import { hooks as HOOKS } from './hooksConfig.mjs';
|
||||
import * as EFFECTS from './effectConfig.mjs';
|
||||
import * as ACTIONS from './actionConfig.mjs';
|
||||
import * as FLAGS from './flagsConfig.mjs';
|
||||
|
|
@ -17,7 +16,6 @@ export const SYSTEM = {
|
|||
ACTOR,
|
||||
ITEM,
|
||||
SETTINGS,
|
||||
HOOKS,
|
||||
EFFECTS,
|
||||
ACTIONS,
|
||||
FLAGS
|
||||
|
|
|
|||
|
|
@ -38,6 +38,15 @@ export default class DHAttackAction extends DHDamageAction {
|
|||
};
|
||||
}
|
||||
|
||||
async use(event, ...args) {
|
||||
const result = await super.use(event, args);
|
||||
|
||||
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
|
||||
await updateCountdowns(CONFIG.DH.GENERAL.countdownTypes.characterAttack.id);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// get modifiers() {
|
||||
// return [];
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -254,7 +254,8 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
hasDamage: !!this.damage?.parts?.length,
|
||||
hasHealing: !!this.healing,
|
||||
hasEffect: !!this.effects?.length,
|
||||
hasSave: this.hasSave
|
||||
hasSave: this.hasSave,
|
||||
selectedRollMode: game.settings.get('core', 'rollMode')
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -350,7 +351,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
}
|
||||
|
||||
get modifiers() {
|
||||
if(!this.actor) return [];
|
||||
if (!this.actor) return [];
|
||||
const modifiers = [];
|
||||
/** Placeholder for specific bonuses **/
|
||||
return modifiers;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
}
|
||||
|
||||
async rollDamage(event, data) {
|
||||
const systemData = data.system ?? data;
|
||||
let formula = this.damage.parts.map(p => this.getFormulaValue(p, data).getFormula(this.actor)).join(' + '),
|
||||
damageTypes = [...new Set(this.damage.parts.reduce((a, c) => a.concat([...c.type]), []))];
|
||||
|
||||
|
|
@ -19,15 +20,15 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
let roll = { formula: formula, total: formula },
|
||||
bonusDamage = [];
|
||||
|
||||
if (isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(data.system ?? data));
|
||||
|
||||
if (isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(systemData));
|
||||
|
||||
const config = {
|
||||
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: this.name }),
|
||||
roll: { formula },
|
||||
targets: data.system?.targets.filter(t => t.hit) ?? data.targets,
|
||||
targets: systemData.targets.filter(t => t.hit) ?? data.targets,
|
||||
hasSave: this.hasSave,
|
||||
isCritical: data.system?.roll?.isCritical ?? false,
|
||||
source: data.system?.source,
|
||||
isCritical: systemData.roll?.isCritical ?? false,
|
||||
source: systemData.source,
|
||||
data: this.getRollData(),
|
||||
damageTypes,
|
||||
event
|
||||
|
|
@ -36,6 +37,8 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
if (data.system) {
|
||||
config.source.message = data._id;
|
||||
config.directDamage = false;
|
||||
} else {
|
||||
config.directDamage = true;
|
||||
}
|
||||
|
||||
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
||||
|
|
|
|||
|
|
@ -31,14 +31,29 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
motivesAndTactics: new fields.StringField(),
|
||||
notes: new fields.HTMLField(),
|
||||
difficulty: new fields.NumberField({ required: true, initial: 1, integer: true }),
|
||||
hordeHp: new fields.NumberField({ required: true, initial: 1, integer: true }),
|
||||
hordeHp: new fields.NumberField({
|
||||
required: true,
|
||||
initial: 1,
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.hordeHp'
|
||||
}),
|
||||
damageThresholds: new fields.SchemaField({
|
||||
major: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
severe: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
major: new fields.NumberField({
|
||||
required: true,
|
||||
initial: 0,
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.DamageThresholds.majorThreshold'
|
||||
}),
|
||||
severe: new fields.NumberField({
|
||||
required: true,
|
||||
initial: 0,
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
|
||||
})
|
||||
}),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(0, true),
|
||||
stress: resourceField(0, true)
|
||||
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints', true),
|
||||
stress: resourceField(0, 'DAGGERHEART.GENERAL.stress', true)
|
||||
}),
|
||||
attack: new ActionField({
|
||||
initial: {
|
||||
|
|
@ -75,13 +90,13 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
),
|
||||
bonuses: new fields.SchemaField({
|
||||
roll: new fields.SchemaField({
|
||||
attack: bonusField(),
|
||||
action: bonusField(),
|
||||
reaction: bonusField()
|
||||
attack: bonusField('DAGGERHEART.GENERAL.Roll.attack'),
|
||||
action: bonusField('DAGGERHEART.GENERAL.Roll.action'),
|
||||
reaction: bonusField('DAGGERHEART.GENERAL.Roll.reaction')
|
||||
}),
|
||||
damage: new fields.SchemaField({
|
||||
physical: bonusField(),
|
||||
magical: bonusField()
|
||||
physical: bonusField('DAGGERHEART.GENERAL.Damage.physicalDamage'),
|
||||
magical: bonusField('DAGGERHEART.GENERAL.Damage.magicalDamage')
|
||||
})
|
||||
})
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs';
|
||||
|
||||
const resistanceField = () =>
|
||||
const resistanceField = reductionLabel =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
resistance: new foundry.data.fields.BooleanField({ initial: false }),
|
||||
immunity: new foundry.data.fields.BooleanField({ initial: false }),
|
||||
reduction: new foundry.data.fields.NumberField({ integer: true, initial: 0 })
|
||||
reduction: new foundry.data.fields.NumberField({ integer: true, initial: 0, label: reductionLabel })
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -40,8 +40,8 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
if (this.metadata.isNPC) schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||
if (this.metadata.hasResistances)
|
||||
schema.resistance = new fields.SchemaField({
|
||||
physical: resistanceField(),
|
||||
magical: resistanceField()
|
||||
physical: resistanceField('DAGGERHEART.GENERAL.DamageResistance.physicalReduction'),
|
||||
magical: resistanceField('DAGGERHEART.GENERAL.DamageResistance.magicalReduction')
|
||||
});
|
||||
return schema;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import BaseDataActor from './base.mjs';
|
|||
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
||||
|
||||
export default class DhCharacter extends BaseDataActor {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Character'];
|
||||
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Actor.character',
|
||||
|
|
@ -19,24 +21,36 @@ export default class DhCharacter extends BaseDataActor {
|
|||
return {
|
||||
...super.defineSchema(),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(0, true),
|
||||
stress: resourceField(6, true),
|
||||
hope: resourceField(6)
|
||||
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints', true),
|
||||
stress: resourceField(6, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: resourceField(6, 'DAGGERHEART.GENERAL.hope')
|
||||
}),
|
||||
traits: new fields.SchemaField({
|
||||
agility: attributeField(),
|
||||
strength: attributeField(),
|
||||
finesse: attributeField(),
|
||||
instinct: attributeField(),
|
||||
presence: attributeField(),
|
||||
knowledge: attributeField()
|
||||
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),
|
||||
strength: attributeField('DAGGERHEART.CONFIG.Traits.strength.name'),
|
||||
finesse: attributeField('DAGGERHEART.CONFIG.Traits.finesse.name'),
|
||||
instinct: attributeField('DAGGERHEART.CONFIG.Traits.instinct.name'),
|
||||
presence: attributeField('DAGGERHEART.CONFIG.Traits.presence.name'),
|
||||
knowledge: attributeField('DAGGERHEART.CONFIG.Traits.knowledge.name')
|
||||
}),
|
||||
proficiency: new fields.NumberField({ initial: 1, integer: true }),
|
||||
evasion: new fields.NumberField({ initial: 0, integer: true }),
|
||||
armorScore: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
proficiency: new fields.NumberField({
|
||||
initial: 1,
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.proficiency'
|
||||
}),
|
||||
evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }),
|
||||
armorScore: new fields.NumberField({ integer: true, initial: 0, label: 'DAGGERHEART.GENERAL.armorScore' }),
|
||||
damageThresholds: new fields.SchemaField({
|
||||
severe: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
major: new fields.NumberField({ integer: true, initial: 0 })
|
||||
severe: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.DamageThresholds.majorThreshold'
|
||||
}),
|
||||
major: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
|
||||
})
|
||||
}),
|
||||
experiences: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
|
|
@ -76,25 +90,37 @@ export default class DhCharacter extends BaseDataActor {
|
|||
levelData: new fields.EmbeddedDataField(DhLevelData),
|
||||
bonuses: new fields.SchemaField({
|
||||
roll: new fields.SchemaField({
|
||||
attack: bonusField(),
|
||||
spellcast: bonusField(),
|
||||
trait: bonusField(),
|
||||
action: bonusField(),
|
||||
reaction: bonusField(),
|
||||
primaryWeapon: bonusField(),
|
||||
secondaryWeapon: bonusField()
|
||||
attack: bonusField('DAGGERHEART.GENERAL.Roll.attack'),
|
||||
spellcast: bonusField('DAGGERHEART.GENERAL.Roll.spellcast'),
|
||||
trait: bonusField('DAGGERHEART.GENERAL.Roll.trait'),
|
||||
action: bonusField('DAGGERHEART.GENERAL.Roll.action'),
|
||||
reaction: bonusField('DAGGERHEART.GENERAL.Roll.reaction'),
|
||||
primaryWeapon: bonusField('DAGGERHEART.GENERAL.Roll.primaryWeaponAttack'),
|
||||
secondaryWeapon: bonusField('DAGGERHEART.GENERAL.Roll.secondaryWeaponAttack')
|
||||
}),
|
||||
damage: new fields.SchemaField({
|
||||
physical: bonusField(),
|
||||
magical: bonusField(),
|
||||
primaryWeapon: bonusField(),
|
||||
secondaryWeapon: bonusField()
|
||||
physical: bonusField('DAGGERHEART.GENERAL.Damage.physicalDamage'),
|
||||
magical: bonusField('DAGGERHEART.GENERAL.Damage.magicalDamage'),
|
||||
primaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.primaryWeapon'),
|
||||
secondaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.primaryWeapon')
|
||||
}),
|
||||
healing: bonusField(),
|
||||
healing: bonusField('DAGGERHEART.GENERAL.Healing.healingAmount'),
|
||||
range: new fields.SchemaField({
|
||||
weapon: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
spell: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
other: new fields.NumberField({ integer: true, initial: 0 })
|
||||
weapon: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Range.weapon'
|
||||
}),
|
||||
spell: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Range.spell'
|
||||
}),
|
||||
other: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Range.other'
|
||||
})
|
||||
})
|
||||
}),
|
||||
companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }),
|
||||
|
|
@ -102,25 +128,34 @@ export default class DhCharacter extends BaseDataActor {
|
|||
damageReduction: new fields.SchemaField({
|
||||
maxArmorMarked: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||
bonus: new fields.NumberField({ required: true, integer: true, initial: 0 }),
|
||||
stressExtra: new fields.NumberField({ required: true, integer: true, initial: 0 })
|
||||
bonus: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedBonus'
|
||||
}),
|
||||
stressExtra: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedStress.label',
|
||||
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedStress.hint'
|
||||
})
|
||||
}),
|
||||
stressDamageReduction: new fields.SchemaField({
|
||||
severe: stressDamageReductionRule(),
|
||||
major: stressDamageReductionRule(),
|
||||
minor: stressDamageReductionRule()
|
||||
severe: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.severe'),
|
||||
major: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.major'),
|
||||
minor: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.minor')
|
||||
}),
|
||||
increasePerArmorMark: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 1,
|
||||
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.label',
|
||||
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.hint'
|
||||
}),
|
||||
increasePerArmorMark: new fields.NumberField({ integer: true, initial: 1 }),
|
||||
magical: new fields.BooleanField({ initial: false }),
|
||||
physical: new fields.BooleanField({ initial: false })
|
||||
}),
|
||||
strangePatterns: new fields.NumberField({
|
||||
integer: true,
|
||||
min: 1,
|
||||
max: 12,
|
||||
nullable: true,
|
||||
initial: null
|
||||
}),
|
||||
weapon: new fields.SchemaField({
|
||||
/* Unimplemented
|
||||
-> Should remove the lowest damage dice from weapon damage
|
||||
|
|
|
|||
|
|
@ -24,10 +24,16 @@ export default class DhCompanion extends BaseDataActor {
|
|||
...super.defineSchema(),
|
||||
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
|
||||
resources: new fields.SchemaField({
|
||||
stress: resourceField(3, true),
|
||||
hope: new fields.NumberField({ initial: 0, integer: true })
|
||||
stress: resourceField(3, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.hope' })
|
||||
}),
|
||||
evasion: new fields.NumberField({
|
||||
required: true,
|
||||
min: 1,
|
||||
initial: 10,
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.evasion'
|
||||
}),
|
||||
evasion: new fields.NumberField({ required: true, min: 1, initial: 10, integer: true }),
|
||||
experiences: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({}),
|
||||
|
|
@ -74,8 +80,8 @@ export default class DhCompanion extends BaseDataActor {
|
|||
levelData: new fields.EmbeddedDataField(DhLevelData),
|
||||
bonuses: new fields.SchemaField({
|
||||
damage: new fields.SchemaField({
|
||||
physical: bonusField(),
|
||||
magical: bonusField()
|
||||
physical: bonusField('DAGGERHEART.GENERAL.Damage.physicalDamage'),
|
||||
magical: bonusField('DAGGERHEART.GENERAL.Damage.magicalDamage')
|
||||
})
|
||||
})
|
||||
};
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ class DhCountdown extends foundry.abstract.DataModel {
|
|||
value: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.GENERAL.countdownTypes,
|
||||
initial: CONFIG.DH.GENERAL.countdownTypes.spotlight.id,
|
||||
initial: CONFIG.DH.GENERAL.countdownTypes.custom.id,
|
||||
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.type.value.label'
|
||||
}),
|
||||
label: new fields.StringField({
|
||||
|
|
@ -132,7 +132,13 @@ class DhCountdown extends foundry.abstract.DataModel {
|
|||
export const registerCountdownHooks = () => {
|
||||
Hooks.on(socketEvent.Refresh, ({ refreshType, application }) => {
|
||||
if (refreshType === RefreshType.Countdown) {
|
||||
foundry.applications.instances.get(application)?.render();
|
||||
if (application) {
|
||||
foundry.applications.instances.get(application)?.render();
|
||||
} else {
|
||||
foundry.applications.instances.get('narrative-countdowns').render();
|
||||
foundry.applications.instances.get('encounter-countdowns').render();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,28 +1,32 @@
|
|||
const fields = foundry.data.fields;
|
||||
|
||||
const attributeField = () =>
|
||||
const attributeField = label =>
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||
value: new fields.NumberField({ initial: 0, integer: true, label }),
|
||||
tierMarked: new fields.BooleanField({ initial: false })
|
||||
});
|
||||
|
||||
const resourceField = (max = 0, reverse = false) =>
|
||||
const resourceField = (max = 0, label, reverse = false) =>
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||
value: new fields.NumberField({ initial: 0, integer: true, label }),
|
||||
max: new fields.NumberField({ initial: max, integer: true }),
|
||||
isReversed: new fields.BooleanField({ initial: reverse })
|
||||
});
|
||||
|
||||
const stressDamageReductionRule = () =>
|
||||
const stressDamageReductionRule = localizationPath =>
|
||||
new fields.SchemaField({
|
||||
enabled: new fields.BooleanField({ required: true, initial: false }),
|
||||
cost: new fields.NumberField({ integer: true })
|
||||
cost: new fields.NumberField({
|
||||
integer: true,
|
||||
label: `${localizationPath}.label`,
|
||||
hint: `${localizationPath}.hint`
|
||||
})
|
||||
});
|
||||
|
||||
const bonusField = () =>
|
||||
const bonusField = label =>
|
||||
new fields.SchemaField({
|
||||
bonus: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
bonus: new fields.NumberField({ integer: true, initial: 0, label }),
|
||||
dice: new fields.ArrayField(new fields.StringField())
|
||||
})
|
||||
});
|
||||
|
||||
export { attributeField, resourceField, stressDamageReductionRule, bonusField };
|
||||
export { attributeField, resourceField, stressDamageReductionRule, bonusField };
|
||||
|
|
|
|||
|
|
@ -4,20 +4,22 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
|||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
hope: new fields.BooleanField({
|
||||
required: true,
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hope.label'
|
||||
}), // Label need to be updated into something like "Duality Roll Auto Gain" + a hint
|
||||
hopeFear: new fields.SchemaField({
|
||||
gm: new fields.BooleanField({
|
||||
required: true,
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hopeFear.gm.label'
|
||||
}),
|
||||
players: new fields.BooleanField({
|
||||
required: true,
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hopeFear.players.label'
|
||||
})
|
||||
}),
|
||||
actionPoints: new fields.BooleanField({
|
||||
required: true,
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.actionPoints.label'
|
||||
}),
|
||||
countdowns: new fields.BooleanField({
|
||||
requireD: true,
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.countdowns.label'
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ export default class D20Roll extends DHRoll {
|
|||
value: this.options.data.experiences[m].value
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
this.addModifiers();
|
||||
if (this.options.extraFormula) {
|
||||
this.terms.push(
|
||||
|
|
@ -123,15 +123,17 @@ export default class D20Roll extends DHRoll {
|
|||
|
||||
applyBaseBonus() {
|
||||
const modifiers = [];
|
||||
|
||||
if(this.options.roll.bonus)
|
||||
|
||||
if (this.options.roll.bonus)
|
||||
modifiers.push({
|
||||
label: 'Bonus to Hit',
|
||||
value: this.options.roll.bonus
|
||||
});
|
||||
|
||||
modifiers.push(...this.getBonus(`roll.${this.options.type}`, `${this.options.type.capitalize()} Bonus`));
|
||||
modifiers.push(...this.getBonus(`roll.${this.options.roll.type}`, `${this.options.roll.type.capitalize()} Bonus`));
|
||||
modifiers.push(
|
||||
...this.getBonus(`roll.${this.options.roll.type}`, `${this.options.roll.type.capitalize()} Bonus`)
|
||||
);
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,16 +34,16 @@ export default class DamageRoll extends DHRoll {
|
|||
});
|
||||
const weapons = ['primaryWeapon', 'secondaryWeapon'];
|
||||
weapons.forEach(w => {
|
||||
if(this.options.source.item && this.options.source.item === this.data[w]?.id)
|
||||
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
|
||||
modifiers.push(...this.getBonus(`${type}.${w}`, 'Weapon Bonus'));
|
||||
});
|
||||
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
constructFormula(config) {
|
||||
super.constructFormula(config);
|
||||
|
||||
|
||||
if (config.isCritical) {
|
||||
const tmpRoll = new Roll(this._formula)._evaluateSync({ maximize: true }),
|
||||
criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export default class DHRoll extends Roll {
|
|||
baseTerms = [];
|
||||
constructor(formula, data, options) {
|
||||
super(formula, data, options);
|
||||
if(!this.data || !Object.keys(this.data).length) this.data = options.data;
|
||||
if (!this.data || !Object.keys(this.data).length) this.data = options.data;
|
||||
}
|
||||
|
||||
static messageType = 'adversaryRoll';
|
||||
|
|
@ -87,7 +87,7 @@ export default class DHRoll extends Roll {
|
|||
system: config,
|
||||
rolls: [roll]
|
||||
};
|
||||
return await cls.create(msg);
|
||||
return await cls.create(msg, { rollMode: config.selectedRollMode });
|
||||
}
|
||||
|
||||
static applyKeybindings(config) {
|
||||
|
|
@ -100,7 +100,7 @@ export default class DHRoll extends Roll {
|
|||
}
|
||||
|
||||
formatModifier(modifier) {
|
||||
if(Array.isArray(modifier)) {
|
||||
if (Array.isArray(modifier)) {
|
||||
return [
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
...this.constructor.parse(modifier.join(' + '), this.options.data)
|
||||
|
|
@ -127,12 +127,12 @@ export default class DHRoll extends Roll {
|
|||
getBonus(path, label) {
|
||||
const bonus = foundry.utils.getProperty(this.data.bonuses, path),
|
||||
modifiers = [];
|
||||
if(bonus?.bonus)
|
||||
if (bonus?.bonus)
|
||||
modifiers.push({
|
||||
label: label,
|
||||
value: bonus?.bonus
|
||||
});
|
||||
if(bonus?.dice?.length)
|
||||
if (bonus?.dice?.length)
|
||||
modifiers.push({
|
||||
label: label,
|
||||
value: bonus?.dice
|
||||
|
|
@ -175,9 +175,10 @@ export default class DHRoll extends Roll {
|
|||
|
||||
export const registerRollDiceHooks = () => {
|
||||
Hooks.on(`${CONFIG.DH.id}.postRollDuality`, async (config, message) => {
|
||||
const hopeFearAutomation = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).hopeFear;
|
||||
if (
|
||||
!config.source?.actor ||
|
||||
!game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).hope ||
|
||||
(game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) ||
|
||||
config.roll.type === 'reaction'
|
||||
)
|
||||
return;
|
||||
|
|
@ -185,9 +186,9 @@ export const registerRollDiceHooks = () => {
|
|||
const actor = await fromUuid(config.source.actor),
|
||||
updates = [];
|
||||
if (!actor) return;
|
||||
if (config.roll.isCritical || config.roll.result.duality === 1) updates.push({ type: 'hope', value: 1 });
|
||||
if (config.roll.isCritical) updates.push({ type: 'stress', value: -1 });
|
||||
if (config.roll.result.duality === -1) updates.push({ type: 'fear', value: 1 });
|
||||
if (config.roll.isCritical || config.roll.result.duality === 1) updates.push({ key: 'hope', value: 1 });
|
||||
if (config.roll.isCritical) updates.push({ key: 'stress', value: -1 });
|
||||
if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1 });
|
||||
|
||||
if (updates.length) actor.modifyResource(updates);
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ export default class DualityRoll extends D20Roll {
|
|||
applyBaseBonus() {
|
||||
const modifiers = super.applyBaseBonus();
|
||||
|
||||
if(this.options.roll.trait && this.data.traits[this.options.roll.trait])
|
||||
if (this.options.roll.trait && this.data.traits[this.options.roll.trait])
|
||||
modifiers.unshift({
|
||||
label: `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`,
|
||||
value: this.data.traits[this.options.roll.trait].value
|
||||
|
|
@ -129,7 +129,7 @@ export default class DualityRoll extends D20Roll {
|
|||
|
||||
const weapons = ['primaryWeapon', 'secondaryWeapon'];
|
||||
weapons.forEach(w => {
|
||||
if(this.options.source.item && this.options.source.item === this.data[w]?.id)
|
||||
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
|
||||
modifiers.push(...this.getBonus(`roll.${w}`, 'Weapon Bonus'));
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@ export { default as DHItem } from './item.mjs';
|
|||
export { default as DhpCombat } from './combat.mjs';
|
||||
export { default as DhActiveEffect } from './activeEffect.mjs';
|
||||
export { default as DhChatMessage } from './chatMessage.mjs';
|
||||
export { default as DhToken } from './token.mjs';
|
||||
export { default as DhTooltipManager } from './tooltipManager.mjs';
|
||||
|
|
|
|||
|
|
@ -37,4 +37,15 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER'));
|
||||
});
|
||||
}
|
||||
|
||||
async _preCreate(data, options, user) {
|
||||
options.speaker = ChatMessage.getSpeaker();
|
||||
const rollActorOwner = data.rolls?.[0]?.data?.parent?.owner;
|
||||
if (rollActorOwner) {
|
||||
data.author = rollActorOwner ? rollActorOwner.id : data.author;
|
||||
await this.updateSource({ author: rollActorOwner ?? user });
|
||||
}
|
||||
|
||||
return super._preCreate(data, options, rollActorOwner ?? user);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
35
module/documents/token.mjs
Normal file
35
module/documents/token.mjs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
export default class DHToken extends TokenDocument {
|
||||
/**
|
||||
* Inspect the Actor data model and identify the set of attributes which could be used for a Token Bar.
|
||||
* @param {object} attributes The tracked attributes which can be chosen from
|
||||
* @returns {object} A nested object of attribute choices to display
|
||||
*/
|
||||
static getTrackedAttributeChoices(attributes, model) {
|
||||
attributes = attributes || this.getTrackedAttributes();
|
||||
const barGroup = game.i18n.localize('TOKEN.BarAttributes');
|
||||
const valueGroup = game.i18n.localize('TOKEN.BarValues');
|
||||
|
||||
const bars = attributes.bar.map(v => {
|
||||
const a = v.join('.');
|
||||
const modelLabel = model ? game.i18n.localize(model.schema.getField(`${a}.value`).label) : null;
|
||||
return { group: barGroup, value: a, label: modelLabel ? modelLabel : a };
|
||||
});
|
||||
bars.sort((a, b) => a.label.compare(b.label));
|
||||
|
||||
const invalidAttributes = ['gold', 'levelData', 'rules.damageReduction.maxArmorMarked.value'];
|
||||
const values = attributes.value.reduce((acc, v) => {
|
||||
const a = v.join('.');
|
||||
if (invalidAttributes.some(x => a.startsWith(x))) return acc;
|
||||
|
||||
const field = model ? model.schema.getField(a) : null;
|
||||
const modelLabel = field ? game.i18n.localize(field.label) : null;
|
||||
const hint = field ? game.i18n.localize(field.hint) : null;
|
||||
acc.push({ group: valueGroup, value: a, label: modelLabel ? modelLabel : a, hint: hint });
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
values.sort((a, b) => a.label.compare(b.label));
|
||||
|
||||
return bars.concat(values);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue