mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
Feature/112 items use action datamodel (#194)
* Fix action for items * Cost & Range #1 * remove log * actions * Split methods * Roll classes * Begin damage * g * Actions * before main merge * Fix d20RollDialog costs check * Fix submit on close * Add uses in action dialog * Adversary Attack * 166 - Damage Reduction (#180) * Temp * Fixed Stress Reductions * Changed from index based to object * Fixed stress resources management for DamageReduction * Fix Adversary attack multiplier * Auto add Attack action to newly created weapon * Few fixes * 164 - Add Hope/Fear formula * 163 - Actor Sub Datas (#182) * Added rules/bonuses for all classes and subclasses * More * Add Save * Fix delete action button --------- Co-authored-by: WBHarry <williambjrklund@gmail.com> Co-authored-by: WBHarry <89362246+WBHarry@users.noreply.github.com>
This commit is contained in:
parent
1135669d0b
commit
3593f44612
77 changed files with 3707 additions and 1828 deletions
|
|
@ -16,6 +16,7 @@ import Resources from './module/applications/resources.mjs';
|
||||||
import { NarrativeCountdowns, registerCountdownApplicationHooks } from './module/applications/countdowns.mjs';
|
import { NarrativeCountdowns, registerCountdownApplicationHooks } from './module/applications/countdowns.mjs';
|
||||||
import DHDualityRoll from './module/data/chat-message/dualityRoll.mjs';
|
import DHDualityRoll from './module/data/chat-message/dualityRoll.mjs';
|
||||||
import { DualityRollColor } from './module/data/settings/Appearance.mjs';
|
import { DualityRollColor } from './module/data/settings/Appearance.mjs';
|
||||||
|
import { DHRoll, DualityRoll, D20Roll, DamageRoll, DualityDie } from './module/applications/roll.mjs';
|
||||||
import { DhMeasuredTemplate } from './module/placeables/_module.mjs';
|
import { DhMeasuredTemplate } from './module/placeables/_module.mjs';
|
||||||
import { renderDualityButton } from './module/enrichers/DualityRollEnricher.mjs';
|
import { renderDualityButton } from './module/enrichers/DualityRollEnricher.mjs';
|
||||||
import { renderMeasuredTemplate } from './module/enrichers/TemplateEnricher.mjs';
|
import { renderMeasuredTemplate } from './module/enrichers/TemplateEnricher.mjs';
|
||||||
|
|
@ -50,6 +51,15 @@ Hooks.once('init', () => {
|
||||||
name: game.i18n.localize(x.name)
|
name: game.i18n.localize(x.name)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
CONFIG.Dice.daggerheart = {
|
||||||
|
DualityDie: DualityDie,
|
||||||
|
DHRoll: DHRoll,
|
||||||
|
DualityRoll: DualityRoll,
|
||||||
|
D20Roll: D20Roll,
|
||||||
|
DamageRoll: DamageRoll
|
||||||
|
};
|
||||||
|
|
||||||
|
CONFIG.Dice.rolls = [...CONFIG.Dice.rolls, ...[DHRoll, DualityRoll, D20Roll, DamageRoll]];
|
||||||
CONFIG.MeasuredTemplate.objectClass = DhMeasuredTemplate;
|
CONFIG.MeasuredTemplate.objectClass = DhMeasuredTemplate;
|
||||||
|
|
||||||
CONFIG.Item.documentClass = documents.DhpItem;
|
CONFIG.Item.documentClass = documents.DhpItem;
|
||||||
|
|
@ -307,9 +317,12 @@ const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/views/actionTypes/uuid.hbs',
|
'systems/daggerheart/templates/views/actionTypes/uuid.hbs',
|
||||||
'systems/daggerheart/templates/views/actionTypes/uses.hbs',
|
'systems/daggerheart/templates/views/actionTypes/uses.hbs',
|
||||||
'systems/daggerheart/templates/views/actionTypes/roll.hbs',
|
'systems/daggerheart/templates/views/actionTypes/roll.hbs',
|
||||||
|
'systems/daggerheart/templates/views/actionTypes/save.hbs',
|
||||||
'systems/daggerheart/templates/views/actionTypes/cost.hbs',
|
'systems/daggerheart/templates/views/actionTypes/cost.hbs',
|
||||||
'systems/daggerheart/templates/views/actionTypes/range-target.hbs',
|
'systems/daggerheart/templates/views/actionTypes/range-target.hbs',
|
||||||
'systems/daggerheart/templates/views/actionTypes/effect.hbs',
|
'systems/daggerheart/templates/views/actionTypes/effect.hbs',
|
||||||
'systems/daggerheart/templates/settings/components/settings-item-line.hbs'
|
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
|
||||||
|
|
||||||
|
'systems/daggerheart/templates/chat/parts/target-chat.hbs'
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
42
lang/en.json
42
lang/en.json
|
|
@ -207,6 +207,12 @@
|
||||||
"Session": "Session",
|
"Session": "Session",
|
||||||
"Shortrest": "Short Rest",
|
"Shortrest": "Short Rest",
|
||||||
"Longrest": "Long Rest"
|
"Longrest": "Long Rest"
|
||||||
|
},
|
||||||
|
"Damage": {
|
||||||
|
"Severe": "Severe",
|
||||||
|
"Major": "Major",
|
||||||
|
"Minor": "Minor",
|
||||||
|
"None": "None"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ActionType": {
|
"ActionType": {
|
||||||
|
|
@ -617,6 +623,14 @@
|
||||||
"Stress": {
|
"Stress": {
|
||||||
"Name": "Stress",
|
"Name": "Stress",
|
||||||
"Stress": "STR"
|
"Stress": "STR"
|
||||||
|
},
|
||||||
|
"Hope": {
|
||||||
|
"Name": "Hope",
|
||||||
|
"Abbreviation": "HO"
|
||||||
|
},
|
||||||
|
"ArmorStack": {
|
||||||
|
"Name": "Armor Stack",
|
||||||
|
"Stress": "AS"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ArmorFeature": {
|
"ArmorFeature": {
|
||||||
|
|
@ -1004,14 +1018,20 @@
|
||||||
},
|
},
|
||||||
"AttackRoll": {
|
"AttackRoll": {
|
||||||
"Title": "Attack - {attack}",
|
"Title": "Attack - {attack}",
|
||||||
"RollDamage": "Roll Damage"
|
"RollDamage": "Roll Damage",
|
||||||
|
"RollHealing": "Roll Healing",
|
||||||
|
"ApplyEffect": "Apply Effects"
|
||||||
},
|
},
|
||||||
"DamageRoll": {
|
"DamageRoll": {
|
||||||
"Title": "Damage - {damage}",
|
"Title": "Damage - {damage}",
|
||||||
"DealDamageToTargets": "Damage Hit Targets",
|
"DealDamageToTargets": "Damage Hit Targets",
|
||||||
"DealDamage": "Deal Damage"
|
"DealDamage": "Deal Damage"
|
||||||
},
|
},
|
||||||
|
"ApplyEffect": {
|
||||||
|
"Title": "Apply Effects - {name}"
|
||||||
|
},
|
||||||
"HealingRoll": {
|
"HealingRoll": {
|
||||||
|
"Title": "Heal - {healing}",
|
||||||
"Heal": "Heal"
|
"Heal": "Heal"
|
||||||
},
|
},
|
||||||
"DeathMove": {
|
"DeathMove": {
|
||||||
|
|
@ -1070,6 +1090,21 @@
|
||||||
"Title": "Ownership Selection - {name}",
|
"Title": "Ownership Selection - {name}",
|
||||||
"Default": "Default Ownership"
|
"Default": "Default Ownership"
|
||||||
},
|
},
|
||||||
|
"DamageReduction": {
|
||||||
|
"Title": "Damage Reduction",
|
||||||
|
"ArmorMarks": "Armor Marks",
|
||||||
|
"UsedMarks": "Used Marks",
|
||||||
|
"Stress": "Stress",
|
||||||
|
"ArmorWithStress": "Spend 1 stress to use an extra mark",
|
||||||
|
"UnncessaryStress": "You don't need to expend stress",
|
||||||
|
"StressReduction": "Reduce By Stress",
|
||||||
|
"Notifications": {
|
||||||
|
"DamageAlreadyNone": "The damage has already been reduced to none",
|
||||||
|
"NoAvailableArmorMarks": "You have no more available armor marks",
|
||||||
|
"NotEnoughStress": "You don't have enough stress",
|
||||||
|
"DamageIgnore": "{character} did not take damage"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Sheets": {
|
"Sheets": {
|
||||||
"TABS": {
|
"TABS": {
|
||||||
"features": "Features",
|
"features": "Features",
|
||||||
|
|
@ -1506,6 +1541,11 @@
|
||||||
"Macro": {
|
"Macro": {
|
||||||
"Name": "Macro"
|
"Name": "Macro"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Settings": {
|
||||||
|
"ResultBased": {
|
||||||
|
"label": "Formula based on Hope/Fear result."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"RollTypes": {
|
"RollTypes": {
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,36 @@
|
||||||
import { DualityRollColor } from '../data/settings/Appearance.mjs';
|
|
||||||
import DHDualityRoll from '../data/chat-message/dualityRoll.mjs';
|
|
||||||
|
|
||||||
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
async renderHTML() {
|
async renderHTML() {
|
||||||
if (this.type === 'dualityRoll' || this.type === 'adversaryRoll') {
|
if(this.system.messageTemplate) this.content = await foundry.applications.handlebars.renderTemplate(this.system.messageTemplate, this.system);
|
||||||
this.content = await foundry.applications.handlebars.renderTemplate(this.content, this.system);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
||||||
const html = await super.renderHTML();
|
const html = await super.renderHTML();
|
||||||
|
this.applyPermission(html);
|
||||||
|
|
||||||
if (this.type === 'dualityRoll') {
|
if (this.type === 'dualityRoll') {
|
||||||
html.classList.add('duality');
|
html.classList.add('duality');
|
||||||
const dualityResult = this.system.dualityResult;
|
switch (this.system.roll.result.duality) {
|
||||||
if (dualityResult === DHDualityRoll.dualityResult.hope) html.classList.add('hope');
|
case 1:
|
||||||
else if (dualityResult === DHDualityRoll.dualityResult.fear) html.classList.add('fear');
|
html.classList.add('hope');
|
||||||
else html.classList.add('critical');
|
break;
|
||||||
|
case -1:
|
||||||
|
html.classList.add('fear');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
html.classList.add('critical');
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyPermission(html) {
|
||||||
|
const elements = html.querySelectorAll('[data-perm-id]');
|
||||||
|
elements.forEach(e => {
|
||||||
|
const uuid = e.dataset.permId,
|
||||||
|
document = fromUuidSync(uuid);
|
||||||
|
e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER'));
|
||||||
|
e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER'));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static CLEAN_ARRAYS = ["damage.parts", "cost", "effects"];
|
||||||
|
|
||||||
_getTabs() {
|
_getTabs() {
|
||||||
const tabs = {
|
const tabs = {
|
||||||
base: { active: true, cssClass: '', group: 'primary', id: 'base', icon: null, label: 'Base' },
|
base: { active: true, cssClass: '', group: 'primary', id: 'base', icon: null, label: 'Base' },
|
||||||
|
|
@ -60,8 +62,14 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
context.tabs = this._getTabs();
|
context.tabs = this._getTabs();
|
||||||
context.config = SYSTEM;
|
context.config = SYSTEM;
|
||||||
if (!!this.action.effects) context.effects = this.action.effects.map(e => this.action.item.effects.get(e._id));
|
if (!!this.action.effects) context.effects = this.action.effects.map(e => this.action.item.effects.get(e._id));
|
||||||
if (this.action.damage?.hasOwnProperty('includeBase')) context.hasBaseDamage = !!this.action.parent.damage;
|
if (this.action.damage?.hasOwnProperty('includeBase') && this.action.type === 'attack')
|
||||||
|
context.hasBaseDamage = !!this.action.parent.damage;
|
||||||
context.getRealIndex = this.getRealIndex.bind(this);
|
context.getRealIndex = this.getRealIndex.bind(this);
|
||||||
|
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||||
|
context.disableOption = this.disableOption.bind(this);
|
||||||
|
context.isNPC = this.action.actor && this.action.actor.type !== 'character';
|
||||||
|
context.hasRoll = this.action.hasRoll;
|
||||||
|
console.log(context)
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,27 +78,47 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
this.render(true);
|
this.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disableOption(index, options, choices) {
|
||||||
|
const filtered = foundry.utils.deepClone(options);
|
||||||
|
Object.keys(filtered).forEach(o => {
|
||||||
|
if (choices.find((c, idx) => c.type === o && index !== idx)) delete filtered[o];
|
||||||
|
});
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
getRealIndex(index) {
|
getRealIndex(index) {
|
||||||
const data = this.action.toObject(false);
|
const data = this.action.toObject(false);
|
||||||
return data.damage.parts.find(d => d.base) ? index - 1 : index;
|
return data.damage.parts.find(d => d.base) ? index - 1 : index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEffectDetails(id) {
|
||||||
|
return this.action.item.effects.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
_prepareSubmitData(event, formData) {
|
_prepareSubmitData(event, formData) {
|
||||||
const submitData = foundry.utils.expandObject(formData.object);
|
const submitData = foundry.utils.expandObject(formData.object);
|
||||||
// this.element.querySelectorAll("fieldset[disabled] :is(input, select)").forEach(input => {
|
for ( const keyPath of this.constructor.CLEAN_ARRAYS ) {
|
||||||
// foundry.utils.setProperty(submitData, input.name, input.value);
|
const data = foundry.utils.getProperty(submitData, keyPath);
|
||||||
// });
|
if ( data ) foundry.utils.setProperty(submitData, keyPath, Object.values(data));
|
||||||
|
}
|
||||||
return submitData;
|
return submitData;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
static async updateForm(event, _, formData) {
|
||||||
const submitData = this._prepareSubmitData(event, formData),
|
const submitData = this._prepareSubmitData(event, formData),
|
||||||
data = foundry.utils.expandObject(foundry.utils.mergeObject(this.action.toObject(), submitData)),
|
data = foundry.utils.mergeObject(this.action.toObject(), submitData),
|
||||||
|
container = foundry.utils.getProperty(this.action.parent, this.action.systemPath);
|
||||||
|
let newActions;
|
||||||
|
if (Array.isArray(container)) {
|
||||||
newActions = foundry.utils.getProperty(this.action.parent, this.action.systemPath).map(x => x.toObject()); // Find better way
|
newActions = foundry.utils.getProperty(this.action.parent, this.action.systemPath).map(x => x.toObject()); // Find better way
|
||||||
if (!newActions.findSplice(x => x._id === data._id, data)) newActions.push(data);
|
if (!newActions.findSplice(x => x._id === data._id, data)) newActions.push(data);
|
||||||
|
} else newActions = data;
|
||||||
|
|
||||||
const updates = await this.action.parent.parent.update({ [`system.${this.action.systemPath}`]: newActions });
|
const updates = await this.action.parent.parent.update({ [`system.${this.action.systemPath}`]: newActions });
|
||||||
if (!updates) return;
|
if (!updates) return;
|
||||||
this.action = foundry.utils.getProperty(updates.system, this.action.systemPath)[this.action.index];
|
this.action = Array.isArray(container)
|
||||||
|
? foundry.utils.getProperty(updates.system, this.action.systemPath)[this.action.index]
|
||||||
|
: foundry.utils.getProperty(updates.system, this.action.systemPath);
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,6 +131,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static removeElement(event) {
|
static removeElement(event) {
|
||||||
|
event.stopPropagation();
|
||||||
const data = this.action.toObject(),
|
const data = this.action.toObject(),
|
||||||
key = event.target.closest('.action-category-data').dataset.key,
|
key = event.target.closest('.action-category-data').dataset.key,
|
||||||
index = event.target.dataset.index;
|
index = event.target.dataset.index;
|
||||||
|
|
@ -132,6 +161,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
data = this.action.toObject();
|
data = this.action.toObject();
|
||||||
data.effects.push({ _id: created._id });
|
data.effects.push({ _id: created._id });
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
this.action.item.effects.get(created._id).sheet.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -156,5 +186,8 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
this.action.item.deleteEmbeddedDocuments('ActiveEffect', [effectId]);
|
this.action.item.deleteEmbeddedDocuments('ActiveEffect', [effectId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static editEffect(event) {}
|
static editEffect(event) {
|
||||||
|
const id = event.target.closest('[data-effect-id]')?.dataset?.effectId;
|
||||||
|
this.action.item.effects.get(id).sheet.render(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,4 +21,15 @@ export default class DhContextMenu extends ContextMenu {
|
||||||
item?.callback(this.#jQuery ? $(this.target) : this.target, event);
|
item?.callback(this.#jQuery ? $(this.target) : this.target, event);
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static triggerContextMenu(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
const { clientX, clientY } = event;
|
||||||
|
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
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
66
module/applications/costSelectionDialog.mjs
Normal file
66
module/applications/costSelectionDialog.mjs
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
|
export default class CostSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor(costs, uses, action, resolve) {
|
||||||
|
super({});
|
||||||
|
this.costs = costs;
|
||||||
|
this.uses = uses;
|
||||||
|
this.action = action;
|
||||||
|
this.resolve = resolve;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
classes: ['daggerheart', 'views', 'damage-selection'],
|
||||||
|
position: {
|
||||||
|
width: 400,
|
||||||
|
height: 'auto'
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
sendCost: this.sendCost
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
handler: this.updateForm,
|
||||||
|
submitOnChange: true,
|
||||||
|
closeOnSubmit: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
costSelection: {
|
||||||
|
id: 'costSelection',
|
||||||
|
template: 'systems/daggerheart/templates/views/costSelection.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
get title() {
|
||||||
|
return `Cost Options`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const updatedCosts = this.action.calcCosts(this.costs),
|
||||||
|
updatedUses = this.action.calcUses(this.uses);
|
||||||
|
return {
|
||||||
|
costs: updatedCosts,
|
||||||
|
uses: updatedUses,
|
||||||
|
canUse: this.action.hasCost(updatedCosts) && this.action.hasUses(updatedUses)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateForm(event, _, formData) {
|
||||||
|
const data = foundry.utils.expandObject(formData.object);
|
||||||
|
this.costs = foundry.utils.mergeObject(this.costs, data.costs);
|
||||||
|
this.uses = foundry.utils.mergeObject(this.uses, data.uses);
|
||||||
|
this.render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static sendCost(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.resolve({ costs: this.action.getRealCosts(this.costs), uses: this.uses });
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
220
module/applications/damageReductionDialog.mjs
Normal file
220
module/applications/damageReductionDialog.mjs
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
import { damageKeyToNumber, getDamageLabel } from '../helpers/utils.mjs';
|
||||||
|
|
||||||
|
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
|
export default class DamageReductionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor(resolve, reject, actor, damage) {
|
||||||
|
super({});
|
||||||
|
|
||||||
|
this.resolve = resolve;
|
||||||
|
this.reject = reject;
|
||||||
|
this.actor = actor;
|
||||||
|
this.damage = damage;
|
||||||
|
|
||||||
|
const maxArmorMarks = Math.min(
|
||||||
|
actor.system.armorScore - actor.system.armor.system.marks.value,
|
||||||
|
actor.system.rules.maxArmorMarked.total
|
||||||
|
);
|
||||||
|
|
||||||
|
const armor = [...Array(maxArmorMarks).keys()].reduce((acc, _) => {
|
||||||
|
acc[foundry.utils.randomID()] = { selected: false };
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
const stress = [...Array(actor.system.rules.maxArmorMarked.stressExtra ?? 0).keys()].reduce((acc, _) => {
|
||||||
|
acc[foundry.utils.randomID()] = { selected: false };
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
this.marks = { armor, stress };
|
||||||
|
|
||||||
|
this.availableStressReductions = Object.keys(actor.system.rules.stressDamageReduction).reduce((acc, key) => {
|
||||||
|
const dr = actor.system.rules.stressDamageReduction[key];
|
||||||
|
if (dr.enabled) {
|
||||||
|
if (acc === null) acc = {};
|
||||||
|
|
||||||
|
const damage = damageKeyToNumber(key);
|
||||||
|
acc[damage] = {
|
||||||
|
cost: dr.cost,
|
||||||
|
selected: false,
|
||||||
|
from: getDamageLabel(damage),
|
||||||
|
to: getDamageLabel(damage - 1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return game.i18n.localize('DAGGERHEART.DamageReduction.Title');
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
classes: ['daggerheart', 'views', 'damage-reduction'],
|
||||||
|
position: {
|
||||||
|
width: 240,
|
||||||
|
height: 'auto'
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setMarks: this.setMarks,
|
||||||
|
useStressReduction: this.useStressReduction,
|
||||||
|
takeDamage: this.takeDamage
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
handler: this.updateData,
|
||||||
|
submitOnChange: true,
|
||||||
|
closeOnSubmit: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
damageSelection: {
|
||||||
|
id: 'damageReduction',
|
||||||
|
template: 'systems/daggerheart/templates/views/damageReduction.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
get title() {
|
||||||
|
return game.i18n.localize('DAGGERHEART.DamageReduction.Title');
|
||||||
|
}
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
|
||||||
|
const { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage } =
|
||||||
|
this.getDamageInfo();
|
||||||
|
|
||||||
|
context.armorScore = this.actor.system.armorScore;
|
||||||
|
context.armorMarks = currentMarks;
|
||||||
|
context.basicMarksUsed = selectedArmorMarks.length === this.actor.system.rules.maxArmorMarked.total;
|
||||||
|
|
||||||
|
const stressReductionStress = this.availableStressReductions
|
||||||
|
? stressReductions.reduce((acc, red) => acc + red.cost, 0)
|
||||||
|
: 0;
|
||||||
|
context.stress =
|
||||||
|
selectedStressMarks.length > 0 || this.availableStressReductions
|
||||||
|
? {
|
||||||
|
value:
|
||||||
|
this.actor.system.resources.stress.value + selectedStressMarks.length + stressReductionStress,
|
||||||
|
maxTotal: this.actor.system.resources.stress.maxTotal
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
|
context.marks = this.marks;
|
||||||
|
context.availableStressReductions = this.availableStressReductions;
|
||||||
|
|
||||||
|
context.damage = getDamageLabel(this.damage);
|
||||||
|
context.reducedDamage = currentDamage !== this.damage ? getDamageLabel(currentDamage) : null;
|
||||||
|
context.currentDamage = context.reducedDamage ?? context.damage;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static updateData(event, _, formData) {
|
||||||
|
const form = foundry.utils.expandObject(formData.object);
|
||||||
|
this.render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDamageInfo = () => {
|
||||||
|
const selectedArmorMarks = Object.values(this.marks.armor).filter(x => x.selected);
|
||||||
|
const selectedStressMarks = Object.values(this.marks.stress).filter(x => x.selected);
|
||||||
|
const stressReductions = Object.values(this.availableStressReductions).filter(red => red.selected);
|
||||||
|
const currentMarks =
|
||||||
|
this.actor.system.armor.system.marks.value + selectedArmorMarks.length + selectedStressMarks.length;
|
||||||
|
|
||||||
|
const currentDamage =
|
||||||
|
this.damage - selectedArmorMarks.length - selectedStressMarks.length - stressReductions.length;
|
||||||
|
|
||||||
|
return { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage };
|
||||||
|
};
|
||||||
|
|
||||||
|
static setMarks(_, target) {
|
||||||
|
const currentMark = this.marks[target.dataset.type][target.dataset.key];
|
||||||
|
const { selectedStressMarks, stressReductions, currentMarks, currentDamage } = this.getDamageInfo();
|
||||||
|
if (!currentMark.selected && currentDamage === 0) {
|
||||||
|
ui.notifications.info(game.i18n.localize('DAGGERHEART.DamageReduction.Notifications.DamageAlreadyNone'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentMark.selected && currentMarks === this.actor.system.armorScore) {
|
||||||
|
ui.notifications.info(
|
||||||
|
game.i18n.localize('DAGGERHEART.DamageReduction.Notifications.NoAvailableArmorMarks')
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMark.selected) {
|
||||||
|
const currentDamageLabel = getDamageLabel(currentDamage);
|
||||||
|
for (let reduction of stressReductions) {
|
||||||
|
if (reduction.selected && reduction.to === currentDamageLabel) {
|
||||||
|
reduction.selected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.dataset.type === 'armor' && selectedStressMarks.length > 0) {
|
||||||
|
selectedStressMarks.forEach(mark => (mark.selected = false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMark.selected = !currentMark.selected;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static useStressReduction(_, target) {
|
||||||
|
const damageValue = Number(target.dataset.reduction);
|
||||||
|
const stressReduction = this.availableStressReductions[damageValue];
|
||||||
|
const { currentDamage, selectedStressMarks, stressReductions } = this.getDamageInfo();
|
||||||
|
|
||||||
|
if (stressReduction.selected) {
|
||||||
|
stressReduction.selected = false;
|
||||||
|
|
||||||
|
const currentDamageLabel = getDamageLabel(currentDamage);
|
||||||
|
for (let reduction of stressReductions) {
|
||||||
|
if (reduction.selected && reduction.to === currentDamageLabel) {
|
||||||
|
reduction.selected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
} else {
|
||||||
|
const stressReductionStress = this.availableStressReductions
|
||||||
|
? stressReductions.reduce((acc, red) => acc + red.cost, 0)
|
||||||
|
: 0;
|
||||||
|
const currentStress =
|
||||||
|
this.actor.system.resources.stress.value + selectedStressMarks.length + stressReductionStress;
|
||||||
|
if (currentStress + stressReduction.cost > this.actor.system.resources.stress.maxTotal) {
|
||||||
|
ui.notifications.info(game.i18n.localize('DAGGERHEART.DamageReduction.Notifications.NotEnoughStress'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reducedDamage = currentDamage !== this.damage ? getDamageLabel(currentDamage) : null;
|
||||||
|
const currentDamageLabel = reducedDamage ?? getDamageLabel(this.damage);
|
||||||
|
|
||||||
|
if (stressReduction.from !== currentDamageLabel) return;
|
||||||
|
|
||||||
|
stressReduction.selected = true;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async takeDamage() {
|
||||||
|
const { selectedArmorMarks, selectedStressMarks, stressReductions, currentDamage } = this.getDamageInfo();
|
||||||
|
const armorSpent = selectedArmorMarks.length + selectedStressMarks.length;
|
||||||
|
const stressSpent = selectedStressMarks.length + stressReductions.reduce((acc, red) => acc + red.cost, 0);
|
||||||
|
|
||||||
|
this.resolve({ modifiedDamage: currentDamage, armorSpent, stressSpent });
|
||||||
|
await this.close(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async close(fromSave) {
|
||||||
|
if (!fromSave) {
|
||||||
|
this.reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
await super.close({});
|
||||||
|
}
|
||||||
|
}
|
||||||
403
module/applications/roll.mjs
Normal file
403
module/applications/roll.mjs
Normal file
|
|
@ -0,0 +1,403 @@
|
||||||
|
import D20RollDialog from '../dialogs/d20RollDialog.mjs';
|
||||||
|
import DamageDialog from '../dialogs/damageDialog.mjs';
|
||||||
|
|
||||||
|
/*
|
||||||
|
- Damage & other resources roll
|
||||||
|
- Close dialog => don't roll
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class DHRoll extends Roll {
|
||||||
|
constructor(formula, data, options) {
|
||||||
|
super(formula, data, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async build(config = {}, message = {}) {
|
||||||
|
const roll = await this.buildConfigure(config, message);
|
||||||
|
if (!roll) return;
|
||||||
|
await this.buildEvaluate(roll, config, (message = {}));
|
||||||
|
await this.buildPost(roll, config, (message = {}));
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async buildConfigure(config = {}, message = {}) {
|
||||||
|
config.hooks = [...(config.hooks ?? []), ''];
|
||||||
|
config.dialog ??= {};
|
||||||
|
for (const hook of config.hooks) {
|
||||||
|
if (Hooks.call(`${SYSTEM.id}.preRoll${hook.capitalize()}`, config, message) === false) return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.applyKeybindings(config);
|
||||||
|
|
||||||
|
if (config.dialog.configure !== false) {
|
||||||
|
// Open Roll Dialog
|
||||||
|
const DialogClass = config.dialog?.class ?? this.DefaultDialog;
|
||||||
|
config = await DialogClass.configure(config, message);
|
||||||
|
if (!config) return;
|
||||||
|
}
|
||||||
|
let roll = new this(config.formula, config.data, config);
|
||||||
|
|
||||||
|
for (const hook of config.hooks) {
|
||||||
|
if (Hooks.call(`${SYSTEM.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false)
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return roll;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||||
|
if (config.evaluate !== false) await roll.evaluate();
|
||||||
|
this.postEvaluate(roll, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async buildPost(roll, config, message) {
|
||||||
|
for (const hook of config.hooks) {
|
||||||
|
if (Hooks.call(`${SYSTEM.id}.postRoll${hook.capitalize()}`, config, message) === false) return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Chat Message
|
||||||
|
if (message.data) {
|
||||||
|
} else {
|
||||||
|
const messageData = {};
|
||||||
|
config.message = await this.toMessage(roll, config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async postEvaluate(roll, config = {}) {}
|
||||||
|
|
||||||
|
static async toMessage(roll, config) {
|
||||||
|
const cls = getDocumentClass('ChatMessage'),
|
||||||
|
msg = {
|
||||||
|
type: this.messageType,
|
||||||
|
user: game.user.id,
|
||||||
|
sound: config.mute ? null : CONFIG.sounds.dice,
|
||||||
|
system: config,
|
||||||
|
rolls: [roll]
|
||||||
|
};
|
||||||
|
return await cls.create(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static applyKeybindings(config) {
|
||||||
|
config.dialog.configure ??= true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DHopeDie
|
||||||
|
// DFearDie
|
||||||
|
// DualityDie
|
||||||
|
// D20Die
|
||||||
|
|
||||||
|
export class DualityDie extends foundry.dice.terms.Die {
|
||||||
|
constructor({ number = 1, faces = 12, ...args } = {}) {
|
||||||
|
super({ number, faces, ...args });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class D20Roll extends DHRoll {
|
||||||
|
constructor(formula, data = {}, options = {}) {
|
||||||
|
super(formula, data, options);
|
||||||
|
this.createBaseDice();
|
||||||
|
this.configureModifiers();
|
||||||
|
|
||||||
|
this._formula = this.resetFormula();
|
||||||
|
}
|
||||||
|
|
||||||
|
static ADV_MODE = {
|
||||||
|
NORMAL: 0,
|
||||||
|
ADVANTAGE: 1,
|
||||||
|
DISADVANTAGE: -1
|
||||||
|
};
|
||||||
|
|
||||||
|
static messageType = 'adversaryRoll';
|
||||||
|
|
||||||
|
static CRITICAL_TRESHOLD = 20;
|
||||||
|
|
||||||
|
static DefaultDialog = D20RollDialog;
|
||||||
|
|
||||||
|
get d20() {
|
||||||
|
if (!(this.terms[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
||||||
|
return this.terms[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
set d20(faces) {
|
||||||
|
if (!(this.terms[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
|
||||||
|
this.terms[0].faces = faces;
|
||||||
|
}
|
||||||
|
|
||||||
|
get dAdvantage() {
|
||||||
|
return this.dice[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCritical() {
|
||||||
|
if (!this.d20._evaluated) return;
|
||||||
|
return this.d20.total >= this.constructor.CRITICAL_TRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasAdvantage() {
|
||||||
|
return this.options.advantage === this.constructor.ADV_MODE.ADVANTAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasDisadvantage() {
|
||||||
|
return this.options.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
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
createBaseDice() {
|
||||||
|
if (this.terms[0] instanceof foundry.dice.terms.Die) return;
|
||||||
|
this.terms[0] = new foundry.dice.terms.Die({ faces: 20 });
|
||||||
|
}
|
||||||
|
|
||||||
|
applyAdvantage() {
|
||||||
|
this.d20.modifiers.findSplice(m => ['kh', 'kl'].includes(m));
|
||||||
|
if (!this.hasAdvantage && !this.hasAdvantage) this.number = 1;
|
||||||
|
else {
|
||||||
|
this.d20.number = 2;
|
||||||
|
this.d20.modifiers.push(this.hasAdvantage ? 'kh' : 'kl');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trait bonus != Adversary
|
||||||
|
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].description,
|
||||||
|
value: this.options.data.experiences[m].total
|
||||||
|
});
|
||||||
|
});
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
applyBaseBonus() {
|
||||||
|
if (this.options.type === 'attack')
|
||||||
|
this.terms.push(...this.formatModifier(this.options.data.attack.roll.bonus));
|
||||||
|
}
|
||||||
|
|
||||||
|
static async postEvaluate(roll, config = {}) {
|
||||||
|
if (config.targets?.length) {
|
||||||
|
config.targets.forEach(target => {
|
||||||
|
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;
|
||||||
|
config.roll.total = roll.total;
|
||||||
|
config.roll.formula = roll.formula;
|
||||||
|
config.roll.advantage = {
|
||||||
|
type: config.advantage,
|
||||||
|
dice: roll.dAdvantage?.denomination,
|
||||||
|
value: roll.dAdvantage?.total
|
||||||
|
};
|
||||||
|
config.roll.modifierTotal = config.roll.modifiers.reduce((a, c) => a + c.value, 0);
|
||||||
|
config.roll.dice = [];
|
||||||
|
roll.dice.forEach(d => {
|
||||||
|
config.roll.dice.push({
|
||||||
|
dice: d.denomination,
|
||||||
|
total: d.total,
|
||||||
|
formula: d.formula,
|
||||||
|
results: d.results
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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) })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
resetFormula() {
|
||||||
|
return (this._formula = this.constructor.getFormula(this.terms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DualityRoll extends D20Roll {
|
||||||
|
constructor(formula, data = {}, options = {}) {
|
||||||
|
super(formula, data, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static messageType = 'dualityRoll';
|
||||||
|
|
||||||
|
static DefaultDialog = D20RollDialog;
|
||||||
|
|
||||||
|
get dHope() {
|
||||||
|
// if ( !(this.terms[0] instanceof foundry.dice.terms.Die) ) return;
|
||||||
|
if (!(this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice();
|
||||||
|
return this.dice[0];
|
||||||
|
// return this.#hopeDice;
|
||||||
|
}
|
||||||
|
|
||||||
|
set dHope(faces) {
|
||||||
|
if (!(this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice();
|
||||||
|
this.terms[0].faces = faces;
|
||||||
|
// this.#hopeDice = `d${face}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get dFear() {
|
||||||
|
// if ( !(this.terms[1] instanceof foundry.dice.terms.Die) ) return;
|
||||||
|
if (!(this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice();
|
||||||
|
return this.dice[1];
|
||||||
|
// return this.#fearDice;
|
||||||
|
}
|
||||||
|
|
||||||
|
set dFear(faces) {
|
||||||
|
if (!(this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice();
|
||||||
|
this.dice[1].faces = faces;
|
||||||
|
// this.#fearDice = `d${face}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get dAdvantage() {
|
||||||
|
return this.dice[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCritical() {
|
||||||
|
if (!this.dHope._evaluated || !this.dFear._evaluated) return;
|
||||||
|
return this.dHope.total === this.dFear.total;
|
||||||
|
}
|
||||||
|
|
||||||
|
get withHope() {
|
||||||
|
if (!this._evaluated) return;
|
||||||
|
return this.dHope.total > this.dFear.total;
|
||||||
|
}
|
||||||
|
|
||||||
|
get withFear() {
|
||||||
|
if (!this._evaluated) return;
|
||||||
|
return this.dHope.total < this.dFear.total;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasBarRally() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalLabel() {
|
||||||
|
const label = this.withHope
|
||||||
|
? 'DAGGERHEART.General.Hope'
|
||||||
|
: this.withFear
|
||||||
|
? 'DAGGERHEART.General.Fear'
|
||||||
|
: 'DAGGERHEART.General.CriticalSuccess';
|
||||||
|
|
||||||
|
return game.i18n.localize(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
createBaseDice() {
|
||||||
|
if (
|
||||||
|
this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie &&
|
||||||
|
this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
if (!(this.dice[0] instanceof 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
applyAdvantage() {
|
||||||
|
const dieFaces = 6,
|
||||||
|
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: '+' }));
|
||||||
|
if (bardRallyFaces) {
|
||||||
|
const rallyDie = new foundry.dice.terms.Die({ faces: bardRallyFaces });
|
||||||
|
if (this.hasAdvantage) {
|
||||||
|
this.terms.push(
|
||||||
|
new foundry.dice.terms.PoolTerm({
|
||||||
|
terms: [advDie.formula, rallyDie.formula],
|
||||||
|
modifiers: ['kh']
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (this.hasDisadvantage) {
|
||||||
|
this.terms.push(advDie, new foundry.dice.terms.OperatorTerm({ operator: '+' }), rallyDie);
|
||||||
|
}
|
||||||
|
} else if (this.hasAdvantage || this.hasDisadvantage) this.terms.push(advDie);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyBaseBonus() {
|
||||||
|
if (!this.options.roll.modifiers) this.options.roll.modifiers = [];
|
||||||
|
if (this.options.roll?.trait)
|
||||||
|
this.options.roll.modifiers.push({
|
||||||
|
label: `DAGGERHEART.Abilities.${this.options.roll.trait}.name`,
|
||||||
|
value: this.options.data.traits[this.options.roll.trait].total
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async postEvaluate(roll, config = {}) {
|
||||||
|
super.postEvaluate(roll, config);
|
||||||
|
config.roll.hope = {
|
||||||
|
dice: roll.dHope.denomination,
|
||||||
|
value: roll.dHope.total
|
||||||
|
};
|
||||||
|
config.roll.fear = {
|
||||||
|
dice: roll.dFear.denomination,
|
||||||
|
value: roll.dFear.total
|
||||||
|
};
|
||||||
|
config.roll.result = {
|
||||||
|
duality: roll.withHope ? 1 : roll.withFear ? -1 : 0,
|
||||||
|
total: roll.dHope.total + roll.dFear.total,
|
||||||
|
label: roll.totalLabel
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DamageRoll extends DHRoll {
|
||||||
|
constructor(formula, data = {}, options = {}) {
|
||||||
|
super(formula, data, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static messageType = 'damageRoll';
|
||||||
|
|
||||||
|
static DefaultDialog = DamageDialog;
|
||||||
|
|
||||||
|
static async postEvaluate(roll, config = {}) {
|
||||||
|
config.roll = {
|
||||||
|
total: roll.total,
|
||||||
|
formula: roll.formula,
|
||||||
|
type: config.type
|
||||||
|
};
|
||||||
|
config.roll.dice = [];
|
||||||
|
roll.dice.forEach(d => {
|
||||||
|
config.roll.dice.push({
|
||||||
|
dice: d.denomination,
|
||||||
|
total: d.total,
|
||||||
|
formula: d.formula,
|
||||||
|
results: d.results
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
export default class RollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
export default class RollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
constructor(experiences, hopeResource, resolve) {
|
constructor(experiences, costs, action, resolve) {
|
||||||
super({}, {});
|
super({}, {});
|
||||||
|
|
||||||
this.experiences = experiences;
|
this.experiences = experiences;
|
||||||
|
this.costs = costs;
|
||||||
|
this.action = action;
|
||||||
this.resolve = resolve;
|
this.resolve = resolve;
|
||||||
this.isNpc;
|
this.isNpc;
|
||||||
this.selectedExperiences = [];
|
this.selectedExperiences = [];
|
||||||
|
|
@ -15,8 +17,7 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
],
|
],
|
||||||
hope: ['d12'],
|
hope: ['d12'],
|
||||||
fear: ['d12'],
|
fear: ['d12'],
|
||||||
advantage: null,
|
advantage: null
|
||||||
hopeResource: hopeResource
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,6 +43,10 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
|
costSelection: {
|
||||||
|
id: 'costSelection',
|
||||||
|
template: 'systems/daggerheart/templates/views/costSelection.hbs'
|
||||||
|
},
|
||||||
damageSelection: {
|
damageSelection: {
|
||||||
id: 'damageSelection',
|
id: 'damageSelection',
|
||||||
template: 'systems/daggerheart/templates/views/rollSelection.hbs'
|
template: 'systems/daggerheart/templates/views/rollSelection.hbs'
|
||||||
|
|
@ -60,15 +65,19 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
context.fear = this.data.fear;
|
context.fear = this.data.fear;
|
||||||
context.advantage = this.data.advantage;
|
context.advantage = this.data.advantage;
|
||||||
context.experiences = Object.keys(this.experiences).map(id => ({ id, ...this.experiences[id] }));
|
context.experiences = Object.keys(this.experiences).map(id => ({ id, ...this.experiences[id] }));
|
||||||
context.hopeResource = this.data.hopeResource + 1;
|
if(this.costs?.length) {
|
||||||
|
const updatedCosts = this.action.calcCosts(this.costs);
|
||||||
|
context.costs = updatedCosts
|
||||||
|
context.canRoll = this.action.getRealCosts(updatedCosts)?.hasCost;
|
||||||
|
} else context.canRoll = true;
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateSelection(event, _, formData) {
|
static updateSelection(event, _, formData) {
|
||||||
const { ...rest } = foundry.utils.expandObject(formData.object);
|
const { ...rest } = foundry.utils.expandObject(formData.object);
|
||||||
|
|
||||||
this.data = foundry.utils.mergeObject(this.data, rest);
|
this.data = foundry.utils.mergeObject(this.data, rest);
|
||||||
|
this.costs = foundry.utils.mergeObject(this.costs, rest.costs);
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,10 +99,10 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
|
|
||||||
static async finish() {
|
static async finish() {
|
||||||
const { diceOptions, ...rest } = this.data;
|
const { diceOptions, ...rest } = this.data;
|
||||||
|
|
||||||
this.resolve({
|
this.resolve({
|
||||||
...rest,
|
...rest,
|
||||||
experiences: this.selectedExperiences.map(x => ({ id: x, ...this.experiences[x] }))
|
experiences: this.selectedExperiences.map(x => ({ id: x, ...this.experiences[x] })),
|
||||||
|
costs: this.action.getRealCosts(this.costs)
|
||||||
});
|
});
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import DHActionConfig from '../config/Action.mjs';
|
||||||
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
||||||
|
|
||||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||||
|
|
@ -9,6 +10,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
actions: {
|
actions: {
|
||||||
reactionRoll: this.reactionRoll,
|
reactionRoll: this.reactionRoll,
|
||||||
attackRoll: this.attackRoll,
|
attackRoll: this.attackRoll,
|
||||||
|
attackConfigure: this.attackConfigure,
|
||||||
addExperience: this.addExperience,
|
addExperience: this.addExperience,
|
||||||
removeExperience: this.removeExperience,
|
removeExperience: this.removeExperience,
|
||||||
toggleHP: this.toggleHP,
|
toggleHP: this.toggleHP,
|
||||||
|
|
@ -51,7 +53,10 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
context.document = this.document;
|
context.document = this.document;
|
||||||
context.tabs = super._getTabs(this.constructor.TABS);
|
context.tabs = super._getTabs(this.constructor.TABS);
|
||||||
|
context.systemFields.attack.fields = this.document.system.attack.schema.fields;
|
||||||
|
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||||
|
context.isNPC = true;
|
||||||
|
console.log(context)
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,26 +82,16 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
this.actor.diceRoll(config);
|
this.actor.diceRoll(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEffectDetails(id) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
static async attackRoll(event) {
|
static async attackRoll(event) {
|
||||||
const { modifier, damage, name: attackName } = this.actor.system.attack,
|
this.actor.system.attack.use(event);
|
||||||
config = {
|
}
|
||||||
event: event,
|
|
||||||
title: attackName,
|
static async attackConfigure(event) {
|
||||||
roll: {
|
await new DHActionConfig(this.document.system.attack).render(true);
|
||||||
modifier: modifier,
|
|
||||||
type: 'action'
|
|
||||||
},
|
|
||||||
chatMessage: {
|
|
||||||
type: 'adversaryRoll',
|
|
||||||
template: 'systems/daggerheart/templates/chat/adversary-attack-roll.hbs'
|
|
||||||
},
|
|
||||||
damage: {
|
|
||||||
value: damage.value,
|
|
||||||
type: damage.type
|
|
||||||
},
|
|
||||||
checkTarget: true
|
|
||||||
};
|
|
||||||
this.actor.diceRoll(config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async addExperience() {
|
static async addExperience() {
|
||||||
|
|
|
||||||
|
|
@ -139,9 +139,10 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
*/
|
*/
|
||||||
static async #removeAction(event, button) {
|
static async #removeAction(event, button) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
const actionIndex = button.closest('[data-index]').dataset.index;
|
||||||
await this.document.update({
|
await this.document.update({
|
||||||
'system.actions': this.document.system.actions.filter(
|
'system.actions': this.document.system.actions.filter(
|
||||||
(_, index) => index !== Number.parseInt(button.dataset.index)
|
(_, index) => index !== Number.parseInt(actionIndex)
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
toggleLoadoutView: this.toggleLoadoutView,
|
toggleLoadoutView: this.toggleLoadoutView,
|
||||||
attackRoll: this.attackRoll,
|
attackRoll: this.attackRoll,
|
||||||
useDomainCard: this.useDomainCard,
|
useDomainCard: this.useDomainCard,
|
||||||
removeCard: this.removeDomainCard,
|
|
||||||
selectClass: this.selectClass,
|
selectClass: this.selectClass,
|
||||||
selectSubclass: this.selectSubclass,
|
selectSubclass: this.selectSubclass,
|
||||||
selectAncestry: this.selectAncestry,
|
selectAncestry: this.selectAncestry,
|
||||||
|
|
@ -50,7 +49,8 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
toggleEquipItem: this.toggleEquipItem,
|
toggleEquipItem: this.toggleEquipItem,
|
||||||
toggleVault: this.toggleVault,
|
toggleVault: this.toggleVault,
|
||||||
levelManagement: this.levelManagement,
|
levelManagement: this.levelManagement,
|
||||||
editImage: this._onEditImage
|
editImage: this._onEditImage,
|
||||||
|
triggerContextMenu: this.triggerContextMenu
|
||||||
},
|
},
|
||||||
window: {
|
window: {
|
||||||
resizable: true
|
resizable: true
|
||||||
|
|
@ -223,14 +223,18 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
useItem: {
|
useItem: {
|
||||||
name: 'DAGGERHEART.Sheets.PC.ContextMenu.UseItem',
|
name: 'DAGGERHEART.Sheets.PC.ContextMenu.UseItem',
|
||||||
icon: '<i class="fa-solid fa-burst"></i>',
|
icon: '<i class="fa-solid fa-burst"></i>',
|
||||||
callback: (element, event) => this.constructor.useItem.bind(this)(event, element)
|
condition: el => {
|
||||||
|
const item = this.getItem(el);
|
||||||
|
return !['class', 'subclass'].includes(item.type);
|
||||||
|
},
|
||||||
|
callback: (button, event) => this.constructor.useItem.bind(this)(event, button)
|
||||||
},
|
},
|
||||||
equip: {
|
equip: {
|
||||||
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Equip',
|
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Equip',
|
||||||
icon: '<i class="fa-solid fa-hands"></i>',
|
icon: '<i class="fa-solid fa-hands"></i>',
|
||||||
condition: el => {
|
condition: el => {
|
||||||
const item = foundry.utils.fromUuidSync(el.dataset.uuid);
|
const item = this.getItem(el);
|
||||||
return !item.system.equipped;
|
return ['weapon', 'armor'].includes(item.type) && !item.system.equipped;
|
||||||
},
|
},
|
||||||
callback: this.constructor.toggleEquipItem.bind(this)
|
callback: this.constructor.toggleEquipItem.bind(this)
|
||||||
},
|
},
|
||||||
|
|
@ -238,11 +242,34 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Unequip',
|
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Unequip',
|
||||||
icon: '<i class="fa-solid fa-hands"></i>',
|
icon: '<i class="fa-solid fa-hands"></i>',
|
||||||
condition: el => {
|
condition: el => {
|
||||||
const item = foundry.utils.fromUuidSync(el.dataset.uuid);
|
const item = this.getItem(el);
|
||||||
return item.system.equipped;
|
return ['weapon', 'armor'].includes(item.type) && item.system.equipped;
|
||||||
},
|
},
|
||||||
callback: this.constructor.toggleEquipItem.bind(this)
|
callback: this.constructor.toggleEquipItem.bind(this)
|
||||||
},
|
},
|
||||||
|
sendToLoadout: {
|
||||||
|
name: 'DAGGERHEART.Sheets.PC.ContextMenu.ToLoadout',
|
||||||
|
icon: '<i class="fa-solid fa-arrow-up"></i>',
|
||||||
|
condition: el => {
|
||||||
|
const item = this.getItem(el);
|
||||||
|
return ['domainCard'].includes(item.type) && item.system.inVault;
|
||||||
|
},
|
||||||
|
callback: this.constructor.toggleVault.bind(this)
|
||||||
|
},
|
||||||
|
sendToVault: {
|
||||||
|
name: 'DAGGERHEART.Sheets.PC.ContextMenu.ToVault',
|
||||||
|
icon: '<i class="fa-solid fa-arrow-down"></i>',
|
||||||
|
condition: el => {
|
||||||
|
const item = this.getItem(el);
|
||||||
|
return ['domainCard'].includes(item.type) && !item.system.inVault;
|
||||||
|
},
|
||||||
|
callback: this.constructor.toggleVault.bind(this)
|
||||||
|
},
|
||||||
|
sendToChat: {
|
||||||
|
name: 'DAGGERHEART.Sheets.PC.ContextMenu.SendToChat',
|
||||||
|
icon: '<i class="fa-regular fa-message"></i>',
|
||||||
|
callback: this.constructor.toChat.bind(this)
|
||||||
|
},
|
||||||
edit: {
|
edit: {
|
||||||
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Edit',
|
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Edit',
|
||||||
icon: '<i class="fa-solid fa-pen-to-square"></i>',
|
icon: '<i class="fa-solid fa-pen-to-square"></i>',
|
||||||
|
|
@ -252,66 +279,12 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Delete',
|
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Delete',
|
||||||
icon: '<i class="fa-solid fa-trash"></i>',
|
icon: '<i class="fa-solid fa-trash"></i>',
|
||||||
callback: this.constructor.deleteItem.bind(this)
|
callback: this.constructor.deleteItem.bind(this)
|
||||||
},
|
|
||||||
sendToLoadout: {
|
|
||||||
name: 'DAGGERHEART.Sheets.PC.ContextMenu.ToLoadout',
|
|
||||||
icon: '<i class="fa-solid fa-arrow-up"></i>',
|
|
||||||
condition: el => {
|
|
||||||
const item = foundry.utils.fromUuidSync(el.dataset.uuid);
|
|
||||||
return item.system.inVault;
|
|
||||||
},
|
|
||||||
callback: this.constructor.toggleVault.bind(this)
|
|
||||||
},
|
|
||||||
sendToVault: {
|
|
||||||
name: 'DAGGERHEART.Sheets.PC.ContextMenu.ToVault',
|
|
||||||
icon: '<i class="fa-solid fa-arrow-down"></i>',
|
|
||||||
condition: el => {
|
|
||||||
const item = foundry.utils.fromUuidSync(el.dataset.uuid);
|
|
||||||
return !item.system.inVault;
|
|
||||||
},
|
|
||||||
callback: this.constructor.toggleVault.bind(this)
|
|
||||||
},
|
|
||||||
sendToChat: {
|
|
||||||
name: 'DAGGERHEART.Sheets.PC.ContextMenu.SendToChat',
|
|
||||||
icon: '<i class="fa-regular fa-message"></i>',
|
|
||||||
callback: this.constructor.toChat.bind(this)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMenuOptions = type => () => {
|
this._createContextMenu(() => Object.values(allOptions), `[data-item-id]`, {
|
||||||
let menuItems = ['class', 'subclass'].includes(type) ? [] : [allOptions.useItem];
|
parentClassHooks: false,
|
||||||
switch (type) {
|
fixed: true
|
||||||
case 'weapon':
|
|
||||||
case 'armor':
|
|
||||||
menuItems.push(...[allOptions.equip, allOptions.unequip]);
|
|
||||||
break;
|
|
||||||
case 'domainCard':
|
|
||||||
menuItems.push(...[allOptions.sendToLoadout, allOptions.sendToVault]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
menuItems.push(...[allOptions.sendToChat, allOptions.edit, allOptions.delete]);
|
|
||||||
|
|
||||||
return menuItems;
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuConfigs = [
|
|
||||||
'armor',
|
|
||||||
'weapon',
|
|
||||||
'miscellaneous',
|
|
||||||
'consumable',
|
|
||||||
'domainCard',
|
|
||||||
'miscellaneous',
|
|
||||||
'ancestry',
|
|
||||||
'community',
|
|
||||||
'class',
|
|
||||||
'subclass'
|
|
||||||
];
|
|
||||||
menuConfigs.forEach(type => {
|
|
||||||
this._createContextMenu(getMenuOptions(type), `.${type}-context-menu`, {
|
|
||||||
eventName: 'click',
|
|
||||||
parentClassHooks: false,
|
|
||||||
fixed: true
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -319,10 +292,16 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
super._attachPartListeners(partId, htmlElement, options);
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
htmlElement.querySelector('.level-value')?.addEventListener('change', this.onLevelChange.bind(this));
|
htmlElement.querySelector('.level-value')?.addEventListener('change', this.onLevelChange.bind(this));
|
||||||
// To Remove when ContextMenu Handler is made
|
}
|
||||||
htmlElement
|
|
||||||
.querySelectorAll('[data-item-id]')
|
getItem(element) {
|
||||||
.forEach(element => element.addEventListener('contextmenu', this.editItem.bind(this)));
|
const itemId = (element.target ?? element).closest('[data-item-id]').dataset.itemId,
|
||||||
|
item = this.document.items.get(itemId);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static triggerContextMenu(event, button) {
|
||||||
|
return CONFIG.ux.ContextMenu.triggerContextMenu(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static _onEditImage() {
|
static _onEditImage() {
|
||||||
|
|
@ -469,51 +448,12 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
const abilityLabel = game.i18n.localize(abilities[button.dataset.attribute].label);
|
const abilityLabel = game.i18n.localize(abilities[button.dataset.attribute].label);
|
||||||
const config = {
|
const config = {
|
||||||
event: event,
|
event: event,
|
||||||
title: game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', {
|
title: game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', { ability: abilityLabel }),
|
||||||
ability: abilityLabel
|
|
||||||
}),
|
|
||||||
roll: {
|
roll: {
|
||||||
label: abilityLabel,
|
trait: button.dataset.attribute
|
||||||
modifier: button.dataset.value
|
|
||||||
},
|
|
||||||
chatMessage: {
|
|
||||||
template: 'systems/daggerheart/templates/chat/duality-roll.hbs'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.document.diceRoll(config);
|
this.document.diceRoll(config);
|
||||||
|
|
||||||
// Delete when new roll logic test done
|
|
||||||
/* const { roll, hope, fear, advantage, disadvantage, modifiers } = await this.document.dualityRoll(
|
|
||||||
{ title: game.i18n.localize(abilities[button.dataset.attribute].label), value: button.dataset.value },
|
|
||||||
event.shiftKey
|
|
||||||
);
|
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
|
||||||
|
|
||||||
const systemContent = new DHDualityRoll({
|
|
||||||
title: game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', {
|
|
||||||
ability: game.i18n.localize(abilities[button.dataset.attribute].label)
|
|
||||||
}),
|
|
||||||
origin: this.document.id,
|
|
||||||
roll: roll._formula,
|
|
||||||
modifiers: modifiers,
|
|
||||||
hope: hope,
|
|
||||||
fear: fear,
|
|
||||||
advantage: advantage,
|
|
||||||
disadvantage: disadvantage
|
|
||||||
});
|
|
||||||
|
|
||||||
await cls.create({
|
|
||||||
type: 'dualityRoll',
|
|
||||||
sound: CONFIG.sounds.dice,
|
|
||||||
system: systemContent,
|
|
||||||
user: game.user.id,
|
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
|
||||||
'systems/daggerheart/templates/chat/duality-roll.hbs',
|
|
||||||
systemContent
|
|
||||||
),
|
|
||||||
rolls: [roll]
|
|
||||||
}); */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async toggleMarks(_, button) {
|
static async toggleMarks(_, button) {
|
||||||
|
|
@ -592,8 +532,9 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
new DhlevelUp(this.document).render(true);
|
new DhlevelUp(this.document).render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async useDomainCard(_, button) {
|
static async useDomainCard(event, button) {
|
||||||
const card = this.document.items.find(x => x.uuid === button.dataset.key);
|
const card = this.getItem(event);
|
||||||
|
if (!card) return;
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
const systemData = {
|
const systemData = {
|
||||||
|
|
@ -617,13 +558,6 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
cls.create(msg.toObject());
|
cls.create(msg.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
static async removeDomainCard(_, button) {
|
|
||||||
if (button.dataset.type === 'domainCard') {
|
|
||||||
const card = this.document.items.find(x => x.uuid === button.dataset.key);
|
|
||||||
await card.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async selectClass() {
|
static async selectClass() {
|
||||||
(await game.packs.get('daggerheart.classes'))?.render(true);
|
(await game.packs.get('daggerheart.classes'))?.render(true);
|
||||||
}
|
}
|
||||||
|
|
@ -659,23 +593,22 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async useItem(event, button) {
|
static async useItem(event, button) {
|
||||||
const id = button.closest('a').id,
|
const item = this.getItem(button);
|
||||||
item = this.document.items.get(id);
|
if (!item) return;
|
||||||
const wasUsed = await item.use(event);
|
const wasUsed = await item.use(event);
|
||||||
if (wasUsed && item.type === 'weapon') {
|
if (wasUsed && item.type === 'weapon') {
|
||||||
Hooks.callAll(SYSTEM.HOOKS.characterAttack, {});
|
Hooks.callAll(SYSTEM.HOOKS.characterAttack, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async viewObject(element, button) {
|
static async viewObject(event, button) {
|
||||||
const object = await fromUuid((button ?? element).dataset.uuid);
|
const item = this.getItem(event);
|
||||||
|
if (!item) return;
|
||||||
object.sheet.render(true);
|
item.sheet.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
editItem(event) {
|
editItem(event) {
|
||||||
const uuid = event.target.closest('[data-item-id]').dataset.itemId,
|
const item = this.getItem(event);
|
||||||
item = this.document.items.find(i => i.uuid === uuid);
|
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
if (item.sheet.editMode) item.sheet.editMode = false;
|
if (item.sheet.editMode) item.sheet.editMode = false;
|
||||||
|
|
@ -719,29 +652,26 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async itemUpdate(event) {
|
|
||||||
const name = event.currentTarget.dataset.item;
|
|
||||||
const item = await fromUuid($(event.currentTarget).closest('[data-item-id]')[0].dataset.itemId);
|
|
||||||
await item.update({ [name]: event.currentTarget.value });
|
|
||||||
}
|
|
||||||
|
|
||||||
async onLevelChange(event) {
|
async onLevelChange(event) {
|
||||||
await this.document.updateLevel(Number(event.currentTarget.value));
|
await this.document.updateLevel(Number(event.currentTarget.value));
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteItem(element, button) {
|
static async deleteItem(event, button) {
|
||||||
const item = await fromUuid((button ?? element).closest('a').dataset.uuid);
|
const item = this.getItem(event);
|
||||||
|
if (!item) return;
|
||||||
await item.delete();
|
await item.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async setItemQuantity(button, value) {
|
static async setItemQuantity(button, value) {
|
||||||
const item = await fromUuid($(button).closest('[data-item-id]')[0].dataset.itemId);
|
const item = this.getItem(button);
|
||||||
|
if (!item) return;
|
||||||
await item.update({ 'system.quantity': Math.max(item.system.quantity + value, 1) });
|
await item.update({ 'system.quantity': Math.max(item.system.quantity + value, 1) });
|
||||||
}
|
}
|
||||||
|
|
||||||
static async useFeature(_, button) {
|
static async useFeature(event, button) {
|
||||||
const item = await fromUuid(button.dataset.id);
|
const item = this.getItem(event);
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
const systemData = {
|
const systemData = {
|
||||||
|
|
@ -765,7 +695,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
cls.create(msg.toObject());
|
cls.create(msg.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
static async toChat(element, button) {
|
static async toChat(event, button) {
|
||||||
if (button?.dataset?.type === 'experience') {
|
if (button?.dataset?.type === 'experience') {
|
||||||
const experience = this.document.system.experiences[button.dataset.uuid];
|
const experience = this.document.system.experiences[button.dataset.uuid];
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
|
@ -787,7 +717,8 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
|
|
||||||
cls.create(msg.toObject());
|
cls.create(msg.toObject());
|
||||||
} else {
|
} else {
|
||||||
const item = await fromUuid((button ?? element).dataset.uuid);
|
const item = this.getItem(event);
|
||||||
|
if (!item) return;
|
||||||
item.toChat(this.document.id);
|
item.toChat(this.document.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -846,9 +777,9 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
cls.create(msg.toObject());
|
cls.create(msg.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
static async toggleEquipItem(element, button) {
|
static async toggleEquipItem(event, button) {
|
||||||
const id = (button ?? element).closest('a').id;
|
const item = this.getItem(event);
|
||||||
const item = this.document.items.get(id);
|
if (!item) return;
|
||||||
if (item.system.equipped) {
|
if (item.system.equipped) {
|
||||||
await item.update({ 'system.equipped': false });
|
await item.update({ 'system.equipped': false });
|
||||||
return;
|
return;
|
||||||
|
|
@ -872,9 +803,9 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async toggleVault(element, button) {
|
static async toggleVault(event, button) {
|
||||||
const id = (button ?? element).closest('a').id;
|
const item = this.getItem(event);
|
||||||
const item = this.document.items.get(id);
|
if (!item) return;
|
||||||
await item.update({ 'system.inVault': !item.system.inVault });
|
await item.update({ 'system.inVault': !item.system.inVault });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
context.document = this.document;
|
context.document = this.document;
|
||||||
context.tabs = super._getTabs(this.constructor.TABS);
|
context.tabs = super._getTabs(this.constructor.TABS);
|
||||||
|
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
@ -62,6 +63,10 @@ export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) {
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEffectDetails(id) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
static async addAdversary() {
|
static async addAdversary() {
|
||||||
await this.document.update({
|
await this.document.update({
|
||||||
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
|
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,21 @@ export const actionTypes = {
|
||||||
name: 'DAGGERHEART.Actions.Types.Attack.Name',
|
name: 'DAGGERHEART.Actions.Types.Attack.Name',
|
||||||
icon: 'fa-swords'
|
icon: 'fa-swords'
|
||||||
},
|
},
|
||||||
spellcast: {
|
// spellcast: {
|
||||||
id: 'spellcast',
|
// id: 'spellcast',
|
||||||
name: 'DAGGERHEART.Actions.Types.Spellcast.Name',
|
// name: 'DAGGERHEART.Actions.Types.Spellcast.Name',
|
||||||
icon: 'fa-book-sparkles'
|
// icon: 'fa-book-sparkles'
|
||||||
},
|
// },
|
||||||
healing: {
|
healing: {
|
||||||
id: 'healing',
|
id: 'healing',
|
||||||
name: 'DAGGERHEART.Actions.Types.Healing.Name',
|
name: 'DAGGERHEART.Actions.Types.Healing.Name',
|
||||||
icon: 'fa-kit-medical'
|
icon: 'fa-kit-medical'
|
||||||
},
|
},
|
||||||
resource: {
|
// resource: {
|
||||||
id: 'resource',
|
// id: 'resource',
|
||||||
name: 'DAGGERHEART.Actions.Types.Resource.Name',
|
// name: 'DAGGERHEART.Actions.Types.Resource.Name',
|
||||||
icon: 'fa-honey-pot'
|
// icon: 'fa-honey-pot'
|
||||||
},
|
// },
|
||||||
damage: {
|
damage: {
|
||||||
id: 'damage',
|
id: 'damage',
|
||||||
name: 'DAGGERHEART.Actions.Types.Damage.Name',
|
name: 'DAGGERHEART.Actions.Types.Damage.Name',
|
||||||
|
|
@ -46,8 +46,34 @@ export const targetTypes = {
|
||||||
id: 'self',
|
id: 'self',
|
||||||
label: 'Self'
|
label: 'Self'
|
||||||
},
|
},
|
||||||
other: {
|
friendly: {
|
||||||
id: 'other',
|
id: 'friendly',
|
||||||
label: 'Other'
|
label: 'Friendly'
|
||||||
|
},
|
||||||
|
hostile: {
|
||||||
|
id: 'hostile',
|
||||||
|
label: 'Hostile'
|
||||||
|
},
|
||||||
|
any: {
|
||||||
|
id: 'any',
|
||||||
|
label: 'Any'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const damageOnSave = {
|
||||||
|
none: {
|
||||||
|
id: 'none',
|
||||||
|
label: 'None',
|
||||||
|
mod: 0
|
||||||
|
},
|
||||||
|
half: {
|
||||||
|
id: 'half',
|
||||||
|
label: 'Half Damage',
|
||||||
|
mod: 0.5
|
||||||
|
},
|
||||||
|
full: {
|
||||||
|
id: 'full',
|
||||||
|
label: 'Full damage',
|
||||||
|
mod: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,8 @@ export const damageTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const healingTypes = {
|
export const healingTypes = {
|
||||||
health: {
|
hitPoints: {
|
||||||
id: 'health',
|
id: 'hitPoints',
|
||||||
label: 'DAGGERHEART.HealingType.HitPoints.Name',
|
label: 'DAGGERHEART.HealingType.HitPoints.Name',
|
||||||
abbreviation: 'DAGGERHEART.HealingType.HitPoints.Abbreviation'
|
abbreviation: 'DAGGERHEART.HealingType.HitPoints.Abbreviation'
|
||||||
},
|
},
|
||||||
|
|
@ -77,6 +77,16 @@ export const healingTypes = {
|
||||||
id: 'stress',
|
id: 'stress',
|
||||||
label: 'DAGGERHEART.HealingType.Stress.Name',
|
label: 'DAGGERHEART.HealingType.Stress.Name',
|
||||||
abbreviation: 'DAGGERHEART.HealingType.Stress.Abbreviation'
|
abbreviation: 'DAGGERHEART.HealingType.Stress.Abbreviation'
|
||||||
|
},
|
||||||
|
hope: {
|
||||||
|
id: 'hope',
|
||||||
|
label: 'DAGGERHEART.HealingType.Hope.Name',
|
||||||
|
abbreviation: 'DAGGERHEART.HealingType.Hope.Abbreviation'
|
||||||
|
},
|
||||||
|
armorStack: {
|
||||||
|
id: 'armorStack',
|
||||||
|
label: 'DAGGERHEART.HealingType.ArmorStack.Name',
|
||||||
|
abbreviation: 'DAGGERHEART.HealingType.ArmorStack.Abbreviation'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -290,13 +300,15 @@ export const diceTypes = {
|
||||||
d4: 'd4',
|
d4: 'd4',
|
||||||
d6: 'd6',
|
d6: 'd6',
|
||||||
d8: 'd8',
|
d8: 'd8',
|
||||||
|
d10: 'd10',
|
||||||
d12: 'd12',
|
d12: 'd12',
|
||||||
d20: 'd20'
|
d20: 'd20'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const multiplierTypes = {
|
export const multiplierTypes = {
|
||||||
proficiency: 'Proficiency',
|
proficiency: 'Proficiency',
|
||||||
spellcast: 'Spellcast'
|
spellcast: 'Spellcast',
|
||||||
|
flat: 'Flat'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDiceSoNicePresets = () => {
|
export const getDiceSoNicePresets = () => {
|
||||||
|
|
@ -360,7 +372,35 @@ export const abilityCosts = {
|
||||||
},
|
},
|
||||||
stress: {
|
stress: {
|
||||||
id: 'stress',
|
id: 'stress',
|
||||||
label: 'Stress'
|
label: 'DAGGERHEART.HealingType.Stress.Name'
|
||||||
|
},
|
||||||
|
armor: {
|
||||||
|
id: 'armor',
|
||||||
|
label: 'Armor Stack'
|
||||||
|
},
|
||||||
|
hp: {
|
||||||
|
id: 'hp',
|
||||||
|
label: 'DAGGERHEART.HealingType.HitPoints.Name'
|
||||||
|
},
|
||||||
|
prayer: {
|
||||||
|
id: 'prayer',
|
||||||
|
label: 'Prayer Dice'
|
||||||
|
},
|
||||||
|
favor: {
|
||||||
|
id: 'favor',
|
||||||
|
label: 'Favor Points'
|
||||||
|
},
|
||||||
|
slayer: {
|
||||||
|
id: 'slayer',
|
||||||
|
label: 'Slayer Dice'
|
||||||
|
},
|
||||||
|
tide: {
|
||||||
|
id: 'tide',
|
||||||
|
label: 'Tide'
|
||||||
|
},
|
||||||
|
chaos: {
|
||||||
|
id: 'chaos',
|
||||||
|
label: 'Chaos'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,16 @@ import {
|
||||||
DHEffectAction,
|
DHEffectAction,
|
||||||
DHHealingAction,
|
DHHealingAction,
|
||||||
DHMacroAction,
|
DHMacroAction,
|
||||||
DHResourceAction,
|
// DHResourceAction,
|
||||||
DHSpellCastAction,
|
// DHSpellCastAction,
|
||||||
DHSummonAction
|
DHSummonAction
|
||||||
} from './action.mjs';
|
} from './action.mjs';
|
||||||
|
|
||||||
export const actionsTypes = {
|
export const actionsTypes = {
|
||||||
base: DHBaseAction,
|
base: DHBaseAction,
|
||||||
attack: DHAttackAction,
|
attack: DHAttackAction,
|
||||||
spellcast: DHSpellCastAction,
|
// spellcast: DHSpellCastAction,
|
||||||
resource: DHResourceAction,
|
// resource: DHResourceAction,
|
||||||
damage: DHDamageAction,
|
damage: DHDamageAction,
|
||||||
healing: DHHealingAction,
|
healing: DHHealingAction,
|
||||||
summon: DHSummonAction,
|
summon: DHSummonAction,
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,38 @@
|
||||||
import { abilities } from '../../config/actorConfig.mjs';
|
import CostSelectionDialog from '../../applications/costSelectionDialog.mjs';
|
||||||
import { DHActionDiceData, DHDamageData, DHDamageField } from './actionDice.mjs';
|
import { DHActionDiceData, DHDamageData, DHDamageField } from './actionDice.mjs';
|
||||||
|
import DhpActor from '../../documents/actor.mjs';
|
||||||
export default class DHAction extends foundry.abstract.DataModel {
|
import D20RollDialog from '../../dialogs/d20RollDialog.mjs';
|
||||||
static defineSchema() {
|
|
||||||
const fields = foundry.data.fields;
|
|
||||||
return {
|
|
||||||
id: new fields.DocumentIdField(),
|
|
||||||
name: new fields.StringField({ initial: 'New Action' }),
|
|
||||||
damage: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({ choices: SYSTEM.GENERAL.damageTypes, nullable: true, initial: null }),
|
|
||||||
value: new fields.StringField({})
|
|
||||||
}),
|
|
||||||
healing: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({ choices: SYSTEM.GENERAL.healingTypes, nullable: true, initial: null }),
|
|
||||||
value: new fields.StringField()
|
|
||||||
}),
|
|
||||||
conditions: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
name: new fields.StringField(),
|
|
||||||
icon: new fields.StringField(),
|
|
||||||
description: new fields.StringField()
|
|
||||||
})
|
|
||||||
),
|
|
||||||
cost: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({ choices: SYSTEM.GENERAL.abilityCosts, nullable: true, initial: null }),
|
|
||||||
value: new fields.NumberField({ nullable: true, initial: null })
|
|
||||||
}),
|
|
||||||
target: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({
|
|
||||||
choices: SYSTEM.ACTIONS.targetTypes,
|
|
||||||
initial: SYSTEM.ACTIONS.targetTypes.other.id
|
|
||||||
})
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
/*
|
||||||
|
!!! I'm currently refactoring the whole Action thing, it's a WIP !!!
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ToDo
|
ToDo
|
||||||
- Apply ActiveEffect => Add to Chat message like Damage Button ?
|
- Add setting and/or checkbox for cost and damage like
|
||||||
- Add Drag & Drop for documentUUID field (Macro & Summon)
|
- Target Check / Target Picker
|
||||||
- Add optionnal Role for Healing ?
|
|
||||||
- Handle Roll result as part of formula if needed
|
|
||||||
- Target Check
|
|
||||||
- Cost Check
|
|
||||||
- Range Check
|
- Range Check
|
||||||
- Area of effect and measurement placement
|
- Area of effect and measurement placement
|
||||||
- Auto use costs and action
|
- Summon Action create method
|
||||||
|
|
||||||
|
Other
|
||||||
|
- Auto use action <= Into Roll
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class DHBaseAction extends foundry.abstract.DataModel {
|
export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
|
static extraSchemas = [];
|
||||||
|
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
_id: new fields.DocumentIdField(),
|
_id: new fields.DocumentIdField(),
|
||||||
systemPath: new fields.StringField({ required: true, initial: 'actions' }),
|
systemPath: new fields.StringField({ required: true, initial: 'actions' }),
|
||||||
type: new fields.StringField({ initial: undefined, readonly: true, required: true }),
|
type: new fields.StringField({ initial: undefined, readonly: true, required: true }),
|
||||||
name: new fields.StringField({ initial: undefined }),
|
name: new fields.StringField({ initial: undefined }),
|
||||||
|
description: new fields.HTMLField(),
|
||||||
img: new fields.FilePathField({ initial: undefined, categories: ['IMAGE'], base64: false }),
|
img: new fields.FilePathField({ initial: undefined, categories: ['IMAGE'], base64: false }),
|
||||||
|
chatDisplay: new fields.BooleanField({ initial: true, label: 'Display in chat' }),
|
||||||
actionType: new fields.StringField({ choices: SYSTEM.ITEM.actionTypes, initial: 'action', nullable: true }),
|
actionType: new fields.StringField({ choices: SYSTEM.ITEM.actionTypes, initial: 'action', nullable: true }),
|
||||||
cost: new fields.ArrayField(
|
cost: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
|
|
@ -84,35 +58,85 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
}),
|
}),
|
||||||
range: new fields.StringField({
|
range: new fields.StringField({
|
||||||
choices: SYSTEM.GENERAL.range,
|
choices: SYSTEM.GENERAL.range,
|
||||||
required: true,
|
required: false,
|
||||||
blank: false,
|
blank: true
|
||||||
initial: 'self'
|
// initial: null
|
||||||
})
|
}),
|
||||||
|
...this.defineExtraSchema()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static defineExtraSchema() {
|
||||||
|
const extraFields = {
|
||||||
|
damage: new DHDamageField(),
|
||||||
|
roll: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }),
|
||||||
|
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
|
||||||
|
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
|
||||||
|
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 })
|
||||||
|
}),
|
||||||
|
save: new fields.SchemaField({
|
||||||
|
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
|
||||||
|
difficulty: new fields.NumberField({ nullable: true, initial: 10, integer: true, min: 0 }),
|
||||||
|
damageMod: new fields.StringField({ initial: SYSTEM.ACTIONS.damageOnSave.none.id, choices: SYSTEM.ACTIONS.damageOnSave })
|
||||||
|
}),
|
||||||
|
target: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({
|
||||||
|
choices: SYSTEM.ACTIONS.targetTypes,
|
||||||
|
initial: SYSTEM.ACTIONS.targetTypes.any.id,
|
||||||
|
nullable: true,
|
||||||
|
initial: null
|
||||||
|
}),
|
||||||
|
amount: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 })
|
||||||
|
}),
|
||||||
|
effects: new fields.ArrayField( // ActiveEffect
|
||||||
|
new fields.SchemaField({
|
||||||
|
_id: new fields.DocumentIdField(),
|
||||||
|
onSave: new fields.BooleanField({ initial: false })
|
||||||
|
})
|
||||||
|
),
|
||||||
|
healing: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({
|
||||||
|
choices: SYSTEM.GENERAL.healingTypes,
|
||||||
|
required: true,
|
||||||
|
blank: false,
|
||||||
|
initial: SYSTEM.GENERAL.healingTypes.hitPoints.id,
|
||||||
|
label: 'Healing'
|
||||||
|
}),
|
||||||
|
resultBased: new fields.BooleanField({ initial: false, label: "DAGGERHEART.Actions.Settings.ResultBased.label" }),
|
||||||
|
value: new fields.EmbeddedDataField(DHActionDiceData),
|
||||||
|
valueAlt: new fields.EmbeddedDataField(DHActionDiceData),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
extraSchemas = {};
|
||||||
|
|
||||||
|
this.extraSchemas.forEach(s => (extraSchemas[s] = extraFields[s]));
|
||||||
|
return extraSchemas;
|
||||||
|
}
|
||||||
|
|
||||||
prepareData() {}
|
prepareData() {}
|
||||||
|
|
||||||
get index() {
|
get index() {
|
||||||
return foundry.utils.getProperty(this.parent, this.systemPath).indexOf(this);
|
return foundry.utils.getProperty(this.parent, this.systemPath).indexOf(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get id() {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
get item() {
|
get item() {
|
||||||
return this.parent.parent;
|
return this.parent.parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
get actor() {
|
get actor() {
|
||||||
return this.item?.actor;
|
return this.item instanceof DhpActor ? this.item : this.item?.actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
get chatTemplate() {
|
get chatTemplate() {
|
||||||
return 'systems/daggerheart/templates/chat/duality-roll.hbs';
|
return 'systems/daggerheart/templates/chat/duality-roll.hbs';
|
||||||
}
|
}
|
||||||
get chatTitle() {
|
|
||||||
return this.item.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getRollType() {
|
static getRollType(parent) {
|
||||||
return 'ability';
|
return 'ability';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,96 +145,425 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
updateSource.img ??= parent?.img ?? parent?.system?.img;
|
updateSource.img ??= parent?.img ?? parent?.system?.img;
|
||||||
if (parent?.system?.trait) {
|
if (parent?.system?.trait) {
|
||||||
updateSource['roll'] = {
|
updateSource['roll'] = {
|
||||||
type: this.getRollType(),
|
type: this.getRollType(parent),
|
||||||
trait: parent.system.trait
|
trait: parent.system.trait
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (parent?.type === 'weapon' && !!this.schema.fields.damage) {
|
||||||
|
updateSource['damage'] = { includeBase: true };
|
||||||
|
}
|
||||||
if (parent?.system?.range) {
|
if (parent?.system?.range) {
|
||||||
updateSource['range'] = parent?.system?.range;
|
updateSource['range'] = parent?.system?.range;
|
||||||
}
|
}
|
||||||
return updateSource;
|
return updateSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
async use(event) {
|
getRollData() {
|
||||||
if (this.roll.type && this.roll.trait) {
|
const actorData = this.actor.getRollData(false);
|
||||||
const modifierValue =
|
|
||||||
this.actor.system.traits[this.roll.trait].value + (this.actor.system.bonuses.attack ?? 0);
|
// Remove when included directly in Actor getRollData
|
||||||
const config = {
|
actorData.prof = actorData.proficiency?.value ?? 1,
|
||||||
event: event,
|
actorData.cast = actorData.spellcast?.value ?? 1,
|
||||||
title: this.chatTitle,
|
actorData.scale = this.cost.length
|
||||||
roll: {
|
? this.cost.reduce((a, c) => {
|
||||||
modifier: modifierValue,
|
a[c.type] = c.value;
|
||||||
label: game.i18n.localize(abilities[this.roll.trait].label),
|
return a;
|
||||||
type: this.actionType,
|
}, {})
|
||||||
difficulty: this.roll?.difficulty
|
: 1,
|
||||||
},
|
actorData.roll = {}
|
||||||
chatMessage: {
|
|
||||||
template: this.chatTemplate
|
return actorData;
|
||||||
|
}
|
||||||
|
|
||||||
|
async use(event, ...args) {
|
||||||
|
const isFastForward = event.shiftKey || (!this.hasRoll && !this.hasSave);
|
||||||
|
// Prepare base Config
|
||||||
|
const initConfig = this.initActionConfig(event);
|
||||||
|
// let config = this.initActionConfig(event);
|
||||||
|
|
||||||
|
// Prepare Targets
|
||||||
|
const targetConfig = this.prepareTarget();
|
||||||
|
if (isFastForward && !targetConfig) return ui.notifications.warn('Too many targets selected for that actions.');
|
||||||
|
// config = this.prepareTarget(config);
|
||||||
|
|
||||||
|
// Prepare Range
|
||||||
|
const rangeConfig = this.prepareRange();
|
||||||
|
// config = this.prepareRange(config);
|
||||||
|
|
||||||
|
// Prepare Costs
|
||||||
|
const costsConfig = this.prepareCost();
|
||||||
|
if(isFastForward && !this.hasCost(costsConfig)) return ui.notifications.warn("You don't have the resources to use that action.");
|
||||||
|
// config = this.prepareUseCost(config)
|
||||||
|
|
||||||
|
// Prepare Uses
|
||||||
|
const usesConfig = this.prepareUse();
|
||||||
|
if(isFastForward && !this.hasUses(usesConfig)) return ui.notifications.warn("That action doesn't have remaining uses.");
|
||||||
|
// config = this.prepareUseCost(config)
|
||||||
|
|
||||||
|
// Prepare Roll Data
|
||||||
|
const actorData = this.getRollData();
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
...initConfig,
|
||||||
|
targets: targetConfig,
|
||||||
|
range: rangeConfig,
|
||||||
|
costs: costsConfig,
|
||||||
|
uses: usesConfig,
|
||||||
|
data: actorData
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Hooks.call(`${SYSTEM.id}.preUseAction`, this, config) === false ) return;
|
||||||
|
|
||||||
|
// Display configuration window if necessary
|
||||||
|
if ( config.dialog.configure && this.requireConfigurationDialog(config) ) {
|
||||||
|
config = await D20RollDialog.configure(config);
|
||||||
|
if (!config) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.hasRoll ) {
|
||||||
|
const rollConfig = this.prepareRoll(config);
|
||||||
|
config.roll = rollConfig;
|
||||||
|
config = await this.actor.diceRoll(config);
|
||||||
|
if (!config) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( this.hasSave ) {
|
||||||
|
/* config.targets.forEach((t) => {
|
||||||
|
if(t.hit) {
|
||||||
|
const target = game.canvas.tokens.get(t.id),
|
||||||
|
actor = target?.actor;
|
||||||
|
console.log(actor)
|
||||||
|
if(!actor) return;
|
||||||
|
actor.saveRoll({
|
||||||
|
event,
|
||||||
|
title: 'Roll Save',
|
||||||
|
roll: {
|
||||||
|
trait: this.save.trait,
|
||||||
|
difficulty: this.save.difficulty
|
||||||
|
},
|
||||||
|
dialog: {
|
||||||
|
configure: false
|
||||||
|
},
|
||||||
|
data: actor.getRollData()
|
||||||
|
}).then(async (result) => {
|
||||||
|
t.saved = result;
|
||||||
|
setTimeout(async () => {
|
||||||
|
const message = ui.chat.collection.get(config.message.id),
|
||||||
|
msgTargets = message.system.targets,
|
||||||
|
msgTarget = msgTargets.find(mt => mt.id === t.id);
|
||||||
|
msgTarget.saved = result;
|
||||||
|
await message.update({'system.targets': msgTargets});
|
||||||
|
},100)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
};
|
}) */
|
||||||
if (this.target?.type) config.checkTarget = true;
|
}
|
||||||
if (this.damage.parts.length) {
|
|
||||||
config.damage = {
|
if ( this.doFollowUp() ) {
|
||||||
value: this.damage.parts.map(p => p.getFormula(this.actor)).join(' + '),
|
if(this.rollDamage) await this.rollDamage(event, config);
|
||||||
type: this.damage.parts[0].type
|
if(this.rollHealing) await this.rollHealing(event, config);
|
||||||
};
|
if(this.trigger) await this.trigger(event, config);
|
||||||
}
|
}
|
||||||
if (this.effects.length) {
|
|
||||||
// Apply Active Effects. In Chat Message ?
|
// Consume resources
|
||||||
}
|
await this.consume(config);
|
||||||
return this.actor.diceRoll(config);
|
|
||||||
|
if ( Hooks.call(`${SYSTEM.id}.postUseAction`, this, config) === false ) return;
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* */
|
||||||
|
initActionConfig(event) {
|
||||||
|
return {
|
||||||
|
event,
|
||||||
|
title: this.item.name,
|
||||||
|
source: {
|
||||||
|
item: this.item._id,
|
||||||
|
action: this._id
|
||||||
|
// action: this
|
||||||
|
},
|
||||||
|
dialog: {
|
||||||
|
configure: true
|
||||||
|
},
|
||||||
|
type: this.type,
|
||||||
|
hasDamage: !!this.damage?.parts?.length,
|
||||||
|
hasHealing: !!this.healing,
|
||||||
|
hasEffect: !!this.effects?.length,
|
||||||
|
hasSave: this.hasSave
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const extraDefineSchema = (field, option) => {
|
requireConfigurationDialog(config) {
|
||||||
return {
|
return !config.event.shiftkey && !this.hasRoll && (config.costs?.length || config.uses);
|
||||||
[field]: {
|
}
|
||||||
// damage: new fields.SchemaField({
|
|
||||||
// parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData))
|
|
||||||
// }),
|
|
||||||
damage: new DHDamageField(option),
|
|
||||||
roll: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }),
|
|
||||||
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
|
|
||||||
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 })
|
|
||||||
}),
|
|
||||||
target: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({
|
|
||||||
choices: SYSTEM.ACTIONS.targetTypes,
|
|
||||||
initial: SYSTEM.ACTIONS.targetTypes.other.id
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
effects: new fields.ArrayField( // ActiveEffect
|
|
||||||
new fields.SchemaField({
|
|
||||||
_id: new fields.DocumentIdField()
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}[field]
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export class DHAttackAction extends DHBaseAction {
|
prepareCost() {
|
||||||
static defineSchema() {
|
const costs = this.cost?.length ? foundry.utils.deepClone(this.cost) : [];
|
||||||
|
return costs;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareUse() {
|
||||||
|
const uses = this.uses?.max ? foundry.utils.deepClone(this.uses) : null;
|
||||||
|
if (uses && !uses.value) uses.value = 0;
|
||||||
|
return uses;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareTarget() {
|
||||||
|
let targets;
|
||||||
|
if (this.target?.type === SYSTEM.ACTIONS.targetTypes.self.id)
|
||||||
|
targets = this.formatTarget(this.actor.token ?? this.actor.prototypeToken);
|
||||||
|
targets = Array.from(game.user.targets);
|
||||||
|
// foundry.CONST.TOKEN_DISPOSITIONS.FRIENDLY
|
||||||
|
if (this.target?.type && this.target.type !== SYSTEM.ACTIONS.targetTypes.any.id) {
|
||||||
|
targets = targets.filter(t => this.isTargetFriendly(t));
|
||||||
|
if (this.target.amount && targets.length > this.target.amount) targets = [];
|
||||||
|
}
|
||||||
|
targets = targets.map(t => this.formatTarget(t));
|
||||||
|
return targets;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareRange() {
|
||||||
|
const range = this.range ?? null;
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareRoll() {
|
||||||
|
const roll = {
|
||||||
|
modifiers: [],
|
||||||
|
trait: this.roll?.trait,
|
||||||
|
label: 'Attack',
|
||||||
|
type: this.actionType,
|
||||||
|
difficulty: this.roll?.difficulty
|
||||||
|
};
|
||||||
|
return roll;
|
||||||
|
}
|
||||||
|
|
||||||
|
doFollowUp(config) {
|
||||||
|
return !this.hasRoll;
|
||||||
|
}
|
||||||
|
|
||||||
|
async consume(config) {
|
||||||
|
const resources = config.costs.filter(c => c.enabled !== false).map(c => {
|
||||||
|
return { type: c.type, value: c.total * -1 };
|
||||||
|
});
|
||||||
|
await this.actor.modifyResource(resources);
|
||||||
|
if(config.uses?.enabled) {
|
||||||
|
const newActions = foundry.utils.getProperty(this.item.system, this.systemPath).map(x => x.toObject());
|
||||||
|
newActions[this.index].uses.value++;
|
||||||
|
await this.item.update({ [`system.${this.systemPath}`]: newActions });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* */
|
||||||
|
|
||||||
|
/* ROLL */
|
||||||
|
get hasRoll() {
|
||||||
|
return !!this.roll?.type;
|
||||||
|
}
|
||||||
|
/* ROLL */
|
||||||
|
|
||||||
|
/* SAVE */
|
||||||
|
get hasSave() {
|
||||||
|
return !!this.save?.trait;
|
||||||
|
}
|
||||||
|
/* SAVE */
|
||||||
|
|
||||||
|
/* COST */
|
||||||
|
|
||||||
|
getRealCosts(costs) {
|
||||||
|
const realCosts = costs?.length ? costs.filter(c => c.enabled) : [];
|
||||||
|
return realCosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
calcCosts(costs) {
|
||||||
|
return costs.map(c => {
|
||||||
|
c.scale = c.scale ?? 1;
|
||||||
|
c.step = c.step ?? 1;
|
||||||
|
c.total = c.value * c.scale * c.step;
|
||||||
|
c.enabled = c.hasOwnProperty('enabled') ? c.enabled : true;
|
||||||
|
return c;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hasCost(costs) {
|
||||||
|
const realCosts = this.getRealCosts(costs);
|
||||||
|
return realCosts.reduce((a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value), true);
|
||||||
|
}
|
||||||
|
/* COST */
|
||||||
|
|
||||||
|
/* USES */
|
||||||
|
calcUses(uses) {
|
||||||
|
if(!uses) return null;
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...uses,
|
||||||
...extraDefineSchema('damage', true),
|
enabled: uses.hasOwnProperty('enabled') ? uses.enabled : true
|
||||||
...extraDefineSchema('roll'),
|
|
||||||
...extraDefineSchema('target'),
|
|
||||||
...extraDefineSchema('effects')
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static getRollType() {
|
hasUses(uses) {
|
||||||
return 'weapon';
|
if(!uses) return true;
|
||||||
|
return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= uses.max;
|
||||||
|
}
|
||||||
|
/* USES */
|
||||||
|
|
||||||
|
/* TARGET */
|
||||||
|
isTargetFriendly(target) {
|
||||||
|
const actorDisposition = this.actor.token
|
||||||
|
? this.actor.token.disposition
|
||||||
|
: this.actor.prototypeToken.disposition,
|
||||||
|
targetDisposition = target.document.disposition;
|
||||||
|
return (
|
||||||
|
(this.target.type === SYSTEM.ACTIONS.targetTypes.friendly.id && actorDisposition === targetDisposition) ||
|
||||||
|
(this.target.type === SYSTEM.ACTIONS.targetTypes.hostile.id && actorDisposition + targetDisposition === 0)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get chatTitle() {
|
formatTarget(actor) {
|
||||||
return game.i18n.format('DAGGERHEART.Chat.AttackRoll.Title', {
|
return {
|
||||||
attack: this.item.name
|
id: actor.id,
|
||||||
|
actorId: actor.actor.uuid,
|
||||||
|
name: actor.actor.name,
|
||||||
|
img: actor.actor.img,
|
||||||
|
difficulty: actor.actor.system.difficulty,
|
||||||
|
evasion: actor.actor.system.evasion?.total
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/* TARGET */
|
||||||
|
|
||||||
|
/* RANGE */
|
||||||
|
|
||||||
|
/* RANGE */
|
||||||
|
|
||||||
|
/* EFFECTS */
|
||||||
|
async applyEffects(event, data, force = false) {
|
||||||
|
if (!this.effects?.length || !data.system.targets.length) return;
|
||||||
|
let effects = this.effects;
|
||||||
|
data.system.targets.forEach(async token => {
|
||||||
|
if (!token.hit && !force) return;
|
||||||
|
if(this.hasSave && token.saved.success === true) {
|
||||||
|
effects = this.effects.filter(e => e.onSave === true)
|
||||||
|
}
|
||||||
|
if(!effects.length) return;
|
||||||
|
effects.forEach(async e => {
|
||||||
|
const actor = canvas.tokens.get(token.id)?.actor,
|
||||||
|
effect = this.item.effects.get(e._id);
|
||||||
|
if (!actor || !effect) return;
|
||||||
|
await this.applyEffect(effect, actor);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async applyEffect(effect, actor) {
|
||||||
|
// Enable an existing effect on the target if it originated from this effect
|
||||||
|
const existingEffect = actor.effects.find(e => e.origin === origin.uuid);
|
||||||
|
if (existingEffect) {
|
||||||
|
return existingEffect.update(
|
||||||
|
foundry.utils.mergeObject({
|
||||||
|
...effect.constructor.getInitialDuration(),
|
||||||
|
disabled: false
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, create a new effect on the target
|
||||||
|
const effectData = foundry.utils.mergeObject({
|
||||||
|
...effect.toObject(),
|
||||||
|
disabled: false,
|
||||||
|
transfer: false,
|
||||||
|
origin: origin.uuid
|
||||||
|
});
|
||||||
|
await ActiveEffect.implementation.create(effectData, { parent: actor });
|
||||||
|
}
|
||||||
|
/* EFFECTS */
|
||||||
|
|
||||||
|
/* SAVE */
|
||||||
|
async rollSave(target, event, message) {
|
||||||
|
if(!target?.actor) return;
|
||||||
|
target.actor.diceRoll({
|
||||||
|
event,
|
||||||
|
title: 'Roll Save',
|
||||||
|
roll: {
|
||||||
|
trait: this.save.trait,
|
||||||
|
difficulty: this.save.difficulty,
|
||||||
|
type: "reaction"
|
||||||
|
},
|
||||||
|
data: target.actor.getRollData()
|
||||||
|
}).then(async (result) => {
|
||||||
|
this.updateChatMessage(message, target.id, {result: result.roll.total, success: result.roll.success});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateChatMessage(message, targetId, changes, chain=true) {
|
||||||
|
setTimeout(async () => {
|
||||||
|
const chatMessage = ui.chat.collection.get(message._id),
|
||||||
|
msgTargets = chatMessage.system.targets,
|
||||||
|
msgTarget = msgTargets.find(mt => mt.id === targetId);
|
||||||
|
msgTarget.saved = changes;
|
||||||
|
await chatMessage.update({'system.targets': msgTargets});
|
||||||
|
},100);
|
||||||
|
if(chain) {
|
||||||
|
if(message.system.source.message) this.updateChatMessage(ui.chat.collection.get(message.system.source.message), targetId, changes, false);
|
||||||
|
const relatedChatMessages = ui.chat.collection.filter(c => c.system.source.message === message._id);
|
||||||
|
relatedChatMessages.forEach(c => {
|
||||||
|
this.updateChatMessage(c, targetId, changes, false);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* SAVE */
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DHDamageAction extends DHBaseAction {
|
||||||
|
static extraSchemas = ['damage', 'target', 'effects'];
|
||||||
|
|
||||||
|
/* async use(event, ...args) {
|
||||||
|
const config = await super.use(event, args);
|
||||||
|
if (!config || ['error', 'warning'].includes(config.type)) return;
|
||||||
|
if (!this.directDamage) return;
|
||||||
|
return await this.rollDamage(event, config);
|
||||||
|
} */
|
||||||
|
|
||||||
|
getFormulaValue(part, data) {
|
||||||
|
let formulaValue = part.value;
|
||||||
|
if(this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt;
|
||||||
|
return formulaValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
async rollDamage(event, data) {
|
||||||
|
let formula = this.damage.parts.map(p => this.getFormulaValue(p, data).getFormula(this.actor)).join(' + ');
|
||||||
|
|
||||||
|
if (!formula || formula == '') return;
|
||||||
|
let roll = { formula: formula, total: formula },
|
||||||
|
bonusDamage = [];
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
title: game.i18n.format('DAGGERHEART.Chat.DamageRoll.Title', { damage: this.name }),
|
||||||
|
formula,
|
||||||
|
targets: (data.system?.targets.filter(t => t.hit) ?? data.targets),
|
||||||
|
hasSave: this.hasSave,
|
||||||
|
source: data.system?.source
|
||||||
|
};
|
||||||
|
if(this.hasSave) config.onSave = this.save.damageMod;
|
||||||
|
if(data.system) {
|
||||||
|
config.source.message = data._id;
|
||||||
|
config.directDamage = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DHAttackAction extends DHDamageAction {
|
||||||
|
static extraSchemas = [...super.extraSchemas, ...['roll', 'save']];
|
||||||
|
|
||||||
|
static getRollType(parent) {
|
||||||
|
return parent.type === 'weapon' ? 'weapon' : 'spellcast';
|
||||||
|
}
|
||||||
|
|
||||||
|
get chatTemplate() {
|
||||||
|
return 'systems/daggerheart/templates/chat/duality-roll.hbs';
|
||||||
|
}
|
||||||
|
|
||||||
prepareData() {
|
prepareData() {
|
||||||
super.prepareData();
|
super.prepareData();
|
||||||
if (this.damage.includeBase && !!this.item?.system?.damage) {
|
if (this.damage.includeBase && !!this.item?.system?.damage) {
|
||||||
|
|
@ -221,115 +574,49 @@ export class DHAttackAction extends DHBaseAction {
|
||||||
|
|
||||||
getParentDamage() {
|
getParentDamage() {
|
||||||
return {
|
return {
|
||||||
multiplier: 'proficiency',
|
value: {
|
||||||
dice: this.item?.system?.damage.value,
|
multiplier: 'proficiency',
|
||||||
bonus: this.item?.system?.damage.bonus ?? 0,
|
dice: this.item?.system?.damage.value,
|
||||||
|
bonus: this.item?.system?.damage.bonus ?? 0
|
||||||
|
},
|
||||||
type: this.item?.system?.damage.type,
|
type: this.item?.system?.damage.type,
|
||||||
base: true
|
base: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary until full formula parser
|
|
||||||
// getDamageFormula() {
|
|
||||||
// return this.damage.parts.map(p => p.formula).join(' + ');
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DHSpellCastAction extends DHBaseAction {
|
|
||||||
static defineSchema() {
|
|
||||||
return {
|
|
||||||
...super.defineSchema(),
|
|
||||||
...extraDefineSchema('damage'),
|
|
||||||
...extraDefineSchema('roll'),
|
|
||||||
...extraDefineSchema('target'),
|
|
||||||
...extraDefineSchema('effects')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static getRollType() {
|
|
||||||
return 'spellcast';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DHDamageAction extends DHBaseAction {
|
|
||||||
static defineSchema() {
|
|
||||||
return {
|
|
||||||
...super.defineSchema(),
|
|
||||||
...extraDefineSchema('damage', false),
|
|
||||||
...extraDefineSchema('target'),
|
|
||||||
...extraDefineSchema('effects')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async use(event) {
|
|
||||||
const formula = this.damage.parts.map(p => p.getFormula(this.actor)).join(' + ');
|
|
||||||
if (!formula || formula == '') return;
|
|
||||||
|
|
||||||
let roll = { formula: formula, total: formula };
|
|
||||||
if (isNaN(formula)) {
|
|
||||||
roll = await new Roll(formula).evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
|
||||||
const msg = new cls({
|
|
||||||
user: game.user.id,
|
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
|
||||||
'systems/daggerheart/templates/chat/damage-roll.hbs',
|
|
||||||
{
|
|
||||||
roll: roll.formula,
|
|
||||||
total: roll.total,
|
|
||||||
type: this.damage.parts.map(p => p.type)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
cls.create(msg.toObject());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DHHealingAction extends DHBaseAction {
|
export class DHHealingAction extends DHBaseAction {
|
||||||
static defineSchema() {
|
static extraSchemas = ['target', 'effects', 'healing', 'roll'];
|
||||||
return {
|
|
||||||
...super.defineSchema(),
|
static getRollType(parent) {
|
||||||
healing: new fields.SchemaField({
|
return 'spellcast';
|
||||||
type: new fields.StringField({
|
|
||||||
choices: SYSTEM.GENERAL.healingTypes,
|
|
||||||
required: true,
|
|
||||||
blank: false,
|
|
||||||
initial: SYSTEM.GENERAL.healingTypes.health.id,
|
|
||||||
label: 'Healing'
|
|
||||||
}),
|
|
||||||
value: new fields.EmbeddedDataField(DHActionDiceData)
|
|
||||||
}),
|
|
||||||
...extraDefineSchema('target'),
|
|
||||||
...extraDefineSchema('effects')
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async use(event) {
|
getFormulaValue(data) {
|
||||||
const formula = this.healing.value.getFormula(this.actor);
|
let formulaValue = this.healing.value;
|
||||||
|
if(this.hasRoll && this.healing.resultBased && data.system.roll.result.duality === -1) return this.healing.valueAlt;
|
||||||
|
return formulaValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
async rollHealing(event, data) {
|
||||||
|
let formulaValue = this.getFormulaValue(data),
|
||||||
|
formula = formulaValue.getFormula(this.actor);
|
||||||
|
|
||||||
if (!formula || formula == '') return;
|
if (!formula || formula == '') return;
|
||||||
|
let roll = { formula: formula, total: formula },
|
||||||
|
bonusDamage = [];
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
title: game.i18n.format('DAGGERHEART.Chat.HealingRoll.Title', {
|
||||||
|
healing: game.i18n.localize(SYSTEM.GENERAL.healingTypes[this.healing.type].label)
|
||||||
|
}),
|
||||||
|
formula,
|
||||||
|
targets: (data.system?.targets ?? data.targets).filter(t => t.hit),
|
||||||
|
messageType: 'healing',
|
||||||
|
type: this.healing.type
|
||||||
|
};
|
||||||
|
|
||||||
// const roll = await super.use(event);
|
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
||||||
let roll = { formula: formula, total: formula };
|
|
||||||
if (isNaN(formula)) {
|
|
||||||
roll = await new Roll(formula).evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
|
||||||
const msg = new cls({
|
|
||||||
user: game.user.id,
|
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
|
||||||
'systems/daggerheart/templates/chat/healing-roll.hbs',
|
|
||||||
{
|
|
||||||
roll: roll.formula,
|
|
||||||
total: roll.total,
|
|
||||||
type: this.healing.type
|
|
||||||
}
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
cls.create(msg.toObject());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get chatTemplate() {
|
get chatTemplate() {
|
||||||
|
|
@ -337,42 +624,60 @@ export class DHHealingAction extends DHBaseAction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DHResourceAction extends DHBaseAction {
|
|
||||||
static defineSchema() {
|
|
||||||
return {
|
|
||||||
...super.defineSchema(),
|
|
||||||
// ...extraDefineSchema('roll'),
|
|
||||||
...extraDefineSchema('target'),
|
|
||||||
...extraDefineSchema('effects'),
|
|
||||||
resource: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({
|
|
||||||
choices: [],
|
|
||||||
blank: true,
|
|
||||||
required: false,
|
|
||||||
initial: '',
|
|
||||||
label: 'Resource'
|
|
||||||
}),
|
|
||||||
value: new fields.NumberField({ initial: 0, label: 'Value' })
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DHSummonAction extends DHBaseAction {
|
export class DHSummonAction extends DHBaseAction {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
documentUUID: new fields.StringField({ blank: true, initial: '', placeholder: 'Enter a Creature UUID' })
|
documentUUID: new fields.DocumentUUIDField({ type: 'Actor' })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async trigger(event, ...args) {
|
||||||
|
if (!this.canSummon || !canvas.scene) return;
|
||||||
|
// const config = await super.use(event, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
get canSummon() {
|
||||||
|
return game.user.can('TOKEN_CREATE');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DHEffectAction extends DHBaseAction {
|
export class DHEffectAction extends DHBaseAction {
|
||||||
static defineSchema() {
|
static extraSchemas = ['effects', 'target'];
|
||||||
return {
|
|
||||||
...super.defineSchema(),
|
async use(event, ...args) {
|
||||||
...extraDefineSchema('effects')
|
const config = await super.use(event, args);
|
||||||
};
|
if (['error', 'warning'].includes(config.type)) return;
|
||||||
|
return await this.chatApplyEffects(event, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
async chatApplyEffects(event, data) {
|
||||||
|
const cls = getDocumentClass('ChatMessage'),
|
||||||
|
systemData = {
|
||||||
|
title: game.i18n.format('DAGGERHEART.Chat.ApplyEffect.Title', { name: this.name }),
|
||||||
|
origin: this.actor._id,
|
||||||
|
description: '',
|
||||||
|
targets: data.targets.map(x => ({ id: x.id, name: x.name, img: x.img, hit: true })),
|
||||||
|
action: {
|
||||||
|
itemId: this.item._id,
|
||||||
|
actionId: this._id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
msg = new cls({
|
||||||
|
type: 'applyEffect',
|
||||||
|
user: game.user.id,
|
||||||
|
system: systemData,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/apply-effects.hbs',
|
||||||
|
systemData
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
cls.create(msg.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
get chatTemplate() {
|
||||||
|
return 'systems/daggerheart/templates/chat/apply-effects.hbs';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -380,11 +685,13 @@ export class DHMacroAction extends DHBaseAction {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
documentUUID: new fields.StringField({ blank: true, initial: '', placeholder: 'Enter a macro UUID' })
|
documentUUID: new fields.DocumentUUIDField({ type: 'Macro' })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async use(event) {
|
async trigger(event, ...args) {
|
||||||
|
// const config = await super.use(event, args);
|
||||||
|
// if (['error', 'warning'].includes(config.type)) return;
|
||||||
const fixUUID = !this.documentUUID.includes('Macro.') ? `Macro.${this.documentUUID}` : this.documentUUID,
|
const fixUUID = !this.documentUUID.includes('Macro.') ? `Macro.${this.documentUUID}` : this.documentUUID,
|
||||||
macro = await fromUuid(fixUUID);
|
macro = await fromUuid(fixUUID);
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
||||||
initial: 'proficiency',
|
initial: 'proficiency',
|
||||||
label: 'Multiplier'
|
label: 'Multiplier'
|
||||||
}),
|
}),
|
||||||
|
flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }),
|
||||||
dice: new fields.StringField({ choices: SYSTEM.GENERAL.diceTypes, initial: 'd6', label: 'Formula' }),
|
dice: new fields.StringField({ choices: SYSTEM.GENERAL.diceTypes, initial: 'd6', label: 'Formula' }),
|
||||||
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }),
|
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }),
|
||||||
custom: new fields.SchemaField({
|
custom: new fields.SchemaField({
|
||||||
|
|
@ -21,27 +22,29 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
getFormula(actor) {
|
getFormula(actor) {
|
||||||
|
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : actor.system[this.multiplier]?.total;
|
||||||
return this.custom.enabled
|
return this.custom.enabled
|
||||||
? this.custom.formula
|
? this.custom.formula
|
||||||
: `${actor.system[this.multiplier]?.total ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`;
|
: `${multiplier ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DHDamageField extends fields.SchemaField {
|
export class DHDamageField extends fields.SchemaField {
|
||||||
constructor(hasBase, options, context = {}) {
|
constructor(options, context = {}) {
|
||||||
const damageFields = {
|
const damageFields = {
|
||||||
parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData))
|
parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)),
|
||||||
|
includeBase: new fields.BooleanField({ initial: false })
|
||||||
};
|
};
|
||||||
if (hasBase) damageFields.includeBase = new fields.BooleanField({ initial: true });
|
// if (hasBase) damageFields.includeBase = new fields.BooleanField({ initial: true });
|
||||||
super(damageFields, options, context);
|
super(damageFields, options, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DHDamageData extends DHActionDiceData {
|
export class DHDamageData extends foundry.abstract.DataModel {
|
||||||
/** @override */
|
/** @override */
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
// ...super.defineSchema(),
|
||||||
base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }),
|
base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }),
|
||||||
type: new fields.StringField({
|
type: new fields.StringField({
|
||||||
choices: SYSTEM.GENERAL.damageTypes,
|
choices: SYSTEM.GENERAL.damageTypes,
|
||||||
|
|
@ -49,7 +52,10 @@ export class DHDamageData extends DHActionDiceData {
|
||||||
label: 'Type',
|
label: 'Type',
|
||||||
nullable: false,
|
nullable: false,
|
||||||
required: true
|
required: true
|
||||||
})
|
}),
|
||||||
|
resultBased: new fields.BooleanField({ initial: false, label: "DAGGERHEART.Actions.Settings.ResultBased.label" }),
|
||||||
|
value: new fields.EmbeddedDataField(DHActionDiceData),
|
||||||
|
valueAlt: new fields.EmbeddedDataField(DHActionDiceData),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import ActionField from '../fields/actionField.mjs';
|
||||||
import BaseDataActor from './base.mjs';
|
import BaseDataActor from './base.mjs';
|
||||||
|
|
||||||
const resourceField = () =>
|
const resourceField = () =>
|
||||||
|
|
@ -39,30 +40,41 @@ export default class DhpAdversary extends BaseDataActor {
|
||||||
hitPoints: resourceField(),
|
hitPoints: resourceField(),
|
||||||
stress: resourceField()
|
stress: resourceField()
|
||||||
}),
|
}),
|
||||||
attack: new fields.SchemaField({
|
attack: new ActionField({
|
||||||
name: new fields.StringField({}),
|
initial: {
|
||||||
modifier: new fields.NumberField({ required: true, integer: true, initial: 0 }),
|
name: 'Attack',
|
||||||
range: new fields.StringField({
|
_id: foundry.utils.randomID(),
|
||||||
required: true,
|
systemPath: 'attack',
|
||||||
choices: SYSTEM.GENERAL.range,
|
type: 'attack',
|
||||||
initial: SYSTEM.GENERAL.range.melee.id
|
range: 'melee',
|
||||||
}),
|
target: {
|
||||||
damage: new fields.SchemaField({
|
type: 'any',
|
||||||
value: new fields.StringField(),
|
amount: 1
|
||||||
type: new fields.StringField({
|
},
|
||||||
required: true,
|
roll: {
|
||||||
choices: SYSTEM.GENERAL.damageTypes,
|
type: 'weapon'
|
||||||
initial: SYSTEM.GENERAL.damageTypes.physical.id
|
},
|
||||||
})
|
damage: {
|
||||||
})
|
parts: [
|
||||||
|
{
|
||||||
|
multiplier: 'flat'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
experiences: new fields.TypedObjectField(
|
experiences: new fields.TypedObjectField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
name: new fields.StringField(),
|
name: new fields.StringField(),
|
||||||
value: new fields.NumberField({ required: true, integer: true, initial: 1 })
|
value: new fields.NumberField({ required: true, integer: true, initial: 1 })
|
||||||
})
|
})
|
||||||
)
|
),
|
||||||
/* Features waiting on pseudo-document data model addition */
|
bonuses: new fields.SchemaField({
|
||||||
|
difficulty: new fields.SchemaField({
|
||||||
|
all: new fields.NumberField({ integer: true, initial: 0 }),
|
||||||
|
reaction: new fields.NumberField({ integer: true, initial: 0 })
|
||||||
|
})
|
||||||
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,12 @@ const resourceField = max =>
|
||||||
max: new foundry.data.fields.NumberField({ initial: max, integer: true })
|
max: new foundry.data.fields.NumberField({ initial: max, integer: true })
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const stressDamageReductionRule = () =>
|
||||||
|
new foundry.data.fields.SchemaField({
|
||||||
|
enabled: new foundry.data.fields.BooleanField({ required: true, initial: false }),
|
||||||
|
cost: new foundry.data.fields.NumberField({ integer: true })
|
||||||
|
});
|
||||||
|
|
||||||
export default class DhCharacter extends BaseDataActor {
|
export default class DhCharacter extends BaseDataActor {
|
||||||
static get metadata() {
|
static get metadata() {
|
||||||
return foundry.utils.mergeObject(super.metadata, {
|
return foundry.utils.mergeObject(super.metadata, {
|
||||||
|
|
@ -35,7 +41,9 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
bonus: new foundry.data.fields.NumberField({ initial: 0, integer: true })
|
bonus: new foundry.data.fields.NumberField({ initial: 0, integer: true })
|
||||||
}),
|
}),
|
||||||
stress: resourceField(6),
|
stress: resourceField(6),
|
||||||
hope: resourceField(6)
|
hope: resourceField(6),
|
||||||
|
tokens: new fields.ObjectField(),
|
||||||
|
dice: new fields.ObjectField()
|
||||||
}),
|
}),
|
||||||
traits: new fields.SchemaField({
|
traits: new fields.SchemaField({
|
||||||
agility: attributeField(),
|
agility: attributeField(),
|
||||||
|
|
@ -90,9 +98,42 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
}),
|
}),
|
||||||
levelData: new fields.EmbeddedDataField(DhPCLevelData),
|
levelData: new fields.EmbeddedDataField(DhPCLevelData),
|
||||||
bonuses: new fields.SchemaField({
|
bonuses: new fields.SchemaField({
|
||||||
attack: new fields.NumberField({ integer: true, initial: 0 }),
|
armorScore: new fields.NumberField({ integer: true, initial: 0 }),
|
||||||
spellcast: new fields.NumberField({ integer: true, initial: 0 }),
|
damageThresholds: new fields.SchemaField({
|
||||||
armorScore: new fields.NumberField({ integer: true, initial: 0 })
|
severe: new fields.NumberField({ integer: true, initial: 0 }),
|
||||||
|
major: new fields.NumberField({ integer: true, initial: 0 })
|
||||||
|
}),
|
||||||
|
roll: new fields.SchemaField({
|
||||||
|
attack: new fields.NumberField({ integer: true, initial: 0 }),
|
||||||
|
spellcast: new fields.NumberField({ integer: true, initial: 0 }),
|
||||||
|
action: new fields.NumberField({ integer: true, initial: 0 }),
|
||||||
|
hopeOrFear: new fields.NumberField({ integer: true, initial: 0 })
|
||||||
|
}),
|
||||||
|
damage: new fields.SchemaField({
|
||||||
|
all: new fields.NumberField({ integer: true, initial: 0 }),
|
||||||
|
physical: new fields.NumberField({ integer: true, initial: 0 }),
|
||||||
|
magic: new fields.NumberField({ integer: true, initial: 0 })
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
rules: 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 })
|
||||||
|
}),
|
||||||
|
stressDamageReduction: new fields.SchemaField({
|
||||||
|
severe: stressDamageReductionRule(),
|
||||||
|
major: stressDamageReductionRule(),
|
||||||
|
minor: stressDamageReductionRule()
|
||||||
|
}),
|
||||||
|
strangePatterns: new fields.NumberField({
|
||||||
|
integer: true,
|
||||||
|
min: 1,
|
||||||
|
max: 12,
|
||||||
|
nullable: true,
|
||||||
|
initial: null
|
||||||
|
}),
|
||||||
|
runeWard: new fields.BooleanField({ initial: false })
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -246,6 +287,9 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
experience.total = experience.value + experience.bonus;
|
experience.total = experience.value + experience.bonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.rules.maxArmorMarked.total = this.rules.maxArmorMarked.value + this.rules.maxArmorMarked.bonus;
|
||||||
|
|
||||||
|
this.armorScore = this.armor ? this.armor.system.baseScore + (this.bonuses.armorScore ?? 0) : 0;
|
||||||
this.resources.hitPoints.maxTotal = (this.class.value?.system?.hitPoints ?? 0) + this.resources.hitPoints.bonus;
|
this.resources.hitPoints.maxTotal = (this.class.value?.system?.hitPoints ?? 0) + this.resources.hitPoints.bonus;
|
||||||
this.resources.stress.maxTotal = this.resources.stress.max + this.resources.stress.bonus;
|
this.resources.stress.maxTotal = this.resources.stress.max + this.resources.stress.bonus;
|
||||||
this.evasion.total = (this.class?.evasion ?? 0) + this.evasion.bonus;
|
this.evasion.total = (this.class?.evasion ?? 0) + this.evasion.bonus;
|
||||||
|
|
@ -256,7 +300,11 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
const data = super.getRollData();
|
const data = super.getRollData();
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
tier: this.tier
|
...this.resources.tokens,
|
||||||
|
...this.resources.dice,
|
||||||
|
...this.bonuses,
|
||||||
|
tier: this.tier,
|
||||||
|
level: this.levelData.level.current
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,21 @@
|
||||||
import DHAbilityUse from './abilityUse.mjs';
|
import DHAbilityUse from "./abilityUse.mjs";
|
||||||
import DHAdversaryRoll from './adversaryRoll.mjs';
|
import DHAdversaryRoll from "./adversaryRoll.mjs";
|
||||||
import DHDamageRoll from './damageRoll.mjs';
|
import DHDamageRoll from "./damageRoll.mjs";
|
||||||
import DHDualityRoll from './dualityRoll.mjs';
|
import DHDualityRoll from "./dualityRoll.mjs";
|
||||||
|
import DHApplyEffect from './applyEffects.mjs'
|
||||||
|
|
||||||
export { DHAbilityUse, DHAdversaryRoll, DHDamageRoll, DHDualityRoll };
|
export {
|
||||||
|
DHAbilityUse,
|
||||||
|
DHAdversaryRoll,
|
||||||
|
DHDamageRoll,
|
||||||
|
DHDualityRoll,
|
||||||
|
DHApplyEffect
|
||||||
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
abilityUse: DHAbilityUse,
|
abilityUse: DHAbilityUse,
|
||||||
adversaryRoll: DHAdversaryRoll,
|
adversaryRoll: DHAdversaryRoll,
|
||||||
damageRoll: DHDamageRoll,
|
damageRoll: DHDamageRoll,
|
||||||
dualityRoll: DHDualityRoll
|
dualityRoll: DHDualityRoll,
|
||||||
|
applyEffect: DHApplyEffect
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,43 +4,35 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: new fields.StringField(),
|
title: new fields.StringField(),
|
||||||
origin: new fields.StringField({ required: true }),
|
|
||||||
dice: new fields.DataField(),
|
|
||||||
roll: new fields.DataField(),
|
roll: new fields.DataField(),
|
||||||
modifiers: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
value: new fields.NumberField({ integer: true }),
|
|
||||||
label: new fields.StringField({})
|
|
||||||
})
|
|
||||||
),
|
|
||||||
advantageState: new fields.BooleanField({ nullable: true, initial: null }),
|
|
||||||
advantage: new fields.SchemaField({
|
|
||||||
dice: new fields.StringField({}),
|
|
||||||
value: new fields.NumberField({ integer: true })
|
|
||||||
}),
|
|
||||||
targets: new fields.ArrayField(
|
targets: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
id: new fields.StringField({}),
|
id: new fields.StringField({}),
|
||||||
|
actorId: new fields.StringField({}),
|
||||||
name: new fields.StringField({}),
|
name: new fields.StringField({}),
|
||||||
img: new fields.StringField({}),
|
img: new fields.StringField({}),
|
||||||
difficulty: new fields.NumberField({ integer: true, nullable: true }),
|
difficulty: new fields.NumberField({ integer: true, nullable: true }),
|
||||||
evasion: new fields.NumberField({ integer: true }),
|
evasion: new fields.NumberField({ integer: true }),
|
||||||
hit: new fields.BooleanField({ initial: false })
|
hit: new fields.BooleanField({ initial: false }),
|
||||||
|
saved: new fields.SchemaField({
|
||||||
|
result: new fields.NumberField(),
|
||||||
|
success: new fields.BooleanField({ nullable: true, initial: null })
|
||||||
|
})
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
damage: new fields.SchemaField(
|
hasDamage: new fields.BooleanField({ initial: false }),
|
||||||
{
|
hasHealing: new fields.BooleanField({ initial: false }),
|
||||||
value: new fields.StringField({}),
|
hasEffect: new fields.BooleanField({ initial: false }),
|
||||||
type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false })
|
hasSave: new fields.BooleanField({ initial: false }),
|
||||||
},
|
source: new fields.SchemaField({
|
||||||
{ nullable: true, initial: null }
|
actor: new fields.StringField(),
|
||||||
)
|
item: new fields.StringField(),
|
||||||
|
action: new fields.StringField()
|
||||||
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareDerivedData() {
|
get messageTemplate() {
|
||||||
this.targets.forEach(target => {
|
return 'systems/daggerheart/templates/chat/adversary-roll.hbs';
|
||||||
target.hit = target.difficulty ? this.total >= target.difficulty : this.total >= target.evasion;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
23
module/data/chat-message/applyEffects.mjs
Normal file
23
module/data/chat-message/applyEffects.mjs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
export default class DHApplyEffect extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: new fields.StringField(),
|
||||||
|
origin: new fields.StringField({}),
|
||||||
|
description: new fields.StringField({}),
|
||||||
|
targets: new fields.ArrayField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
id: new fields.StringField({ required: true }),
|
||||||
|
name: new fields.StringField(),
|
||||||
|
img: new fields.StringField(),
|
||||||
|
hit: new fields.BooleanField({ initial: false })
|
||||||
|
})
|
||||||
|
),
|
||||||
|
action: new fields.SchemaField({
|
||||||
|
itemId: new fields.StringField(),
|
||||||
|
actionId: new fields.StringField()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,32 +3,35 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
messageType: new fields.StringField({initial: 'damage'}),
|
||||||
title: new fields.StringField(),
|
title: new fields.StringField(),
|
||||||
roll: new fields.StringField({ required: true }),
|
roll: new fields.DataField({}),
|
||||||
damage: new fields.SchemaField({
|
|
||||||
total: new fields.NumberField({ required: true, integer: true }),
|
|
||||||
type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false })
|
|
||||||
}),
|
|
||||||
dice: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
type: new fields.StringField({ required: true }),
|
|
||||||
rolls: new fields.ArrayField(new fields.NumberField({ required: true, integer: true })),
|
|
||||||
total: new fields.NumberField({ integer: true })
|
|
||||||
})
|
|
||||||
),
|
|
||||||
modifiers: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
value: new fields.NumberField({ required: true, integer: true }),
|
|
||||||
operator: new fields.StringField({ required: true, choices: ['+', '-', '*', '/'] })
|
|
||||||
})
|
|
||||||
),
|
|
||||||
targets: new fields.ArrayField(
|
targets: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
id: new fields.StringField({ required: true }),
|
id: new fields.StringField({ required: true }),
|
||||||
|
actorId: new fields.StringField({}),
|
||||||
name: new fields.StringField(),
|
name: new fields.StringField(),
|
||||||
img: new fields.StringField()
|
img: new fields.StringField(),
|
||||||
|
hit: new fields.BooleanField({ initial: false }),
|
||||||
|
saved: new fields.SchemaField({
|
||||||
|
result: new fields.NumberField(),
|
||||||
|
success: new fields.BooleanField({ nullable: true, initial: null })
|
||||||
|
})
|
||||||
})
|
})
|
||||||
)
|
),
|
||||||
|
hasSave: new fields.BooleanField({ initial: false }),
|
||||||
|
onSave: new fields.StringField(),
|
||||||
|
source: new fields.SchemaField({
|
||||||
|
actor: new fields.StringField(),
|
||||||
|
item: new fields.StringField(),
|
||||||
|
action: new fields.StringField(),
|
||||||
|
message: new fields.StringField()
|
||||||
|
}),
|
||||||
|
directDamage: new fields.BooleanField({initial: true})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get messageTemplate() {
|
||||||
|
return `systems/daggerheart/templates/chat/${this.messageType}-roll.hbs`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,4 @@
|
||||||
import { DualityRollColor } from '../settings/Appearance.mjs';
|
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
const diceField = () =>
|
|
||||||
new fields.SchemaField({
|
|
||||||
dice: new fields.StringField({}),
|
|
||||||
value: new fields.NumberField({ integer: true })
|
|
||||||
});
|
|
||||||
|
|
||||||
export default class DHDualityRoll extends foundry.abstract.TypeDataModel {
|
export default class DHDualityRoll extends foundry.abstract.TypeDataModel {
|
||||||
static dualityResult = {
|
static dualityResult = {
|
||||||
|
|
@ -17,92 +10,35 @@ export default class DHDualityRoll extends foundry.abstract.TypeDataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
title: new fields.StringField(),
|
title: new fields.StringField(),
|
||||||
origin: new fields.StringField({ required: true }),
|
|
||||||
roll: new fields.DataField({}),
|
roll: new fields.DataField({}),
|
||||||
modifiers: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
value: new fields.NumberField({ integer: true }),
|
|
||||||
label: new fields.StringField({})
|
|
||||||
})
|
|
||||||
),
|
|
||||||
hope: diceField(),
|
|
||||||
fear: diceField(),
|
|
||||||
advantageState: new fields.BooleanField({ nullable: true, initial: null }),
|
|
||||||
advantage: diceField(),
|
|
||||||
targets: new fields.ArrayField(
|
targets: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
id: new fields.StringField({}),
|
id: new fields.StringField({}),
|
||||||
|
actorId: new fields.StringField({}),
|
||||||
name: new fields.StringField({}),
|
name: new fields.StringField({}),
|
||||||
img: new fields.StringField({}),
|
img: new fields.StringField({}),
|
||||||
difficulty: new fields.NumberField({ integer: true, nullable: true }),
|
difficulty: new fields.NumberField({ integer: true, nullable: true }),
|
||||||
evasion: new fields.NumberField({ integer: true }),
|
evasion: new fields.NumberField({ integer: true }),
|
||||||
hit: new fields.BooleanField({ initial: false })
|
hit: new fields.BooleanField({ initial: false }),
|
||||||
|
saved: new fields.SchemaField({
|
||||||
|
result: new fields.NumberField(),
|
||||||
|
success: new fields.BooleanField({ nullable: true, initial: null })
|
||||||
|
})
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
damage: new fields.SchemaField({
|
hasDamage: new fields.BooleanField({ initial: false }),
|
||||||
value: new fields.StringField({}),
|
hasHealing: new fields.BooleanField({ initial: false }),
|
||||||
type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false }),
|
hasEffect: new fields.BooleanField({ initial: false }),
|
||||||
bonusDamage: new fields.ArrayField(
|
hasSave: new fields.BooleanField({ initial: false }),
|
||||||
new fields.SchemaField({
|
source: new fields.SchemaField({
|
||||||
value: new fields.StringField({}),
|
actor: new fields.StringField(),
|
||||||
type: new fields.StringField({
|
item: new fields.StringField(),
|
||||||
choices: Object.keys(SYSTEM.GENERAL.damageTypes),
|
action: new fields.StringField()
|
||||||
integer: false
|
|
||||||
}),
|
|
||||||
initiallySelected: new fields.BooleanField(),
|
|
||||||
appliesOn: new fields.StringField(
|
|
||||||
{ choices: Object.keys(SYSTEM.EFFECTS.applyLocations) },
|
|
||||||
{ nullable: true, initial: null }
|
|
||||||
),
|
|
||||||
description: new fields.StringField({}),
|
|
||||||
hopeIncrease: new fields.StringField({ nullable: true })
|
|
||||||
}),
|
|
||||||
{ nullable: true, initial: null }
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get diceTotal() {
|
get messageTemplate() {
|
||||||
return this.hope.value + this.fear.value;
|
return 'systems/daggerheart/templates/chat/duality-roll.hbs';
|
||||||
}
|
|
||||||
|
|
||||||
get modifierTotal() {
|
|
||||||
const total = this.modifiers.reduce((acc, x) => acc + x.value, 0);
|
|
||||||
return {
|
|
||||||
value: total,
|
|
||||||
label: total > 0 ? `+${total}` : total < 0 ? `${total}` : ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get dualityResult() {
|
|
||||||
return this.hope.value > this.fear.value
|
|
||||||
? this.constructor.dualityResult.hope
|
|
||||||
: this.fear.value > this.hope.value
|
|
||||||
? this.constructor.dualityResult.fear
|
|
||||||
: this.constructor.dualityResult.critical;
|
|
||||||
}
|
|
||||||
|
|
||||||
get totalLabel() {
|
|
||||||
const label =
|
|
||||||
this.hope.value > this.fear.value
|
|
||||||
? 'DAGGERHEART.General.Hope'
|
|
||||||
: this.fear.value > this.hope.value
|
|
||||||
? 'DAGGERHEART.General.Fear'
|
|
||||||
: 'DAGGERHEART.General.CriticalSuccess';
|
|
||||||
|
|
||||||
return game.i18n.localize(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
get colorful() {
|
|
||||||
return (
|
|
||||||
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).dualityColorScheme ===
|
|
||||||
DualityRollColor.colorful.value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareDerivedData() {
|
|
||||||
this.hope.discarded = this.hope.value < this.fear.value;
|
|
||||||
this.fear.discarded = this.fear.value < this.hope.value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ export default class DHArmor extends BaseDataItem {
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
marks: new fields.SchemaField({
|
marks: new fields.SchemaField({
|
||||||
max: new fields.NumberField({ initial: 6, integer: true }),
|
|
||||||
value: new fields.NumberField({ initial: 0, integer: true })
|
value: new fields.NumberField({ initial: 0, integer: true })
|
||||||
}),
|
}),
|
||||||
baseThresholds: new fields.SchemaField({
|
baseThresholds: new fields.SchemaField({
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { actionsTypes } from '../action/_module.mjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes metadata about the item data model type
|
* Describes metadata about the item data model type
|
||||||
* @typedef {Object} ItemDataModelMetadata
|
* @typedef {Object} ItemDataModelMetadata
|
||||||
|
|
@ -50,4 +52,24 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
const data = { ...actorRollData, item: { ...this } };
|
const data = { ...actorRollData, item: { ...this } };
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _preCreate(data, options, user) {
|
||||||
|
if(!this.constructor.metadata.hasInitialAction || !foundry.utils.isEmpty(this.actions)) return;
|
||||||
|
const actionType = {
|
||||||
|
weapon: 'attack'
|
||||||
|
}[this.constructor.metadata.type],
|
||||||
|
cls = actionsTypes.attack,
|
||||||
|
action = new cls(
|
||||||
|
{
|
||||||
|
_id: foundry.utils.randomID(),
|
||||||
|
type: actionType,
|
||||||
|
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType].name),
|
||||||
|
...cls.getSourceConfig(this.parent)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parent: this.parent
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.updateSource({actions: [action]});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { getTier } from '../../helpers/utils.mjs';
|
import { getTier } from '../../helpers/utils.mjs';
|
||||||
import DHAction from '../action/action.mjs';
|
|
||||||
import BaseDataItem from './base.mjs';
|
import BaseDataItem from './base.mjs';
|
||||||
import ActionField from '../fields/actionField.mjs';
|
import ActionField from '../fields/actionField.mjs';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import BaseDataItem from './base.mjs';
|
||||||
import FormulaField from '../fields/formulaField.mjs';
|
import FormulaField from '../fields/formulaField.mjs';
|
||||||
import ActionField from '../fields/actionField.mjs';
|
import ActionField from '../fields/actionField.mjs';
|
||||||
import { weaponFeatures } from '../../config/itemConfig.mjs';
|
import { weaponFeatures } from '../../config/itemConfig.mjs';
|
||||||
import { actionsTypes } from '../../data/_module.mjs';
|
import { actionsTypes } from '../action/_module.mjs';
|
||||||
|
|
||||||
export default class DHWeapon extends BaseDataItem {
|
export default class DHWeapon extends BaseDataItem {
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
|
|
@ -14,7 +14,8 @@ export default class DHWeapon extends BaseDataItem {
|
||||||
isQuantifiable: true,
|
isQuantifiable: true,
|
||||||
embedded: {
|
embedded: {
|
||||||
feature: 'featureTest'
|
feature: 'featureTest'
|
||||||
}
|
},
|
||||||
|
hasInitialAction: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
113
module/dialogs/d20RollDialog.mjs
Normal file
113
module/dialogs/d20RollDialog.mjs
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
|
export default class D20RollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor(config = {}, options = {}) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
this.config = config;
|
||||||
|
this.config.experiences = [];
|
||||||
|
|
||||||
|
if (config.source?.action) {
|
||||||
|
this.item = config.data.parent.items.get(config.source.item);
|
||||||
|
this.action =
|
||||||
|
config.data.attack?._id == config.source.action
|
||||||
|
? config.data.attack
|
||||||
|
: this.item.system.actions.find(a => a._id === config.source.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
id: 'roll-selection',
|
||||||
|
classes: ['daggerheart', 'views', 'roll-selection'],
|
||||||
|
position: {
|
||||||
|
width: 400,
|
||||||
|
height: 'auto'
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
updateIsAdvantage: this.updateIsAdvantage,
|
||||||
|
selectExperience: this.selectExperience,
|
||||||
|
submitRoll: this.submitRoll
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
handler: this.updateRollConfiguration,
|
||||||
|
submitOnChange: true,
|
||||||
|
submitOnClose: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
costSelection: {
|
||||||
|
id: 'costSelection',
|
||||||
|
template: 'systems/daggerheart/templates/views/costSelection.hbs'
|
||||||
|
},
|
||||||
|
rollSelection: {
|
||||||
|
id: 'rollSelection',
|
||||||
|
template: 'systems/daggerheart/templates/views/rollSelection.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.hasRoll = !!this.config.roll;
|
||||||
|
context.experiences = Object.keys(this.config.data.experiences).map(id => ({
|
||||||
|
id,
|
||||||
|
...this.config.data.experiences[id]
|
||||||
|
}));
|
||||||
|
context.selectedExperiences = this.config.experiences;
|
||||||
|
context.advantage = this.config.advantage;
|
||||||
|
/* context.diceOptions = this.diceOptions; */
|
||||||
|
context.canRoll = true;
|
||||||
|
if (this.config.costs?.length) {
|
||||||
|
const updatedCosts = this.action.calcCosts(this.config.costs);
|
||||||
|
context.costs = updatedCosts;
|
||||||
|
context.canRoll = this.action.hasCost(updatedCosts);
|
||||||
|
}
|
||||||
|
if (this.config.uses?.max) {
|
||||||
|
context.uses = this.action.calcUses(this.config.uses);
|
||||||
|
context.canRoll = context.canRoll && this.action.hasUses(context.uses);
|
||||||
|
}
|
||||||
|
console.log(context, _options)
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static updateRollConfiguration(event, _, formData) {
|
||||||
|
const { ...rest } = foundry.utils.expandObject(formData.object);
|
||||||
|
if (this.config.costs) this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs);
|
||||||
|
if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static updateIsAdvantage(_, button) {
|
||||||
|
const advantage = Number(button.dataset.advantage);
|
||||||
|
this.config.advantage = this.config.advantage === advantage ? 0 : advantage;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
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.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async submitRoll() {
|
||||||
|
await this.close({ submitted: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_onClose(options = {}) {
|
||||||
|
if (!options.submitted) this.config = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async configure(config = {}, options={}) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const app = new this(config, options);
|
||||||
|
app.addEventListener('close', () => resolve(app.config), { once: true });
|
||||||
|
app.render({ force: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
59
module/dialogs/damageDialog.mjs
Normal file
59
module/dialogs/damageDialog.mjs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
|
export default class DamageDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor(config={}, options={}) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
id: 'roll-selection',
|
||||||
|
classes: ['daggerheart', 'views', 'damage-selection'],
|
||||||
|
position: {
|
||||||
|
width: 400,
|
||||||
|
height: 'auto'
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
submitRoll: this.submitRoll
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
handler: this.updateRollConfiguration,
|
||||||
|
submitOnChange: true,
|
||||||
|
submitOnClose: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
damageSelection: {
|
||||||
|
id: 'damageSelection',
|
||||||
|
template: 'systems/daggerheart/templates/views/damageSelection.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.title = this.config.title;
|
||||||
|
context.formula = this.config.formula;
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async submitRoll() {
|
||||||
|
await this.close({ submitted: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_onClose(options={}) {
|
||||||
|
if ( !options.submitted ) this.config = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async configure(config={}) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const app = new this(config);
|
||||||
|
app.addEventListener("close", () => resolve(app.config), { once: true });
|
||||||
|
app.render({ force: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
import DamageSelectionDialog from '../applications/damageSelectionDialog.mjs';
|
import DamageSelectionDialog from '../applications/damageSelectionDialog.mjs';
|
||||||
import NpcRollSelectionDialog from '../applications/npcRollSelectionDialog.mjs';
|
|
||||||
import RollSelectionDialog from '../applications/rollSelectionDialog.mjs';
|
|
||||||
import { GMUpdateEvent, socketEvent } from '../helpers/socket.mjs';
|
import { GMUpdateEvent, socketEvent } from '../helpers/socket.mjs';
|
||||||
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
import DamageReductionDialog from '../applications/damageReductionDialog.mjs';
|
||||||
import DHDualityRoll from '../data/chat-message/dualityRoll.mjs';
|
|
||||||
|
|
||||||
export default class DhpActor extends Actor {
|
export default class DhpActor extends Actor {
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
|
|
@ -268,150 +265,26 @@ export default class DhpActor extends Actor {
|
||||||
* @param {boolean} [config.roll.simple=false]
|
* @param {boolean} [config.roll.simple=false]
|
||||||
* @param {string} [config.roll.type]
|
* @param {string} [config.roll.type]
|
||||||
* @param {number} [config.roll.difficulty]
|
* @param {number} [config.roll.difficulty]
|
||||||
* @param {any} [config.damage]
|
* @param {boolean} [config.hasDamage]
|
||||||
|
* @param {boolean} [config.hasEffect]
|
||||||
* @param {object} [config.chatMessage]
|
* @param {object} [config.chatMessage]
|
||||||
* @param {string} config.chatMessage.template
|
* @param {string} config.chatMessage.template
|
||||||
* @param {boolean} [config.chatMessage.mute]
|
* @param {boolean} [config.chatMessage.mute]
|
||||||
* @param {boolean} [config.checkTarget]
|
* @param {object} [config.targets]
|
||||||
|
* @param {object} [config.costs]
|
||||||
*/
|
*/
|
||||||
async diceRoll(config) {
|
async diceRoll(config) {
|
||||||
let hopeDice = 'd12',
|
config.source = {...(config.source ?? {}), actor: this.uuid};
|
||||||
fearDice = 'd12',
|
config.data = this.getRollData();
|
||||||
advantageDice = 'd6',
|
return await this.rollClass.build(config);
|
||||||
disadvantageDice = 'd6',
|
}
|
||||||
advantage = config.event.altKey ? true : config.event.ctrlKey ? false : null,
|
|
||||||
targets,
|
|
||||||
damage = config.damage,
|
|
||||||
modifiers = this.formatRollModifier(config.roll),
|
|
||||||
rollConfig,
|
|
||||||
formula,
|
|
||||||
hope,
|
|
||||||
fear;
|
|
||||||
|
|
||||||
if (!config.event.shiftKey && !config.event.altKey && !config.event.ctrlKey) {
|
get rollClass() {
|
||||||
const dialogClosed = new Promise((resolve, _) => {
|
return CONFIG.Dice.daggerheart[this.type === 'character' ? 'DualityRoll' : 'D20Roll'];
|
||||||
this.type === 'character'
|
}
|
||||||
? new RollSelectionDialog(
|
|
||||||
this.system.experiences,
|
|
||||||
this.system.resources.hope.value,
|
|
||||||
resolve
|
|
||||||
).render(true)
|
|
||||||
: new NpcRollSelectionDialog(this.system.experiences, resolve).render(true);
|
|
||||||
});
|
|
||||||
rollConfig = await dialogClosed;
|
|
||||||
|
|
||||||
advantage = rollConfig.advantage;
|
getRollData() {
|
||||||
hopeDice = rollConfig.hope;
|
return this.system;
|
||||||
fearDice = rollConfig.fear;
|
|
||||||
|
|
||||||
rollConfig.experiences.forEach(x =>
|
|
||||||
modifiers.push({
|
|
||||||
value: x.value,
|
|
||||||
label: x.value >= 0 ? `+${x.value}` : `-${x.value}`,
|
|
||||||
title: x.description
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.type === 'character') {
|
|
||||||
const automateHope = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).hope;
|
|
||||||
|
|
||||||
if (automateHope && result.hopeUsed) {
|
|
||||||
await this.update({
|
|
||||||
'system.resources.hope.value': this.system.resources.hope.value - result.hopeUsed
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.type === 'character') {
|
|
||||||
formula = `1${hopeDice} + 1${fearDice}${advantage === true ? ` + 1d6` : advantage === false ? ` - 1d6` : ''}`;
|
|
||||||
} else {
|
|
||||||
formula = `${advantage === true || advantage === false ? 2 : 1}d20${advantage === true ? 'kh' : advantage === false ? 'kl' : ''}`;
|
|
||||||
}
|
|
||||||
formula += ` ${modifiers.map(x => `+ ${x.value}`).join(' ')}`;
|
|
||||||
const roll = await Roll.create(formula).evaluate();
|
|
||||||
const dice = roll.dice.flatMap(dice => ({
|
|
||||||
denomination: dice.denomination,
|
|
||||||
number: dice.number,
|
|
||||||
total: dice.total,
|
|
||||||
results: dice.results.map(result => ({ result: result.result, discarded: !result.active }))
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (this.type === 'character') {
|
|
||||||
setDiceSoNiceForDualityRoll(roll, advantage);
|
|
||||||
hope = roll.dice[0].results[0].result;
|
|
||||||
fear = roll.dice[1].results[0].result;
|
|
||||||
if (
|
|
||||||
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).hope &&
|
|
||||||
config.roll.type === 'action'
|
|
||||||
) {
|
|
||||||
if (hope > fear) {
|
|
||||||
await this.update({
|
|
||||||
'system.resources.hope.value': Math.min(
|
|
||||||
this.system.resources.hope.value + 1,
|
|
||||||
this.system.resources.hope.max
|
|
||||||
)
|
|
||||||
});
|
|
||||||
} else if (hope === fear) {
|
|
||||||
await this.update({
|
|
||||||
'system.resources': {
|
|
||||||
'hope.value': Math.min(
|
|
||||||
this.system.resources.hope.value + 1,
|
|
||||||
this.system.resources.hope.max
|
|
||||||
),
|
|
||||||
'stress.value': Math.max(this.system.resources.stress.value - 1, 0)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.checkTarget) {
|
|
||||||
targets = Array.from(game.user.targets).map(x => {
|
|
||||||
const target = {
|
|
||||||
id: x.id,
|
|
||||||
name: x.actor.name,
|
|
||||||
img: x.actor.img,
|
|
||||||
difficulty: x.actor.system.difficulty,
|
|
||||||
evasion: x.actor.system.evasion?.value
|
|
||||||
};
|
|
||||||
|
|
||||||
target.hit = target.difficulty ? roll.total >= target.difficulty : roll.total >= target.evasion;
|
|
||||||
|
|
||||||
return target;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.chatMessage) {
|
|
||||||
const configRoll = {
|
|
||||||
title: config.title,
|
|
||||||
origin: this.id,
|
|
||||||
dice,
|
|
||||||
roll,
|
|
||||||
modifiers: modifiers.filter(x => x.label),
|
|
||||||
advantageState: advantage
|
|
||||||
};
|
|
||||||
if (this.type === 'character') {
|
|
||||||
configRoll.hope = { dice: hopeDice, value: hope };
|
|
||||||
configRoll.fear = { dice: fearDice, value: fear };
|
|
||||||
configRoll.advantage = { dice: advantageDice, value: roll.dice[2]?.results[0].result ?? null };
|
|
||||||
}
|
|
||||||
if (damage) configRoll.damage = damage;
|
|
||||||
if (targets) configRoll.targets = targets;
|
|
||||||
const systemData =
|
|
||||||
this.type === 'character' && !config.roll.simple ? new DHDualityRoll(configRoll) : configRoll,
|
|
||||||
cls = getDocumentClass('ChatMessage'),
|
|
||||||
msg = new cls({
|
|
||||||
type: config.chatMessage.type ?? 'dualityRoll',
|
|
||||||
sound: config.chatMessage.mute ? null : CONFIG.sounds.dice,
|
|
||||||
system: systemData,
|
|
||||||
content: config.chatMessage.template,
|
|
||||||
rolls: [roll]
|
|
||||||
});
|
|
||||||
|
|
||||||
await cls.create(msg.toObject());
|
|
||||||
}
|
|
||||||
return roll;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
formatRollModifier(roll) {
|
formatRollModifier(roll) {
|
||||||
|
|
@ -507,124 +380,76 @@ export default class DhpActor extends Actor {
|
||||||
? 1
|
? 1
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
const update = {
|
if (
|
||||||
'system.resources.hitPoints.value': Math.min(
|
this.type === 'character' &&
|
||||||
this.system.resources.hitPoints.value + hpDamage,
|
this.system.armor &&
|
||||||
this.system.resources.hitPoints.max
|
this.system.armor.system.marks.value < this.system.armorScore
|
||||||
)
|
) {
|
||||||
};
|
new Promise((resolve, reject) => {
|
||||||
|
new DamageReductionDialog(resolve, reject, this, hpDamage).render(true);
|
||||||
if (game.user.isGM) {
|
})
|
||||||
await this.update(update);
|
.then(async ({ modifiedDamage, armorSpent, stressSpent }) => {
|
||||||
|
const resources = [
|
||||||
|
{ value: modifiedDamage, type: 'hitPoints' },
|
||||||
|
...(armorSpent ? [{ value: armorSpent, type: 'armorStack' }] : []),
|
||||||
|
...(stressSpent ? [{ value: stressSpent, type: 'stress' }] : [])
|
||||||
|
];
|
||||||
|
await this.modifyResource(resources);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const msg = new cls({
|
||||||
|
user: game.user.id,
|
||||||
|
content: game.i18n.format('DAGGERHEART.DamageReduction.Notifications.DamageIgnore', {
|
||||||
|
character: this.name
|
||||||
|
})
|
||||||
|
});
|
||||||
|
cls.create(msg.toObject());
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
await this.modifyResource([{ value: hpDamage, type: 'hitPoints' }]);
|
||||||
action: socketEvent.GMUpdate,
|
|
||||||
data: {
|
|
||||||
action: GMUpdateEvent.UpdateDocument,
|
|
||||||
uuid: this.uuid,
|
|
||||||
update: update
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async takeHealing(healing, type) {
|
async takeHealing(resources) {
|
||||||
let update = {};
|
resources.forEach(r => (r.value *= -1));
|
||||||
switch (type) {
|
await this.modifyResource(resources);
|
||||||
case SYSTEM.GENERAL.healingTypes.health.id:
|
|
||||||
update = {
|
|
||||||
'system.resources.hitPoints.value': Math.min(
|
|
||||||
this.system.resources.hitPoints.value + healing,
|
|
||||||
this.system.resources.hitPoints.max
|
|
||||||
)
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case SYSTEM.GENERAL.healingTypes.stress.id:
|
|
||||||
update = {
|
|
||||||
'system.resources.stress.value': Math.min(
|
|
||||||
this.system.resources.stress.value + healing,
|
|
||||||
this.system.resources.stress.max
|
|
||||||
)
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game.user.isGM) {
|
|
||||||
await this.update(update);
|
|
||||||
} else {
|
|
||||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
|
||||||
action: socketEvent.GMUpdate,
|
|
||||||
data: {
|
|
||||||
action: GMUpdateEvent.UpdateDocument,
|
|
||||||
uuid: this.uuid,
|
|
||||||
update: update
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Move to action-scope?
|
async modifyResource(resources) {
|
||||||
async useAction(action) {
|
if (!resources.length) return;
|
||||||
const userTargets = Array.from(game.user.targets);
|
let updates = { actor: { target: this, resources: {} }, armor: { target: this.system.armor, resources: {} } };
|
||||||
const otherTarget = action.target.type === SYSTEM.ACTIONS.targetTypes.other.id;
|
resources.forEach(r => {
|
||||||
if (otherTarget && userTargets.length === 0) {
|
switch (r.type) {
|
||||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.ActionRequiresTarget'));
|
case 'armorStack':
|
||||||
return;
|
updates.armor.resources['system.marks.value'] = Math.max(
|
||||||
}
|
Math.min(this.system.armor.system.marks.value + r.value, this.system.armorScore),
|
||||||
|
0
|
||||||
if (action.cost.type != null && action.cost.value != null) {
|
);
|
||||||
if (
|
break;
|
||||||
this.system.resources[action.cost.type].value - action.cost.value <=
|
default:
|
||||||
this.system.resources[action.cost.type].min
|
updates.actor.resources[`system.resources.${r.type}.value`] = Math.max(
|
||||||
) {
|
Math.min(this.system.resources[r.type].value + r.value, this.system.resources[r.type].max),
|
||||||
ui.notifications.error(game.i18n.localize(`Insufficient ${action.cost.type} to use this ability`));
|
0
|
||||||
return;
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
Object.values(updates).forEach(async u => {
|
||||||
// const targets = otherTarget ? userTargets : [game.user.character];
|
if (Object.keys(u.resources).length > 0) {
|
||||||
if (action.damage.type) {
|
if (game.user.isGM) {
|
||||||
let roll = { formula: action.damage.value, result: action.damage.value };
|
await u.target.update(u.resources);
|
||||||
if (Number.isNaN(Number.parseInt(action.damage.value))) {
|
} else {
|
||||||
roll = await new Roll(`1${action.damage.value}`).evaluate();
|
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||||
|
action: socketEvent.GMUpdate,
|
||||||
|
data: {
|
||||||
|
action: GMUpdateEvent.UpdateDocument,
|
||||||
|
uuid: u.target.uuid,
|
||||||
|
update: u.resources
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
const cls = getDocumentClass('ChatMessage');
|
|
||||||
const msg = new cls({
|
|
||||||
user: game.user.id,
|
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
|
||||||
'systems/daggerheart/templates/chat/damage-roll.hbs',
|
|
||||||
{
|
|
||||||
roll: roll.formula,
|
|
||||||
total: roll.result,
|
|
||||||
type: action.damage.type
|
|
||||||
}
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
cls.create(msg.toObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.healing.type) {
|
|
||||||
let roll = { formula: action.healing.value, result: action.healing.value };
|
|
||||||
if (Number.isNaN(Number.parseInt(action.healing.value))) {
|
|
||||||
roll = await new Roll(`1${action.healing.value}`).evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
|
||||||
const msg = new cls({
|
|
||||||
user: game.user.id,
|
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
|
||||||
'systems/daggerheart/templates/chat/healing-roll.hbs',
|
|
||||||
{
|
|
||||||
roll: roll.formula,
|
|
||||||
total: roll.result,
|
|
||||||
type: action.healing.type
|
|
||||||
}
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
cls.create(msg.toObject());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,14 +131,7 @@ export default class DhpItem extends Item {
|
||||||
action = await this.selectActionDialog();
|
action = await this.selectActionDialog();
|
||||||
}
|
}
|
||||||
if (action) response = action.use(event);
|
if (action) response = action.use(event);
|
||||||
// Check Target
|
|
||||||
// If action.roll => Roll Dialog
|
|
||||||
// Else If action.cost => Cost Dialog
|
|
||||||
// Then
|
|
||||||
// Apply Cost
|
|
||||||
// Apply Effect
|
|
||||||
}
|
}
|
||||||
// Display Item Card in chat
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -233,3 +233,29 @@ Roll.replaceFormulaData = function (formula, data, { missing, warn = false } = {
|
||||||
formula = terms.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula);
|
formula = terms.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula);
|
||||||
return nativeReplaceFormulaData(formula, data, { missing, warn });
|
return nativeReplaceFormulaData(formula, data, { missing, warn });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDamageLabel = damage => {
|
||||||
|
switch (damage) {
|
||||||
|
case 3:
|
||||||
|
return game.i18n.localize('DAGGERHEART.General.Damage.Severe');
|
||||||
|
case 2:
|
||||||
|
return game.i18n.localize('DAGGERHEART.General.Damage.Major');
|
||||||
|
case 1:
|
||||||
|
return game.i18n.localize('DAGGERHEART.General.Damage.Minor');
|
||||||
|
case 0:
|
||||||
|
return game.i18n.localize('DAGGERHEART.General.Damage.None');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const damageKeyToNumber = key => {
|
||||||
|
switch (key) {
|
||||||
|
case 'severe':
|
||||||
|
return 3;
|
||||||
|
case 'major':
|
||||||
|
return 2;
|
||||||
|
case 'minor':
|
||||||
|
return 1;
|
||||||
|
case 'none':
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,18 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
}
|
}
|
||||||
|
|
||||||
addChatListeners = async (app, html, data) => {
|
addChatListeners = async (app, html, data) => {
|
||||||
html.querySelectorAll('.duality-action').forEach(element =>
|
html.querySelectorAll('.duality-action-damage').forEach(element =>
|
||||||
element.addEventListener('click', event => this.onRollDamage(event, data.message))
|
element.addEventListener('click', event => this.onRollDamage(event, data.message))
|
||||||
);
|
);
|
||||||
|
html.querySelectorAll('.duality-action-healing').forEach(element =>
|
||||||
|
element.addEventListener('click', event => this.onRollHealing(event, data.message))
|
||||||
|
);
|
||||||
|
html.querySelectorAll('.target-save-container').forEach(element =>
|
||||||
|
element.addEventListener('click', event => this.onRollSave(event, data.message))
|
||||||
|
);
|
||||||
|
html.querySelectorAll('.duality-action-effect').forEach(element =>
|
||||||
|
element.addEventListener('click', event => this.onApplyEffect(event, data.message))
|
||||||
|
);
|
||||||
html.querySelectorAll('.target-container').forEach(element => {
|
html.querySelectorAll('.target-container').forEach(element => {
|
||||||
element.addEventListener('mouseenter', this.hoverTarget);
|
element.addEventListener('mouseenter', this.hoverTarget);
|
||||||
element.addEventListener('mouseleave', this.unhoverTarget);
|
element.addEventListener('mouseleave', this.unhoverTarget);
|
||||||
|
|
@ -27,7 +36,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
html.querySelectorAll('.damage-button').forEach(element =>
|
html.querySelectorAll('.damage-button').forEach(element =>
|
||||||
element.addEventListener('click', event => this.onDamage(event, data.message))
|
element.addEventListener('click', event => this.onDamage(event, data.message))
|
||||||
);
|
);
|
||||||
html.querySelectorAll('.healing-button').forEach(element => element.addEventListener('click', this.onHealing));
|
html.querySelectorAll('.healing-button').forEach(element =>
|
||||||
|
element.addEventListener('click', event => this.onHealing(event, data.message))
|
||||||
|
);
|
||||||
html.querySelectorAll('.target-indicator').forEach(element =>
|
html.querySelectorAll('.target-indicator').forEach(element =>
|
||||||
element.addEventListener('click', this.onToggleTargets)
|
element.addEventListener('click', this.onToggleTargets)
|
||||||
);
|
);
|
||||||
|
|
@ -54,17 +65,65 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
super.close(options);
|
super.close(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getActor(id) {
|
||||||
|
// return game.actors.get(id);
|
||||||
|
return await fromUuid(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAction(actor, itemId, actionId) {
|
||||||
|
const item = actor.items.get(itemId),
|
||||||
|
action =
|
||||||
|
actor.system.attack?._id === actionId
|
||||||
|
? actor.system.attack
|
||||||
|
: item?.system?.actions?.find(a => a._id === actionId);
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
onRollDamage = async (event, message) => {
|
onRollDamage = async (event, message) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const actor = game.actors.get(message.system.origin);
|
const actor = await this.getActor(message.system.source.actor);
|
||||||
if (!actor || !game.user.isGM) return true;
|
if (!actor || !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;
|
||||||
|
await action.rollDamage(event, message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
await actor.damageRoll(
|
onRollHealing = async (event, message) => {
|
||||||
message.system.title,
|
event.stopPropagation();
|
||||||
message.system.damage,
|
const actor = await this.getActor(message.system.source.actor);
|
||||||
message.system.targets.filter(x => x.hit).map(x => ({ id: x.id, name: x.name, img: x.img })),
|
if (!actor || !game.user.isGM) return true;
|
||||||
event.shiftKey
|
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?.rollHealing) return;
|
||||||
|
await action.rollHealing(event, message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onRollSave = async (event, message) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const actor = await this.getActor(message.system.source.actor),
|
||||||
|
tokenId = event.target.closest('[data-token]')?.dataset.token,
|
||||||
|
token = game.canvas.tokens.get(tokenId);
|
||||||
|
if (!token?.actor || !token.isOwner) return true;
|
||||||
|
console.log(token.actor.canUserModify(game.user, 'update'));
|
||||||
|
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?.hasSave) return;
|
||||||
|
action.rollSave(token, event, message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onApplyEffect = async (event, message) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const actor = await this.getActor(message.system.source.actor);
|
||||||
|
if (!actor || !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?.applyEffects) return;
|
||||||
|
await action.applyEffects(event, message);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
hoverTarget = event => {
|
hoverTarget = event => {
|
||||||
|
|
@ -95,24 +154,36 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
? message.system.targets.map(target => game.canvas.tokens.get(target.id))
|
? message.system.targets.map(target => game.canvas.tokens.get(target.id))
|
||||||
: Array.from(game.user.targets);
|
: Array.from(game.user.targets);
|
||||||
|
|
||||||
|
if(message.system.onSave && event.currentTarget.dataset.targetHit) {
|
||||||
|
console.log(message.system.targets)
|
||||||
|
const pendingingSaves = message.system.targets.filter(target => target.hit && target.saved.success === null);
|
||||||
|
if(pendingingSaves.length) {
|
||||||
|
const confirm = await foundry.applications.api.DialogV2.confirm({
|
||||||
|
window: {title: "Pending Reaction Rolls found"},
|
||||||
|
content: `<p>Some Tokens still need to roll their Reaction Roll.</p><p>Are you sure you want to continue ?</p><p><i>Undone reaction rolls will be considered as failed</i></p>`
|
||||||
|
});
|
||||||
|
if ( !confirm ) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (targets.length === 0)
|
if (targets.length === 0)
|
||||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
||||||
|
for (let target of targets) {
|
||||||
for (var target of targets) {
|
let damage = message.system.roll.total;
|
||||||
await target.actor.takeDamage(message.system.damage.total, message.system.damage.type);
|
if(message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true) damage = Math.ceil(damage * (SYSTEM.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1));
|
||||||
|
await target.actor.takeDamage(damage, message.system.roll.type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onHealing = async event => {
|
onHealing = async (event, message) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const healing = Number.parseInt(event.currentTarget.dataset.value);
|
|
||||||
const targets = Array.from(game.user.targets);
|
const targets = Array.from(game.user.targets);
|
||||||
|
|
||||||
if (targets.length === 0)
|
if (targets.length === 0)
|
||||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
||||||
|
|
||||||
for (var target of targets) {
|
for (var target of targets) {
|
||||||
await target.actor.takeHealing(healing, event.currentTarget.dataset.type);
|
await target.actor.takeHealing([{ value: message.system.roll.total, type: message.system.roll.type }]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -139,7 +210,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
const action = message.system.actions[Number.parseInt(event.currentTarget.dataset.index)];
|
const action = message.system.actions[Number.parseInt(event.currentTarget.dataset.index)];
|
||||||
const actor = game.actors.get(message.system.origin);
|
const actor = game.actors.get(message.system.source.actor);
|
||||||
await actor.useAction(action);
|
await actor.useAction(action);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,10 @@ div.daggerheart.views.multiclass {
|
||||||
filter: invert(0%) sepia(100%) saturate(0%) hue-rotate(21deg) brightness(17%) contrast(103%);
|
filter: invert(0%) sepia(100%) saturate(0%) hue-rotate(21deg) brightness(17%) contrast(103%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#roll-selection-costSelection footer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.roll-dialog-container {
|
.roll-dialog-container {
|
||||||
.disadvantage,
|
.disadvantage,
|
||||||
|
|
@ -368,6 +372,7 @@ div.daggerheart.views.multiclass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.roll-dialog-experience-container {
|
.roll-dialog-experience-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fieldset.daggerheart.chat {
|
||||||
|
padding: 0;
|
||||||
|
border-left-width: 0;
|
||||||
|
border-right-width: 0;
|
||||||
|
border-bottom-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.daggerheart.chat {
|
.daggerheart.chat {
|
||||||
&.downtime {
|
&.downtime {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -227,19 +234,35 @@
|
||||||
background: @miss;
|
background: @miss;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img, .target-save-container {
|
||||||
flex: 0;
|
|
||||||
width: 22px;
|
width: 22px;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
margin-left: 8px;
|
|
||||||
align-self: center;
|
align-self: center;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
flex: 0;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.target-save-container {
|
||||||
|
margin-right: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: unset;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
.target-inner-container {
|
.target-inner-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
font-size: var(--font-size-16);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:has(.target-save-container)) .target-inner-container {
|
||||||
margin-right: @hugeMargin;
|
margin-right: @hugeMargin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -314,12 +337,29 @@
|
||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-use-perm='false'] {
|
||||||
|
pointer-events: none;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
[data-view-perm='false'] {
|
||||||
|
> * {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
content: "??";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-colorful {
|
.theme-colorful {
|
||||||
.chat-message.duality {
|
.chat-message.duality {
|
||||||
border-color: black;
|
border-color: black;
|
||||||
padding: 8px 0 0 0;
|
padding: 8px 0 0 0;
|
||||||
|
fieldset.daggerheart.chat {
|
||||||
|
border-top-width: 0;
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
.message-header {
|
.message-header {
|
||||||
color: var(--color-light-3);
|
color: var(--color-light-3);
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
|
|
@ -450,6 +490,10 @@
|
||||||
.duality-action {
|
.duality-action {
|
||||||
border-radius: 0 6px 0 0;
|
border-radius: 0 6px 0 0;
|
||||||
margin-left: -8px;
|
margin-left: -8px;
|
||||||
|
&.duality-action-effect {
|
||||||
|
border-top-left-radius: 6px;
|
||||||
|
margin-left: initial;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.duality-result {
|
.duality-result {
|
||||||
border-radius: 6px 0 0 0;
|
border-radius: 6px 0 0 0;
|
||||||
|
|
|
||||||
|
|
@ -1400,6 +1400,12 @@
|
||||||
.chat-message .dice-title {
|
.chat-message .dice-title {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
fieldset.daggerheart.chat {
|
||||||
|
padding: 0;
|
||||||
|
border-left-width: 0;
|
||||||
|
border-right-width: 0;
|
||||||
|
border-bottom-width: 0;
|
||||||
|
}
|
||||||
.daggerheart.chat.downtime {
|
.daggerheart.chat.downtime {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -1553,18 +1559,32 @@
|
||||||
.daggerheart.chat.roll .target-section .target-container.miss {
|
.daggerheart.chat.roll .target-section .target-container.miss {
|
||||||
background: #ff0000;
|
background: #ff0000;
|
||||||
}
|
}
|
||||||
.daggerheart.chat.roll .target-section .target-container img {
|
.daggerheart.chat.roll .target-section .target-container img,
|
||||||
flex: 0;
|
.daggerheart.chat.roll .target-section .target-container .target-save-container {
|
||||||
width: 22px;
|
width: 22px;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
margin-left: 8px;
|
|
||||||
align-self: center;
|
align-self: center;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
.daggerheart.chat.roll .target-section .target-container img {
|
||||||
|
flex: 0;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
.daggerheart.chat.roll .target-section .target-container .target-save-container {
|
||||||
|
margin-right: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: unset;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
.daggerheart.chat.roll .target-section .target-container .target-inner-container {
|
.daggerheart.chat.roll .target-section .target-container .target-inner-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
font-size: var(--font-size-16);
|
||||||
|
}
|
||||||
|
.daggerheart.chat.roll .target-section .target-container:not(:has(.target-save-container)) .target-inner-container {
|
||||||
margin-right: 32px;
|
margin-right: 32px;
|
||||||
}
|
}
|
||||||
.daggerheart.chat.roll .dice-actions {
|
.daggerheart.chat.roll .dice-actions {
|
||||||
|
|
@ -1622,10 +1642,24 @@
|
||||||
.daggerheart.chat.domain-card img {
|
.daggerheart.chat.domain-card img {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
.daggerheart.chat [data-use-perm='false'] {
|
||||||
|
pointer-events: none;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
.daggerheart.chat [data-view-perm='false'] > * {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.daggerheart.chat [data-view-perm='false']::after {
|
||||||
|
content: "??";
|
||||||
|
}
|
||||||
.theme-colorful .chat-message.duality {
|
.theme-colorful .chat-message.duality {
|
||||||
border-color: black;
|
border-color: black;
|
||||||
padding: 8px 0 0 0;
|
padding: 8px 0 0 0;
|
||||||
}
|
}
|
||||||
|
.theme-colorful .chat-message.duality fieldset.daggerheart.chat {
|
||||||
|
border-top-width: 0;
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
.theme-colorful .chat-message.duality .message-header {
|
.theme-colorful .chat-message.duality .message-header {
|
||||||
color: var(--color-light-3);
|
color: var(--color-light-3);
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
|
|
@ -1752,6 +1786,10 @@
|
||||||
border-radius: 0 6px 0 0;
|
border-radius: 0 6px 0 0;
|
||||||
margin-left: -8px;
|
margin-left: -8px;
|
||||||
}
|
}
|
||||||
|
.theme-colorful .chat-message.duality .message-content .dice-result .dice-actions .duality-action.duality-action-effect {
|
||||||
|
border-top-left-radius: 6px;
|
||||||
|
margin-left: initial;
|
||||||
|
}
|
||||||
.theme-colorful .chat-message.duality .message-content .dice-result .dice-actions .duality-result {
|
.theme-colorful .chat-message.duality .message-content .dice-result .dice-actions .duality-result {
|
||||||
border-radius: 6px 0 0 0;
|
border-radius: 6px 0 0 0;
|
||||||
margin-right: -8px;
|
margin-right: -8px;
|
||||||
|
|
@ -1978,6 +2016,9 @@ div.daggerheart.views.multiclass {
|
||||||
.daggerheart.views.roll-selection .roll-selection-container i {
|
.daggerheart.views.roll-selection .roll-selection-container i {
|
||||||
filter: invert(0%) sepia(100%) saturate(0%) hue-rotate(21deg) brightness(17%) contrast(103%);
|
filter: invert(0%) sepia(100%) saturate(0%) hue-rotate(21deg) brightness(17%) contrast(103%);
|
||||||
}
|
}
|
||||||
|
.daggerheart.views.roll-selection #roll-selection-costSelection footer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.daggerheart.views.roll-selection .roll-dialog-container .disadvantage,
|
.daggerheart.views.roll-selection .roll-dialog-container .disadvantage,
|
||||||
.daggerheart.views.roll-selection .roll-dialog-container .advantage {
|
.daggerheart.views.roll-selection .roll-dialog-container .advantage {
|
||||||
border: 2px solid #708090;
|
border: 2px solid #708090;
|
||||||
|
|
@ -3110,6 +3151,125 @@ div.daggerheart.views.multiclass {
|
||||||
.daggerheart.views.ownership-selection .ownership-outer-container .ownership-container select {
|
.daggerheart.views.ownership-selection .ownership-outer-container .ownership-container select {
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
}
|
}
|
||||||
|
.daggerheart.views.damage-reduction .window-content {
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .section-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .padded {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .armor-title {
|
||||||
|
margin: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .resources-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .resources-container .resource-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .mark-selection {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .mark-selection .mark-selection-inner {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .mark-selection .mark-selection-inner:not(:last-child) {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .mark-selection .mark-selection-inner .mark-container {
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid light-dark(#18162e, #f3c267);
|
||||||
|
border-radius: 6px;
|
||||||
|
height: 26px;
|
||||||
|
padding: 0 1px;
|
||||||
|
font-size: 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .mark-selection .mark-selection-inner .mark-container.selected {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .mark-selection .mark-selection-inner .mark-container.inactive {
|
||||||
|
cursor: initial;
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .mark-selection .mark-selection-inner .mark-container .fa-shield {
|
||||||
|
position: relative;
|
||||||
|
right: 0.5px;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .stress-reduction-container {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .stress-reduction-container .stress-reduction {
|
||||||
|
border: 1px solid light-dark(#18162e, #f3c267);
|
||||||
|
border-radius: 6px;
|
||||||
|
height: 26px;
|
||||||
|
padding: 0 4px;
|
||||||
|
font-size: 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .stress-reduction-container .stress-reduction.active {
|
||||||
|
opacity: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .stress-reduction-container .stress-reduction.selected {
|
||||||
|
opacity: 1;
|
||||||
|
background: var(--color-warm-2);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .stress-reduction-container .stress-reduction .stress-reduction-cost {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .markers-subtitle {
|
||||||
|
margin: -4px 0 0 0;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container .markers-subtitle.bold {
|
||||||
|
font-variant: all-small-caps;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container footer {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container footer button {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container footer button .damage-value {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.daggerheart.views.damage-reduction .damage-reduction-container footer button .damage-value.reduced-value {
|
||||||
|
opacity: 0.4;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
:root {
|
:root {
|
||||||
--shadow-text-stroke: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
--shadow-text-stroke: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
||||||
--fear-animation: background 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease, opacity 0.3s ease;
|
--fear-animation: background 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease, opacity 0.3s ease;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
@import './characterCreation.less';
|
@import './characterCreation.less';
|
||||||
@import './levelup.less';
|
@import './levelup.less';
|
||||||
@import './ownershipSelection.less';
|
@import './ownershipSelection.less';
|
||||||
|
@import './damageReduction.less';
|
||||||
@import './resources.less';
|
@import './resources.less';
|
||||||
@import './countdown.less';
|
@import './countdown.less';
|
||||||
@import './settings.less';
|
@import './settings.less';
|
||||||
|
|
|
||||||
145
styles/damageReduction.less
Normal file
145
styles/damageReduction.less
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
.daggerheart.views.damage-reduction {
|
||||||
|
.window-content {
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.damage-reduction-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.section-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.padded {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.armor-title {
|
||||||
|
margin: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resources-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.resource-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mark-selection {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.mark-selection-inner {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mark-container {
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
border-radius: 6px;
|
||||||
|
height: 26px;
|
||||||
|
padding: 0 1px;
|
||||||
|
font-size: 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0.4;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.inactive {
|
||||||
|
cursor: initial;
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-shield {
|
||||||
|
position: relative;
|
||||||
|
right: 0.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stress-reduction-container {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.stress-reduction {
|
||||||
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
border-radius: 6px;
|
||||||
|
height: 26px;
|
||||||
|
padding: 0 4px;
|
||||||
|
font-size: 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
opacity: 0.4;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
opacity: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
opacity: 1;
|
||||||
|
background: var(--color-warm-2);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stress-reduction-cost {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.markers-subtitle {
|
||||||
|
margin: -4px 0 0 0;
|
||||||
|
|
||||||
|
&.bold {
|
||||||
|
font-variant: all-small-caps;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.damage-value {
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&.reduced-value {
|
||||||
|
opacity: 0.4;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.actor.dh-style.character {
|
.application.sheet.daggerheart.actor.dh-style.character {
|
||||||
.window-content {
|
.window-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
@import '../../utils/colors.less';
|
@import '../../utils/colors.less';
|
||||||
@import '../../utils/fonts.less';
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.actor.dh-style.character {
|
.application.sheet.daggerheart.actor.dh-style.character {
|
||||||
.tab.biography {
|
.tab.biography {
|
||||||
.items-section {
|
.items-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 98%, transparent 100%);
|
mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 98%, transparent 100%);
|
||||||
padding-bottom: 40px;
|
padding-bottom: 40px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
@import '../../utils/colors.less';
|
@import '../../utils/colors.less';
|
||||||
@import '../../utils/fonts.less';
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.actor.dh-style.character {
|
.application.sheet.daggerheart.actor.dh-style.character {
|
||||||
.tab.features {
|
.tab.features {
|
||||||
.features-sections {
|
.features-sections {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
|
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
height: 95%;
|
height: 95%;
|
||||||
|
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,65 +1,65 @@
|
||||||
@import '../../utils/colors.less';
|
@import '../../utils/colors.less';
|
||||||
@import '../../utils/fonts.less';
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.actor.dh-style.character {
|
.application.sheet.daggerheart.actor.dh-style.character {
|
||||||
.tab.inventory {
|
.tab.inventory {
|
||||||
.search-section {
|
.search-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.search-bar {
|
.search-bar {
|
||||||
position: relative;
|
position: relative;
|
||||||
color: light-dark(@dark-blue-50, @beige-50);
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
background: light-dark(@dark-blue-10, @golden-10);
|
background: light-dark(@dark-blue-10, @golden-10);
|
||||||
border: none;
|
border: none;
|
||||||
outline: 2px solid transparent;
|
outline: 2px solid transparent;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
outline: 2px solid light-dark(@dark, @golden);
|
outline: 2px solid light-dark(@dark, @golden);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:placeholder {
|
&:placeholder {
|
||||||
color: light-dark(@dark-blue-50, @beige-50);
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
align-content: center;
|
align-content: center;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
color: light-dark(@dark-blue-50, @beige-50);
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.items-section {
|
.items-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
|
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
height: 80%;
|
height: 80%;
|
||||||
|
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.currency-section {
|
.currency-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,101 +1,101 @@
|
||||||
@import '../../utils/colors.less';
|
@import '../../utils/colors.less';
|
||||||
@import '../../utils/fonts.less';
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.actor.dh-style.character {
|
.application.sheet.daggerheart.actor.dh-style.character {
|
||||||
.tab.loadout {
|
.tab.loadout {
|
||||||
.search-section {
|
.search-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
.search-bar {
|
.search-bar {
|
||||||
position: relative;
|
position: relative;
|
||||||
color: light-dark(@dark-blue-50, @beige-50);
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
width: 80%;
|
width: 80%;
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
background: light-dark(@dark-blue-10, @golden-10);
|
background: light-dark(@dark-blue-10, @golden-10);
|
||||||
border: none;
|
border: none;
|
||||||
outline: 2px solid transparent;
|
outline: 2px solid transparent;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
outline: 2px solid light-dark(@dark, @golden);
|
outline: 2px solid light-dark(@dark, @golden);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:placeholder {
|
&:placeholder {
|
||||||
color: light-dark(@dark-blue-50, @beige-50);
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
align-content: center;
|
align-content: center;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
color: light-dark(@dark-blue-50, @beige-50);
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-toggle-view {
|
.btn-toggle-view {
|
||||||
background: light-dark(@dark-blue-10, @dark-blue);
|
background: light-dark(@dark-blue-10, @dark-blue);
|
||||||
border: 1px solid light-dark(@dark-blue, @golden);
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
width: 62px;
|
width: 62px;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
margin: 1px;
|
margin: 1px;
|
||||||
width: 26px;
|
width: 26px;
|
||||||
color: light-dark(@dark-blue, @golden);
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
|
||||||
&.list-icon {
|
&.list-icon {
|
||||||
i {
|
i {
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.grid-icon {
|
&.grid-icon {
|
||||||
i {
|
i {
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.list-active {
|
&.list-active {
|
||||||
border-radius: 32px 3px 3px 32px;
|
border-radius: 32px 3px 3px 32px;
|
||||||
background-color: light-dark(@dark-blue, @golden);
|
background-color: light-dark(@dark-blue, @golden);
|
||||||
color: light-dark(@beige, @dark-blue);
|
color: light-dark(@beige, @dark-blue);
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.grid-active {
|
&.grid-active {
|
||||||
border-radius: 3px 32px 32px 3px;
|
border-radius: 3px 32px 32px 3px;
|
||||||
background-color: light-dark(@dark-blue, @golden);
|
background-color: light-dark(@dark-blue, @golden);
|
||||||
color: light-dark(@beige, @dark-blue);
|
color: light-dark(@beige, @dark-blue);
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.items-section {
|
.items-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 98%, transparent 100%);
|
mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 98%, transparent 100%);
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
height: 90%;
|
height: 90%;
|
||||||
|
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,32 @@
|
||||||
@import '../../utils/colors.less';
|
@import '../../utils/colors.less';
|
||||||
@import '../../utils/fonts.less';
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.actor.dh-style.character {
|
.application.sheet.daggerheart.actor.dh-style.character {
|
||||||
.window-content {
|
.window-content {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 275px 1fr;
|
grid-template-columns: 275px 1fr;
|
||||||
grid-template-rows: 283px 1fr;
|
grid-template-rows: 283px 1fr;
|
||||||
gap: 15px 0;
|
gap: 15px 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.character-sidebar-sheet {
|
.character-sidebar-sheet {
|
||||||
grid-row: 1 / span 2;
|
grid-row: 1 / span 2;
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.character-header-sheet {
|
.character-header-sheet {
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
grid-row: 2;
|
grid-row: 2;
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.old-sheet {
|
.old-sheet {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,310 +1,310 @@
|
||||||
@import '../../utils/colors.less';
|
@import '../../utils/colors.less';
|
||||||
@import '../../utils/fonts.less';
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.actor.dh-style.character {
|
.application.sheet.daggerheart.actor.dh-style.character {
|
||||||
.character-sidebar-sheet {
|
.character-sidebar-sheet {
|
||||||
width: 275px;
|
width: 275px;
|
||||||
min-width: 275px;
|
min-width: 275px;
|
||||||
border-right: 1px solid light-dark(@dark-blue, @golden);
|
border-right: 1px solid light-dark(@dark-blue, @golden);
|
||||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
|
|
||||||
.theme-light & {
|
.theme-light & {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.portrait {
|
.portrait {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 235px;
|
height: 235px;
|
||||||
width: 275px;
|
width: 275px;
|
||||||
border-bottom: 1px solid light-dark(@dark-blue, @golden);
|
border-bottom: 1px solid light-dark(@dark-blue, @golden);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
height: 235px;
|
height: 235px;
|
||||||
width: 275px;
|
width: 275px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.death-roll-btn {
|
.death-roll-btn {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.death-roll {
|
&.death-roll {
|
||||||
filter: grayscale(1);
|
filter: grayscale(1);
|
||||||
|
|
||||||
.death-roll-btn {
|
.death-roll-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 30%;
|
top: 30%;
|
||||||
right: 30%;
|
right: 30%;
|
||||||
font-size: 6rem;
|
font-size: 6rem;
|
||||||
color: @beige;
|
color: @beige;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
text-shadow: 0 0 8px @beige;
|
text-shadow: 0 0 8px @beige;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-section {
|
.info-section {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
top: -20px;
|
top: -20px;
|
||||||
gap: 30px;
|
gap: 30px;
|
||||||
margin-bottom: -10px;
|
margin-bottom: -10px;
|
||||||
|
|
||||||
.resources-section {
|
.resources-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
|
|
||||||
.status-bar {
|
.status-bar {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
|
|
||||||
.status-label {
|
.status-label {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 40px;
|
top: 40px;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
width: 79px;
|
width: 79px;
|
||||||
clip-path: path('M0 0H79L74 16.5L39 22L4 16.5L0 0Z');
|
clip-path: path('M0 0H79L74 16.5L39 22L4 16.5L0 0Z');
|
||||||
background: light-dark(@dark-blue, @golden);
|
background: light-dark(@dark-blue, @golden);
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
color: light-dark(@beige, @dark-blue);
|
color: light-dark(@beige, @dark-blue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.status-value {
|
.status-value {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
color: @beige;
|
color: @beige;
|
||||||
|
|
||||||
input[type='number'] {
|
input[type='number'] {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border: none;
|
border: none;
|
||||||
outline: 2px solid transparent;
|
outline: 2px solid transparent;
|
||||||
color: @beige;
|
color: @beige;
|
||||||
|
|
||||||
&.bar-input {
|
&.bar-input {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: @beige;
|
color: @beige;
|
||||||
backdrop-filter: none;
|
backdrop-filter: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
background: @semi-transparent-dark-blue;
|
background: @semi-transparent-dark-blue;
|
||||||
backdrop-filter: blur(9.5px);
|
backdrop-filter: blur(9.5px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bar-label {
|
.bar-label {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border: 1px solid light-dark(@dark-blue, @golden);
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
&::-webkit-progress-bar {
|
&::-webkit-progress-bar {
|
||||||
border: none;
|
border: none;
|
||||||
background: @dark-blue;
|
background: @dark-blue;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
&::-webkit-progress-value {
|
&::-webkit-progress-value {
|
||||||
background: @gradient-hp;
|
background: @gradient-hp;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
&.stress-color::-webkit-progress-value {
|
&.stress-color::-webkit-progress-value {
|
||||||
background: @gradient-stress;
|
background: @gradient-stress;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
&::-moz-progress-value,
|
&::-moz-progress-value,
|
||||||
&::-moz-progress-bar {
|
&::-moz-progress-bar {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-moz-progress-bar {
|
&::-moz-progress-bar {
|
||||||
background: @gradient-hp;
|
background: @gradient-hp;
|
||||||
}
|
}
|
||||||
&.stress-color::-moz-progress-bar {
|
&.stress-color::-moz-progress-bar {
|
||||||
background: @gradient-stress;
|
background: @gradient-stress;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-section {
|
.status-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
.status-number {
|
.status-number {
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
|
|
||||||
.status-value {
|
.status-value {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
border: 1px solid light-dark(@dark-blue, @golden);
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-radius: 6px 6px 0 0;
|
border-radius: 6px 6px 0 0;
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: light-dark(transparent, @dark-blue);
|
background: light-dark(transparent, @dark-blue);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
||||||
&.armor-slots {
|
&.armor-slots {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-label {
|
.status-label {
|
||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background: light-dark(@dark-blue, @golden);
|
background: light-dark(@dark-blue, @golden);
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: light-dark(@beige, @dark-blue);
|
color: light-dark(@beige, @dark-blue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.items-sidebar-list {
|
.items-sidebar-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
|
||||||
.inventory-item {
|
.inventory-item {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.equipment-section {
|
.equipment-section {
|
||||||
.title {
|
.title {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.items-list {
|
.items-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.loadout-section {
|
.loadout-section {
|
||||||
.title {
|
.title {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.experience-section {
|
.experience-section {
|
||||||
.title {
|
.title {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.experience-list {
|
.experience-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.experience-row {
|
.experience-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
input[type='text'] {
|
input[type='text'] {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: 180px;
|
width: 180px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
outline: 2px solid transparent;
|
outline: 2px solid transparent;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
color: light-dark(@dark, @beige);
|
color: light-dark(@dark, @beige);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
outline: 2px solid light-dark(@dark, @beige);
|
outline: 2px solid light-dark(@dark, @beige);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.experience-value {
|
.experience-value {
|
||||||
height: 25px;
|
height: 25px;
|
||||||
width: 35px;
|
width: 35px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
color: light-dark(@dark, @beige);
|
color: light-dark(@dark, @beige);
|
||||||
align-content: center;
|
align-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: url(../assets/svg/experience-shield.svg) no-repeat;
|
background: url(../assets/svg/experience-shield.svg) no-repeat;
|
||||||
|
|
||||||
.theme-light & {
|
.theme-light & {
|
||||||
background: url('../assets/svg/experience-shield-light.svg') no-repeat;
|
background: url('../assets/svg/experience-shield-light.svg') no-repeat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.actor.dh-style.character {
|
.application.sheet.daggerheart.actor.dh-style.character {
|
||||||
.items-list {
|
.items-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.card-list {
|
.card-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,133 +1,133 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.application.sheet.daggerheart.actor.dh-style.character {
|
.application.sheet.daggerheart.actor.dh-style.character {
|
||||||
.inventory-item {
|
.inventory-item {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 40px 1fr 60px;
|
grid-template-columns: 40px 1fr 60px;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.item-img {
|
.item-img {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-label {
|
.item-label {
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
|
|
||||||
.item-name {
|
.item-name {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-tags,
|
.item-tags,
|
||||||
.item-labels {
|
.item-labels {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
.tag {
|
.tag {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 3px 5px;
|
padding: 3px 5px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
background: light-dark(@dark-15, @beige-15);
|
background: light-dark(@dark-15, @beige-15);
|
||||||
border: 1px solid light-dark(@dark, @beige);
|
border: 1px solid light-dark(@dark, @beige);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls {
|
.controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
&.unequipped {
|
&.unequipped {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.card-item {
|
.card-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 120px;
|
height: 120px;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
border: 1px solid light-dark(@dark-blue, @golden);
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.card-label {
|
.card-label {
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
.controls {
|
.controls {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
max-height: 16px;
|
max-height: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-img {
|
.card-img {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-label {
|
.card-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: @dark-blue;
|
background-color: @dark-blue;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
mask-image: linear-gradient(180deg, transparent 0%, black 20%);
|
mask-image: linear-gradient(180deg, transparent 0%, black 20%);
|
||||||
|
|
||||||
.card-name {
|
.card-name {
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 15px;
|
line-height: 15px;
|
||||||
|
|
||||||
color: @beige;
|
color: @beige;
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls {
|
.controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
max-height: 0px;
|
max-height: 0px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: collapse;
|
visibility: collapse;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
color: @beige;
|
color: @beige;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
@import '../utils/colors.less';
|
@import '../utils/colors.less';
|
||||||
@import '../utils/fonts.less';
|
@import '../utils/fonts.less';
|
||||||
|
|
||||||
.sheet.daggerheart.dh-style {
|
.sheet.daggerheart.dh-style {
|
||||||
.tab-navigation {
|
.tab-navigation {
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
|
|
||||||
.feature-tab {
|
.feature-tab {
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: light-dark(@dark-blue, @golden);
|
color: light-dark(@dark-blue, @golden);
|
||||||
font-family: @font-body;
|
font-family: @font-body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,9 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "JimCanE"
|
"name": "JimCanE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Po0lp"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"scripts": ["build/daggerheart.js"],
|
"scripts": ["build/daggerheart.js"],
|
||||||
|
|
@ -252,7 +255,8 @@
|
||||||
"dualityRoll": {},
|
"dualityRoll": {},
|
||||||
"adversaryRoll": {},
|
"adversaryRoll": {},
|
||||||
"damageRoll": {},
|
"damageRoll": {},
|
||||||
"abilityUse": {}
|
"abilityUse": {},
|
||||||
|
"applyEffect": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"primaryTokenAttribute": "resources.health",
|
"primaryTokenAttribute": "resources.health",
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<button class="duality-action" data-value="{{roll.total}}" data-damage="{{damage.value}}" data-damage-type="{{damage.type}}"{{#if damage.disabled}} disabled{{/if}}><span>Roll Damage</span></button>
|
<button class="duality-action" data-value="{{roll.total}}"><span>Roll Damage</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1,33 +1,43 @@
|
||||||
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||||
|
<div class="dice-flavor">{{title}}</div>
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="dice-formula">{{roll.formula}}</div>
|
<div class="dice-formula">{{roll.formula}}</div>
|
||||||
<div class="dice-tooltip">
|
<div class="dice-tooltip">
|
||||||
<ol class="dice-rolls">
|
<div class="wrapper">
|
||||||
<div class="dice-hope-container">
|
<section class="tooltip-part">
|
||||||
{{#each dice}}
|
<div class="dice">
|
||||||
|
{{#each roll.dice}}
|
||||||
<header class="part-header flexrow">
|
<header class="part-header flexrow">
|
||||||
<span class="part-formula">{{number}}{{denomination}}</span>
|
<span class="part-formula">{{formula}}</span>
|
||||||
<span class="part-total">{{total}}</span>
|
<span class="part-total">{{total}}</span>
|
||||||
</header>
|
</header>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<ol class="dice-rolls">
|
<ol class="dice-rolls">
|
||||||
{{#each results}}
|
{{#each results}}
|
||||||
<li class="roll die {{../denomination}}{{#if discarded}} discarded{{/if}} min">{{result}}</li>
|
<li class="roll die {{../dice}}{{#if discarded}} discarded{{/if}} min">{{result}}</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ol>
|
</ol>
|
||||||
<div class="attack-roll-advantage-container">{{#if ../advantageState}}{{localize "DAGGERHEART.General.Advantage.Full"}}{{/if}}{{#if (eq ../advantageState false)}}{{localize "DAGGERHEART.General.Disadvantage.Full"}}{{/if}}</div>
|
<div class="attack-roll-advantage-container">
|
||||||
|
{{#if (eq ../roll.advantage.type 1)}}{{localize "DAGGERHEART.General.Advantage.Full"}}{{/if}}{{#if (eq ../roll.advantage.type -1)}}{{localize "DAGGERHEART.General.Disadvantage.Full"}}{{/if}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
<div class="modifiers-container">
|
</section>
|
||||||
{{#each modifiers}}
|
</div>
|
||||||
<li class="modifier-value" data-value="{{value}}" title="{{title}}">{{label}}</li>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</ol>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="dice-total">
|
<div class="dice-total">
|
||||||
<div class="dice-total-value">{{roll.total}}</div>
|
<div class="dice-total-value">{{roll.total}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{> 'systems/daggerheart/templates/chat/parts/target-chat.hbs'}}
|
||||||
|
{{#if hasDamage}}
|
||||||
|
<div class="dice-roll daggerheart chat roll">
|
||||||
|
<div class="dice-result">
|
||||||
|
<div class="flexrow">
|
||||||
|
<button class="duality-action duality-action-damage" data-value="{{roll.total}}"><span>Roll Damage</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
9
templates/chat/apply-effects.hbs
Normal file
9
templates/chat/apply-effects.hbs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||||
|
<div class="dice-flavor">{{title}}</div>
|
||||||
|
<div>{{{description}}}</div>
|
||||||
|
<div class="dice-result">
|
||||||
|
<div class="dice-actions">
|
||||||
|
<button class="duality-action-effect">{{localize "DAGGERHEART.Chat.AttackRoll.ApplyEffect"}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -1,21 +1,19 @@
|
||||||
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||||
<div class="dice-flavor">{{title}}</div>
|
<div class="dice-flavor">{{title}}</div>
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="dice-formula">{{roll}}</div>
|
<div class="dice-formula">{{roll.formula}}</div>
|
||||||
|
|
||||||
<div class="dice-tooltip">
|
<div class="dice-tooltip">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<section class="tooltip-part">
|
<section class="tooltip-part">
|
||||||
{{#each dice}}
|
{{#each roll.dice}}
|
||||||
<div class="dice">
|
<div class="dice">
|
||||||
<header class="part-header flexrow">
|
<header class="part-header flexrow">
|
||||||
<span class="part-formula">{{rolls.length}}{{type}}</span>
|
<span class="part-formula">{{formula}}</span>
|
||||||
|
<span class="part-total">{{total}}</span>
|
||||||
<span class="part-total">{{this.total}}</span>
|
|
||||||
</header>
|
</header>
|
||||||
<ol class="dice-rolls">
|
<ol class="dice-rolls">
|
||||||
{{#each rolls}}
|
{{#each results}}
|
||||||
<li class="roll die {{../type}} min">{{this}}</li>
|
<li class="roll die {{../denomination}} min">{{result}}</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -23,7 +21,12 @@
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dice-total">{{damage.total}}</div>
|
<div class="dice-total">{{roll.total}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{> 'systems/daggerheart/templates/chat/parts/target-chat.hbs'}}
|
||||||
|
<div class="dice-roll daggerheart chat roll">
|
||||||
|
<div class="dice-result">
|
||||||
<div class="dice-actions">
|
<div class="dice-actions">
|
||||||
<button class="damage-button" data-target-hit="true" {{#if (eq targets.length 0)}}disabled{{/if}}>{{localize "DAGGERHEART.Chat.DamageRoll.DealDamageToTargets"}}</button>
|
<button class="damage-button" data-target-hit="true" {{#if (eq targets.length 0)}}disabled{{/if}}>{{localize "DAGGERHEART.Chat.DamageRoll.DealDamageToTargets"}}</button>
|
||||||
<button class="damage-button">{{localize "DAGGERHEART.Chat.DamageRoll.DealDamage"}}</button>
|
<button class="damage-button">{{localize "DAGGERHEART.Chat.DamageRoll.DealDamage"}}</button>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||||
<div class="dice-flavor">{{title}}</div>
|
<div class="dice-flavor">{{title}}</div>
|
||||||
<div class="duality-modifiers">
|
<div class="duality-modifiers">
|
||||||
{{#each modifiers}}
|
{{#each roll.modifiers}}
|
||||||
<div class="duality-modifier">
|
<div class="duality-modifier">
|
||||||
{{label}}
|
{{localize label}} {{#if (gte value 0)}}+{{/if}}{{value}}
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{#if advantageState}}
|
{{#if (eq roll.advantage.type 1)}}
|
||||||
<div class="duality-modifier">
|
<div class="duality-modifier">
|
||||||
{{localize "DAGGERHEART.General.Advantage.Full"}}
|
{{localize "DAGGERHEART.General.Advantage.Full"}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq advantageState false)}}
|
{{#if (eq roll.advantage.type -1)}}
|
||||||
<div class="duality-modifier">
|
<div class="duality-modifier">
|
||||||
{{localize "DAGGERHEART.General.Disadvantage.Full"}}
|
{{localize "DAGGERHEART.General.Disadvantage.Full"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -25,56 +25,56 @@
|
||||||
<div class="dice">
|
<div class="dice">
|
||||||
<header class="part-header flexrow">
|
<header class="part-header flexrow">
|
||||||
<span class="part-formula">
|
<span class="part-formula">
|
||||||
<span>1{{hope.dice}}</span>
|
<span>1{{roll.hope.dice}}</span>
|
||||||
|
|
|
|
||||||
<span>1{{fear.dice}}</span>
|
<span>1{{roll.fear.dice}}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="part-total">{{diceTotal}}</span>
|
<span class="part-total">{{roll.result.total}}</span>
|
||||||
</header>
|
</header>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<ol class="dice-rolls duality">
|
<ol class="dice-rolls duality">
|
||||||
<li class="roll die {{hope.dice}}" title="{{localize "DAGGERHEART.General.Hope"}}">
|
<li class="roll die {{roll.hope.dice}}" title="{{localize "DAGGERHEART.General.Hope"}}">
|
||||||
<div class="dice-container">
|
<div class="dice-container">
|
||||||
<div class="dice-title">{{localize "DAGGERHEART.General.Hope"}}</div>
|
<div class="dice-title">{{localize "DAGGERHEART.General.Hope"}}</div>
|
||||||
<div class="dice-inner-container hope" title="{{localize "DAGGERHEART.General.Hope"}}">
|
<div class="dice-inner-container hope" title="{{localize "DAGGERHEART.General.Hope"}}">
|
||||||
<div class="dice-wrapper">
|
<div class="dice-wrapper">
|
||||||
<img class="dice" src="../icons/svg/d12-grey.svg"/>
|
<img class="dice" src="../icons/svg/d12-grey.svg"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="dice-value">{{hope.value}}</div>
|
<div class="dice-value">{{roll.hope.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="roll die {{fear.dice}}" title="{{localize "DAGGERHEART.General.Fear"}}">
|
<li class="roll die {{roll.fear.dice}}" title="{{localize "DAGGERHEART.General.Fear"}}">
|
||||||
<div class="dice-container">
|
<div class="dice-container">
|
||||||
<div class="dice-title">{{localize "DAGGERHEART.General.Fear"}}</div>
|
<div class="dice-title">{{localize "DAGGERHEART.General.Fear"}}</div>
|
||||||
<div class="dice-inner-container fear" title="{{localize "DAGGERHEART.General.Fear"}}">
|
<div class="dice-inner-container fear" title="{{localize "DAGGERHEART.General.Fear"}}">
|
||||||
<div class="dice-wrapper">
|
<div class="dice-wrapper">
|
||||||
<img class="dice" src="../icons/svg/d12-grey.svg"/>
|
<img class="dice" src="../icons/svg/d12-grey.svg"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="dice-value">{{fear.value}}</div>
|
<div class="dice-value">{{roll.fear.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if advantageState}}
|
{{#if roll.advantage.type}}
|
||||||
<div class="dice">
|
<div class="dice">
|
||||||
<header class="part-header flexrow">
|
<header class="part-header flexrow">
|
||||||
<span class="part-formula">
|
<span class="part-formula">
|
||||||
<span>1{{advantage.dice}}</span>
|
<span>1{{roll.advantage.dice}}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="part-total">{{advantage.value}}</span>
|
<span class="part-total">{{roll.advantage.value}}</span>
|
||||||
</header>
|
</header>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<ol class="dice-rolls">
|
<ol class="dice-rolls">
|
||||||
<li class="roll die {{advantage.dice}}">
|
<li class="roll die {{roll.advantage.dice}}">
|
||||||
<div class="dice-container">
|
<div class="dice-container">
|
||||||
<div class="dice-inner-container advantage">
|
<div class="dice-inner-container {{#if (eq roll.advantage.type 1)}}advantage{{else}}disadvantage{{/if}}">
|
||||||
<div class="dice-wrapper">
|
<div class="dice-wrapper">
|
||||||
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="dice-value">{{advantage.value}}</div>
|
<div class="dice-value">{{roll.advantage.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -82,58 +82,34 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq advantageState false)}}
|
{{#if roll.modifierTotal}}<div class="duality-modifier">{{#if (gt roll.modifierTotal 0)}}+{{/if}}{{roll.modifierTotal}}</div>{{/if}}
|
||||||
<div class="dice">
|
|
||||||
<header class="part-header flexrow">
|
|
||||||
<span class="part-formula">
|
|
||||||
<span>1{{advantage.dice}}</span>
|
|
||||||
</span>
|
|
||||||
<span class="part-total">{{advantage.value}}</span>
|
|
||||||
</header>
|
|
||||||
<div class="flexrow">
|
|
||||||
<ol class="dice-rolls">
|
|
||||||
<li class="roll die {{advantage.dice}}">
|
|
||||||
<div class="dice-container">
|
|
||||||
<div class="dice-inner-container disadvantage">
|
|
||||||
<div class="dice-wrapper">
|
|
||||||
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
|
||||||
</div>
|
|
||||||
<div class="dice-value">{{advantage.value}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{#if modifierTotal.value}}<div class="duality-modifier">{{modifierTotal.label}}</div>{{/if}}
|
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dice-total duality {{#if fear.discarded}}hope{{else}}{{#if hope.discarded}}fear{{else}}critical{{/if}}{{/if}}">
|
<div class="dice-total duality {{#if (eq roll.result.duality 1)}}hope{{else}}{{#if (eq roll.result.duality -1)}}fear{{else}}critical{{/if}}{{/if}}">
|
||||||
<div class="dice-total-label">{{totalLabel}}</div>
|
<div class="dice-total-label">{{roll.result.label}}</div>
|
||||||
<div class="dice-total-value">
|
<div class="dice-total-value">
|
||||||
{{roll.total}}
|
{{roll.total}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if (gt targets.length 0)}}
|
</div>
|
||||||
<div class="target-section">
|
</div>
|
||||||
{{#each targets as |target|}}
|
{{> 'systems/daggerheart/templates/chat/parts/target-chat.hbs'}}
|
||||||
<div class="dice-total target-container {{#if target.hit}}hit{{else}}{{#if (not ../total.alternate)}}miss{{/if}}{{/if}}" data-token="{{target.id}}">
|
<div class="dice-roll daggerheart chat roll">
|
||||||
<img src="{{target.img}}" />
|
<div class="dice-result">
|
||||||
<div class="target-inner-container">
|
<div class="dice-actions{{#unless (or hasDamage hasHealing)}} duality-alone{{/unless}}">
|
||||||
{{#if target.hit}}{{localize "Hit"}}{{else}}{{#if (not ../total.alternate)}}{{localize "Miss"}}{{else}}?{{/if}}{{/if}}
|
{{#if hasDamage}}
|
||||||
</div>
|
<button class="duality-action duality-action-damage" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.Chat.AttackRoll.RollDamage"}}</span></button>
|
||||||
</div>
|
{{else}}
|
||||||
{{/each}}
|
{{#if hasHealing}}
|
||||||
</div>
|
<button class="duality-action duality-action-healing" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.Chat.AttackRoll.RollHealing"}}</span></button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="dice-actions{{#unless damage.value}} duality-alone{{/unless}}">
|
{{/if}}
|
||||||
{{#if damage.value}}
|
{{#if hasEffect}}
|
||||||
<button class="duality-action" data-value="{{roll.total}}" data-damage="{{damage.value}}" data-damage-type="{{damage.type}}" {{#if damage.disabled}}disabled{{/if}}><span>{{localize "DAGGERHEART.Chat.AttackRoll.RollDamage"}}</span></button>
|
<button class="duality-action{{#if (or hasDamage hasHealing)}} duality-action-effect{{/if}}" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.Chat.AttackRoll.ApplyEffect"}}</span></button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="duality-result">
|
<div class="duality-result">
|
||||||
<div>{{roll.total}} {{#if (eq dualityResult 1)}}With Hope{{else}}{{#if (eq dualityResult 2)}}With Fear{{else}}Critical Success{{/if}}{{/if}}</div>
|
<div>{{roll.total}} {{#if (eq roll.result.duality 1)}}With Hope{{else}}{{#if (eq roll.result.duality -1)}}With Fear{{else}}Critical Success{{/if}}{{/if}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,29 @@
|
||||||
<div class="dice-roll daggerheart chat roll">
|
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||||
|
<div class="dice-flavor">{{title}}</div>
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="dice-formula">{{this.roll}}</div>
|
<div class="dice-formula">{{roll.formula}}</div>
|
||||||
<div class="dice-tooltip">
|
<div class="dice-tooltip">
|
||||||
<ol class="dice-rolls">
|
<div class="wrapper">
|
||||||
{{#each dice}}
|
<section class="tooltip-part">
|
||||||
<li class="roll die {{this.type}}">{{this.value}}</li>
|
{{#each roll.dice}}
|
||||||
{{/each}}
|
<div class="dice">
|
||||||
{{#each modifiers}}
|
<header class="part-header flexrow">
|
||||||
<li class="modifier-value">{{this}}</li>
|
<span class="part-formula">{{formula}}</span>
|
||||||
{{/each}}
|
<span class="part-total">{{total}}</span>
|
||||||
</ol>
|
</header>
|
||||||
|
<ol class="dice-rolls">
|
||||||
|
{{#each results}}
|
||||||
|
<li class="roll die {{../denomination}} min">{{result}}</li>
|
||||||
|
{{/each}}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dice-total">{{this.total}}</div>
|
<div class="dice-total">{{roll.total}}</div>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<button class="healing-button" data-value="{{this.total}}" data-type="{{this.type}}"><span>{{localize "DAGGERHEART.Chat.HealingRoll.Heal"}}</span></button>
|
<button class="healing-button"><span>{{localize "DAGGERHEART.Chat.HealingRoll.Heal"}}</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
32
templates/chat/parts/target-chat.hbs
Normal file
32
templates/chat/parts/target-chat.hbs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
{{#if (gt targets.length 0)}}
|
||||||
|
<fieldset class="dice-roll daggerheart chat roll expanded" data-action="expandRoll">
|
||||||
|
<legend class="dice-flavor">Targets</legend>
|
||||||
|
<div class="dice-result">
|
||||||
|
<div class="dice-tooltip">
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="target-section">
|
||||||
|
{{#each targets as |target|}}
|
||||||
|
<div class="dice-total target-container {{#if target.hit}}hit{{else}}{{#if (not ../total.alternate)}}miss{{/if}}{{/if}}" data-token="{{target.id}}">
|
||||||
|
<img src="{{target.img}}" />
|
||||||
|
<div class="target-inner-container">
|
||||||
|
{{#if ../directDamage}}
|
||||||
|
<div data-perm-id="{{target.actorId}}"><span>{{target.name}}</span></div>
|
||||||
|
{{else}}
|
||||||
|
{{#if target.hit}}{{localize "Hit"}}{{else}}{{#if (not ../total.alternate)}}{{localize "Miss"}}{{else}}?{{/if}}{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{#if ../hasSave}}
|
||||||
|
<button class="target-save-container{{#if target.saved.result includeZero=true}} is-rolled{{/if}}"{{#unless target.hit}} style="visibility: hidden;"{{/unless}} data-perm-id="{{target.actorId}}">
|
||||||
|
{{!-- {{target.saved.result}} --}}
|
||||||
|
<i class="fa-solid {{#if target.saved.result includeZero=true}}{{#if target.saved.success}}fa-check{{else}}fa-xmark{{/if}}{{else}}fa-shield{{/if}}">
|
||||||
|
</i>
|
||||||
|
</button>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
{{/if}}
|
||||||
|
|
@ -49,13 +49,32 @@
|
||||||
|
|
||||||
<fieldset class="two-columns even">
|
<fieldset class="two-columns even">
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Attack"}}</legend>
|
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Attack"}}</legend>
|
||||||
|
<button data-action="attackConfigure">Configure</button>
|
||||||
{{formGroup systemFields.attack.fields.name value=source.system.attack.name}}
|
|
||||||
<button data-action="attackRoll">Attack</button>
|
<button data-action="attackRoll">Attack</button>
|
||||||
{{formGroup systemFields.attack.fields.modifier value=source.system.attack.modifier}}
|
<fieldset class="action-category" style="grid-column: 1 / -1;">
|
||||||
{{formGroup systemFields.attack.fields.range value=source.system.attack.range localize=true}}
|
<legend class="action-category-label" data-action="toggleSection" data-section="range">
|
||||||
{{formGroup systemFields.attack.fields.damage.fields.value value=source.system.attack.damage.value}}
|
<div>Name</div>
|
||||||
{{formGroup systemFields.attack.fields.damage.fields.type value=source.system.attack.damage.type localize=true}}
|
</legend>
|
||||||
|
<div class="action-category-data open">
|
||||||
|
{{formGroup systemFields.attack.fields.name value=source.system.attack.name name="system.attack.name"}}
|
||||||
|
{{formGroup systemFields.attack.fields.img value=source.img label="Icon" name="system.attack.img"}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<div>
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="range">
|
||||||
|
<div>Bonus to Hit</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open">
|
||||||
|
{{formField systemFields.attack.fields.roll.fields.bonus value=source.system.attack.roll.bonus name="system.attack.roll.bonus"}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
{{> 'systems/daggerheart/templates/views/actionTypes/range-target.hbs' fields=(object range=systemFields.attack.fields.range target=systemFields.attack.fields.target.fields) source=(object target=source.system.attack.target range=source.system.attack.range) path="system.attack."}}
|
||||||
|
</div>
|
||||||
|
{{> 'systems/daggerheart/templates/views/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=source.system.attack.damage path="system.attack."}}
|
||||||
|
<div style="grid-column: 1 / -1;">
|
||||||
|
{{> 'systems/daggerheart/templates/views/actionTypes/effect.hbs' fields=systemFields.attack.fields.effects.element.fields source=source.system.attack.effects}}
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,8 @@
|
||||||
<div class="hope-section">
|
<div class="hope-section">
|
||||||
<h4>{{localize "DAGGERHEART.General.Hope"}}</h4>
|
<h4>{{localize "DAGGERHEART.General.Hope"}}</h4>
|
||||||
{{#times document.system.resources.hope.max}}
|
{{#times document.system.resources.hope.max}}
|
||||||
<span class='hope-value' data-action='toggleHope' data-value="{{this}}">
|
<span class='hope-value' data-action='toggleHope' data-value="{{add this 1}}">
|
||||||
{{#if (gte ../document.system.resources.hope.value this)}}
|
{{#if (gte ../document.system.resources.hope.value (add this 1))}}
|
||||||
<i class='fa-solid fa-diamond'></i>
|
<i class='fa-solid fa-diamond'></i>
|
||||||
{{else}}
|
{{else}}
|
||||||
<i class='fa-regular fa-circle'></i>
|
<i class='fa-regular fa-circle'></i>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<li class="item inventory-item">
|
<li class="item inventory-item">
|
||||||
<div class="inventory-row" data-item-id="{{item.uuid}}">
|
<div class="inventory-row" data-item-id="{{item.uuid}}">
|
||||||
<div class="inventory-item-title-container">
|
<div class="inventory-item-title-container">
|
||||||
<div data-action="viewObject" data-value="{{item.uuid}}" class="inventory-item-title">
|
<div data-action="useItem" data-value="{{item.uuid}}" class="inventory-item-title">
|
||||||
<img src="{{item.img}}" />
|
<img src="{{item.img}}" />
|
||||||
{{item.name}}
|
{{item.name}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<li class="card-item">
|
<li class="card-item" data-item-id="{{item.id}}">
|
||||||
<img src="{{item.img}}" data-action="viewObject" data-uuid="{{item.uuid}}" class="card-img" />
|
<img src="{{item.img}}" data-action="useItem" class="card-img" />
|
||||||
<div class="card-label">
|
<div class="card-label">
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
{{#if (eq type 'weapon')}}
|
{{#if (eq type 'weapon')}}
|
||||||
|
|
@ -14,18 +14,18 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq type 'domainCard')}}
|
{{#if (eq type 'domainCard')}}
|
||||||
{{#unless item.system.inVault}}
|
{{#unless item.system.inVault}}
|
||||||
<a data-action="toggleVault" id="{{item.id}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToVault'}}">
|
<a data-action="toggleVault" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToVault'}}">
|
||||||
<i class="fa-solid fa-arrow-down"></i>
|
<i class="fa-solid fa-arrow-down"></i>
|
||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a data-action="toggleVault" id="{{item.id}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToLoadout'}}">
|
<a data-action="toggleVault" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToLoadout'}}">
|
||||||
<i class="fa-solid fa-arrow-up"></i>
|
<i class="fa-solid fa-arrow-up"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<a data-action="toChat" data-uuid="{{item.uuid}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a>
|
<a data-action="toChat" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a>
|
||||||
<a class="{{concat type "-context-menu"}}" data-type="{{type}}" data-uuid="{{item.uuid}}" id="{{item.id}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a>
|
<a data-action="triggerContextMenu" data-tooltip="{{localize 'DAGGERHEART.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-name">{{item.name}}</div>
|
<div class="card-name">{{item.name}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<li class="inventory-item">
|
<li class="inventory-item" data-item-id="{{item.id}}">
|
||||||
<img src="{{item.img}}" data-action="viewObject" data-uuid="{{item.uuid}}" class="item-img" />
|
<img src="{{item.img}}" class="item-img" data-action="useItem"/>
|
||||||
<div class="item-label">
|
<div class="item-label">
|
||||||
<div class="item-name">{{item.name}}</div>
|
<div class="item-name">{{item.name}}</div>
|
||||||
{{#if (eq type 'weapon')}}
|
{{#if (eq type 'weapon')}}
|
||||||
|
|
@ -103,28 +103,28 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
{{#if (eq type 'weapon')}}
|
{{#if (eq type 'weapon')}}
|
||||||
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem" id="{{item.id}}" data-tooltip="{{#unless item.system.equipped}}{{localize 'DAGGERHEART.Tooltip.equip'}}{{else}}{{localize 'DAGGERHEART.Tooltip.unequip'}}{{/unless}}">
|
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem" data-tooltip="{{#unless item.system.equipped}}{{localize 'DAGGERHEART.Tooltip.equip'}}{{else}}{{localize 'DAGGERHEART.Tooltip.unequip'}}{{/unless}}">
|
||||||
<i class="fa-solid fa-hands"></i>
|
<i class="fa-solid fa-hands"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq type 'armor')}}
|
{{#if (eq type 'armor')}}
|
||||||
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem" id="{{item.id}}" data-tooltip="{{#unless item.system.equipped}}{{localize 'DAGGERHEART.Tooltip.equip'}}{{else}}{{localize 'DAGGERHEART.Tooltip.unequip'}}{{/unless}}">
|
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem" data-tooltip="{{#unless item.system.equipped}}{{localize 'DAGGERHEART.Tooltip.equip'}}{{else}}{{localize 'DAGGERHEART.Tooltip.unequip'}}{{/unless}}">
|
||||||
<i class="fa-solid fa-shield"></i>
|
<i class="fa-solid fa-shield"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq type 'domainCard')}}
|
{{#if (eq type 'domainCard')}}
|
||||||
{{#unless item.system.inVault}}
|
{{#unless item.system.inVault}}
|
||||||
<a data-action="toggleVault" id="{{item.id}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToVault'}}">
|
<a data-action="toggleVault" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToVault'}}">
|
||||||
<i class="fa-solid fa-arrow-down"></i>
|
<i class="fa-solid fa-arrow-down"></i>
|
||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a data-action="toggleVault" id="{{item.id}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToLoadout'}}">
|
<a data-action="toggleVault" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToLoadout'}}">
|
||||||
<i class="fa-solid fa-arrow-up"></i>
|
<i class="fa-solid fa-arrow-up"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<a data-action="toChat" data-uuid="{{item.uuid}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a>
|
<a data-action="toChat" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a>
|
||||||
<a class="{{concat type '-context-menu'}}" data-type="{{type}}" data-uuid="{{item.uuid}}" id="{{item.id}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a>
|
<a data-action="triggerContextMenu" data-tooltip="{{localize 'DAGGERHEART.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -7,12 +7,14 @@
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Global.Actions"}} <a><i class="fa-solid fa-plus icon-button" data-action="addAction"></i></a></legend>
|
<legend>{{localize "DAGGERHEART.Sheets.Global.Actions"}} <a><i class="fa-solid fa-plus icon-button" data-action="addAction"></i></a></legend>
|
||||||
<div class="actions-list">
|
<div class="actions-list">
|
||||||
{{#each document.system.actions as |action index|}}
|
{{#each document.system.actions as |action index|}}
|
||||||
<div class="action-item">
|
<div class="action-item"
|
||||||
|
data-action="editAction"
|
||||||
|
data-index="{{index}}"
|
||||||
|
>
|
||||||
<img class="image" src="{{action.img}}" />
|
<img class="image" src="{{action.img}}" />
|
||||||
<span>{{action.name}}</span>
|
<span>{{action.name}}</span>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<a data-action="editAction" data-index="{{index}}"><i class="fa-solid fa-pen-to-square"></i></a>
|
<a data-action="removeAction"><i class="fa-solid fa-trash"></i></a>
|
||||||
<a data-action="removeAction" data-index="{{index}}"><i class="fa-solid fa-trash"></i></a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="hope-container">
|
<div class="hope-container">
|
||||||
{{#times 6}}
|
{{#times 6}}
|
||||||
<span class="hope-inner-container">
|
<span class="hope-inner-container">
|
||||||
<input class="hope-value" type="checkbox" data-action="toggleHope" data-value="{{this}}" {{ checked (gte ../document.system.resources.hope.value this) }} {{#if (gte this ../document.system.resources.hope.max)}}disabled{{/if}} />
|
<input class="hope-value" type="checkbox" data-action="toggleHope" data-value="{{add this 1}}" {{ checked (gte ../document.system.resources.hope.value (add this 1)) }} {{#if (gte this ../document.system.resources.hope.max)}}disabled{{/if}} />
|
||||||
{{#if (gte this ../document.system.resources.hope.max)}}<i class="fa-solid fa-droplet-slash hope-scar"></i>{{/if}}
|
{{#if (gte this ../document.system.resources.hope.max)}}<i class="fa-solid fa-droplet-slash hope-scar"></i>{{/if}}
|
||||||
</span>
|
</span>
|
||||||
{{/times}}
|
{{/times}}
|
||||||
|
|
|
||||||
|
|
@ -20,21 +20,33 @@
|
||||||
{{formField fields.name value=source.name label="Name" name="name"}}
|
{{formField fields.name value=source.name label="Name" name="name"}}
|
||||||
{{formField fields.img value=source.img label="Icon" name="img"}}
|
{{formField fields.img value=source.img label="Icon" name="img"}}
|
||||||
{{formField fields.actionType value=source.actionType label="Type" name="actionType" localize=true}}
|
{{formField fields.actionType value=source.actionType label="Type" name="actionType" localize=true}}
|
||||||
|
{{formField fields.chatDisplay value=source.chatDisplay name="chatDisplay"}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="identity">
|
||||||
|
<div>Description</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open">
|
||||||
|
{{formInput fields.description value=source.description name="description" }}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{#if fields.roll}}{{> 'systems/daggerheart/templates/views/actionTypes/roll.hbs' fields=fields.roll.fields source=source.roll}}{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tab {{this.tabs.config.cssClass}}" data-group="primary" data-tab="config">
|
<div class="tab {{this.tabs.config.cssClass}}" data-group="primary" data-tab="config">
|
||||||
|
{{#unless isNPC}}
|
||||||
{{> 'systems/daggerheart/templates/views/actionTypes/uses.hbs' fields=fields.uses.fields source=source.uses}}
|
{{> 'systems/daggerheart/templates/views/actionTypes/uses.hbs' fields=fields.uses.fields source=source.uses}}
|
||||||
{{> 'systems/daggerheart/templates/views/actionTypes/cost.hbs' fields=fields.cost.element.fields source=source.cost}}
|
{{> 'systems/daggerheart/templates/views/actionTypes/cost.hbs' fields=fields.cost.element.fields source=source.cost}}
|
||||||
|
{{/unless}}
|
||||||
{{#if fields.target}}{{> 'systems/daggerheart/templates/views/actionTypes/range-target.hbs' fields=(object range=fields.range target=fields.target.fields) source=(object target=source.target range=source.range)}}{{/if}}
|
{{#if fields.target}}{{> 'systems/daggerheart/templates/views/actionTypes/range-target.hbs' fields=(object range=fields.range target=fields.target.fields) source=(object target=source.target range=source.range)}}{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="tab {{this.tabs.effect.cssClass}}" data-group="primary" data-tab="effect">
|
<div class="tab {{this.tabs.effect.cssClass}}" data-group="primary" data-tab="effect">
|
||||||
|
{{#if fields.roll}}{{> 'systems/daggerheart/templates/views/actionTypes/roll.hbs' fields=fields.roll.fields source=source.roll}}{{/if}}
|
||||||
|
{{#if fields.save}}{{> 'systems/daggerheart/templates/views/actionTypes/save.hbs' fields=fields.save.fields source=source.save}}{{/if}}
|
||||||
{{#if fields.damage}}{{> 'systems/daggerheart/templates/views/actionTypes/damage.hbs' fields=fields.damage.fields.parts.element.fields source=source.damage}}{{/if}}
|
{{#if fields.damage}}{{> 'systems/daggerheart/templates/views/actionTypes/damage.hbs' fields=fields.damage.fields.parts.element.fields source=source.damage}}{{/if}}
|
||||||
{{#if fields.healing}}{{> 'systems/daggerheart/templates/views/actionTypes/healing.hbs' fields=fields.healing.fields source=source.healing}}{{/if}}
|
{{#if fields.healing}}{{> 'systems/daggerheart/templates/views/actionTypes/healing.hbs' fields=fields.healing.fields source=source.healing}}{{/if}}
|
||||||
{{#if fields.resource}}{{> 'systems/daggerheart/templates/views/actionTypes/resource.hbs' fields=fields.resource.fields source=source.resource}}{{/if}}
|
{{#if fields.resource}}{{> 'systems/daggerheart/templates/views/actionTypes/resource.hbs' fields=fields.resource.fields source=source.resource}}{{/if}}
|
||||||
{{#if fields.documentUUID}}{{> 'systems/daggerheart/templates/views/actionTypes/uuid.hbs' fields=fields.documentUUID source=source.documentUUID}}{{/if}}
|
{{#if fields.documentUUID}}{{> 'systems/daggerheart/templates/views/actionTypes/uuid.hbs' fields=fields.documentUUID source=source.documentUUID}}{{/if}}
|
||||||
{{#if fields.effects}}{{> 'systems/daggerheart/templates/views/actionTypes/effect.hbs'}}{{/if}}
|
{{#if fields.effects}}{{> 'systems/daggerheart/templates/views/actionTypes/effect.hbs' fields=fields.effects.element.fields source=source.effects}}{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
{{#each source as |cost index|}}
|
{{#each source as |cost index|}}
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="multi-display">
|
<div class="multi-display">
|
||||||
{{formField ../fields.type label="Resource" value=cost.type name=(concat "cost." index ".type") localize=true}}
|
{{formField ../fields.type choices=(@root.disableOption index ../fields.type.choices ../source) label="Resource" value=cost.type name=(concat "cost." index ".type") localize=true}}
|
||||||
{{formField ../fields.value label="Value" value=cost.value name=(concat "cost." index ".value")}}
|
{{formField ../fields.value label="Value" value=cost.value name=(concat "cost." index ".value")}}
|
||||||
</div>
|
</div>
|
||||||
<div class="multi-display">
|
<div class="multi-display">
|
||||||
|
|
|
||||||
|
|
@ -4,33 +4,72 @@
|
||||||
<div>Damage</div>
|
<div>Damage</div>
|
||||||
</legend>
|
</legend>
|
||||||
<div class="action-category-data open">
|
<div class="action-category-data open">
|
||||||
<div class="fas fa-plus icon-button" data-action="addDamage"></div>
|
{{#unless @root.isNPC}}
|
||||||
{{#if @root.hasBaseDamage}}
|
<div class="fas fa-plus icon-button" data-action="addDamage"></div>
|
||||||
<div>
|
{{#if @root.hasBaseDamage}}
|
||||||
{{!-- <input type="checkbox" data-action="addBaseDamage"{{#if @root.hasBaseDamage}} checked{{/if}}> --}}
|
<div>
|
||||||
{{formField @root.fields.damage.fields.includeBase value=@root.source.damage.includeBase label="Include Item Damage" name="damage.includeBase" }}
|
{{formField @root.fields.damage.fields.includeBase value=@root.source.damage.includeBase label="Include Item Damage" name="damage.includeBase" }}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{/unless}}
|
||||||
{{#each source.parts as |dmg index|}}
|
{{#each source.parts as |dmg index|}}
|
||||||
{{#with (@root.getRealIndex index) as | realIndex |}}
|
{{#if @root.isNPC}}
|
||||||
<fieldset{{#if dmg.base}} disabled{{/if}}>
|
{{formField ../fields.value.fields.custom.fields.enabled value=dmg.value.custom.enabled name=(concat ../path "damage.parts." index ".value.custom.enabled")}}
|
||||||
{{#unless dmg.base}}
|
<input type="hidden" name="{{../path}}damage.parts.{{index}}.value.multiplier" value="{{dmg.value.multiplier}}">
|
||||||
{{formField ../../fields.custom.fields.enabled value=dmg.custom.enabled name=(concat "damage.parts." realIndex ".custom.enabled")}}
|
{{#if dmg.value.custom.enabled}}
|
||||||
{{/unless}}
|
{{formField ../fields.value.fields.custom.fields.formula value=dmg.value.custom.formula name=(concat ../path "damage.parts." index ".value.custom.formula") localize=true}}
|
||||||
{{#if dmg.custom.enabled}}
|
|
||||||
{{formField ../../fields.custom.fields.formula value=dmg.custom.formula name=(concat "damage.parts." realIndex ".custom.formula") localize=true}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="multi-display">
|
<div class="multi-display">
|
||||||
{{formField ../../fields.multiplier value=dmg.multiplier name=(concat "damage.parts." realIndex ".multiplier") localize=true}}
|
{{formField ../fields.value.fields.flatMultiplier value=dmg.value.flatMultiplier name=(concat ../path "damage.parts." index ".value.flatMultiplier") label="Multiplier" }}
|
||||||
{{formField ../../fields.dice value=dmg.dice name=(concat "damage.parts." realIndex ".dice")}}
|
{{formField ../fields.value.fields.dice value=dmg.value.dice name=(concat ../path "damage.parts." index ".value.dice")}}
|
||||||
{{formField ../../fields.bonus value=dmg.bonus name=(concat "damage.parts." realIndex ".bonus") localize=true}}
|
{{formField ../fields.value.fields.bonus value=dmg.value.bonus name=(concat ../path "damage.parts." index ".value.bonus") localize=true}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{formField ../../fields.type value=dmg.type name=(concat "damage.parts." realIndex ".type") localize=true}}
|
{{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." index ".type") localize=true}}
|
||||||
<input type="hidden" name="damage.parts.{{realIndex}}.base" value="{{dmg.base}}">
|
{{else}}
|
||||||
{{#unless dmg.base}}<div class="fas fa-trash" data-action="removeDamage" data-index="{{realIndex}}"></div>{{/unless}}
|
{{#with (@root.getRealIndex index) as | realIndex |}}
|
||||||
</fieldset>
|
<fieldset{{#if dmg.base}} disabled{{/if}}>
|
||||||
{{/with}}
|
{{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base))}}
|
||||||
|
{{formField ../../fields.resultBased value=dmg.resultBased name=(concat "damage.parts." realIndex ".resultBased") localize=true}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base) dmg.resultBased)}}
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
<div>With Hope</div>
|
||||||
|
</legend>
|
||||||
|
{{> formula fields=../../fields.value.fields type=../../fields.type dmg=dmg source=dmg.value target="value" realIndex=realIndex}}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
<div>With Fear</div>
|
||||||
|
</legend>
|
||||||
|
{{> formula fields=../../fields.valueAlt.fields type=../../fields.type dmg=dmg source=dmg.valueAlt target="valueAlt" realIndex=realIndex}}
|
||||||
|
</fieldset>
|
||||||
|
{{else}}
|
||||||
|
{{> formula fields=../../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" realIndex=realIndex}}
|
||||||
|
{{/if}}
|
||||||
|
{{formField ../../fields.type value=dmg.type name=(concat "damage.parts." realIndex ".type") localize=true}}
|
||||||
|
<input type="hidden" name="damage.parts.{{realIndex}}.base" value="{{dmg.base}}">
|
||||||
|
{{#unless dmg.base}}<div class="fas fa-trash" data-action="removeDamage" data-index="{{realIndex}}"></div>{{/unless}}
|
||||||
|
</fieldset>
|
||||||
|
{{/with}}
|
||||||
|
{{/if}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
{{#*inline "formula"}}
|
||||||
|
{{#unless dmg.base}}
|
||||||
|
{{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat "damage.parts." realIndex "." target ".custom.enabled")}}
|
||||||
|
{{/unless}}
|
||||||
|
{{#if source.custom.enabled}}
|
||||||
|
{{formField fields.custom.fields.formula value=source.custom.formula name=(concat "damage.parts." realIndex "." target ".custom.formula") localize=true}}
|
||||||
|
{{else}}
|
||||||
|
<div class="multi-display">
|
||||||
|
{{formField fields.multiplier value=source.multiplier name=(concat "damage.parts." realIndex "." target ".multiplier") localize=true}}
|
||||||
|
{{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat "damage.parts." realIndex ".flatMultiplier") }}{{/if}}
|
||||||
|
{{formField fields.dice value=source.dice name=(concat "damage.parts." realIndex "." target ".dice")}}
|
||||||
|
{{formField fields.bonus value=source.bonus name=(concat "damage.parts." realIndex "." target ".bonus") localize=true}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/inline}}
|
||||||
|
|
@ -4,15 +4,31 @@
|
||||||
</legend>
|
</legend>
|
||||||
<div class="action-category-data open" data-key="effects">
|
<div class="action-category-data open" data-key="effects">
|
||||||
<div class="fas fa-plus icon-button" data-action="addEffect"></div>
|
<div class="fas fa-plus icon-button" data-action="addEffect"></div>
|
||||||
{{#each @root.effects as | effect index | }}
|
{{!-- {{#each @root.effects as | effect index | }}
|
||||||
<fieldset>
|
<fieldset>
|
||||||
{{!-- <div class="multi-display"> --}}
|
<div class="multi-display">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<img src="{{img}}">
|
<img src="{{img}}">
|
||||||
<label data-action="editEffect">{{name}}</label>
|
<label data-action="editEffect">{{name}}</label>
|
||||||
<div class="fas fa-trash" data-action="removeEffect" data-index="{{index}}"></div>
|
<div class="fas fa-trash" data-action="removeEffect" data-index="{{index}}"></div>
|
||||||
</div>
|
</div>
|
||||||
{{!-- </div> --}}
|
{{formfield }}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
{{/each}} --}}
|
||||||
|
{{#each source as | effect index |}}
|
||||||
|
<fieldset data-effect-id="{{effect._id}}">
|
||||||
|
<div class="multi-display">
|
||||||
|
{{#with (@root.getEffectDetails effect._id) as | details |}}
|
||||||
|
<div class="form-group" data-action="editEffect">
|
||||||
|
<img src="{{img}}">
|
||||||
|
<label>{{name}}</label>
|
||||||
|
<div class="fas fa-trash" data-action="removeEffect" data-index="{{index}}"></div>
|
||||||
|
</div>
|
||||||
|
{{/with}}
|
||||||
|
<input type="hidden" name="effects.{{index}}._id" value="{{effect._id}}">
|
||||||
|
{{formField ../fields.onSave value=effect.onSave name=(concat "effects." index ".onSave")}}
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,38 @@
|
||||||
<div class="action-category-data open">
|
<div class="action-category-data open">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
{{formField fields.type value=source.type name="healing.type" localize=true}}
|
{{formField fields.type value=source.type name="healing.type" localize=true}}
|
||||||
<div class="multi-display">
|
{{#if (and (not @root.isNPC) @root.hasRoll)}}
|
||||||
{{formField fields.value.fields.custom.fields.enabled value=source.value.custom.enabled name="healing.value.custom.enabled"}}
|
{{formField fields.resultBased value=source.resultBased name="healing.resultBased" localize=true}}
|
||||||
{{#if source.value.custom.enabled}}
|
{{/if}}
|
||||||
{{formField fields.value.fields.custom.fields.formula value=source.value.custom.formula name="healing.value.custom.formula" localize=true}}
|
{{#if (and (not @root.isNPC) @root.hasRoll source.resultBased)}}
|
||||||
{{else}}
|
<fieldset>
|
||||||
{{formField fields.value.fields.multiplier value=source.value.multiplier name="healing.value.multiplier" localize=true}}
|
<legend>
|
||||||
{{formField fields.value.fields.dice value=source.value.dice name="healing.value.dice"}}
|
<div>With Hope</div>
|
||||||
{{formField fields.value.fields.bonus value=source.value.bonus name="healing.value.bonus" localize=true}}
|
</legend>
|
||||||
{{/if}}
|
{{> formula fields=fields.value.fields source=source.value target="value"}}
|
||||||
</div>
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
<div>With Fear</div>
|
||||||
|
</legend>
|
||||||
|
{{> formula fields=fields.valueAlt.fields source=source.valueAlt target="valueAlt"}}
|
||||||
|
</fieldset>
|
||||||
|
{{else}}
|
||||||
|
{{> formula fields=fields.value.fields source=source.value target="value"}}
|
||||||
|
{{/if}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
{{#*inline "formula"}}
|
||||||
|
<div class="multi-display">
|
||||||
|
{{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat "healing." target ".custom.enabled")}}
|
||||||
|
{{#if source.custom.enabled}}
|
||||||
|
{{formField fields.custom.fields.formula value=source.custom.formula name=(concat "healing." target ".custom.formula") localize=true}}
|
||||||
|
{{else}}
|
||||||
|
{{formField fields.multiplier value=source.multiplier name=(concat "healing." target ".multiplier") localize=true}}
|
||||||
|
{{formField fields.dice value=source.dice name=(concat "healing." target ".dice")}}
|
||||||
|
{{formField fields.bonus value=source.bonus name=(concat "healing." target ".bonus") localize=true}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/inline}}
|
||||||
|
|
@ -3,11 +3,16 @@
|
||||||
<div>Range{{#if fields.target}} & Target{{/if}}</div>
|
<div>Range{{#if fields.target}} & Target{{/if}}</div>
|
||||||
</legend>
|
</legend>
|
||||||
<div class="action-category-data open">
|
<div class="action-category-data open">
|
||||||
{{formField fields.range value=source.range label="Range" name="range" localize=true}}
|
{{formField fields.range value=source.range label="Range" name=(concat path "range") localize=true}}
|
||||||
</div>
|
</div>
|
||||||
{{#if fields.target}}
|
{{#if fields.target}}
|
||||||
<div class="action-category-data open">
|
<div class="action-category-data open">
|
||||||
{{formField fields.target.type value=source.target.type label="Target" name="target.type" localize=true}}
|
<div class="multi-display">
|
||||||
|
{{#if (and source.target.type (not (eq source.target.type 'self')))}}
|
||||||
|
{{ formField fields.target.amount value=source.target.amount label="Amount" name=(concat path "target.amount") }}
|
||||||
|
{{/if}}
|
||||||
|
{{ formField fields.target.type value=source.target.type label="Target" name=(concat path "target.type") localize=true }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
@ -3,8 +3,12 @@
|
||||||
<div>Roll</div>
|
<div>Roll</div>
|
||||||
</legend>
|
</legend>
|
||||||
<div class="action-category-data open">
|
<div class="action-category-data open">
|
||||||
{{formField fields.type label="Type" name="roll.type" value=source.type localize=true}}
|
{{#if @root.isNPC}}
|
||||||
{{formField fields.trait label="Trait" name="roll.trait" value=source.trait localize=true}}
|
{{formField fields.bonus label="Bonus" name="roll.bonus" value=source.bonus}}
|
||||||
{{formField fields.difficulty label="Difficulty" name="roll.difficulty" value=source.difficulty}}
|
{{else}}
|
||||||
|
{{formField fields.type label="Type" name="roll.type" value=source.type localize=true}}
|
||||||
|
{{formField fields.trait label="Trait" name="roll.trait" value=source.trait localize=true disabled=(not source.type)}}
|
||||||
|
{{formField fields.difficulty label="Difficulty" name="roll.difficulty" value=source.difficulty disabled=(not source.type)}}
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
10
templates/views/actionTypes/save.hbs
Normal file
10
templates/views/actionTypes/save.hbs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="roll">
|
||||||
|
<div>Save</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open">
|
||||||
|
{{formField fields.trait label="Trait" name="save.trait" value=source.trait localize=true}}
|
||||||
|
{{formField fields.difficulty label="Difficulty" name="save.difficulty" value=source.difficulty disabled=(not source.trait)}}
|
||||||
|
{{formField fields.damageMod label="Damage on Save" name="save.damageMod" value=source.damageMod localize=true disabled=(not source.trait)}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
24
templates/views/costSelection.hbs
Normal file
24
templates/views/costSelection.hbs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<div>
|
||||||
|
{{#if uses}}
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-fields">
|
||||||
|
<label for="uses.enabled">Uses: {{uses.value}}/{{uses.max}}</label>
|
||||||
|
<input name="uses.enabled" type="checkbox"{{#if uses.enabled}} checked{{/if}}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#each costs as | cost index |}}
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-fields">
|
||||||
|
<label for="{{type}}">{{type}}: {{total}}</label>
|
||||||
|
<input name="costs.{{index}}.enabled" type="checkbox"{{#if enabled}} checked{{/if}}>
|
||||||
|
{{#if scalable}}
|
||||||
|
<input type="range" value="{{scale}}" min="1" max="10" step="{{step}}" name="costs.{{index}}.scale">
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{!-- <footer>
|
||||||
|
<button data-action="sendCost"{{#unless canUse}} disabled{{/unless}}>Accept</button>
|
||||||
|
</footer> --}}
|
||||||
|
</div>
|
||||||
76
templates/views/damageReduction.hbs
Normal file
76
templates/views/damageReduction.hbs
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
<div class="damage-reduction-container">
|
||||||
|
<div class="section-container padded">
|
||||||
|
<div class="resources-container">
|
||||||
|
<div class="resource-container">
|
||||||
|
<h4 class="armor-title">{{localize "DAGGERHEART.DamageReduction.ArmorMarks"}}</h4>
|
||||||
|
<div class="markers-subtitle">{{armorMarks}}/{{armorScore}}</div>
|
||||||
|
</div>
|
||||||
|
{{#if this.stress}}
|
||||||
|
<div class="resource-container">
|
||||||
|
<h4 class="armor-title">{{localize "DAGGERHEART.DamageReduction.Stress"}}</h4>
|
||||||
|
<div class="markers-subtitle">{{this.stress.value}}/{{this.stress.maxTotal}}</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-container">
|
||||||
|
<h4 class="mark-selection divider">
|
||||||
|
<div class="mark-selection-inner">
|
||||||
|
{{#each marks.armor}}
|
||||||
|
<div
|
||||||
|
class="mark-container {{#if this.selected}}selected{{/if}}"
|
||||||
|
data-action="setMarks" data-key="{{@key}}" data-type="armor"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-shield"></i>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
<div class="mark-selection-inner">
|
||||||
|
{{#each marks.stress}}
|
||||||
|
<div
|
||||||
|
class="mark-container {{#if this.selected}}selected{{/if}} {{#if (not @root.basicMarksUsed)}}inactive{{/if}}"
|
||||||
|
{{#if @root.basicMarksUsed}}data-action="setMarks"{{/if}} data-key="{{@key}}" data-type="stress" data-tooltip="{{#if @root.basicMarksUsed}}{{localize "DAGGERHEART.DamageReduction.ArmorWithStress"}}{{else}}{{localize "DAGGERHEART.DamageReduction.UnncessaryStress"}}{{/if}}"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-bolt"></i>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</h4>
|
||||||
|
<div class="markers-subtitle bold">{{localize "DAGGERHEART.DamageReduction.UsedMarks"}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resources-container">
|
||||||
|
<div class="resource-container">
|
||||||
|
<h4 class="armor-title">{{localize "DAGGERHEART.DamageReduction.StressReduction"}}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#each availableStressReductions}}
|
||||||
|
<div class="section-container">
|
||||||
|
<h4 class="stress-reduction-container divider">
|
||||||
|
<div class="stress-reduction {{#if (eq this.from @root.currentDamage)}}active{{/if}} {{#if this.selected}}selected{{/if}}" data-action="useStressReduction" data-reduction="{{@key}}">
|
||||||
|
{{this.from}}
|
||||||
|
<i class="fa-solid fa-arrow-right-long"></i>
|
||||||
|
{{this.to}}
|
||||||
|
<div class="stress-reduction-cost">
|
||||||
|
{{this.cost}}
|
||||||
|
<i class="fa-solid fa-bolt"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<footer class="padded">
|
||||||
|
<button type="button" data-action="takeDamage">
|
||||||
|
{{localize "Take"}}
|
||||||
|
<div class="damage-value {{#if this.reducedDamage}}reduced-value{{/if}}">{{this.damage}}</div>
|
||||||
|
{{#if this.reducedDamage}}
|
||||||
|
<i class="fa-solid fa-arrow-right-long"></i>
|
||||||
|
<div class="damage-value">{{this.reducedDamage}}</div>
|
||||||
|
{{/if}}
|
||||||
|
{{localize "Damage"}}
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label><strong>Total Damage</strong></label>
|
<label><strong>{{title}}</strong></label>
|
||||||
<div class="form-fields">
|
<div class="form-fields">
|
||||||
<input type="text" value="{{this.rollString}}" disabled />
|
<input type="text" value="{{formula}}" disabled />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#each this.bonusDamage as |damage index|}}
|
{{!-- {{#each bonusDamage as |damage index|}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label><strong>{{damage.description}}</strong></label>
|
<label><strong>{{damage.description}}</strong></label>
|
||||||
<div class="form-fields">
|
<div class="form-fields">
|
||||||
|
|
@ -22,8 +22,8 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}} --}}
|
||||||
<footer>
|
<footer>
|
||||||
<button data-action="rollDamage">Roll</button>
|
<button data-action="submitRoll">Roll</button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1,26 +1,27 @@
|
||||||
<div>
|
<div>
|
||||||
|
{{#if @root.hasRoll}}
|
||||||
<div class="roll-dialog-container">
|
<div class="roll-dialog-container">
|
||||||
<div class="flexcol">
|
<div class="flexcol">
|
||||||
<div class="roll-dialog-experience-container">
|
<div class="roll-dialog-experience-container">
|
||||||
{{#each this.experiences}}
|
{{#each experiences}}
|
||||||
{{#if this.description}}
|
{{#if description}}
|
||||||
<div class="roll-dialog-chip {{#if this.selected}}selected{{/if}}" data-action="selectExperience" data-key="{{this.id}}">
|
<div class="roll-dialog-chip {{#if (includes ../selectedExperiences id)}}selected{{/if}}" data-action="selectExperience" data-key="{{id}}">
|
||||||
<span>{{this.description}}</span>
|
<span>{{description}}</span>
|
||||||
<span>+{{this.value}}</span>
|
<span>+{{value}}</span>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<button class="disadvantage flex1 {{#if this.advantage}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="true">{{localize "DAGGERHEART.General.Advantage.Full"}}</button>
|
<button class="disadvantage flex1 {{#if (eq advantage 1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="1">{{localize "DAGGERHEART.General.Advantage.Full"}}</button>
|
||||||
<button class="disadvantage flex1 {{#if (eq this.advantage false)}}selected{{/if}}" data-action="updateIsAdvantage">{{localize "DAGGERHEART.General.Disadvantage.Full"}}</button>
|
<button class="disadvantage flex1 {{#if (eq advantage -1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="-1">{{localize "DAGGERHEART.General.Disadvantage.Full"}}</button>
|
||||||
</div>
|
</div>
|
||||||
{{#if (not this.isNpc)}}
|
{{!-- {{#if (not isNpc)}} --}}
|
||||||
<div class="form-group">
|
{{!-- <div class="form-group">
|
||||||
<label>Hope</label>
|
<label>Hope</label>
|
||||||
<div class="form-fields">
|
<div class="form-fields">
|
||||||
<select name="hope">
|
<select name="hope">
|
||||||
{{selectOptions this.diceOptions selected=this.hope valueAttr="value" labelAttr="name" localize=true}}
|
{{selectOptions diceOptions selected=hope valueAttr="value" labelAttr="name" localize=true}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -28,14 +29,15 @@
|
||||||
<label>Fear</label>
|
<label>Fear</label>
|
||||||
<div class="form-fields">
|
<div class="form-fields">
|
||||||
<select name="fear">
|
<select name="fear">
|
||||||
{{selectOptions this.diceOptions selected=this.fear valueAttr="value" labelAttr="name" localize=true}}
|
{{selectOptions diceOptions selected=fear valueAttr="value" labelAttr="name" localize=true}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> --}}
|
||||||
{{/if}}
|
{{!-- {{/if}} --}}
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
|
||||||
<button data-action="finish">Roll</button>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<footer>
|
||||||
|
<button data-action="submitRoll"{{#unless canRoll}} disabled{{/unless}}>Roll</button>
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue