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 DHDualityRoll from './module/data/chat-message/dualityRoll.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 { renderDualityButton } from './module/enrichers/DualityRollEnricher.mjs';
|
||||
import { renderMeasuredTemplate } from './module/enrichers/TemplateEnricher.mjs';
|
||||
|
|
@ -50,6 +51,15 @@ Hooks.once('init', () => {
|
|||
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.Item.documentClass = documents.DhpItem;
|
||||
|
|
@ -307,9 +317,12 @@ const preloadHandlebarsTemplates = async function () {
|
|||
'systems/daggerheart/templates/views/actionTypes/uuid.hbs',
|
||||
'systems/daggerheart/templates/views/actionTypes/uses.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/range-target.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",
|
||||
"Shortrest": "Short Rest",
|
||||
"Longrest": "Long Rest"
|
||||
},
|
||||
"Damage": {
|
||||
"Severe": "Severe",
|
||||
"Major": "Major",
|
||||
"Minor": "Minor",
|
||||
"None": "None"
|
||||
}
|
||||
},
|
||||
"ActionType": {
|
||||
|
|
@ -617,6 +623,14 @@
|
|||
"Stress": {
|
||||
"Name": "Stress",
|
||||
"Stress": "STR"
|
||||
},
|
||||
"Hope": {
|
||||
"Name": "Hope",
|
||||
"Abbreviation": "HO"
|
||||
},
|
||||
"ArmorStack": {
|
||||
"Name": "Armor Stack",
|
||||
"Stress": "AS"
|
||||
}
|
||||
},
|
||||
"ArmorFeature": {
|
||||
|
|
@ -1004,14 +1018,20 @@
|
|||
},
|
||||
"AttackRoll": {
|
||||
"Title": "Attack - {attack}",
|
||||
"RollDamage": "Roll Damage"
|
||||
"RollDamage": "Roll Damage",
|
||||
"RollHealing": "Roll Healing",
|
||||
"ApplyEffect": "Apply Effects"
|
||||
},
|
||||
"DamageRoll": {
|
||||
"Title": "Damage - {damage}",
|
||||
"DealDamageToTargets": "Damage Hit Targets",
|
||||
"DealDamage": "Deal Damage"
|
||||
},
|
||||
"ApplyEffect": {
|
||||
"Title": "Apply Effects - {name}"
|
||||
},
|
||||
"HealingRoll": {
|
||||
"Title": "Heal - {healing}",
|
||||
"Heal": "Heal"
|
||||
},
|
||||
"DeathMove": {
|
||||
|
|
@ -1070,6 +1090,21 @@
|
|||
"Title": "Ownership Selection - {name}",
|
||||
"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": {
|
||||
"TABS": {
|
||||
"features": "Features",
|
||||
|
|
@ -1506,6 +1541,11 @@
|
|||
"Macro": {
|
||||
"Name": "Macro"
|
||||
}
|
||||
},
|
||||
"Settings": {
|
||||
"ResultBased": {
|
||||
"label": "Formula based on Hope/Fear result."
|
||||
}
|
||||
}
|
||||
},
|
||||
"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 {
|
||||
async renderHTML() {
|
||||
if (this.type === 'dualityRoll' || this.type === 'adversaryRoll') {
|
||||
this.content = await foundry.applications.handlebars.renderTemplate(this.content, this.system);
|
||||
}
|
||||
if(this.system.messageTemplate) this.content = await foundry.applications.handlebars.renderTemplate(this.system.messageTemplate, this.system);
|
||||
|
||||
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
||||
const html = await super.renderHTML();
|
||||
this.applyPermission(html);
|
||||
|
||||
if (this.type === 'dualityRoll') {
|
||||
html.classList.add('duality');
|
||||
const dualityResult = this.system.dualityResult;
|
||||
if (dualityResult === DHDualityRoll.dualityResult.hope) html.classList.add('hope');
|
||||
else if (dualityResult === DHDualityRoll.dualityResult.fear) html.classList.add('fear');
|
||||
else html.classList.add('critical');
|
||||
switch (this.system.roll.result.duality) {
|
||||
case 1:
|
||||
html.classList.add('hope');
|
||||
break;
|
||||
case -1:
|
||||
html.classList.add('fear');
|
||||
break;
|
||||
default:
|
||||
html.classList.add('critical');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
const tabs = {
|
||||
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.config = SYSTEM;
|
||||
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.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;
|
||||
}
|
||||
|
||||
|
|
@ -70,27 +78,47 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
|||
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) {
|
||||
const data = this.action.toObject(false);
|
||||
return data.damage.parts.find(d => d.base) ? index - 1 : index;
|
||||
}
|
||||
|
||||
getEffectDetails(id) {
|
||||
return this.action.item.effects.get(id);
|
||||
}
|
||||
|
||||
_prepareSubmitData(event, formData) {
|
||||
const submitData = foundry.utils.expandObject(formData.object);
|
||||
// this.element.querySelectorAll("fieldset[disabled] :is(input, select)").forEach(input => {
|
||||
// foundry.utils.setProperty(submitData, input.name, input.value);
|
||||
// });
|
||||
for ( const keyPath of this.constructor.CLEAN_ARRAYS ) {
|
||||
const data = foundry.utils.getProperty(submitData, keyPath);
|
||||
if ( data ) foundry.utils.setProperty(submitData, keyPath, Object.values(data));
|
||||
}
|
||||
return submitData;
|
||||
}
|
||||
|
||||
static async updateForm(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
|
||||
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 });
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +131,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
|||
}
|
||||
|
||||
static removeElement(event) {
|
||||
event.stopPropagation();
|
||||
const data = this.action.toObject(),
|
||||
key = event.target.closest('.action-category-data').dataset.key,
|
||||
index = event.target.dataset.index;
|
||||
|
|
@ -132,6 +161,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
|||
data = this.action.toObject();
|
||||
data.effects.push({ _id: created._id });
|
||||
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]);
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
export default class RollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(experiences, hopeResource, resolve) {
|
||||
constructor(experiences, costs, action, resolve) {
|
||||
super({}, {});
|
||||
|
||||
|
||||
this.experiences = experiences;
|
||||
this.costs = costs;
|
||||
this.action = action;
|
||||
this.resolve = resolve;
|
||||
this.isNpc;
|
||||
this.selectedExperiences = [];
|
||||
|
|
@ -15,8 +17,7 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
|||
],
|
||||
hope: ['d12'],
|
||||
fear: ['d12'],
|
||||
advantage: null,
|
||||
hopeResource: hopeResource
|
||||
advantage: null
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -42,6 +43,10 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
|||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
costSelection: {
|
||||
id: 'costSelection',
|
||||
template: 'systems/daggerheart/templates/views/costSelection.hbs'
|
||||
},
|
||||
damageSelection: {
|
||||
id: 'damageSelection',
|
||||
template: 'systems/daggerheart/templates/views/rollSelection.hbs'
|
||||
|
|
@ -60,15 +65,19 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
|||
context.fear = this.data.fear;
|
||||
context.advantage = this.data.advantage;
|
||||
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;
|
||||
}
|
||||
|
||||
static updateSelection(event, _, formData) {
|
||||
const { ...rest } = foundry.utils.expandObject(formData.object);
|
||||
|
||||
this.data = foundry.utils.mergeObject(this.data, rest);
|
||||
this.costs = foundry.utils.mergeObject(this.costs, rest.costs);
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
|
@ -90,10 +99,10 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
|||
|
||||
static async finish() {
|
||||
const { diceOptions, ...rest } = this.data;
|
||||
|
||||
this.resolve({
|
||||
...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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import DHActionConfig from '../config/Action.mjs';
|
||||
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
||||
|
||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||
|
|
@ -9,6 +10,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
actions: {
|
||||
reactionRoll: this.reactionRoll,
|
||||
attackRoll: this.attackRoll,
|
||||
attackConfigure: this.attackConfigure,
|
||||
addExperience: this.addExperience,
|
||||
removeExperience: this.removeExperience,
|
||||
toggleHP: this.toggleHP,
|
||||
|
|
@ -51,7 +53,10 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
const context = await super._prepareContext(_options);
|
||||
context.document = this.document;
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -77,26 +82,16 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
this.actor.diceRoll(config);
|
||||
}
|
||||
|
||||
getEffectDetails(id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
static async attackRoll(event) {
|
||||
const { modifier, damage, name: attackName } = this.actor.system.attack,
|
||||
config = {
|
||||
event: event,
|
||||
title: attackName,
|
||||
roll: {
|
||||
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);
|
||||
this.actor.system.attack.use(event);
|
||||
}
|
||||
|
||||
static async attackConfigure(event) {
|
||||
await new DHActionConfig(this.document.system.attack).render(true);
|
||||
}
|
||||
|
||||
static async addExperience() {
|
||||
|
|
|
|||
|
|
@ -139,9 +139,10 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
*/
|
||||
static async #removeAction(event, button) {
|
||||
event.stopPropagation();
|
||||
const actionIndex = button.closest('[data-index]').dataset.index;
|
||||
await this.document.update({
|
||||
'system.actions': this.document.system.actions.filter(
|
||||
(_, index) => index !== Number.parseInt(button.dataset.index)
|
||||
(_, index) => index !== Number.parseInt(actionIndex)
|
||||
)
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
toggleLoadoutView: this.toggleLoadoutView,
|
||||
attackRoll: this.attackRoll,
|
||||
useDomainCard: this.useDomainCard,
|
||||
removeCard: this.removeDomainCard,
|
||||
selectClass: this.selectClass,
|
||||
selectSubclass: this.selectSubclass,
|
||||
selectAncestry: this.selectAncestry,
|
||||
|
|
@ -50,7 +49,8 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
toggleEquipItem: this.toggleEquipItem,
|
||||
toggleVault: this.toggleVault,
|
||||
levelManagement: this.levelManagement,
|
||||
editImage: this._onEditImage
|
||||
editImage: this._onEditImage,
|
||||
triggerContextMenu: this.triggerContextMenu
|
||||
},
|
||||
window: {
|
||||
resizable: true
|
||||
|
|
@ -223,14 +223,18 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
useItem: {
|
||||
name: 'DAGGERHEART.Sheets.PC.ContextMenu.UseItem',
|
||||
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: {
|
||||
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Equip',
|
||||
icon: '<i class="fa-solid fa-hands"></i>',
|
||||
condition: el => {
|
||||
const item = foundry.utils.fromUuidSync(el.dataset.uuid);
|
||||
return !item.system.equipped;
|
||||
const item = this.getItem(el);
|
||||
return ['weapon', 'armor'].includes(item.type) && !item.system.equipped;
|
||||
},
|
||||
callback: this.constructor.toggleEquipItem.bind(this)
|
||||
},
|
||||
|
|
@ -238,11 +242,34 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Unequip',
|
||||
icon: '<i class="fa-solid fa-hands"></i>',
|
||||
condition: el => {
|
||||
const item = foundry.utils.fromUuidSync(el.dataset.uuid);
|
||||
return item.system.equipped;
|
||||
const item = this.getItem(el);
|
||||
return ['weapon', 'armor'].includes(item.type) && item.system.equipped;
|
||||
},
|
||||
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: {
|
||||
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Edit',
|
||||
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',
|
||||
icon: '<i class="fa-solid fa-trash"></i>',
|
||||
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 => () => {
|
||||
let menuItems = ['class', 'subclass'].includes(type) ? [] : [allOptions.useItem];
|
||||
switch (type) {
|
||||
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
|
||||
});
|
||||
this._createContextMenu(() => Object.values(allOptions), `[data-item-id]`, {
|
||||
parentClassHooks: false,
|
||||
fixed: true
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -319,10 +292,16 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelector('.level-value')?.addEventListener('change', this.onLevelChange.bind(this));
|
||||
// To Remove when ContextMenu Handler is made
|
||||
htmlElement
|
||||
.querySelectorAll('[data-item-id]')
|
||||
.forEach(element => element.addEventListener('contextmenu', this.editItem.bind(this)));
|
||||
}
|
||||
|
||||
getItem(element) {
|
||||
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() {
|
||||
|
|
@ -469,51 +448,12 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
const abilityLabel = game.i18n.localize(abilities[button.dataset.attribute].label);
|
||||
const config = {
|
||||
event: event,
|
||||
title: game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', {
|
||||
ability: abilityLabel
|
||||
}),
|
||||
title: game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', { ability: abilityLabel }),
|
||||
roll: {
|
||||
label: abilityLabel,
|
||||
modifier: button.dataset.value
|
||||
},
|
||||
chatMessage: {
|
||||
template: 'systems/daggerheart/templates/chat/duality-roll.hbs'
|
||||
trait: button.dataset.attribute
|
||||
}
|
||||
};
|
||||
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) {
|
||||
|
|
@ -592,8 +532,9 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
new DhlevelUp(this.document).render(true);
|
||||
}
|
||||
|
||||
static async useDomainCard(_, button) {
|
||||
const card = this.document.items.find(x => x.uuid === button.dataset.key);
|
||||
static async useDomainCard(event, button) {
|
||||
const card = this.getItem(event);
|
||||
if (!card) return;
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = {
|
||||
|
|
@ -617,13 +558,6 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
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() {
|
||||
(await game.packs.get('daggerheart.classes'))?.render(true);
|
||||
}
|
||||
|
|
@ -659,23 +593,22 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
}
|
||||
|
||||
static async useItem(event, button) {
|
||||
const id = button.closest('a').id,
|
||||
item = this.document.items.get(id);
|
||||
const item = this.getItem(button);
|
||||
if (!item) return;
|
||||
const wasUsed = await item.use(event);
|
||||
if (wasUsed && item.type === 'weapon') {
|
||||
Hooks.callAll(SYSTEM.HOOKS.characterAttack, {});
|
||||
}
|
||||
}
|
||||
|
||||
static async viewObject(element, button) {
|
||||
const object = await fromUuid((button ?? element).dataset.uuid);
|
||||
|
||||
object.sheet.render(true);
|
||||
static async viewObject(event, button) {
|
||||
const item = this.getItem(event);
|
||||
if (!item) return;
|
||||
item.sheet.render(true);
|
||||
}
|
||||
|
||||
editItem(event) {
|
||||
const uuid = event.target.closest('[data-item-id]').dataset.itemId,
|
||||
item = this.document.items.find(i => i.uuid === uuid);
|
||||
const item = this.getItem(event);
|
||||
if (!item) return;
|
||||
|
||||
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) {
|
||||
await this.document.updateLevel(Number(event.currentTarget.value));
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async deleteItem(element, button) {
|
||||
const item = await fromUuid((button ?? element).closest('a').dataset.uuid);
|
||||
static async deleteItem(event, button) {
|
||||
const item = this.getItem(event);
|
||||
if (!item) return;
|
||||
await item.delete();
|
||||
}
|
||||
|
||||
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) });
|
||||
}
|
||||
|
||||
static async useFeature(_, button) {
|
||||
const item = await fromUuid(button.dataset.id);
|
||||
static async useFeature(event, button) {
|
||||
const item = this.getItem(event);
|
||||
if (!item) return;
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = {
|
||||
|
|
@ -765,7 +695,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
cls.create(msg.toObject());
|
||||
}
|
||||
|
||||
static async toChat(element, button) {
|
||||
static async toChat(event, button) {
|
||||
if (button?.dataset?.type === 'experience') {
|
||||
const experience = this.document.system.experiences[button.dataset.uuid];
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
|
|
@ -787,7 +717,8 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
|
||||
cls.create(msg.toObject());
|
||||
} else {
|
||||
const item = await fromUuid((button ?? element).dataset.uuid);
|
||||
const item = this.getItem(event);
|
||||
if (!item) return;
|
||||
item.toChat(this.document.id);
|
||||
}
|
||||
}
|
||||
|
|
@ -846,9 +777,9 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
cls.create(msg.toObject());
|
||||
}
|
||||
|
||||
static async toggleEquipItem(element, button) {
|
||||
const id = (button ?? element).closest('a').id;
|
||||
const item = this.document.items.get(id);
|
||||
static async toggleEquipItem(event, button) {
|
||||
const item = this.getItem(event);
|
||||
if (!item) return;
|
||||
if (item.system.equipped) {
|
||||
await item.update({ 'system.equipped': false });
|
||||
return;
|
||||
|
|
@ -872,9 +803,9 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
this.render();
|
||||
}
|
||||
|
||||
static async toggleVault(element, button) {
|
||||
const id = (button ?? element).closest('a').id;
|
||||
const item = this.document.items.get(id);
|
||||
static async toggleVault(event, button) {
|
||||
const item = this.getItem(event);
|
||||
if (!item) return;
|
||||
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);
|
||||
context.document = this.document;
|
||||
context.tabs = super._getTabs(this.constructor.TABS);
|
||||
context.getEffectDetails = this.getEffectDetails.bind(this);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
@ -62,6 +63,10 @@ export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) {
|
|||
this.render();
|
||||
}
|
||||
|
||||
getEffectDetails(id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
static async addAdversary() {
|
||||
await this.document.update({
|
||||
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
|
||||
|
|
|
|||
|
|
@ -4,21 +4,21 @@ export const actionTypes = {
|
|||
name: 'DAGGERHEART.Actions.Types.Attack.Name',
|
||||
icon: 'fa-swords'
|
||||
},
|
||||
spellcast: {
|
||||
id: 'spellcast',
|
||||
name: 'DAGGERHEART.Actions.Types.Spellcast.Name',
|
||||
icon: 'fa-book-sparkles'
|
||||
},
|
||||
// spellcast: {
|
||||
// id: 'spellcast',
|
||||
// name: 'DAGGERHEART.Actions.Types.Spellcast.Name',
|
||||
// icon: 'fa-book-sparkles'
|
||||
// },
|
||||
healing: {
|
||||
id: 'healing',
|
||||
name: 'DAGGERHEART.Actions.Types.Healing.Name',
|
||||
icon: 'fa-kit-medical'
|
||||
},
|
||||
resource: {
|
||||
id: 'resource',
|
||||
name: 'DAGGERHEART.Actions.Types.Resource.Name',
|
||||
icon: 'fa-honey-pot'
|
||||
},
|
||||
// resource: {
|
||||
// id: 'resource',
|
||||
// name: 'DAGGERHEART.Actions.Types.Resource.Name',
|
||||
// icon: 'fa-honey-pot'
|
||||
// },
|
||||
damage: {
|
||||
id: 'damage',
|
||||
name: 'DAGGERHEART.Actions.Types.Damage.Name',
|
||||
|
|
@ -46,8 +46,34 @@ export const targetTypes = {
|
|||
id: 'self',
|
||||
label: 'Self'
|
||||
},
|
||||
other: {
|
||||
id: 'other',
|
||||
label: 'Other'
|
||||
friendly: {
|
||||
id: 'friendly',
|
||||
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 = {
|
||||
health: {
|
||||
id: 'health',
|
||||
hitPoints: {
|
||||
id: 'hitPoints',
|
||||
label: 'DAGGERHEART.HealingType.HitPoints.Name',
|
||||
abbreviation: 'DAGGERHEART.HealingType.HitPoints.Abbreviation'
|
||||
},
|
||||
|
|
@ -77,6 +77,16 @@ export const healingTypes = {
|
|||
id: 'stress',
|
||||
label: 'DAGGERHEART.HealingType.Stress.Name',
|
||||
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',
|
||||
d6: 'd6',
|
||||
d8: 'd8',
|
||||
d10: 'd10',
|
||||
d12: 'd12',
|
||||
d20: 'd20'
|
||||
};
|
||||
|
||||
export const multiplierTypes = {
|
||||
proficiency: 'Proficiency',
|
||||
spellcast: 'Spellcast'
|
||||
spellcast: 'Spellcast',
|
||||
flat: 'Flat'
|
||||
};
|
||||
|
||||
export const getDiceSoNicePresets = () => {
|
||||
|
|
@ -360,7 +372,35 @@ export const abilityCosts = {
|
|||
},
|
||||
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,
|
||||
DHHealingAction,
|
||||
DHMacroAction,
|
||||
DHResourceAction,
|
||||
DHSpellCastAction,
|
||||
// DHResourceAction,
|
||||
// DHSpellCastAction,
|
||||
DHSummonAction
|
||||
} from './action.mjs';
|
||||
|
||||
export const actionsTypes = {
|
||||
base: DHBaseAction,
|
||||
attack: DHAttackAction,
|
||||
spellcast: DHSpellCastAction,
|
||||
resource: DHResourceAction,
|
||||
// spellcast: DHSpellCastAction,
|
||||
// resource: DHResourceAction,
|
||||
damage: DHDamageAction,
|
||||
healing: DHHealingAction,
|
||||
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';
|
||||
|
||||
export default class DHAction extends foundry.abstract.DataModel {
|
||||
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
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
import DhpActor from '../../documents/actor.mjs';
|
||||
import D20RollDialog from '../../dialogs/d20RollDialog.mjs';
|
||||
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
/*
|
||||
!!! I'm currently refactoring the whole Action thing, it's a WIP !!!
|
||||
*/
|
||||
|
||||
/*
|
||||
ToDo
|
||||
- Apply ActiveEffect => Add to Chat message like Damage Button ?
|
||||
- Add Drag & Drop for documentUUID field (Macro & Summon)
|
||||
- Add optionnal Role for Healing ?
|
||||
- Handle Roll result as part of formula if needed
|
||||
- Target Check
|
||||
- Cost Check
|
||||
- Add setting and/or checkbox for cost and damage like
|
||||
- Target Check / Target Picker
|
||||
- Range Check
|
||||
- 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 {
|
||||
static extraSchemas = [];
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
_id: new fields.DocumentIdField(),
|
||||
systemPath: new fields.StringField({ required: true, initial: 'actions' }),
|
||||
type: new fields.StringField({ initial: undefined, readonly: true, required: true }),
|
||||
name: new fields.StringField({ initial: undefined }),
|
||||
description: new fields.HTMLField(),
|
||||
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 }),
|
||||
cost: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
|
|
@ -84,35 +58,85 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
|||
}),
|
||||
range: new fields.StringField({
|
||||
choices: SYSTEM.GENERAL.range,
|
||||
required: true,
|
||||
blank: false,
|
||||
initial: 'self'
|
||||
})
|
||||
required: false,
|
||||
blank: true
|
||||
// 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() {}
|
||||
|
||||
get index() {
|
||||
return foundry.utils.getProperty(this.parent, this.systemPath).indexOf(this);
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get item() {
|
||||
return this.parent.parent;
|
||||
}
|
||||
|
||||
get actor() {
|
||||
return this.item?.actor;
|
||||
return this.item instanceof DhpActor ? this.item : this.item?.actor;
|
||||
}
|
||||
|
||||
get chatTemplate() {
|
||||
return 'systems/daggerheart/templates/chat/duality-roll.hbs';
|
||||
}
|
||||
get chatTitle() {
|
||||
return this.item.name;
|
||||
}
|
||||
|
||||
static getRollType() {
|
||||
static getRollType(parent) {
|
||||
return 'ability';
|
||||
}
|
||||
|
||||
|
|
@ -121,96 +145,425 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
|||
updateSource.img ??= parent?.img ?? parent?.system?.img;
|
||||
if (parent?.system?.trait) {
|
||||
updateSource['roll'] = {
|
||||
type: this.getRollType(),
|
||||
type: this.getRollType(parent),
|
||||
trait: parent.system.trait
|
||||
};
|
||||
}
|
||||
if (parent?.type === 'weapon' && !!this.schema.fields.damage) {
|
||||
updateSource['damage'] = { includeBase: true };
|
||||
}
|
||||
if (parent?.system?.range) {
|
||||
updateSource['range'] = parent?.system?.range;
|
||||
}
|
||||
return updateSource;
|
||||
}
|
||||
|
||||
async use(event) {
|
||||
if (this.roll.type && this.roll.trait) {
|
||||
const modifierValue =
|
||||
this.actor.system.traits[this.roll.trait].value + (this.actor.system.bonuses.attack ?? 0);
|
||||
const config = {
|
||||
event: event,
|
||||
title: this.chatTitle,
|
||||
roll: {
|
||||
modifier: modifierValue,
|
||||
label: game.i18n.localize(abilities[this.roll.trait].label),
|
||||
type: this.actionType,
|
||||
difficulty: this.roll?.difficulty
|
||||
},
|
||||
chatMessage: {
|
||||
template: this.chatTemplate
|
||||
getRollData() {
|
||||
const actorData = this.actor.getRollData(false);
|
||||
|
||||
// Remove when included directly in Actor getRollData
|
||||
actorData.prof = actorData.proficiency?.value ?? 1,
|
||||
actorData.cast = actorData.spellcast?.value ?? 1,
|
||||
actorData.scale = this.cost.length
|
||||
? this.cost.reduce((a, c) => {
|
||||
a[c.type] = c.value;
|
||||
return a;
|
||||
}, {})
|
||||
: 1,
|
||||
actorData.roll = {}
|
||||
|
||||
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 = {
|
||||
value: this.damage.parts.map(p => p.getFormula(this.actor)).join(' + '),
|
||||
type: this.damage.parts[0].type
|
||||
};
|
||||
}
|
||||
if (this.effects.length) {
|
||||
// Apply Active Effects. In Chat Message ?
|
||||
}
|
||||
return this.actor.diceRoll(config);
|
||||
}) */
|
||||
}
|
||||
|
||||
if ( this.doFollowUp() ) {
|
||||
if(this.rollDamage) await this.rollDamage(event, config);
|
||||
if(this.rollHealing) await this.rollHealing(event, config);
|
||||
if(this.trigger) await this.trigger(event, config);
|
||||
}
|
||||
|
||||
// Consume resources
|
||||
await this.consume(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) => {
|
||||
return {
|
||||
[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]
|
||||
};
|
||||
};
|
||||
requireConfigurationDialog(config) {
|
||||
return !config.event.shiftkey && !this.hasRoll && (config.costs?.length || config.uses);
|
||||
}
|
||||
|
||||
export class DHAttackAction extends DHBaseAction {
|
||||
static defineSchema() {
|
||||
prepareCost() {
|
||||
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 {
|
||||
...super.defineSchema(),
|
||||
...extraDefineSchema('damage', true),
|
||||
...extraDefineSchema('roll'),
|
||||
...extraDefineSchema('target'),
|
||||
...extraDefineSchema('effects')
|
||||
...uses,
|
||||
enabled: uses.hasOwnProperty('enabled') ? uses.enabled : true
|
||||
};
|
||||
}
|
||||
|
||||
static getRollType() {
|
||||
return 'weapon';
|
||||
hasUses(uses) {
|
||||
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() {
|
||||
return game.i18n.format('DAGGERHEART.Chat.AttackRoll.Title', {
|
||||
attack: this.item.name
|
||||
formatTarget(actor) {
|
||||
return {
|
||||
id: actor.id,
|
||||
actorId: actor.actor.uuid,
|
||||
name: actor.actor.name,
|
||||
img: actor.actor.img,
|
||||
difficulty: actor.actor.system.difficulty,
|
||||
evasion: actor.actor.system.evasion?.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() {
|
||||
super.prepareData();
|
||||
if (this.damage.includeBase && !!this.item?.system?.damage) {
|
||||
|
|
@ -221,115 +574,49 @@ export class DHAttackAction extends DHBaseAction {
|
|||
|
||||
getParentDamage() {
|
||||
return {
|
||||
multiplier: 'proficiency',
|
||||
dice: this.item?.system?.damage.value,
|
||||
bonus: this.item?.system?.damage.bonus ?? 0,
|
||||
value: {
|
||||
multiplier: 'proficiency',
|
||||
dice: this.item?.system?.damage.value,
|
||||
bonus: this.item?.system?.damage.bonus ?? 0
|
||||
},
|
||||
type: this.item?.system?.damage.type,
|
||||
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 {
|
||||
static defineSchema() {
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
healing: new fields.SchemaField({
|
||||
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')
|
||||
};
|
||||
static extraSchemas = ['target', 'effects', 'healing', 'roll'];
|
||||
|
||||
static getRollType(parent) {
|
||||
return 'spellcast';
|
||||
}
|
||||
|
||||
async use(event) {
|
||||
const formula = this.healing.value.getFormula(this.actor);
|
||||
getFormulaValue(data) {
|
||||
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;
|
||||
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);
|
||||
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());
|
||||
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
||||
}
|
||||
|
||||
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 {
|
||||
static defineSchema() {
|
||||
return {
|
||||
...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 {
|
||||
static defineSchema() {
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
...extraDefineSchema('effects')
|
||||
};
|
||||
static extraSchemas = ['effects', 'target'];
|
||||
|
||||
async use(event, ...args) {
|
||||
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() {
|
||||
return {
|
||||
...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,
|
||||
macro = await fromUuid(fixUUID);
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
|||
initial: 'proficiency',
|
||||
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' }),
|
||||
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }),
|
||||
custom: new fields.SchemaField({
|
||||
|
|
@ -21,27 +22,29 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
|||
}
|
||||
|
||||
getFormula(actor) {
|
||||
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : actor.system[this.multiplier]?.total;
|
||||
return this.custom.enabled
|
||||
? 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 {
|
||||
constructor(hasBase, options, context = {}) {
|
||||
constructor(options, context = {}) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
export class DHDamageData extends DHActionDiceData {
|
||||
export class DHDamageData extends foundry.abstract.DataModel {
|
||||
/** @override */
|
||||
static defineSchema() {
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
// ...super.defineSchema(),
|
||||
base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }),
|
||||
type: new fields.StringField({
|
||||
choices: SYSTEM.GENERAL.damageTypes,
|
||||
|
|
@ -49,7 +52,10 @@ export class DHDamageData extends DHActionDiceData {
|
|||
label: 'Type',
|
||||
nullable: false,
|
||||
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';
|
||||
|
||||
const resourceField = () =>
|
||||
|
|
@ -39,30 +40,41 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
hitPoints: resourceField(),
|
||||
stress: resourceField()
|
||||
}),
|
||||
attack: new fields.SchemaField({
|
||||
name: new fields.StringField({}),
|
||||
modifier: new fields.NumberField({ required: true, integer: true, initial: 0 }),
|
||||
range: new fields.StringField({
|
||||
required: true,
|
||||
choices: SYSTEM.GENERAL.range,
|
||||
initial: SYSTEM.GENERAL.range.melee.id
|
||||
}),
|
||||
damage: new fields.SchemaField({
|
||||
value: new fields.StringField(),
|
||||
type: new fields.StringField({
|
||||
required: true,
|
||||
choices: SYSTEM.GENERAL.damageTypes,
|
||||
initial: SYSTEM.GENERAL.damageTypes.physical.id
|
||||
})
|
||||
})
|
||||
attack: new ActionField({
|
||||
initial: {
|
||||
name: 'Attack',
|
||||
_id: foundry.utils.randomID(),
|
||||
systemPath: 'attack',
|
||||
type: 'attack',
|
||||
range: 'melee',
|
||||
target: {
|
||||
type: 'any',
|
||||
amount: 1
|
||||
},
|
||||
roll: {
|
||||
type: 'weapon'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
multiplier: 'flat'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}),
|
||||
experiences: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField(),
|
||||
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 })
|
||||
});
|
||||
|
||||
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 {
|
||||
static get 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 })
|
||||
}),
|
||||
stress: resourceField(6),
|
||||
hope: resourceField(6)
|
||||
hope: resourceField(6),
|
||||
tokens: new fields.ObjectField(),
|
||||
dice: new fields.ObjectField()
|
||||
}),
|
||||
traits: new fields.SchemaField({
|
||||
agility: attributeField(),
|
||||
|
|
@ -90,9 +98,42 @@ export default class DhCharacter extends BaseDataActor {
|
|||
}),
|
||||
levelData: new fields.EmbeddedDataField(DhPCLevelData),
|
||||
bonuses: new fields.SchemaField({
|
||||
attack: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
spellcast: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
armorScore: new fields.NumberField({ integer: true, initial: 0 })
|
||||
armorScore: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
damageThresholds: new fields.SchemaField({
|
||||
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;
|
||||
}
|
||||
|
||||
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.stress.maxTotal = this.resources.stress.max + this.resources.stress.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();
|
||||
return {
|
||||
...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 DHAdversaryRoll from './adversaryRoll.mjs';
|
||||
import DHDamageRoll from './damageRoll.mjs';
|
||||
import DHDualityRoll from './dualityRoll.mjs';
|
||||
import DHAbilityUse from "./abilityUse.mjs";
|
||||
import DHAdversaryRoll from "./adversaryRoll.mjs";
|
||||
import DHDamageRoll from "./damageRoll.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 = {
|
||||
abilityUse: DHAbilityUse,
|
||||
adversaryRoll: DHAdversaryRoll,
|
||||
damageRoll: DHDamageRoll,
|
||||
dualityRoll: DHDualityRoll
|
||||
abilityUse: DHAbilityUse,
|
||||
adversaryRoll: DHAdversaryRoll,
|
||||
damageRoll: DHDamageRoll,
|
||||
dualityRoll: DHDualityRoll,
|
||||
applyEffect: DHApplyEffect
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,43 +4,35 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel {
|
|||
|
||||
return {
|
||||
title: new fields.StringField(),
|
||||
origin: new fields.StringField({ required: true }),
|
||||
dice: 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(
|
||||
new fields.SchemaField({
|
||||
id: new fields.StringField({}),
|
||||
actorId: new fields.StringField({}),
|
||||
name: new fields.StringField({}),
|
||||
img: new fields.StringField({}),
|
||||
difficulty: new fields.NumberField({ integer: true, nullable: 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(
|
||||
{
|
||||
value: new fields.StringField({}),
|
||||
type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false })
|
||||
},
|
||||
{ nullable: true, initial: null }
|
||||
)
|
||||
hasDamage: new fields.BooleanField({ initial: false }),
|
||||
hasHealing: new fields.BooleanField({ initial: false }),
|
||||
hasEffect: new fields.BooleanField({ initial: false }),
|
||||
hasSave: new fields.BooleanField({ initial: false }),
|
||||
source: new fields.SchemaField({
|
||||
actor: new fields.StringField(),
|
||||
item: new fields.StringField(),
|
||||
action: new fields.StringField()
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
this.targets.forEach(target => {
|
||||
target.hit = target.difficulty ? this.total >= target.difficulty : this.total >= target.evasion;
|
||||
});
|
||||
get messageTemplate() {
|
||||
return 'systems/daggerheart/templates/chat/adversary-roll.hbs';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
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;
|
||||
|
||||
return {
|
||||
messageType: new fields.StringField({initial: 'damage'}),
|
||||
title: new fields.StringField(),
|
||||
roll: new fields.StringField({ required: true }),
|
||||
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: ['+', '-', '*', '/'] })
|
||||
})
|
||||
),
|
||||
roll: new fields.DataField({}),
|
||||
targets: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
id: new fields.StringField({ required: true }),
|
||||
actorId: 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 diceField = () =>
|
||||
new fields.SchemaField({
|
||||
dice: new fields.StringField({}),
|
||||
value: new fields.NumberField({ integer: true })
|
||||
});
|
||||
|
||||
export default class DHDualityRoll extends foundry.abstract.TypeDataModel {
|
||||
static dualityResult = {
|
||||
|
|
@ -17,92 +10,35 @@ export default class DHDualityRoll extends foundry.abstract.TypeDataModel {
|
|||
static defineSchema() {
|
||||
return {
|
||||
title: new fields.StringField(),
|
||||
origin: new fields.StringField({ required: true }),
|
||||
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(
|
||||
new fields.SchemaField({
|
||||
id: new fields.StringField({}),
|
||||
actorId: new fields.StringField({}),
|
||||
name: new fields.StringField({}),
|
||||
img: new fields.StringField({}),
|
||||
difficulty: new fields.NumberField({ integer: true, nullable: 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({
|
||||
value: new fields.StringField({}),
|
||||
type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false }),
|
||||
bonusDamage: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.StringField({}),
|
||||
type: new fields.StringField({
|
||||
choices: Object.keys(SYSTEM.GENERAL.damageTypes),
|
||||
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 }
|
||||
)
|
||||
hasDamage: new fields.BooleanField({ initial: false }),
|
||||
hasHealing: new fields.BooleanField({ initial: false }),
|
||||
hasEffect: new fields.BooleanField({ initial: false }),
|
||||
hasSave: new fields.BooleanField({ initial: false }),
|
||||
source: new fields.SchemaField({
|
||||
actor: new fields.StringField(),
|
||||
item: new fields.StringField(),
|
||||
action: new fields.StringField()
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
get diceTotal() {
|
||||
return this.hope.value + this.fear.value;
|
||||
}
|
||||
|
||||
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;
|
||||
get messageTemplate() {
|
||||
return 'systems/daggerheart/templates/chat/duality-roll.hbs';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ export default class DHArmor extends BaseDataItem {
|
|||
})
|
||||
),
|
||||
marks: new fields.SchemaField({
|
||||
max: new fields.NumberField({ initial: 6, integer: true }),
|
||||
value: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
baseThresholds: new fields.SchemaField({
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { actionsTypes } from '../action/_module.mjs';
|
||||
|
||||
/**
|
||||
* Describes metadata about the item data model type
|
||||
* @typedef {Object} ItemDataModelMetadata
|
||||
|
|
@ -50,4 +52,24 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
const data = { ...actorRollData, item: { ...this } };
|
||||
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 DHAction from '../action/action.mjs';
|
||||
import BaseDataItem from './base.mjs';
|
||||
import ActionField from '../fields/actionField.mjs';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import BaseDataItem from './base.mjs';
|
|||
import FormulaField from '../fields/formulaField.mjs';
|
||||
import ActionField from '../fields/actionField.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 {
|
||||
/** @inheritDoc */
|
||||
|
|
@ -14,7 +14,8 @@ export default class DHWeapon extends BaseDataItem {
|
|||
isQuantifiable: true,
|
||||
embedded: {
|
||||
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 NpcRollSelectionDialog from '../applications/npcRollSelectionDialog.mjs';
|
||||
import RollSelectionDialog from '../applications/rollSelectionDialog.mjs';
|
||||
import { GMUpdateEvent, socketEvent } from '../helpers/socket.mjs';
|
||||
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||
import DHDualityRoll from '../data/chat-message/dualityRoll.mjs';
|
||||
import DamageReductionDialog from '../applications/damageReductionDialog.mjs';
|
||||
|
||||
export default class DhpActor extends Actor {
|
||||
async _preCreate(data, options, user) {
|
||||
|
|
@ -268,150 +265,26 @@ export default class DhpActor extends Actor {
|
|||
* @param {boolean} [config.roll.simple=false]
|
||||
* @param {string} [config.roll.type]
|
||||
* @param {number} [config.roll.difficulty]
|
||||
* @param {any} [config.damage]
|
||||
* @param {boolean} [config.hasDamage]
|
||||
* @param {boolean} [config.hasEffect]
|
||||
* @param {object} [config.chatMessage]
|
||||
* @param {string} config.chatMessage.template
|
||||
* @param {boolean} [config.chatMessage.mute]
|
||||
* @param {boolean} [config.checkTarget]
|
||||
* @param {object} [config.targets]
|
||||
* @param {object} [config.costs]
|
||||
*/
|
||||
async diceRoll(config) {
|
||||
let hopeDice = 'd12',
|
||||
fearDice = 'd12',
|
||||
advantageDice = 'd6',
|
||||
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;
|
||||
config.source = {...(config.source ?? {}), actor: this.uuid};
|
||||
config.data = this.getRollData();
|
||||
return await this.rollClass.build(config);
|
||||
}
|
||||
|
||||
if (!config.event.shiftKey && !config.event.altKey && !config.event.ctrlKey) {
|
||||
const dialogClosed = new Promise((resolve, _) => {
|
||||
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;
|
||||
get rollClass() {
|
||||
return CONFIG.Dice.daggerheart[this.type === 'character' ? 'DualityRoll' : 'D20Roll'];
|
||||
}
|
||||
|
||||
advantage = rollConfig.advantage;
|
||||
hopeDice = rollConfig.hope;
|
||||
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;
|
||||
getRollData() {
|
||||
return this.system;
|
||||
}
|
||||
|
||||
formatRollModifier(roll) {
|
||||
|
|
@ -507,124 +380,76 @@ export default class DhpActor extends Actor {
|
|||
? 1
|
||||
: 0;
|
||||
|
||||
const update = {
|
||||
'system.resources.hitPoints.value': Math.min(
|
||||
this.system.resources.hitPoints.value + hpDamage,
|
||||
this.system.resources.hitPoints.max
|
||||
)
|
||||
};
|
||||
|
||||
if (game.user.isGM) {
|
||||
await this.update(update);
|
||||
if (
|
||||
this.type === 'character' &&
|
||||
this.system.armor &&
|
||||
this.system.armor.system.marks.value < this.system.armorScore
|
||||
) {
|
||||
new Promise((resolve, reject) => {
|
||||
new DamageReductionDialog(resolve, reject, this, hpDamage).render(true);
|
||||
})
|
||||
.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 {
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: {
|
||||
action: GMUpdateEvent.UpdateDocument,
|
||||
uuid: this.uuid,
|
||||
update: update
|
||||
}
|
||||
});
|
||||
await this.modifyResource([{ value: hpDamage, type: 'hitPoints' }]);
|
||||
}
|
||||
}
|
||||
|
||||
async takeHealing(healing, type) {
|
||||
let update = {};
|
||||
switch (type) {
|
||||
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
|
||||
}
|
||||
});
|
||||
}
|
||||
async takeHealing(resources) {
|
||||
resources.forEach(r => (r.value *= -1));
|
||||
await this.modifyResource(resources);
|
||||
}
|
||||
|
||||
//Move to action-scope?
|
||||
async useAction(action) {
|
||||
const userTargets = Array.from(game.user.targets);
|
||||
const otherTarget = action.target.type === SYSTEM.ACTIONS.targetTypes.other.id;
|
||||
if (otherTarget && userTargets.length === 0) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.ActionRequiresTarget'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (action.cost.type != null && action.cost.value != null) {
|
||||
if (
|
||||
this.system.resources[action.cost.type].value - action.cost.value <=
|
||||
this.system.resources[action.cost.type].min
|
||||
) {
|
||||
ui.notifications.error(game.i18n.localize(`Insufficient ${action.cost.type} to use this ability`));
|
||||
return;
|
||||
async modifyResource(resources) {
|
||||
if (!resources.length) return;
|
||||
let updates = { actor: { target: this, resources: {} }, armor: { target: this.system.armor, resources: {} } };
|
||||
resources.forEach(r => {
|
||||
switch (r.type) {
|
||||
case 'armorStack':
|
||||
updates.armor.resources['system.marks.value'] = Math.max(
|
||||
Math.min(this.system.armor.system.marks.value + r.value, this.system.armorScore),
|
||||
0
|
||||
);
|
||||
break;
|
||||
default:
|
||||
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),
|
||||
0
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// const targets = otherTarget ? userTargets : [game.user.character];
|
||||
if (action.damage.type) {
|
||||
let roll = { formula: action.damage.value, result: action.damage.value };
|
||||
if (Number.isNaN(Number.parseInt(action.damage.value))) {
|
||||
roll = await new Roll(`1${action.damage.value}`).evaluate();
|
||||
});
|
||||
Object.values(updates).forEach(async u => {
|
||||
if (Object.keys(u.resources).length > 0) {
|
||||
if (game.user.isGM) {
|
||||
await u.target.update(u.resources);
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
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) => {
|
||||
html.querySelectorAll('.duality-action').forEach(element =>
|
||||
html.querySelectorAll('.duality-action-damage').forEach(element =>
|
||||
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 => {
|
||||
element.addEventListener('mouseenter', this.hoverTarget);
|
||||
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 =>
|
||||
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 =>
|
||||
element.addEventListener('click', this.onToggleTargets)
|
||||
);
|
||||
|
|
@ -54,17 +65,65 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
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) => {
|
||||
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 (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(
|
||||
message.system.title,
|
||||
message.system.damage,
|
||||
message.system.targets.filter(x => x.hit).map(x => ({ id: x.id, name: x.name, img: x.img })),
|
||||
event.shiftKey
|
||||
);
|
||||
onRollHealing = 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?.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 => {
|
||||
|
|
@ -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))
|
||||
: 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)
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
||||
|
||||
for (var target of targets) {
|
||||
await target.actor.takeDamage(message.system.damage.total, message.system.damage.type);
|
||||
for (let target of targets) {
|
||||
let damage = message.system.roll.total;
|
||||
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();
|
||||
const healing = Number.parseInt(event.currentTarget.dataset.value);
|
||||
const targets = Array.from(game.user.targets);
|
||||
|
||||
if (targets.length === 0)
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
||||
|
||||
|
||||
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();
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -227,6 +227,10 @@ div.daggerheart.views.multiclass {
|
|||
filter: invert(0%) sepia(100%) saturate(0%) hue-rotate(21deg) brightness(17%) contrast(103%);
|
||||
}
|
||||
}
|
||||
|
||||
#roll-selection-costSelection footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.roll-dialog-container {
|
||||
.disadvantage,
|
||||
|
|
@ -368,6 +372,7 @@ div.daggerheart.views.multiclass {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.roll-dialog-experience-container {
|
||||
display: flex;
|
||||
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 {
|
||||
&.downtime {
|
||||
display: flex;
|
||||
|
|
@ -227,19 +234,35 @@
|
|||
background: @miss;
|
||||
}
|
||||
|
||||
img {
|
||||
flex: 0;
|
||||
img, .target-save-container {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-left: 8px;
|
||||
align-self: center;
|
||||
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 {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: var(--font-size-16);
|
||||
}
|
||||
|
||||
&:not(:has(.target-save-container)) .target-inner-container {
|
||||
margin-right: @hugeMargin;
|
||||
}
|
||||
}
|
||||
|
|
@ -314,12 +337,29 @@
|
|||
width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-use-perm='false'] {
|
||||
pointer-events: none;
|
||||
border-color: transparent;
|
||||
}
|
||||
[data-view-perm='false'] {
|
||||
> * {
|
||||
display: none;
|
||||
}
|
||||
&::after {
|
||||
content: "??";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.theme-colorful {
|
||||
.chat-message.duality {
|
||||
border-color: black;
|
||||
padding: 8px 0 0 0;
|
||||
fieldset.daggerheart.chat {
|
||||
border-top-width: 0;
|
||||
display: contents;
|
||||
}
|
||||
.message-header {
|
||||
color: var(--color-light-3);
|
||||
padding: 0 8px;
|
||||
|
|
@ -450,6 +490,10 @@
|
|||
.duality-action {
|
||||
border-radius: 0 6px 0 0;
|
||||
margin-left: -8px;
|
||||
&.duality-action-effect {
|
||||
border-top-left-radius: 6px;
|
||||
margin-left: initial;
|
||||
}
|
||||
}
|
||||
.duality-result {
|
||||
border-radius: 6px 0 0 0;
|
||||
|
|
|
|||
|
|
@ -1400,6 +1400,12 @@
|
|||
.chat-message .dice-title {
|
||||
display: none;
|
||||
}
|
||||
fieldset.daggerheart.chat {
|
||||
padding: 0;
|
||||
border-left-width: 0;
|
||||
border-right-width: 0;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.daggerheart.chat.downtime {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -1553,18 +1559,32 @@
|
|||
.daggerheart.chat.roll .target-section .target-container.miss {
|
||||
background: #ff0000;
|
||||
}
|
||||
.daggerheart.chat.roll .target-section .target-container img {
|
||||
flex: 0;
|
||||
.daggerheart.chat.roll .target-section .target-container img,
|
||||
.daggerheart.chat.roll .target-section .target-container .target-save-container {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-left: 8px;
|
||||
align-self: center;
|
||||
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 {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
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;
|
||||
}
|
||||
.daggerheart.chat.roll .dice-actions {
|
||||
|
|
@ -1622,10 +1642,24 @@
|
|||
.daggerheart.chat.domain-card img {
|
||||
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 {
|
||||
border-color: black;
|
||||
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 {
|
||||
color: var(--color-light-3);
|
||||
padding: 0 8px;
|
||||
|
|
@ -1752,6 +1786,10 @@
|
|||
border-radius: 0 6px 0 0;
|
||||
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 {
|
||||
border-radius: 6px 0 0 0;
|
||||
margin-right: -8px;
|
||||
|
|
@ -1978,6 +2016,9 @@ div.daggerheart.views.multiclass {
|
|||
.daggerheart.views.roll-selection .roll-selection-container i {
|
||||
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 .advantage {
|
||||
border: 2px solid #708090;
|
||||
|
|
@ -3110,6 +3151,125 @@ div.daggerheart.views.multiclass {
|
|||
.daggerheart.views.ownership-selection .ownership-outer-container .ownership-container select {
|
||||
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 {
|
||||
--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;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
@import './characterCreation.less';
|
||||
@import './levelup.less';
|
||||
@import './ownershipSelection.less';
|
||||
@import './damageReduction.less';
|
||||
@import './resources.less';
|
||||
@import './countdown.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/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.window-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@import '../utils/colors.less';
|
||||
@import '../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.window-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
@import '../../utils/colors.less';
|
||||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.tab.biography {
|
||||
.items-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 98%, transparent 100%);
|
||||
padding-bottom: 40px;
|
||||
height: 100%;
|
||||
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@import '../../utils/colors.less';
|
||||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.tab.biography {
|
||||
.items-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 98%, transparent 100%);
|
||||
padding-bottom: 40px;
|
||||
height: 100%;
|
||||
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
@import '../../utils/colors.less';
|
||||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.tab.features {
|
||||
.features-sections {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
overflow-y: auto;
|
||||
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
|
||||
padding: 20px 0;
|
||||
padding-top: 10px;
|
||||
height: 95%;
|
||||
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@import '../../utils/colors.less';
|
||||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.tab.features {
|
||||
.features-sections {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
overflow-y: auto;
|
||||
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
|
||||
padding: 20px 0;
|
||||
padding-top: 10px;
|
||||
height: 95%;
|
||||
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,65 +1,65 @@
|
|||
@import '../../utils/colors.less';
|
||||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.tab.inventory {
|
||||
.search-section {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
|
||||
.search-bar {
|
||||
position: relative;
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
width: 100%;
|
||||
padding-top: 5px;
|
||||
|
||||
input {
|
||||
border-radius: 50px;
|
||||
font-family: @font-body;
|
||||
background: light-dark(@dark-blue-10, @golden-10);
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
padding: 0 20px;
|
||||
|
||||
&:hover {
|
||||
outline: 2px solid light-dark(@dark, @golden);
|
||||
}
|
||||
|
||||
&:placeholder {
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
align-content: center;
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
font-size: 16px;
|
||||
z-index: 1;
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.items-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
overflow-y: auto;
|
||||
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
|
||||
padding: 20px 0;
|
||||
height: 80%;
|
||||
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
}
|
||||
|
||||
.currency-section {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@import '../../utils/colors.less';
|
||||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.tab.inventory {
|
||||
.search-section {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
|
||||
.search-bar {
|
||||
position: relative;
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
width: 100%;
|
||||
padding-top: 5px;
|
||||
|
||||
input {
|
||||
border-radius: 50px;
|
||||
font-family: @font-body;
|
||||
background: light-dark(@dark-blue-10, @golden-10);
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
padding: 0 20px;
|
||||
|
||||
&:hover {
|
||||
outline: 2px solid light-dark(@dark, @golden);
|
||||
}
|
||||
|
||||
&:placeholder {
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
align-content: center;
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
font-size: 16px;
|
||||
z-index: 1;
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.items-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
overflow-y: auto;
|
||||
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
|
||||
padding: 20px 0;
|
||||
height: 80%;
|
||||
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
}
|
||||
|
||||
.currency-section {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,101 +1,101 @@
|
|||
@import '../../utils/colors.less';
|
||||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.tab.loadout {
|
||||
.search-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.search-bar {
|
||||
position: relative;
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
width: 80%;
|
||||
padding-top: 5px;
|
||||
|
||||
input {
|
||||
border-radius: 50px;
|
||||
font-family: @font-body;
|
||||
background: light-dark(@dark-blue-10, @golden-10);
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
padding: 0 20px;
|
||||
|
||||
&:hover {
|
||||
outline: 2px solid light-dark(@dark, @golden);
|
||||
}
|
||||
|
||||
&:placeholder {
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
align-content: center;
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
font-size: 16px;
|
||||
z-index: 1;
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-toggle-view {
|
||||
background: light-dark(@dark-blue-10, @dark-blue);
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 15px;
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
width: 62px;
|
||||
|
||||
span {
|
||||
margin: 1px;
|
||||
width: 26px;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
|
||||
&.list-icon {
|
||||
i {
|
||||
margin-left: 3px;
|
||||
}
|
||||
}
|
||||
&.grid-icon {
|
||||
i {
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&.list-active {
|
||||
border-radius: 32px 3px 3px 32px;
|
||||
background-color: light-dark(@dark-blue, @golden);
|
||||
color: light-dark(@beige, @dark-blue);
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
&.grid-active {
|
||||
border-radius: 3px 32px 32px 3px;
|
||||
background-color: light-dark(@dark-blue, @golden);
|
||||
color: light-dark(@beige, @dark-blue);
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.items-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 98%, transparent 100%);
|
||||
padding: 20px 0;
|
||||
height: 90%;
|
||||
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@import '../../utils/colors.less';
|
||||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.tab.loadout {
|
||||
.search-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.search-bar {
|
||||
position: relative;
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
width: 80%;
|
||||
padding-top: 5px;
|
||||
|
||||
input {
|
||||
border-radius: 50px;
|
||||
font-family: @font-body;
|
||||
background: light-dark(@dark-blue-10, @golden-10);
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
padding: 0 20px;
|
||||
|
||||
&:hover {
|
||||
outline: 2px solid light-dark(@dark, @golden);
|
||||
}
|
||||
|
||||
&:placeholder {
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
align-content: center;
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
font-size: 16px;
|
||||
z-index: 1;
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-toggle-view {
|
||||
background: light-dark(@dark-blue-10, @dark-blue);
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 15px;
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
width: 62px;
|
||||
|
||||
span {
|
||||
margin: 1px;
|
||||
width: 26px;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
|
||||
&.list-icon {
|
||||
i {
|
||||
margin-left: 3px;
|
||||
}
|
||||
}
|
||||
&.grid-icon {
|
||||
i {
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&.list-active {
|
||||
border-radius: 32px 3px 3px 32px;
|
||||
background-color: light-dark(@dark-blue, @golden);
|
||||
color: light-dark(@beige, @dark-blue);
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
&.grid-active {
|
||||
border-radius: 3px 32px 32px 3px;
|
||||
background-color: light-dark(@dark-blue, @golden);
|
||||
color: light-dark(@beige, @dark-blue);
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.items-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 98%, transparent 100%);
|
||||
padding: 20px 0;
|
||||
height: 90%;
|
||||
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,32 @@
|
|||
@import '../../utils/colors.less';
|
||||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.window-content {
|
||||
display: grid;
|
||||
grid-template-columns: 275px 1fr;
|
||||
grid-template-rows: 283px 1fr;
|
||||
gap: 15px 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.character-sidebar-sheet {
|
||||
grid-row: 1 / span 2;
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.character-header-sheet {
|
||||
grid-row: 1;
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.tab {
|
||||
grid-row: 2;
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.old-sheet {
|
||||
width: 500px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@import '../../utils/colors.less';
|
||||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.window-content {
|
||||
display: grid;
|
||||
grid-template-columns: 275px 1fr;
|
||||
grid-template-rows: 283px 1fr;
|
||||
gap: 15px 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.character-sidebar-sheet {
|
||||
grid-row: 1 / span 2;
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.character-header-sheet {
|
||||
grid-row: 1;
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.tab {
|
||||
grid-row: 2;
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.old-sheet {
|
||||
width: 500px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,310 +1,310 @@
|
|||
@import '../../utils/colors.less';
|
||||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.character-sidebar-sheet {
|
||||
width: 275px;
|
||||
min-width: 275px;
|
||||
border-right: 1px solid light-dark(@dark-blue, @golden);
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
|
||||
.theme-light & {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.portrait {
|
||||
position: relative;
|
||||
height: 235px;
|
||||
width: 275px;
|
||||
border-bottom: 1px solid light-dark(@dark-blue, @golden);
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
height: 235px;
|
||||
width: 275px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.death-roll-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.death-roll {
|
||||
filter: grayscale(1);
|
||||
|
||||
.death-roll-btn {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
right: 30%;
|
||||
font-size: 6rem;
|
||||
color: @beige;
|
||||
|
||||
&:hover {
|
||||
text-shadow: 0 0 8px @beige;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-section {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
top: -20px;
|
||||
gap: 30px;
|
||||
margin-bottom: -10px;
|
||||
|
||||
.resources-section {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
|
||||
.status-bar {
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
justify-items: center;
|
||||
|
||||
.status-label {
|
||||
position: relative;
|
||||
top: 40px;
|
||||
height: 22px;
|
||||
width: 79px;
|
||||
clip-path: path('M0 0H79L74 16.5L39 22L4 16.5L0 0Z');
|
||||
background: light-dark(@dark-blue, @golden);
|
||||
|
||||
h4 {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
line-height: 18px;
|
||||
color: light-dark(@beige, @dark-blue);
|
||||
}
|
||||
}
|
||||
.status-value {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
padding: 0 6px;
|
||||
font-size: 1.5rem;
|
||||
align-items: center;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
z-index: 2;
|
||||
color: @beige;
|
||||
|
||||
input[type='number'] {
|
||||
background: transparent;
|
||||
font-size: 1.5rem;
|
||||
width: 40px;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
color: @beige;
|
||||
|
||||
&.bar-input {
|
||||
padding: 0;
|
||||
color: @beige;
|
||||
backdrop-filter: none;
|
||||
background: transparent;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: @semi-transparent-dark-blue;
|
||||
backdrop-filter: blur(9.5px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bar-label {
|
||||
width: 40px;
|
||||
}
|
||||
}
|
||||
.progress-bar {
|
||||
position: absolute;
|
||||
appearance: none;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
z-index: 1;
|
||||
|
||||
&::-webkit-progress-bar {
|
||||
border: none;
|
||||
background: @dark-blue;
|
||||
border-radius: 6px;
|
||||
}
|
||||
&::-webkit-progress-value {
|
||||
background: @gradient-hp;
|
||||
border-radius: 6px;
|
||||
}
|
||||
&.stress-color::-webkit-progress-value {
|
||||
background: @gradient-stress;
|
||||
border-radius: 6px;
|
||||
}
|
||||
&::-moz-progress-value,
|
||||
&::-moz-progress-bar {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
&::-moz-progress-bar {
|
||||
background: @gradient-hp;
|
||||
}
|
||||
&.stress-color::-moz-progress-bar {
|
||||
background: @gradient-stress;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
justify-content: center;
|
||||
|
||||
.status-number {
|
||||
justify-items: center;
|
||||
|
||||
.status-value {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 50px;
|
||||
height: 30px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-bottom: none;
|
||||
border-radius: 6px 6px 0 0;
|
||||
padding: 0 6px;
|
||||
font-size: 1.2rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: light-dark(transparent, @dark-blue);
|
||||
z-index: 2;
|
||||
|
||||
&.armor-slots {
|
||||
width: 80px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.status-label {
|
||||
padding: 2px 10px;
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
background: light-dark(@dark-blue, @golden);
|
||||
|
||||
h4 {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
color: light-dark(@beige, @dark-blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.items-sidebar-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
|
||||
.inventory-item {
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.equipment-section {
|
||||
.title {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
.items-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.loadout-section {
|
||||
.title {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.experience-section {
|
||||
.title {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.experience-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
align-items: center;
|
||||
|
||||
.experience-row {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
width: 250px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
input[type='text'] {
|
||||
height: 32px;
|
||||
width: 180px;
|
||||
border: 1px solid transparent;
|
||||
outline: 2px solid transparent;
|
||||
font-size: 14px;
|
||||
font-family: @font-body;
|
||||
transition: all 0.3s ease;
|
||||
color: light-dark(@dark, @beige);
|
||||
|
||||
&:hover {
|
||||
outline: 2px solid light-dark(@dark, @beige);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.experience-value {
|
||||
height: 25px;
|
||||
width: 35px;
|
||||
font-size: 14px;
|
||||
font-family: @font-body;
|
||||
color: light-dark(@dark, @beige);
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
background: url(../assets/svg/experience-shield.svg) no-repeat;
|
||||
|
||||
.theme-light & {
|
||||
background: url('../assets/svg/experience-shield-light.svg') no-repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@import '../../utils/colors.less';
|
||||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.character-sidebar-sheet {
|
||||
width: 275px;
|
||||
min-width: 275px;
|
||||
border-right: 1px solid light-dark(@dark-blue, @golden);
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
|
||||
.theme-light & {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.portrait {
|
||||
position: relative;
|
||||
height: 235px;
|
||||
width: 275px;
|
||||
border-bottom: 1px solid light-dark(@dark-blue, @golden);
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
height: 235px;
|
||||
width: 275px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.death-roll-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.death-roll {
|
||||
filter: grayscale(1);
|
||||
|
||||
.death-roll-btn {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
right: 30%;
|
||||
font-size: 6rem;
|
||||
color: @beige;
|
||||
|
||||
&:hover {
|
||||
text-shadow: 0 0 8px @beige;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-section {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
top: -20px;
|
||||
gap: 30px;
|
||||
margin-bottom: -10px;
|
||||
|
||||
.resources-section {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
|
||||
.status-bar {
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
justify-items: center;
|
||||
|
||||
.status-label {
|
||||
position: relative;
|
||||
top: 40px;
|
||||
height: 22px;
|
||||
width: 79px;
|
||||
clip-path: path('M0 0H79L74 16.5L39 22L4 16.5L0 0Z');
|
||||
background: light-dark(@dark-blue, @golden);
|
||||
|
||||
h4 {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
line-height: 18px;
|
||||
color: light-dark(@beige, @dark-blue);
|
||||
}
|
||||
}
|
||||
.status-value {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
padding: 0 6px;
|
||||
font-size: 1.5rem;
|
||||
align-items: center;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
z-index: 2;
|
||||
color: @beige;
|
||||
|
||||
input[type='number'] {
|
||||
background: transparent;
|
||||
font-size: 1.5rem;
|
||||
width: 40px;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
color: @beige;
|
||||
|
||||
&.bar-input {
|
||||
padding: 0;
|
||||
color: @beige;
|
||||
backdrop-filter: none;
|
||||
background: transparent;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: @semi-transparent-dark-blue;
|
||||
backdrop-filter: blur(9.5px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bar-label {
|
||||
width: 40px;
|
||||
}
|
||||
}
|
||||
.progress-bar {
|
||||
position: absolute;
|
||||
appearance: none;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
z-index: 1;
|
||||
|
||||
&::-webkit-progress-bar {
|
||||
border: none;
|
||||
background: @dark-blue;
|
||||
border-radius: 6px;
|
||||
}
|
||||
&::-webkit-progress-value {
|
||||
background: @gradient-hp;
|
||||
border-radius: 6px;
|
||||
}
|
||||
&.stress-color::-webkit-progress-value {
|
||||
background: @gradient-stress;
|
||||
border-radius: 6px;
|
||||
}
|
||||
&::-moz-progress-value,
|
||||
&::-moz-progress-bar {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
&::-moz-progress-bar {
|
||||
background: @gradient-hp;
|
||||
}
|
||||
&.stress-color::-moz-progress-bar {
|
||||
background: @gradient-stress;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
justify-content: center;
|
||||
|
||||
.status-number {
|
||||
justify-items: center;
|
||||
|
||||
.status-value {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 50px;
|
||||
height: 30px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-bottom: none;
|
||||
border-radius: 6px 6px 0 0;
|
||||
padding: 0 6px;
|
||||
font-size: 1.2rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: light-dark(transparent, @dark-blue);
|
||||
z-index: 2;
|
||||
|
||||
&.armor-slots {
|
||||
width: 80px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.status-label {
|
||||
padding: 2px 10px;
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
background: light-dark(@dark-blue, @golden);
|
||||
|
||||
h4 {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
color: light-dark(@beige, @dark-blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.items-sidebar-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
|
||||
.inventory-item {
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.equipment-section {
|
||||
.title {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
.items-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.loadout-section {
|
||||
.title {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.experience-section {
|
||||
.title {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.experience-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
align-items: center;
|
||||
|
||||
.experience-row {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
width: 250px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
input[type='text'] {
|
||||
height: 32px;
|
||||
width: 180px;
|
||||
border: 1px solid transparent;
|
||||
outline: 2px solid transparent;
|
||||
font-size: 14px;
|
||||
font-family: @font-body;
|
||||
transition: all 0.3s ease;
|
||||
color: light-dark(@dark, @beige);
|
||||
|
||||
&:hover {
|
||||
outline: 2px solid light-dark(@dark, @beige);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.experience-value {
|
||||
height: 25px;
|
||||
width: 35px;
|
||||
font-size: 14px;
|
||||
font-family: @font-body;
|
||||
color: light-dark(@dark, @beige);
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
background: url(../assets/svg/experience-shield.svg) no-repeat;
|
||||
|
||||
.theme-light & {
|
||||
background: url('../assets/svg/experience-shield-light.svg') no-repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
@import '../utils/colors.less';
|
||||
@import '../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.items-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
@import '../utils/colors.less';
|
||||
@import '../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.items-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,133 +1,133 @@
|
|||
@import '../utils/colors.less';
|
||||
@import '../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.inventory-item {
|
||||
display: grid;
|
||||
grid-template-columns: 40px 1fr 60px;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
|
||||
.item-img {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-family: @font-body;
|
||||
align-self: center;
|
||||
|
||||
.item-name {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.item-tags,
|
||||
.item-labels {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
.tag {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 3px 5px;
|
||||
font-size: 12px;
|
||||
|
||||
background: light-dark(@dark-15, @beige-15);
|
||||
border: 1px solid light-dark(@dark, @beige);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
gap: 8px;
|
||||
|
||||
a {
|
||||
text-align: center;
|
||||
|
||||
&.unequipped {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.card-item {
|
||||
position: relative;
|
||||
height: 120px;
|
||||
width: 100px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
.card-label {
|
||||
padding-top: 15px;
|
||||
.controls {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transition: all 0.3s ease;
|
||||
max-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: fit-content;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
background-color: @dark-blue;
|
||||
bottom: 0;
|
||||
mask-image: linear-gradient(180deg, transparent 0%, black 20%);
|
||||
|
||||
.card-name {
|
||||
font-family: @font-body;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
|
||||
color: @beige;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
max-height: 0px;
|
||||
opacity: 0;
|
||||
visibility: collapse;
|
||||
transition: all 0.3s ease;
|
||||
color: @beige;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@import '../utils/colors.less';
|
||||
@import '../utils/fonts.less';
|
||||
|
||||
.application.sheet.daggerheart.actor.dh-style.character {
|
||||
.inventory-item {
|
||||
display: grid;
|
||||
grid-template-columns: 40px 1fr 60px;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
|
||||
.item-img {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-family: @font-body;
|
||||
align-self: center;
|
||||
|
||||
.item-name {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.item-tags,
|
||||
.item-labels {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
.tag {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 3px 5px;
|
||||
font-size: 12px;
|
||||
|
||||
background: light-dark(@dark-15, @beige-15);
|
||||
border: 1px solid light-dark(@dark, @beige);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
gap: 8px;
|
||||
|
||||
a {
|
||||
text-align: center;
|
||||
|
||||
&.unequipped {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.card-item {
|
||||
position: relative;
|
||||
height: 120px;
|
||||
width: 100px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
.card-label {
|
||||
padding-top: 15px;
|
||||
.controls {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transition: all 0.3s ease;
|
||||
max-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: fit-content;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
background-color: @dark-blue;
|
||||
bottom: 0;
|
||||
mask-image: linear-gradient(180deg, transparent 0%, black 20%);
|
||||
|
||||
.card-name {
|
||||
font-family: @font-body;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
|
||||
color: @beige;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
max-height: 0px;
|
||||
opacity: 0;
|
||||
visibility: collapse;
|
||||
transition: all 0.3s ease;
|
||||
color: @beige;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
@import '../utils/colors.less';
|
||||
@import '../utils/fonts.less';
|
||||
|
||||
.sheet.daggerheart.dh-style {
|
||||
.tab-navigation {
|
||||
margin: 5px 0;
|
||||
height: 40px;
|
||||
|
||||
.feature-tab {
|
||||
border: none;
|
||||
|
||||
a {
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
font-family: @font-body;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@import '../utils/colors.less';
|
||||
@import '../utils/fonts.less';
|
||||
|
||||
.sheet.daggerheart.dh-style {
|
||||
.tab-navigation {
|
||||
margin: 5px 0;
|
||||
height: 40px;
|
||||
|
||||
.feature-tab {
|
||||
border: none;
|
||||
|
||||
a {
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
font-family: @font-body;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@
|
|||
},
|
||||
{
|
||||
"name": "JimCanE"
|
||||
},
|
||||
{
|
||||
"name": "Po0lp"
|
||||
}
|
||||
],
|
||||
"scripts": ["build/daggerheart.js"],
|
||||
|
|
@ -252,7 +255,8 @@
|
|||
"dualityRoll": {},
|
||||
"adversaryRoll": {},
|
||||
"damageRoll": {},
|
||||
"abilityUse": {}
|
||||
"abilityUse": {},
|
||||
"applyEffect": {}
|
||||
}
|
||||
},
|
||||
"primaryTokenAttribute": "resources.health",
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
<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>
|
||||
|
|
@ -1,33 +1,43 @@
|
|||
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||
<div class="dice-flavor">{{title}}</div>
|
||||
<div class="dice-result">
|
||||
<div class="dice-formula">{{roll.formula}}</div>
|
||||
<div class="dice-tooltip">
|
||||
<ol class="dice-rolls">
|
||||
<div class="dice-hope-container">
|
||||
{{#each dice}}
|
||||
<div class="wrapper">
|
||||
<section class="tooltip-part">
|
||||
<div class="dice">
|
||||
{{#each roll.dice}}
|
||||
<header class="part-header flexrow">
|
||||
<span class="part-formula">{{number}}{{denomination}}</span>
|
||||
<span class="part-formula">{{formula}}</span>
|
||||
<span class="part-total">{{total}}</span>
|
||||
</header>
|
||||
<div class="flexrow">
|
||||
<ol class="dice-rolls">
|
||||
{{#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}}
|
||||
</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>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="modifiers-container">
|
||||
{{#each modifiers}}
|
||||
<li class="modifier-value" data-value="{{value}}" title="{{title}}">{{label}}</li>
|
||||
{{/each}}
|
||||
</div>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dice-total">
|
||||
<div class="dice-total-value">{{roll.total}}</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-flavor">{{title}}</div>
|
||||
<div class="dice-result">
|
||||
<div class="dice-formula">{{roll}}</div>
|
||||
|
||||
<div class="dice-formula">{{roll.formula}}</div>
|
||||
<div class="dice-tooltip">
|
||||
<div class="wrapper">
|
||||
<section class="tooltip-part">
|
||||
{{#each dice}}
|
||||
{{#each roll.dice}}
|
||||
<div class="dice">
|
||||
<header class="part-header flexrow">
|
||||
<span class="part-formula">{{rolls.length}}{{type}}</span>
|
||||
|
||||
<span class="part-total">{{this.total}}</span>
|
||||
<span class="part-formula">{{formula}}</span>
|
||||
<span class="part-total">{{total}}</span>
|
||||
</header>
|
||||
<ol class="dice-rolls">
|
||||
{{#each rolls}}
|
||||
<li class="roll die {{../type}} min">{{this}}</li>
|
||||
{{#each results}}
|
||||
<li class="roll die {{../denomination}} min">{{result}}</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
</div>
|
||||
|
|
@ -23,7 +21,12 @@
|
|||
</section>
|
||||
</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">
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||
<div class="dice-flavor">{{title}}</div>
|
||||
<div class="duality-modifiers">
|
||||
{{#each modifiers}}
|
||||
{{#each roll.modifiers}}
|
||||
<div class="duality-modifier">
|
||||
{{label}}
|
||||
{{localize label}} {{#if (gte value 0)}}+{{/if}}{{value}}
|
||||
</div>
|
||||
{{/each}}
|
||||
{{#if advantageState}}
|
||||
{{#if (eq roll.advantage.type 1)}}
|
||||
<div class="duality-modifier">
|
||||
{{localize "DAGGERHEART.General.Advantage.Full"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq advantageState false)}}
|
||||
{{#if (eq roll.advantage.type -1)}}
|
||||
<div class="duality-modifier">
|
||||
{{localize "DAGGERHEART.General.Disadvantage.Full"}}
|
||||
</div>
|
||||
|
|
@ -25,56 +25,56 @@
|
|||
<div class="dice">
|
||||
<header class="part-header flexrow">
|
||||
<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 class="part-total">{{diceTotal}}</span>
|
||||
<span class="part-total">{{roll.result.total}}</span>
|
||||
</header>
|
||||
<div class="flexrow">
|
||||
<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-title">{{localize "DAGGERHEART.General.Hope"}}</div>
|
||||
<div class="dice-inner-container hope" title="{{localize "DAGGERHEART.General.Hope"}}">
|
||||
<div class="dice-wrapper">
|
||||
<img class="dice" src="../icons/svg/d12-grey.svg"/>
|
||||
</div>
|
||||
<div class="dice-value">{{hope.value}}</div>
|
||||
<div class="dice-value">{{roll.hope.value}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</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-title">{{localize "DAGGERHEART.General.Fear"}}</div>
|
||||
<div class="dice-inner-container fear" title="{{localize "DAGGERHEART.General.Fear"}}">
|
||||
<div class="dice-wrapper">
|
||||
<img class="dice" src="../icons/svg/d12-grey.svg"/>
|
||||
</div>
|
||||
<div class="dice-value">{{fear.value}}</div>
|
||||
<div class="dice-value">{{roll.fear.value}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
{{#if advantageState}}
|
||||
{{#if roll.advantage.type}}
|
||||
<div class="dice">
|
||||
<header class="part-header flexrow">
|
||||
<span class="part-formula">
|
||||
<span>1{{advantage.dice}}</span>
|
||||
<span>1{{roll.advantage.dice}}</span>
|
||||
</span>
|
||||
<span class="part-total">{{advantage.value}}</span>
|
||||
<span class="part-total">{{roll.advantage.value}}</span>
|
||||
</header>
|
||||
<div class="flexrow">
|
||||
<ol class="dice-rolls">
|
||||
<li class="roll die {{advantage.dice}}">
|
||||
<li class="roll die {{roll.advantage.dice}}">
|
||||
<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">
|
||||
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
||||
</div>
|
||||
<div class="dice-value">{{advantage.value}}</div>
|
||||
<div class="dice-value">{{roll.advantage.value}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
|
@ -82,58 +82,34 @@
|
|||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq advantageState false)}}
|
||||
<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}}
|
||||
{{#if roll.modifierTotal}}<div class="duality-modifier">{{#if (gt roll.modifierTotal 0)}}+{{/if}}{{roll.modifierTotal}}</div>{{/if}}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dice-total duality {{#if fear.discarded}}hope{{else}}{{#if hope.discarded}}fear{{else}}critical{{/if}}{{/if}}">
|
||||
<div class="dice-total-label">{{totalLabel}}</div>
|
||||
<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">{{roll.result.label}}</div>
|
||||
<div class="dice-total-value">
|
||||
{{roll.total}}
|
||||
</div>
|
||||
</div>
|
||||
{{#if (gt targets.length 0)}}
|
||||
<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 target.hit}}{{localize "Hit"}}{{else}}{{#if (not ../total.alternate)}}{{localize "Miss"}}{{else}}?{{/if}}{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="dice-actions{{#unless damage.value}} duality-alone{{/unless}}">
|
||||
{{#if damage.value}}
|
||||
<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>
|
||||
</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{{#unless (or hasDamage hasHealing)}} duality-alone{{/unless}}">
|
||||
{{#if hasDamage}}
|
||||
<button class="duality-action duality-action-damage" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.Chat.AttackRoll.RollDamage"}}</span></button>
|
||||
{{else}}
|
||||
{{#if hasHealing}}
|
||||
<button class="duality-action duality-action-healing" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.Chat.AttackRoll.RollHealing"}}</span></button>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if hasEffect}}
|
||||
<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}}
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -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-formula">{{this.roll}}</div>
|
||||
<div class="dice-formula">{{roll.formula}}</div>
|
||||
<div class="dice-tooltip">
|
||||
<ol class="dice-rolls">
|
||||
{{#each dice}}
|
||||
<li class="roll die {{this.type}}">{{this.value}}</li>
|
||||
{{/each}}
|
||||
{{#each modifiers}}
|
||||
<li class="modifier-value">{{this}}</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
<div class="wrapper">
|
||||
<section class="tooltip-part">
|
||||
{{#each roll.dice}}
|
||||
<div class="dice">
|
||||
<header class="part-header flexrow">
|
||||
<span class="part-formula">{{formula}}</span>
|
||||
<span class="part-total">{{total}}</span>
|
||||
</header>
|
||||
<ol class="dice-rolls">
|
||||
{{#each results}}
|
||||
<li class="roll die {{../denomination}} min">{{result}}</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
</div>
|
||||
{{/each}}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dice-total">{{this.total}}</div>
|
||||
<div class="dice-total">{{roll.total}}</div>
|
||||
<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>
|
||||
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">
|
||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Attack"}}</legend>
|
||||
|
||||
{{formGroup systemFields.attack.fields.name value=source.system.attack.name}}
|
||||
<button data-action="attackConfigure">Configure</button>
|
||||
<button data-action="attackRoll">Attack</button>
|
||||
{{formGroup systemFields.attack.fields.modifier value=source.system.attack.modifier}}
|
||||
{{formGroup systemFields.attack.fields.range value=source.system.attack.range localize=true}}
|
||||
{{formGroup systemFields.attack.fields.damage.fields.value value=source.system.attack.damage.value}}
|
||||
{{formGroup systemFields.attack.fields.damage.fields.type value=source.system.attack.damage.type localize=true}}
|
||||
<fieldset class="action-category" style="grid-column: 1 / -1;">
|
||||
<legend class="action-category-label" data-action="toggleSection" data-section="range">
|
||||
<div>Name</div>
|
||||
</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>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -75,8 +75,8 @@
|
|||
<div class="hope-section">
|
||||
<h4>{{localize "DAGGERHEART.General.Hope"}}</h4>
|
||||
{{#times document.system.resources.hope.max}}
|
||||
<span class='hope-value' data-action='toggleHope' data-value="{{this}}">
|
||||
{{#if (gte ../document.system.resources.hope.value this)}}
|
||||
<span class='hope-value' data-action='toggleHope' data-value="{{add this 1}}">
|
||||
{{#if (gte ../document.system.resources.hope.value (add this 1))}}
|
||||
<i class='fa-solid fa-diamond'></i>
|
||||
{{else}}
|
||||
<i class='fa-regular fa-circle'></i>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<li class="item inventory-item">
|
||||
<div class="inventory-row" data-item-id="{{item.uuid}}">
|
||||
<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}}" />
|
||||
{{item.name}}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<li class="card-item">
|
||||
<img src="{{item.img}}" data-action="viewObject" data-uuid="{{item.uuid}}" class="card-img" />
|
||||
<li class="card-item" data-item-id="{{item.id}}">
|
||||
<img src="{{item.img}}" data-action="useItem" class="card-img" />
|
||||
<div class="card-label">
|
||||
<div class="controls">
|
||||
{{#if (eq type 'weapon')}}
|
||||
|
|
@ -14,18 +14,18 @@
|
|||
{{/if}}
|
||||
{{#if (eq type 'domainCard')}}
|
||||
{{#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>
|
||||
</a>
|
||||
{{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>
|
||||
</a>
|
||||
{{/unless}}
|
||||
|
||||
{{/if}}
|
||||
<a data-action="toChat" data-uuid="{{item.uuid}}" 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="toChat" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a>
|
||||
<a data-action="triggerContextMenu" data-tooltip="{{localize 'DAGGERHEART.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a>
|
||||
</div>
|
||||
<div class="card-name">{{item.name}}</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<li class="inventory-item">
|
||||
<img src="{{item.img}}" data-action="viewObject" data-uuid="{{item.uuid}}" class="item-img" />
|
||||
<li class="inventory-item" data-item-id="{{item.id}}">
|
||||
<img src="{{item.img}}" class="item-img" data-action="useItem"/>
|
||||
<div class="item-label">
|
||||
<div class="item-name">{{item.name}}</div>
|
||||
{{#if (eq type 'weapon')}}
|
||||
|
|
@ -103,28 +103,28 @@
|
|||
</div>
|
||||
<div class="controls">
|
||||
{{#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>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#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>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if (eq type 'domainCard')}}
|
||||
{{#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>
|
||||
</a>
|
||||
{{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>
|
||||
</a>
|
||||
{{/unless}}
|
||||
|
||||
{{/if}}
|
||||
<a data-action="toChat" data-uuid="{{item.uuid}}" 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="toChat" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a>
|
||||
<a data-action="triggerContextMenu" data-tooltip="{{localize 'DAGGERHEART.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a>
|
||||
</div>
|
||||
</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>
|
||||
<div class="actions-list">
|
||||
{{#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}}" />
|
||||
<span>{{action.name}}</span>
|
||||
<div class="controls">
|
||||
<a data-action="editAction" data-index="{{index}}"><i class="fa-solid fa-pen-to-square"></i></a>
|
||||
<a data-action="removeAction" data-index="{{index}}"><i class="fa-solid fa-trash"></i></a>
|
||||
<a data-action="removeAction"><i class="fa-solid fa-trash"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<div class="hope-container">
|
||||
{{#times 6}}
|
||||
<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}}
|
||||
</span>
|
||||
{{/times}}
|
||||
|
|
|
|||
|
|
@ -20,21 +20,33 @@
|
|||
{{formField fields.name value=source.name label="Name" name="name"}}
|
||||
{{formField fields.img value=source.img label="Icon" name="img"}}
|
||||
{{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>
|
||||
</fieldset>
|
||||
{{#if fields.roll}}{{> 'systems/daggerheart/templates/views/actionTypes/roll.hbs' fields=fields.roll.fields source=source.roll}}{{/if}}
|
||||
</div>
|
||||
<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/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}}
|
||||
</div>
|
||||
<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.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.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>
|
||||
</section>
|
||||
</div>
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
{{#each source as |cost index|}}
|
||||
<fieldset>
|
||||
<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")}}
|
||||
</div>
|
||||
<div class="multi-display">
|
||||
|
|
|
|||
|
|
@ -4,33 +4,72 @@
|
|||
<div>Damage</div>
|
||||
</legend>
|
||||
<div class="action-category-data open">
|
||||
<div class="fas fa-plus icon-button" data-action="addDamage"></div>
|
||||
{{#if @root.hasBaseDamage}}
|
||||
<div>
|
||||
{{!-- <input type="checkbox" data-action="addBaseDamage"{{#if @root.hasBaseDamage}} checked{{/if}}> --}}
|
||||
{{formField @root.fields.damage.fields.includeBase value=@root.source.damage.includeBase label="Include Item Damage" name="damage.includeBase" }}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#unless @root.isNPC}}
|
||||
<div class="fas fa-plus icon-button" data-action="addDamage"></div>
|
||||
{{#if @root.hasBaseDamage}}
|
||||
<div>
|
||||
{{formField @root.fields.damage.fields.includeBase value=@root.source.damage.includeBase label="Include Item Damage" name="damage.includeBase" }}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
{{#each source.parts as |dmg index|}}
|
||||
{{#with (@root.getRealIndex index) as | realIndex |}}
|
||||
<fieldset{{#if dmg.base}} disabled{{/if}}>
|
||||
{{#unless dmg.base}}
|
||||
{{formField ../../fields.custom.fields.enabled value=dmg.custom.enabled name=(concat "damage.parts." realIndex ".custom.enabled")}}
|
||||
{{/unless}}
|
||||
{{#if dmg.custom.enabled}}
|
||||
{{formField ../../fields.custom.fields.formula value=dmg.custom.formula name=(concat "damage.parts." realIndex ".custom.formula") localize=true}}
|
||||
{{#if @root.isNPC}}
|
||||
{{formField ../fields.value.fields.custom.fields.enabled value=dmg.value.custom.enabled name=(concat ../path "damage.parts." index ".value.custom.enabled")}}
|
||||
<input type="hidden" name="{{../path}}damage.parts.{{index}}.value.multiplier" value="{{dmg.value.multiplier}}">
|
||||
{{#if dmg.value.custom.enabled}}
|
||||
{{formField ../fields.value.fields.custom.fields.formula value=dmg.value.custom.formula name=(concat ../path "damage.parts." index ".value.custom.formula") localize=true}}
|
||||
{{else}}
|
||||
<div class="multi-display">
|
||||
{{formField ../../fields.multiplier value=dmg.multiplier name=(concat "damage.parts." realIndex ".multiplier") localize=true}}
|
||||
{{formField ../../fields.dice value=dmg.dice name=(concat "damage.parts." realIndex ".dice")}}
|
||||
{{formField ../../fields.bonus value=dmg.bonus name=(concat "damage.parts." realIndex ".bonus") localize=true}}
|
||||
{{formField ../fields.value.fields.flatMultiplier value=dmg.value.flatMultiplier name=(concat ../path "damage.parts." index ".value.flatMultiplier") label="Multiplier" }}
|
||||
{{formField ../fields.value.fields.dice value=dmg.value.dice name=(concat ../path "damage.parts." index ".value.dice")}}
|
||||
{{formField ../fields.value.fields.bonus value=dmg.value.bonus name=(concat ../path "damage.parts." index ".value.bonus") localize=true}}
|
||||
</div>
|
||||
{{/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}}
|
||||
{{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." index ".type") localize=true}}
|
||||
{{else}}
|
||||
{{#with (@root.getRealIndex index) as | realIndex |}}
|
||||
<fieldset{{#if dmg.base}} disabled{{/if}}>
|
||||
{{#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}}
|
||||
</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>
|
||||
<div class="action-category-data open" data-key="effects">
|
||||
<div class="fas fa-plus icon-button" data-action="addEffect"></div>
|
||||
{{#each @root.effects as | effect index | }}
|
||||
{{!-- {{#each @root.effects as | effect index | }}
|
||||
<fieldset>
|
||||
{{!-- <div class="multi-display"> --}}
|
||||
<div class="multi-display">
|
||||
<div class="form-group">
|
||||
<img src="{{img}}">
|
||||
<label data-action="editEffect">{{name}}</label>
|
||||
<div class="fas fa-trash" data-action="removeEffect" data-index="{{index}}"></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>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,16 +6,38 @@
|
|||
<div class="action-category-data open">
|
||||
<fieldset>
|
||||
{{formField fields.type value=source.type name="healing.type" localize=true}}
|
||||
<div class="multi-display">
|
||||
{{formField fields.value.fields.custom.fields.enabled value=source.value.custom.enabled name="healing.value.custom.enabled"}}
|
||||
{{#if source.value.custom.enabled}}
|
||||
{{formField fields.value.fields.custom.fields.formula value=source.value.custom.formula name="healing.value.custom.formula" localize=true}}
|
||||
{{else}}
|
||||
{{formField fields.value.fields.multiplier value=source.value.multiplier name="healing.value.multiplier" localize=true}}
|
||||
{{formField fields.value.fields.dice value=source.value.dice name="healing.value.dice"}}
|
||||
{{formField fields.value.fields.bonus value=source.value.bonus name="healing.value.bonus" localize=true}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#if (and (not @root.isNPC) @root.hasRoll)}}
|
||||
{{formField fields.resultBased value=source.resultBased name="healing.resultBased" localize=true}}
|
||||
{{/if}}
|
||||
{{#if (and (not @root.isNPC) @root.hasRoll source.resultBased)}}
|
||||
<fieldset>
|
||||
<legend>
|
||||
<div>With Hope</div>
|
||||
</legend>
|
||||
{{> formula fields=fields.value.fields source=source.value target="value"}}
|
||||
</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>
|
||||
</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>
|
||||
</legend>
|
||||
<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>
|
||||
{{#if fields.target}}
|
||||
<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>
|
||||
{{/if}}
|
||||
</fieldset>
|
||||
|
|
@ -3,8 +3,12 @@
|
|||
<div>Roll</div>
|
||||
</legend>
|
||||
<div class="action-category-data open">
|
||||
{{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}}
|
||||
{{formField fields.difficulty label="Difficulty" name="roll.difficulty" value=source.difficulty}}
|
||||
{{#if @root.isNPC}}
|
||||
{{formField fields.bonus label="Bonus" name="roll.bonus" value=source.bonus}}
|
||||
{{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>
|
||||
</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 class="form-group">
|
||||
<label><strong>Total Damage</strong></label>
|
||||
<label><strong>{{title}}</strong></label>
|
||||
<div class="form-fields">
|
||||
<input type="text" value="{{this.rollString}}" disabled />
|
||||
<input type="text" value="{{formula}}" disabled />
|
||||
</div>
|
||||
</div>
|
||||
{{#each this.bonusDamage as |damage index|}}
|
||||
{{!-- {{#each bonusDamage as |damage index|}}
|
||||
<div class="form-group">
|
||||
<label><strong>{{damage.description}}</strong></label>
|
||||
<div class="form-fields">
|
||||
|
|
@ -22,8 +22,8 @@
|
|||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{/each}} --}}
|
||||
<footer>
|
||||
<button data-action="rollDamage">Roll</button>
|
||||
<button data-action="submitRoll">Roll</button>
|
||||
</footer>
|
||||
</div>
|
||||
|
|
@ -1,26 +1,27 @@
|
|||
<div>
|
||||
{{#if @root.hasRoll}}
|
||||
<div class="roll-dialog-container">
|
||||
<div class="flexcol">
|
||||
<div class="roll-dialog-experience-container">
|
||||
{{#each this.experiences}}
|
||||
{{#if this.description}}
|
||||
<div class="roll-dialog-chip {{#if this.selected}}selected{{/if}}" data-action="selectExperience" data-key="{{this.id}}">
|
||||
<span>{{this.description}}</span>
|
||||
<span>+{{this.value}}</span>
|
||||
{{#each experiences}}
|
||||
{{#if description}}
|
||||
<div class="roll-dialog-chip {{#if (includes ../selectedExperiences id)}}selected{{/if}}" data-action="selectExperience" data-key="{{id}}">
|
||||
<span>{{description}}</span>
|
||||
<span>+{{value}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
<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 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.Advantage.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>
|
||||
{{#if (not this.isNpc)}}
|
||||
<div class="form-group">
|
||||
{{!-- {{#if (not isNpc)}} --}}
|
||||
{{!-- <div class="form-group">
|
||||
<label>Hope</label>
|
||||
<div class="form-fields">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -28,14 +29,15 @@
|
|||
<label>Fear</label>
|
||||
<div class="form-fields">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div> --}}
|
||||
{{!-- {{/if}} --}}
|
||||
</div>
|
||||
<footer>
|
||||
<button data-action="finish">Roll</button>
|
||||
</footer>
|
||||
</div>
|
||||
{{/if}}
|
||||
<footer>
|
||||
<button data-action="submitRoll"{{#unless canRoll}} disabled{{/unless}}>Roll</button>
|
||||
</footer>
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue