mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 11:41:08 +01:00
merged with main
This commit is contained in:
commit
0aa08deaa3
41 changed files with 805 additions and 446 deletions
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: "[BUG] - "
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Setup Information:**
|
||||||
|
- OS: [e.g. iOS]
|
||||||
|
- Browser [e.g. chrome, safari]
|
||||||
|
- Foundry Version [e.g. v13 b342]
|
||||||
|
- System Version [e.g. main-3593f44]
|
||||||
|
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
25
README.md
25
README.md
|
|
@ -1,8 +1,23 @@
|
||||||
# Daggerheart
|
# Daggerheart
|
||||||
|
|
||||||
This is a repo for a Foundry VTT implementation of daggerheart. It is not associated with Critical Role or Darrington Press.
|
## Table of Contents
|
||||||
|
|
||||||
## Setup
|
- [Overview](#overview)
|
||||||
|
- [User Install Guide](#user-install)
|
||||||
|
- [Developer Setup](#developer-setup)
|
||||||
|
- [Contribution Info](#contributing)
|
||||||
|
|
||||||
|
## Overivew
|
||||||
|
|
||||||
|
This is a community repo for a Foundry VTT implementation of Daggerheart. It is not associated with Critical Role or Darrington Press.
|
||||||
|
|
||||||
|
## User Install
|
||||||
|
|
||||||
|
1. **(Not Yet Supported - No Releases Yet)** Pasting `https://raw.githubusercontent.com/Foundryborne/daggerheart/refs/heads/main/system.json` into the Install System dialog on the Setup menu of the application.
|
||||||
|
2. **(Not Yet Supported - No Releases Yet)** Browsing the repository's Releases page, where you can copy any system.json link for use in the Install System dialog.
|
||||||
|
3. **(Not Yet Supported - No Releases Yet)** Downloading one of the .zip archives from the Releases page and extracting it into your foundry Data folder, under Data/systems/daggerheart.
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
- Open a terminal in the directory with the repo `cd <path>/<to>/<repo>`
|
- Open a terminal in the directory with the repo `cd <path>/<to>/<repo>`
|
||||||
- NOTE: The repo should be placed in the system files are or somewhere else and a link (if on linux) is placed in the system directory
|
- NOTE: The repo should be placed in the system files are or somewhere else and a link (if on linux) is placed in the system directory
|
||||||
|
|
@ -23,8 +38,8 @@ This is a repo for a Foundry VTT implementation of daggerheart. It is not associ
|
||||||
Now you should be able to build the app using `npm start`
|
Now you should be able to build the app using `npm start`
|
||||||
[Foundry VTT Website][1]
|
[Foundry VTT Website][1]
|
||||||
|
|
||||||
[1]: https://foundryvtt.com/
|
[1]: https://foundryvtt.com/
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Looking to contribute to the project? Look no further, check out our [contributing guide](contributing.md), and keep the [Code of Conduct](coc.md) in mind when working on things.
|
Looking to contribute to the project? Look no further, check out our [contributing guide](contributing.md), and keep the [Code of Conduct](coc.md) in mind when working on things.
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ Hooks.once('init', () => {
|
||||||
CONFIG.Dice.rolls = [...CONFIG.Dice.rolls, ...[DHRoll, DualityRoll, D20Roll, DamageRoll]];
|
CONFIG.Dice.rolls = [...CONFIG.Dice.rolls, ...[DHRoll, DualityRoll, D20Roll, DamageRoll]];
|
||||||
CONFIG.MeasuredTemplate.objectClass = DhMeasuredTemplate;
|
CONFIG.MeasuredTemplate.objectClass = DhMeasuredTemplate;
|
||||||
|
|
||||||
CONFIG.Item.documentClass = documents.DhpItem;
|
CONFIG.Item.documentClass = documents.DHItem;
|
||||||
|
|
||||||
//Registering the Item DataModel
|
//Registering the Item DataModel
|
||||||
CONFIG.Item.dataModels = models.items.config;
|
CONFIG.Item.dataModels = models.items.config;
|
||||||
|
|
@ -323,7 +323,7 @@ const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/views/actionTypes/range-target.hbs',
|
'systems/daggerheart/templates/views/actionTypes/range-target.hbs',
|
||||||
'systems/daggerheart/templates/views/actionTypes/effect.hbs',
|
'systems/daggerheart/templates/views/actionTypes/effect.hbs',
|
||||||
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
|
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
|
||||||
|
|
||||||
'systems/daggerheart/templates/chat/parts/target-chat.hbs'
|
'systems/daggerheart/templates/chat/parts/target-chat.hbs'
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
12
lang/en.json
12
lang/en.json
|
|
@ -100,6 +100,15 @@
|
||||||
"FIELDS": {
|
"FIELDS": {
|
||||||
"maxFear": { "label": "Max Fear" },
|
"maxFear": { "label": "Max Fear" },
|
||||||
"traitArray": { "label": "Initial Trait Modifiers" }
|
"traitArray": { "label": "Initial Trait Modifiers" }
|
||||||
|
},
|
||||||
|
"Currency": {
|
||||||
|
"enabled": "Enable Overrides",
|
||||||
|
"title": "Currency Overrides",
|
||||||
|
"currencyName": "Currency Name",
|
||||||
|
"coinName": "Coin Name",
|
||||||
|
"handfullName": "Handfull Name",
|
||||||
|
"bagName": "Bag Name",
|
||||||
|
"chestName": "Chest Name"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Resources": {
|
"Resources": {
|
||||||
|
|
@ -1589,6 +1598,9 @@
|
||||||
},
|
},
|
||||||
"spellcast": {
|
"spellcast": {
|
||||||
"name": "SpellCast"
|
"name": "SpellCast"
|
||||||
|
},
|
||||||
|
"diceSet": {
|
||||||
|
"name": "Dice Set"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
export default function DhpApplicationMixin(Base) {
|
|
||||||
return class DhpSheet extends Base {
|
|
||||||
static applicationType = 'sheets';
|
|
||||||
static documentType = '';
|
|
||||||
|
|
||||||
static get defaultOptions() {
|
|
||||||
return Object.assign(super.defaultOptions, {
|
|
||||||
classes: ['daggerheart', 'sheet', this.documentType],
|
|
||||||
template: `systems/${SYSTEM.id}/templates/${this.applicationType}/${this.documentType}.hbs`,
|
|
||||||
height: 'auto',
|
|
||||||
submitOnChange: true,
|
|
||||||
submitOnClose: false,
|
|
||||||
width: 450
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
get title() {
|
|
||||||
const { documentName, type, name } = this.object;
|
|
||||||
// const typeLabel = game.i18n.localize(CONFIG[documentName].typeLabels[type]);
|
|
||||||
const typeLabel = documentName;
|
|
||||||
return `[${typeLabel}] ${name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// async _renderOuter() {
|
|
||||||
// const html = await super._renderOuter();
|
|
||||||
// // const overlaySrc = "systems/amia/assets/ThePrimordial.png";
|
|
||||||
// const overlay = `<div class="outer-render"></div>`
|
|
||||||
// $(html).find('.window-header').prepend(overlay);
|
|
||||||
// return html;
|
|
||||||
// }
|
|
||||||
|
|
||||||
activateListeners(html) {
|
|
||||||
super.activateListeners(html);
|
|
||||||
html.on('click', '[data-action]', this.#onClickAction.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
async #onClickAction(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const button = event.currentTarget;
|
|
||||||
const action = button.dataset.action;
|
|
||||||
|
|
||||||
return this._handleAction(action, event, button);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _handleAction(action, event, button) {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -11,6 +11,10 @@ export class DHRoll extends Roll {
|
||||||
super(formula, data, options);
|
super(formula, data, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static messageType = 'adversaryRoll';
|
||||||
|
|
||||||
|
static DefaultDialog = D20RollDialog;
|
||||||
|
|
||||||
static async build(config = {}, message = {}) {
|
static async build(config = {}, message = {}) {
|
||||||
const roll = await this.buildConfigure(config, message);
|
const roll = await this.buildConfigure(config, message);
|
||||||
if (!roll) return;
|
if (!roll) return;
|
||||||
|
|
@ -28,19 +32,18 @@ export class DHRoll extends Roll {
|
||||||
|
|
||||||
this.applyKeybindings(config);
|
this.applyKeybindings(config);
|
||||||
|
|
||||||
|
let roll = new this(config.roll.formula, config.data, config);
|
||||||
if (config.dialog.configure !== false) {
|
if (config.dialog.configure !== false) {
|
||||||
// Open Roll Dialog
|
// Open Roll Dialog
|
||||||
const DialogClass = config.dialog?.class ?? this.DefaultDialog;
|
const DialogClass = config.dialog?.class ?? this.DefaultDialog;
|
||||||
config = await DialogClass.configure(config, message);
|
const configDialog = await DialogClass.configure(roll, config, message);
|
||||||
if (!config) return;
|
if (!configDialog) return;
|
||||||
}
|
}
|
||||||
let roll = new this(config.formula, config.data, config);
|
|
||||||
|
|
||||||
for (const hook of config.hooks) {
|
for (const hook of config.hooks) {
|
||||||
if (Hooks.call(`${SYSTEM.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false)
|
if (Hooks.call(`${SYSTEM.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false)
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return roll;
|
return roll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,7 +65,20 @@ export class DHRoll extends Roll {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async postEvaluate(roll, config = {}) {}
|
static postEvaluate(roll, config = {}) {
|
||||||
|
if (!config.roll) config.roll = {};
|
||||||
|
config.roll.total = roll.total;
|
||||||
|
config.roll.formula = roll.formula;
|
||||||
|
config.roll.dice = [];
|
||||||
|
roll.dice.forEach(d => {
|
||||||
|
config.roll.dice.push({
|
||||||
|
dice: d.denomination,
|
||||||
|
total: d.total,
|
||||||
|
formula: d.formula,
|
||||||
|
results: d.results
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static async toMessage(roll, config) {
|
static async toMessage(roll, config) {
|
||||||
const cls = getDocumentClass('ChatMessage'),
|
const cls = getDocumentClass('ChatMessage'),
|
||||||
|
|
@ -77,15 +93,16 @@ export class DHRoll extends Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
static applyKeybindings(config) {
|
static applyKeybindings(config) {
|
||||||
config.dialog.configure ??= true;
|
config.dialog.configure ??= !(config.event.shiftKey || config.event.altKey || config.event.ctrlKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructFormula(config) {
|
||||||
|
// const formula = Roll.replaceFormulaData(this.options.roll.formula, config.data);
|
||||||
|
this.terms = Roll.parse(this.options.roll.formula, config.data);
|
||||||
|
return (this._formula = this.constructor.getFormula(this.terms));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DHopeDie
|
|
||||||
// DFearDie
|
|
||||||
// DualityDie
|
|
||||||
// D20Die
|
|
||||||
|
|
||||||
export class DualityDie extends foundry.dice.terms.Die {
|
export class DualityDie extends foundry.dice.terms.Die {
|
||||||
constructor({ number = 1, faces = 12, ...args } = {}) {
|
constructor({ number = 1, faces = 12, ...args } = {}) {
|
||||||
super({ number, faces, ...args });
|
super({ number, faces, ...args });
|
||||||
|
|
@ -95,10 +112,11 @@ export class DualityDie extends foundry.dice.terms.Die {
|
||||||
export class D20Roll extends DHRoll {
|
export class D20Roll extends DHRoll {
|
||||||
constructor(formula, data = {}, options = {}) {
|
constructor(formula, data = {}, options = {}) {
|
||||||
super(formula, data, options);
|
super(formula, data, options);
|
||||||
this.createBaseDice();
|
// this.createBaseDice();
|
||||||
this.configureModifiers();
|
// this.configureModifiers();
|
||||||
|
|
||||||
this._formula = this.resetFormula();
|
// this._formula = this.resetFormula();
|
||||||
|
this.constructFormula();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ADV_MODE = {
|
static ADV_MODE = {
|
||||||
|
|
@ -165,7 +183,7 @@ export class D20Roll extends DHRoll {
|
||||||
|
|
||||||
applyAdvantage() {
|
applyAdvantage() {
|
||||||
this.d20.modifiers.findSplice(m => ['kh', 'kl'].includes(m));
|
this.d20.modifiers.findSplice(m => ['kh', 'kl'].includes(m));
|
||||||
if (!this.hasAdvantage && !this.hasAdvantage) this.number = 1;
|
if (!this.hasAdvantage && !this.hasDisadvantage) this.number = 1;
|
||||||
else {
|
else {
|
||||||
this.d20.number = 2;
|
this.d20.number = 2;
|
||||||
this.d20.modifiers.push(this.hasAdvantage ? 'kh' : 'kl');
|
this.d20.modifiers.push(this.hasAdvantage ? 'kh' : 'kl');
|
||||||
|
|
@ -175,62 +193,65 @@ export class D20Roll extends DHRoll {
|
||||||
// Trait bonus != Adversary
|
// Trait bonus != Adversary
|
||||||
configureModifiers() {
|
configureModifiers() {
|
||||||
this.applyAdvantage();
|
this.applyAdvantage();
|
||||||
|
// this.options.roll.modifiers = [];
|
||||||
this.applyBaseBonus();
|
this.applyBaseBonus();
|
||||||
|
|
||||||
this.options.experiences?.forEach(m => {
|
this.options.experiences?.forEach(m => {
|
||||||
if (this.options.data.experiences?.[m])
|
if (this.options.data.experiences?.[m])
|
||||||
this.options.roll.modifiers.push({
|
this.options.roll.modifiers.push({
|
||||||
label: this.options.data.experiences[m].description,
|
label: this.options.data.experiences[m].name,
|
||||||
value: this.options.data.experiences[m].total
|
value: this.options.data.experiences[m].total ?? this.options.data.experiences[m].value
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.options.roll.modifiers?.forEach(m => {
|
this.options.roll.modifiers?.forEach(m => {
|
||||||
this.terms.push(...this.formatModifier(m.value));
|
this.terms.push(...this.formatModifier(m.value));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.options.extraFormula)
|
if (this.options.extraFormula) {
|
||||||
this.terms.push(
|
this.terms.push(
|
||||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||||
...this.constructor.parse(this.options.extraFormula, this.getRollData())
|
...this.constructor.parse(this.options.extraFormula, this.getRollData())
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// this.resetFormula();
|
// this.resetFormula();
|
||||||
}
|
}
|
||||||
|
|
||||||
applyBaseBonus() {
|
constructFormula(config) {
|
||||||
if (this.options.type === 'attack')
|
this.terms = [];
|
||||||
this.terms.push(...this.formatModifier(this.options.data.attack.roll.bonus));
|
this.createBaseDice();
|
||||||
|
this.configureModifiers();
|
||||||
|
this.resetFormula();
|
||||||
|
return this._formula;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async postEvaluate(roll, config = {}) {
|
applyBaseBonus() {
|
||||||
|
this.options.roll.modifiers = [
|
||||||
|
{
|
||||||
|
label: 'Bonus to Hit',
|
||||||
|
value: Roll.replaceFormulaData('@attackBonus', this.data)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static postEvaluate(roll, config = {}) {
|
||||||
|
super.postEvaluate(roll, config);
|
||||||
if (config.targets?.length) {
|
if (config.targets?.length) {
|
||||||
config.targets.forEach(target => {
|
config.targets.forEach(target => {
|
||||||
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
|
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
|
||||||
target.hit = this.isCritical || roll.total >= difficulty;
|
target.hit = this.isCritical || roll.total >= difficulty;
|
||||||
});
|
});
|
||||||
} else if (config.roll.difficulty) config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty;
|
} else if (config.roll.difficulty)
|
||||||
config.roll.total = roll.total;
|
config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty;
|
||||||
config.roll.formula = roll.formula;
|
|
||||||
config.roll.advantage = {
|
config.roll.advantage = {
|
||||||
type: config.advantage,
|
type: config.advantage,
|
||||||
dice: roll.dAdvantage?.denomination,
|
dice: roll.dAdvantage?.denomination,
|
||||||
value: roll.dAdvantage?.total
|
value: roll.dAdvantage?.total
|
||||||
};
|
};
|
||||||
config.roll.modifierTotal = config.roll.modifiers.reduce((a, c) => a + c.value, 0);
|
config.roll.modifierTotal = config.roll.modifiers.reduce((a, c) => a + Number(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() {
|
getRollData() {
|
||||||
return this.options.data();
|
return this.options.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
formatModifier(modifier) {
|
formatModifier(modifier) {
|
||||||
|
|
@ -332,7 +353,7 @@ export class DualityRoll extends D20Roll {
|
||||||
bardRallyFaces = this.hasBarRally,
|
bardRallyFaces = this.hasBarRally,
|
||||||
advDie = new foundry.dice.terms.Die({ faces: dieFaces });
|
advDie = new foundry.dice.terms.Die({ faces: dieFaces });
|
||||||
if (this.hasAdvantage || this.hasDisadvantage || bardRallyFaces)
|
if (this.hasAdvantage || this.hasDisadvantage || bardRallyFaces)
|
||||||
this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: '+' }));
|
this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }));
|
||||||
if (bardRallyFaces) {
|
if (bardRallyFaces) {
|
||||||
const rallyDie = new foundry.dice.terms.Die({ faces: bardRallyFaces });
|
const rallyDie = new foundry.dice.terms.Die({ faces: bardRallyFaces });
|
||||||
if (this.hasAdvantage) {
|
if (this.hasAdvantage) {
|
||||||
|
|
@ -349,15 +370,15 @@ export class DualityRoll extends D20Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
applyBaseBonus() {
|
applyBaseBonus() {
|
||||||
if (!this.options.roll.modifiers) 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`,
|
label: `DAGGERHEART.Abilities.${this.options.roll.trait}.name`,
|
||||||
value: this.options.data.traits[this.options.roll.trait].total
|
value: Roll.replaceFormulaData(`@traits.${this.options.roll.trait}.total`, this.data)
|
||||||
});
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
static async postEvaluate(roll, config = {}) {
|
static postEvaluate(roll, config = {}) {
|
||||||
super.postEvaluate(roll, config);
|
super.postEvaluate(roll, config);
|
||||||
config.roll.hope = {
|
config.roll.hope = {
|
||||||
dice: roll.dHope.denomination,
|
dice: roll.dHope.denomination,
|
||||||
|
|
@ -372,6 +393,7 @@ export class DualityRoll extends D20Roll {
|
||||||
total: roll.dHope.total + roll.dFear.total,
|
total: roll.dHope.total + roll.dFear.total,
|
||||||
label: roll.totalLabel
|
label: roll.totalLabel
|
||||||
};
|
};
|
||||||
|
console.log(roll, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -385,19 +407,7 @@ export class DamageRoll extends DHRoll {
|
||||||
static DefaultDialog = DamageDialog;
|
static DefaultDialog = DamageDialog;
|
||||||
|
|
||||||
static async postEvaluate(roll, config = {}) {
|
static async postEvaluate(roll, config = {}) {
|
||||||
config.roll = {
|
super.postEvaluate(roll, config);
|
||||||
total: roll.total,
|
config.roll.type = config.type;
|
||||||
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
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,16 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
editItem: this.editItem,
|
editItem: this.editItem,
|
||||||
removeItem: this.removeItem,
|
removeItem: this.removeItem,
|
||||||
resetMoves: this.resetMoves,
|
resetMoves: this.resetMoves,
|
||||||
save: this.save
|
save: this.save,
|
||||||
|
reset: this.reset
|
||||||
},
|
},
|
||||||
form: { handler: this.updateData, submitOnChange: true }
|
form: { handler: this.updateData, submitOnChange: true }
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
main: {
|
main: {
|
||||||
template: 'systems/daggerheart/templates/settings/homebrew-settings.hbs'
|
template: 'systems/daggerheart/templates/settings/homebrew-settings.hbs',
|
||||||
|
scrollable: ['']
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -154,4 +156,27 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async reset() {
|
||||||
|
const resetSettings = new DhHomebrew();
|
||||||
|
let localizedSettings = this.localizeObject(resetSettings);
|
||||||
|
this.settings.updateSource(localizedSettings);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
localizeObject(obj) {
|
||||||
|
for (let key in obj) {
|
||||||
|
if (obj.hasOwnProperty(key)) {
|
||||||
|
const value = obj[key];
|
||||||
|
if (typeof value === 'object' && value !== null) {
|
||||||
|
obj[key] = this.localizeObject(value);
|
||||||
|
} else {
|
||||||
|
if (typeof value === 'string' && value.startsWith('DAGGERHEART.')) {
|
||||||
|
obj[key] = game.i18n.localize(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,19 +46,18 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
|
|
||||||
switch (partId) {
|
switch (partId) {
|
||||||
case 'description':
|
case 'description':
|
||||||
const value = foundry.utils.getProperty(this.document, "system.description") ?? "";
|
const value = foundry.utils.getProperty(this.document, 'system.description') ?? '';
|
||||||
context.enrichedDescription = await TextEditor.enrichHTML(value, {
|
context.enrichedDescription = await TextEditor.enrichHTML(value, {
|
||||||
relativeTo: this.item,
|
relativeTo: this.item,
|
||||||
rollData: this.item.getRollData(),
|
rollData: this.item.getRollData(),
|
||||||
secrets: this.item.isOwner
|
secrets: this.item.isOwner
|
||||||
})
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Application Clicks Actions */
|
/* Application Clicks Actions */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
@ -70,26 +69,18 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
*/
|
*/
|
||||||
static async selectActionType() {
|
static async selectActionType() {
|
||||||
const content = await foundry.applications.handlebars.renderTemplate(
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
'systems/daggerheart/templates/views/actionType.hbs',
|
'systems/daggerheart/templates/views/actionType.hbs',
|
||||||
{ types: SYSTEM.ACTIONS.actionTypes }
|
{ types: SYSTEM.ACTIONS.actionTypes }
|
||||||
),
|
),
|
||||||
title = 'Select Action Type', //useless var
|
title = 'Select Action Type';
|
||||||
type = 'form',
|
|
||||||
data = {}; //useless var
|
return foundry.applications.api.DialogV2.prompt({
|
||||||
//TODO: use DialogV2
|
window: { title },
|
||||||
return Dialog.prompt({
|
|
||||||
title,
|
|
||||||
label: title,
|
|
||||||
content,
|
content,
|
||||||
type, //this prop is useless
|
ok: {
|
||||||
callback: html => {
|
label: title,
|
||||||
const form = html[0].querySelector('form'),
|
callback: (event, button, dialog) => button.form.elements.type.value
|
||||||
fd = new foundry.applications.ux.FormDataExtended(form);
|
}
|
||||||
foundry.utils.mergeObject(data, fd.object, { inplace: true });
|
|
||||||
// if (!data.name?.trim()) data.name = game.i18n.localize(SYSTEM.ACTIONS.actionTypes[data.type].name);
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
rejectClose: false
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,13 +91,14 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
*/
|
*/
|
||||||
static async #addAction(_event, _button) {
|
static async #addAction(_event, _button) {
|
||||||
const actionType = await DHBaseItemSheet.selectActionType();
|
const actionType = await DHBaseItemSheet.selectActionType();
|
||||||
|
if (!actionType) return;
|
||||||
try {
|
try {
|
||||||
const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack,
|
const cls = actionsTypes[actionType] ?? actionsTypes.attack,
|
||||||
action = new cls(
|
action = new cls(
|
||||||
{
|
{
|
||||||
_id: foundry.utils.randomID(),
|
_id: foundry.utils.randomID(),
|
||||||
type: actionType.type,
|
type: actionType,
|
||||||
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name),
|
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType].name),
|
||||||
...cls.getSourceConfig(this.document)
|
...cls.getSourceConfig(this.document)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -141,9 +133,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const actionIndex = button.closest('[data-index]').dataset.index;
|
const actionIndex = button.closest('[data-index]').dataset.index;
|
||||||
await this.document.update({
|
await this.document.update({
|
||||||
'system.actions': this.document.system.actions.filter(
|
'system.actions': this.document.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex))
|
||||||
(_, index) => index !== Number.parseInt(actionIndex)
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,6 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
resizable: true
|
resizable: true
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
handler: this.updateForm,
|
|
||||||
submitOnChange: true,
|
submitOnChange: true,
|
||||||
closeOnSubmit: false
|
closeOnSubmit: false
|
||||||
},
|
},
|
||||||
|
|
@ -218,6 +217,15 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
this._createContextMenues();
|
this._createContextMenues();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
async _onRender(context, options) {
|
||||||
|
await super._onRender(context, options);
|
||||||
|
|
||||||
|
this._createSearchFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
_createContextMenues() {
|
_createContextMenues() {
|
||||||
const allOptions = {
|
const allOptions = {
|
||||||
useItem: {
|
useItem: {
|
||||||
|
|
@ -332,12 +340,123 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
context.inventory = {
|
||||||
|
currency: {
|
||||||
|
title: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Title'),
|
||||||
|
coins: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Coins'),
|
||||||
|
handfulls: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Handfulls'),
|
||||||
|
bags: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Bags'),
|
||||||
|
chests: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Chests')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const homebrewCurrency = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).currency;
|
||||||
|
if (homebrewCurrency.enabled) {
|
||||||
|
context.inventory.currency = homebrewCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.inventory.length === 0) {
|
||||||
|
context.inventory = Array(1).fill(Array(5).fill([]));
|
||||||
|
}
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
/* -------------------------------------------- */
|
||||||
await this.document.update(formData.object);
|
/* Search Filter */
|
||||||
this.render();
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The currently active search filter.
|
||||||
|
* @type {foundry.applications.ux.SearchFilter}
|
||||||
|
*/
|
||||||
|
#search = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track which item IDs are currently displayed due to a search filter.
|
||||||
|
* @type {{ inventory: Set<string>, loadout: Set<string> }}
|
||||||
|
*/
|
||||||
|
#filteredItems = {
|
||||||
|
inventory: new Set(),
|
||||||
|
loadout: new Set()
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and initialize search filter instances for the inventory and loadout sections.
|
||||||
|
*
|
||||||
|
* Sets up two {@link foundry.applications.ux.SearchFilter} instances:
|
||||||
|
* - One for the inventory, which filters items in the inventory grid.
|
||||||
|
* - One for the loadout, which filters items in the loadout/card grid.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_createSearchFilter() {
|
||||||
|
//Filters could be a application option if needed
|
||||||
|
const filters = [
|
||||||
|
{
|
||||||
|
key: 'inventory',
|
||||||
|
input: 'input[type="search"].search-inventory',
|
||||||
|
content: '[data-application-part="inventory"] .items-section',
|
||||||
|
callback: this._onSearchFilterInventory.bind(this)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'loadout',
|
||||||
|
input: 'input[type="search"].search-loadout',
|
||||||
|
content: '[data-application-part="loadout"] .items-section',
|
||||||
|
callback: this._onSearchFilterCard.bind(this)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { key, input, content, callback } of filters) {
|
||||||
|
const filter = new foundry.applications.ux.SearchFilter({
|
||||||
|
inputSelector: input,
|
||||||
|
contentSelector: content,
|
||||||
|
callback
|
||||||
|
});
|
||||||
|
filter.bind(this.element);
|
||||||
|
this.#search[key] = filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle invetory items search and filtering.
|
||||||
|
* @param {KeyboardEvent} event The keyboard input event.
|
||||||
|
* @param {string} query The input search string.
|
||||||
|
* @param {RegExp} rgx The regular expression query that should be matched against.
|
||||||
|
* @param {HTMLElement} html The container to filter items from.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_onSearchFilterInventory(event, query, rgx, html) {
|
||||||
|
this.#filteredItems.inventory.clear();
|
||||||
|
|
||||||
|
for (const ul of html.querySelectorAll('.items-list')) {
|
||||||
|
for (const li of ul.querySelectorAll('.inventory-item')) {
|
||||||
|
const item = this.document.items.get(li.dataset.itemId);
|
||||||
|
const match = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
|
||||||
|
if (match) this.#filteredItems.inventory.add(item.id);
|
||||||
|
li.hidden = !match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle card items search and filtering.
|
||||||
|
* @param {KeyboardEvent} event The keyboard input event.
|
||||||
|
* @param {string} query The input search string.
|
||||||
|
* @param {RegExp} rgx The regular expression query that should be matched against.
|
||||||
|
* @param {HTMLElement} html The container to filter items from.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_onSearchFilterCard(event, query, rgx, html) {
|
||||||
|
this.#filteredItems.loadout.clear();
|
||||||
|
|
||||||
|
const elements = html.querySelectorAll('.items-list .inventory-item, .card-list .card-item');
|
||||||
|
|
||||||
|
for (const li of elements) {
|
||||||
|
const item = this.document.items.get(li.dataset.itemId);
|
||||||
|
const match = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
|
||||||
|
if (match) this.#filteredItems.loadout.add(item.id);
|
||||||
|
li.hidden = !match;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async rollAttribute(event, button) {
|
static async rollAttribute(event, button) {
|
||||||
|
|
@ -543,7 +662,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async makeDeathMove() {
|
static async makeDeathMove() {
|
||||||
if (this.document.system.resources.hitPoints.value === this.document.system.resources.hitPoints.max) {
|
if (this.document.system.resources.hitPoints.value >= this.document.system.resources.hitPoints.maxTotal) {
|
||||||
await new DhpDeathMove(this.document).render(true);
|
await new DhpDeathMove(this.document).render(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,4 +76,32 @@ export const damageOnSave = {
|
||||||
label: 'Full damage',
|
label: 'Full damage',
|
||||||
mod: 1
|
mod: 1
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export const diceCompare = {
|
||||||
|
below: {
|
||||||
|
id: 'below',
|
||||||
|
label: 'Below',
|
||||||
|
operator: '<'
|
||||||
|
},
|
||||||
|
belowEqual: {
|
||||||
|
id: 'belowEqual',
|
||||||
|
label: 'Below or Equal',
|
||||||
|
operator: '<='
|
||||||
|
},
|
||||||
|
equal: {
|
||||||
|
id: 'equal',
|
||||||
|
label: 'Equal',
|
||||||
|
operator: '='
|
||||||
|
},
|
||||||
|
aboveEqual: {
|
||||||
|
id: 'aboveEqual',
|
||||||
|
label: 'Above or Equal',
|
||||||
|
operator: '>='
|
||||||
|
},
|
||||||
|
above: {
|
||||||
|
id: 'above',
|
||||||
|
label: 'Above',
|
||||||
|
operator: '>'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -163,13 +163,15 @@ export const defaultRestOptions = {
|
||||||
id: 'repairArmor',
|
id: 'repairArmor',
|
||||||
name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Name'),
|
name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Name'),
|
||||||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Description')
|
description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Description'),
|
||||||
|
actions: []
|
||||||
},
|
},
|
||||||
prepare: {
|
prepare: {
|
||||||
id: 'prepare',
|
id: 'prepare',
|
||||||
name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Name'),
|
name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Name'),
|
||||||
img: 'icons/skills/trades/academics-merchant-scribe.webp',
|
img: 'icons/skills/trades/academics-merchant-scribe.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Description')
|
description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Description'),
|
||||||
|
actions: []
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
longRest: () => ({
|
longRest: () => ({
|
||||||
|
|
@ -177,31 +179,36 @@ export const defaultRestOptions = {
|
||||||
id: 'tendToWounds',
|
id: 'tendToWounds',
|
||||||
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Name'),
|
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Name'),
|
||||||
img: 'icons/magic/life/cross-worn-green.webp',
|
img: 'icons/magic/life/cross-worn-green.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Description')
|
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Description'),
|
||||||
|
actions: []
|
||||||
},
|
},
|
||||||
clearStress: {
|
clearStress: {
|
||||||
id: 'clearStress',
|
id: 'clearStress',
|
||||||
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Name'),
|
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Name'),
|
||||||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Description')
|
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Description'),
|
||||||
|
actions: []
|
||||||
},
|
},
|
||||||
repairArmor: {
|
repairArmor: {
|
||||||
id: 'repairArmor',
|
id: 'repairArmor',
|
||||||
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Name'),
|
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Name'),
|
||||||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Description')
|
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Description'),
|
||||||
|
actions: []
|
||||||
},
|
},
|
||||||
prepare: {
|
prepare: {
|
||||||
id: 'prepare',
|
id: 'prepare',
|
||||||
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Name'),
|
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Name'),
|
||||||
img: 'icons/skills/trades/academics-merchant-scribe.webp',
|
img: 'icons/skills/trades/academics-merchant-scribe.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Description')
|
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Description'),
|
||||||
|
actions: []
|
||||||
},
|
},
|
||||||
workOnAProject: {
|
workOnAProject: {
|
||||||
id: 'workOnAProject',
|
id: 'workOnAProject',
|
||||||
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Name'),
|
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Name'),
|
||||||
img: 'icons/skills/social/thumbsup-approval-like.webp',
|
img: 'icons/skills/social/thumbsup-approval-like.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Description')
|
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Description'),
|
||||||
|
actions: []
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -264,8 +271,17 @@ export const diceTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const multiplierTypes = {
|
export const multiplierTypes = {
|
||||||
proficiency: 'Proficiency',
|
prof: 'Proficiency',
|
||||||
spellcast: 'Spellcast',
|
cast: 'Spellcast',
|
||||||
|
scale: 'Cost Scaling',
|
||||||
|
result: 'Roll Result',
|
||||||
|
flat: 'Flat'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const diceSetNumbers = {
|
||||||
|
prof: 'Proficiency',
|
||||||
|
cast: 'Spellcast',
|
||||||
|
scale: 'Cost Scaling',
|
||||||
flat: 'Flat'
|
flat: 'Flat'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -388,6 +404,10 @@ export const rollTypes = {
|
||||||
ability: {
|
ability: {
|
||||||
id: 'ability',
|
id: 'ability',
|
||||||
label: 'DAGGERHEART.RollTypes.ability.name'
|
label: 'DAGGERHEART.RollTypes.ability.name'
|
||||||
|
},
|
||||||
|
diceSet: {
|
||||||
|
id: 'diceSet',
|
||||||
|
label: 'DAGGERHEART.RollTypes.diceSet.name'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import CostSelectionDialog from '../../applications/costSelectionDialog.mjs';
|
import CostSelectionDialog from '../../applications/costSelectionDialog.mjs';
|
||||||
import { DHActionDiceData, DHDamageData, DHDamageField } from './actionDice.mjs';
|
import { DHActionDiceData, DHActionRollData, DHDamageData, DHDamageField } from './actionDice.mjs';
|
||||||
import DhpActor from '../../documents/actor.mjs';
|
import DhpActor from '../../documents/actor.mjs';
|
||||||
import D20RollDialog from '../../dialogs/d20RollDialog.mjs';
|
import D20RollDialog from '../../dialogs/d20RollDialog.mjs';
|
||||||
|
|
||||||
|
|
@ -69,16 +69,14 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
static defineExtraSchema() {
|
static defineExtraSchema() {
|
||||||
const extraFields = {
|
const extraFields = {
|
||||||
damage: new DHDamageField(),
|
damage: new DHDamageField(),
|
||||||
roll: new fields.SchemaField({
|
roll: new fields.EmbeddedDataField(DHActionRollData),
|
||||||
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({
|
save: new fields.SchemaField({
|
||||||
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
|
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
|
||||||
difficulty: new fields.NumberField({ nullable: true, initial: 10, integer: true, min: 0 }),
|
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 })
|
damageMod: new fields.StringField({
|
||||||
|
initial: SYSTEM.ACTIONS.damageOnSave.none.id,
|
||||||
|
choices: SYSTEM.ACTIONS.damageOnSave
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
target: new fields.SchemaField({
|
target: new fields.SchemaField({
|
||||||
type: new fields.StringField({
|
type: new fields.StringField({
|
||||||
|
|
@ -103,9 +101,12 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
initial: SYSTEM.GENERAL.healingTypes.hitPoints.id,
|
initial: SYSTEM.GENERAL.healingTypes.hitPoints.id,
|
||||||
label: 'Healing'
|
label: 'Healing'
|
||||||
}),
|
}),
|
||||||
resultBased: new fields.BooleanField({ initial: false, label: "DAGGERHEART.Actions.Settings.ResultBased.label" }),
|
resultBased: new fields.BooleanField({
|
||||||
|
initial: false,
|
||||||
|
label: 'DAGGERHEART.Actions.Settings.ResultBased.label'
|
||||||
|
}),
|
||||||
value: new fields.EmbeddedDataField(DHActionDiceData),
|
value: new fields.EmbeddedDataField(DHActionDiceData),
|
||||||
valueAlt: new fields.EmbeddedDataField(DHActionDiceData),
|
valueAlt: new fields.EmbeddedDataField(DHActionDiceData)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
extraSchemas = {};
|
extraSchemas = {};
|
||||||
|
|
@ -158,19 +159,23 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
return updateSource;
|
return updateSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRollData() {
|
getRollData(data = {}) {
|
||||||
const actorData = this.actor.getRollData(false);
|
const actorData = this.actor.getRollData(false);
|
||||||
|
|
||||||
// Remove when included directly in Actor getRollData
|
// Remove when included directly in Actor getRollData
|
||||||
actorData.prof = actorData.proficiency?.value ?? 1,
|
actorData.prof = actorData.proficiency?.value ?? 1;
|
||||||
actorData.cast = actorData.spellcast?.value ?? 1,
|
actorData.cast = actorData.spellcast?.value ?? 1;
|
||||||
actorData.scale = this.cost.length
|
actorData.result = data.roll?.total ?? 1;
|
||||||
? this.cost.reduce((a, c) => {
|
/* actorData.scale = data.costs?.length
|
||||||
|
? data.costs.reduce((a, c) => {
|
||||||
a[c.type] = c.value;
|
a[c.type] = c.value;
|
||||||
return a;
|
return a;
|
||||||
}, {})
|
}, {})
|
||||||
: 1,
|
: 1; */
|
||||||
actorData.roll = {}
|
actorData.scale = data.costs?.length // Right now only return the first scalable cost.
|
||||||
|
? (data.costs.find(c => c.scalable)?.total ?? 1)
|
||||||
|
: 1;
|
||||||
|
actorData.roll = {};
|
||||||
|
|
||||||
return actorData;
|
return actorData;
|
||||||
}
|
}
|
||||||
|
|
@ -192,12 +197,14 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
|
|
||||||
// Prepare Costs
|
// Prepare Costs
|
||||||
const costsConfig = this.prepareCost();
|
const costsConfig = this.prepareCost();
|
||||||
if(isFastForward && !this.hasCost(costsConfig)) return ui.notifications.warn("You don't have the resources to use that action.");
|
if (isFastForward && !this.hasCost(costsConfig))
|
||||||
|
return ui.notifications.warn("You don't have the resources to use that action.");
|
||||||
// config = this.prepareUseCost(config)
|
// config = this.prepareUseCost(config)
|
||||||
|
|
||||||
// Prepare Uses
|
// Prepare Uses
|
||||||
const usesConfig = this.prepareUse();
|
const usesConfig = this.prepareUse();
|
||||||
if(isFastForward && !this.hasUses(usesConfig)) return ui.notifications.warn("That action doesn't have remaining uses.");
|
if (isFastForward && !this.hasUses(usesConfig))
|
||||||
|
return ui.notifications.warn("That action doesn't have remaining uses.");
|
||||||
// config = this.prepareUseCost(config)
|
// config = this.prepareUseCost(config)
|
||||||
|
|
||||||
// Prepare Roll Data
|
// Prepare Roll Data
|
||||||
|
|
@ -210,24 +217,24 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
costs: costsConfig,
|
costs: costsConfig,
|
||||||
uses: usesConfig,
|
uses: usesConfig,
|
||||||
data: actorData
|
data: actorData
|
||||||
}
|
};
|
||||||
|
|
||||||
if ( Hooks.call(`${SYSTEM.id}.preUseAction`, this, config) === false ) return;
|
if (Hooks.call(`${SYSTEM.id}.preUseAction`, this, config) === false) return;
|
||||||
|
|
||||||
// Display configuration window if necessary
|
// Display configuration window if necessary
|
||||||
if ( config.dialog.configure && this.requireConfigurationDialog(config) ) {
|
if (config.dialog?.configure && this.requireConfigurationDialog(config)) {
|
||||||
config = await D20RollDialog.configure(config);
|
config = await D20RollDialog.configure(config);
|
||||||
if (!config) return;
|
if (!config) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( this.hasRoll ) {
|
if (this.hasRoll) {
|
||||||
const rollConfig = this.prepareRoll(config);
|
const rollConfig = this.prepareRoll(config);
|
||||||
config.roll = rollConfig;
|
config.roll = rollConfig;
|
||||||
config = await this.actor.diceRoll(config);
|
config = await this.actor.diceRoll(config);
|
||||||
if (!config) return;
|
if (!config) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( this.hasSave ) {
|
if (this.hasSave) {
|
||||||
/* config.targets.forEach((t) => {
|
/* config.targets.forEach((t) => {
|
||||||
if(t.hit) {
|
if(t.hit) {
|
||||||
const target = game.canvas.tokens.get(t.id),
|
const target = game.canvas.tokens.get(t.id),
|
||||||
|
|
@ -259,16 +266,16 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
}) */
|
}) */
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( this.doFollowUp() ) {
|
if (this.doFollowUp()) {
|
||||||
if(this.rollDamage) await this.rollDamage(event, config);
|
if (this.rollDamage) await this.rollDamage(event, config);
|
||||||
if(this.rollHealing) await this.rollHealing(event, config);
|
if (this.rollHealing) await this.rollHealing(event, config);
|
||||||
if(this.trigger) await this.trigger(event, config);
|
if (this.trigger) await this.trigger(event, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consume resources
|
// Consume resources
|
||||||
await this.consume(config);
|
await this.consume(config);
|
||||||
|
|
||||||
if ( Hooks.call(`${SYSTEM.id}.postUseAction`, this, config) === false ) return;
|
if (Hooks.call(`${SYSTEM.id}.postUseAction`, this, config) === false) return;
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
@ -281,21 +288,18 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
source: {
|
source: {
|
||||||
item: this.item._id,
|
item: this.item._id,
|
||||||
action: this._id
|
action: this._id
|
||||||
// action: this
|
|
||||||
},
|
|
||||||
dialog: {
|
|
||||||
configure: true
|
|
||||||
},
|
},
|
||||||
|
dialog: {},
|
||||||
type: this.type,
|
type: this.type,
|
||||||
hasDamage: !!this.damage?.parts?.length,
|
hasDamage: !!this.damage?.parts?.length,
|
||||||
hasHealing: !!this.healing,
|
hasHealing: !!this.healing,
|
||||||
hasEffect: !!this.effects?.length,
|
hasEffect: !!this.effects?.length,
|
||||||
hasSave: this.hasSave
|
hasSave: this.hasSave
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
requireConfigurationDialog(config) {
|
requireConfigurationDialog(config) {
|
||||||
return !config.event.shiftkey && !this.hasRoll && (config.costs?.length || config.uses);
|
return !config.event.shiftKey && !this.hasRoll && (config.costs?.length || config.uses);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareCost() {
|
prepareCost() {
|
||||||
|
|
@ -321,7 +325,6 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
targets = targets.map(t => this.formatTarget(t));
|
targets = targets.map(t => this.formatTarget(t));
|
||||||
return targets;
|
return targets;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareRange() {
|
prepareRange() {
|
||||||
|
|
@ -330,13 +333,16 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareRoll() {
|
prepareRoll() {
|
||||||
const roll = {
|
const roll = {
|
||||||
modifiers: [],
|
modifiers: [],
|
||||||
trait: this.roll?.trait,
|
trait: this.roll?.trait,
|
||||||
label: 'Attack',
|
label: 'Attack',
|
||||||
type: this.actionType,
|
type: this.actionType,
|
||||||
difficulty: this.roll?.difficulty
|
difficulty: this.roll?.difficulty,
|
||||||
|
formula: this.roll.getFormula()
|
||||||
};
|
};
|
||||||
|
if (this.roll?.type === 'diceSet') roll.lite = true;
|
||||||
|
|
||||||
return roll;
|
return roll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -345,11 +351,14 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
async consume(config) {
|
async consume(config) {
|
||||||
const resources = config.costs.filter(c => c.enabled !== false).map(c => {
|
const resources = config.costs
|
||||||
return { type: c.type, value: c.total * -1 };
|
.filter(c => c.enabled !== false)
|
||||||
});
|
.map(c => {
|
||||||
|
return { type: c.type, value: (c.total ?? c.value) * -1 };
|
||||||
|
});
|
||||||
|
|
||||||
await this.actor.modifyResource(resources);
|
await this.actor.modifyResource(resources);
|
||||||
if(config.uses?.enabled) {
|
if (config.uses?.enabled) {
|
||||||
const newActions = foundry.utils.getProperty(this.item.system, this.systemPath).map(x => x.toObject());
|
const newActions = foundry.utils.getProperty(this.item.system, this.systemPath).map(x => x.toObject());
|
||||||
newActions[this.index].uses.value++;
|
newActions[this.index].uses.value++;
|
||||||
await this.item.update({ [`system.${this.systemPath}`]: newActions });
|
await this.item.update({ [`system.${this.systemPath}`]: newActions });
|
||||||
|
|
@ -388,13 +397,16 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
|
|
||||||
hasCost(costs) {
|
hasCost(costs) {
|
||||||
const realCosts = this.getRealCosts(costs);
|
const realCosts = this.getRealCosts(costs);
|
||||||
return realCosts.reduce((a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value), true);
|
return realCosts.reduce(
|
||||||
|
(a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
/* COST */
|
/* COST */
|
||||||
|
|
||||||
/* USES */
|
/* USES */
|
||||||
calcUses(uses) {
|
calcUses(uses) {
|
||||||
if(!uses) return null;
|
if (!uses) return null;
|
||||||
return {
|
return {
|
||||||
...uses,
|
...uses,
|
||||||
enabled: uses.hasOwnProperty('enabled') ? uses.enabled : true
|
enabled: uses.hasOwnProperty('enabled') ? uses.enabled : true
|
||||||
|
|
@ -402,7 +414,7 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
hasUses(uses) {
|
hasUses(uses) {
|
||||||
if(!uses) return true;
|
if (!uses) return true;
|
||||||
return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= uses.max;
|
return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= uses.max;
|
||||||
}
|
}
|
||||||
/* USES */
|
/* USES */
|
||||||
|
|
@ -432,7 +444,7 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
/* TARGET */
|
/* TARGET */
|
||||||
|
|
||||||
/* RANGE */
|
/* RANGE */
|
||||||
|
|
||||||
/* RANGE */
|
/* RANGE */
|
||||||
|
|
||||||
/* EFFECTS */
|
/* EFFECTS */
|
||||||
|
|
@ -441,10 +453,10 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
let effects = this.effects;
|
let effects = this.effects;
|
||||||
data.system.targets.forEach(async token => {
|
data.system.targets.forEach(async token => {
|
||||||
if (!token.hit && !force) return;
|
if (!token.hit && !force) return;
|
||||||
if(this.hasSave && token.saved.success === true) {
|
if (this.hasSave && token.saved.success === true) {
|
||||||
effects = this.effects.filter(e => e.onSave === true)
|
effects = this.effects.filter(e => e.onSave === true);
|
||||||
}
|
}
|
||||||
if(!effects.length) return;
|
if (!effects.length) return;
|
||||||
effects.forEach(async e => {
|
effects.forEach(async e => {
|
||||||
const actor = canvas.tokens.get(token.id)?.actor,
|
const actor = canvas.tokens.get(token.id)?.actor,
|
||||||
effect = this.item.effects.get(e._id);
|
effect = this.item.effects.get(e._id);
|
||||||
|
|
@ -479,35 +491,42 @@ export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
|
|
||||||
/* SAVE */
|
/* SAVE */
|
||||||
async rollSave(target, event, message) {
|
async rollSave(target, event, message) {
|
||||||
if(!target?.actor) return;
|
if (!target?.actor) return;
|
||||||
target.actor.diceRoll({
|
target.actor
|
||||||
event,
|
.diceRoll({
|
||||||
title: 'Roll Save',
|
event,
|
||||||
roll: {
|
title: 'Roll Save',
|
||||||
trait: this.save.trait,
|
roll: {
|
||||||
difficulty: this.save.difficulty,
|
trait: this.save.trait,
|
||||||
type: "reaction"
|
difficulty: this.save.difficulty,
|
||||||
},
|
type: 'reaction'
|
||||||
data: target.actor.getRollData()
|
},
|
||||||
}).then(async (result) => {
|
data: target.actor.getRollData()
|
||||||
this.updateChatMessage(message, target.id, {result: result.roll.total, success: result.roll.success});
|
})
|
||||||
})
|
.then(async result => {
|
||||||
|
if (result)
|
||||||
|
this.updateChatMessage(message, target.id, {
|
||||||
|
result: result.roll.total,
|
||||||
|
success: result.roll.success
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateChatMessage(message, targetId, changes, chain=true) {
|
async updateChatMessage(message, targetId, changes, chain = true) {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
const chatMessage = ui.chat.collection.get(message._id),
|
const chatMessage = ui.chat.collection.get(message._id),
|
||||||
msgTargets = chatMessage.system.targets,
|
msgTargets = chatMessage.system.targets,
|
||||||
msgTarget = msgTargets.find(mt => mt.id === targetId);
|
msgTarget = msgTargets.find(mt => mt.id === targetId);
|
||||||
msgTarget.saved = changes;
|
msgTarget.saved = changes;
|
||||||
await chatMessage.update({'system.targets': msgTargets});
|
await chatMessage.update({ 'system.targets': msgTargets });
|
||||||
},100);
|
}, 100);
|
||||||
if(chain) {
|
if (chain) {
|
||||||
if(message.system.source.message) this.updateChatMessage(ui.chat.collection.get(message.system.source.message), targetId, changes, false);
|
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);
|
const relatedChatMessages = ui.chat.collection.filter(c => c.system.source.message === message._id);
|
||||||
relatedChatMessages.forEach(c => {
|
relatedChatMessages.forEach(c => {
|
||||||
this.updateChatMessage(c, targetId, changes, false);
|
this.updateChatMessage(c, targetId, changes, false);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* SAVE */
|
/* SAVE */
|
||||||
|
|
@ -525,7 +544,7 @@ export class DHDamageAction extends DHBaseAction {
|
||||||
|
|
||||||
getFormulaValue(part, data) {
|
getFormulaValue(part, data) {
|
||||||
let formulaValue = part.value;
|
let formulaValue = part.value;
|
||||||
if(this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt;
|
if (this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt;
|
||||||
return formulaValue;
|
return formulaValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -535,16 +554,19 @@ export class DHDamageAction extends DHBaseAction {
|
||||||
if (!formula || formula == '') return;
|
if (!formula || formula == '') return;
|
||||||
let roll = { formula: formula, total: formula },
|
let roll = { formula: formula, total: formula },
|
||||||
bonusDamage = [];
|
bonusDamage = [];
|
||||||
|
|
||||||
|
if (isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(data.system ?? data));
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
title: game.i18n.format('DAGGERHEART.Chat.DamageRoll.Title', { damage: this.name }),
|
title: game.i18n.format('DAGGERHEART.Chat.DamageRoll.Title', { damage: this.name }),
|
||||||
formula,
|
roll: { formula },
|
||||||
targets: (data.system?.targets.filter(t => t.hit) ?? data.targets),
|
targets: data.system?.targets.filter(t => t.hit) ?? data.targets,
|
||||||
hasSave: this.hasSave,
|
hasSave: this.hasSave,
|
||||||
source: data.system?.source
|
source: data.system?.source,
|
||||||
|
event
|
||||||
};
|
};
|
||||||
if(this.hasSave) config.onSave = this.save.damageMod;
|
if (this.hasSave) config.onSave = this.save.damageMod;
|
||||||
if(data.system) {
|
if (data.system) {
|
||||||
config.source.message = data._id;
|
config.source.message = data._id;
|
||||||
config.directDamage = false;
|
config.directDamage = false;
|
||||||
}
|
}
|
||||||
|
|
@ -575,7 +597,7 @@ export class DHAttackAction extends DHDamageAction {
|
||||||
getParentDamage() {
|
getParentDamage() {
|
||||||
return {
|
return {
|
||||||
value: {
|
value: {
|
||||||
multiplier: 'proficiency',
|
multiplier: 'prof',
|
||||||
dice: this.item?.system?.damage.value,
|
dice: this.item?.system?.damage.value,
|
||||||
bonus: this.item?.system?.damage.bonus ?? 0
|
bonus: this.item?.system?.damage.bonus ?? 0
|
||||||
},
|
},
|
||||||
|
|
@ -594,7 +616,8 @@ export class DHHealingAction extends DHBaseAction {
|
||||||
|
|
||||||
getFormulaValue(data) {
|
getFormulaValue(data) {
|
||||||
let formulaValue = this.healing.value;
|
let formulaValue = this.healing.value;
|
||||||
if(this.hasRoll && this.healing.resultBased && data.system.roll.result.duality === -1) return this.healing.valueAlt;
|
if (this.hasRoll && this.healing.resultBased && data.system.roll.result.duality === -1)
|
||||||
|
return this.healing.valueAlt;
|
||||||
return formulaValue;
|
return formulaValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -605,15 +628,16 @@ export class DHHealingAction extends DHBaseAction {
|
||||||
if (!formula || formula == '') return;
|
if (!formula || formula == '') return;
|
||||||
let roll = { formula: formula, total: formula },
|
let roll = { formula: formula, total: formula },
|
||||||
bonusDamage = [];
|
bonusDamage = [];
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
title: game.i18n.format('DAGGERHEART.Chat.HealingRoll.Title', {
|
title: game.i18n.format('DAGGERHEART.Chat.HealingRoll.Title', {
|
||||||
healing: game.i18n.localize(SYSTEM.GENERAL.healingTypes[this.healing.type].label)
|
healing: game.i18n.localize(SYSTEM.GENERAL.healingTypes[this.healing.type].label)
|
||||||
}),
|
}),
|
||||||
formula,
|
roll: { formula },
|
||||||
targets: (data.system?.targets ?? data.targets).filter(t => t.hit),
|
targets: (data.system?.targets ?? data.targets).filter(t => t.hit),
|
||||||
messageType: 'healing',
|
messageType: 'healing',
|
||||||
type: this.healing.type
|
type: this.healing.type,
|
||||||
|
event
|
||||||
};
|
};
|
||||||
|
|
||||||
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,63 @@ import FormulaField from '../fields/formulaField.mjs';
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
/* Roll Field */
|
||||||
|
|
||||||
|
export class DHActionRollData extends foundry.abstract.DataModel {
|
||||||
|
/** @override */
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
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 }),
|
||||||
|
diceRolling: new fields.SchemaField({
|
||||||
|
multiplier: new fields.StringField({
|
||||||
|
choices: SYSTEM.GENERAL.diceSetNumbers,
|
||||||
|
initial: 'prof',
|
||||||
|
label: 'Dice Number'
|
||||||
|
}),
|
||||||
|
flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }),
|
||||||
|
dice: new fields.StringField({ choices: SYSTEM.GENERAL.diceTypes, initial: 'd6', label: 'Dice Type' }),
|
||||||
|
compare: new fields.StringField({
|
||||||
|
choices: SYSTEM.ACTIONS.diceCompare,
|
||||||
|
initial: 'above',
|
||||||
|
label: 'Should be'
|
||||||
|
}),
|
||||||
|
treshold: new fields.NumberField({ initial: 1, integer: true, min: 1, label: 'Treshold' })
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormula() {
|
||||||
|
if (!this.type) return;
|
||||||
|
let formula = '';
|
||||||
|
switch (this.type) {
|
||||||
|
case 'diceSet':
|
||||||
|
const multiplier =
|
||||||
|
this.diceRolling.multiplier === 'flat'
|
||||||
|
? this.diceRolling.flatMultiplier
|
||||||
|
: `@${this.diceRolling.multiplier}`;
|
||||||
|
formula = `${multiplier}${this.diceRolling.dice}cs${SYSTEM.ACTIONS.diceCompare[this.diceRolling.compare].operator}${this.diceRolling.treshold}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// formula = `${(!!this.parent?.actor?.system?.attack ? `@attackBonus` : `@traits.${this.trait}.total`)}`;
|
||||||
|
formula = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return formula;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Damage & Healing Field */
|
||||||
|
|
||||||
export class DHActionDiceData extends foundry.abstract.DataModel {
|
export class DHActionDiceData extends foundry.abstract.DataModel {
|
||||||
/** @override */
|
/** @override */
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
multiplier: new fields.StringField({
|
multiplier: new fields.StringField({
|
||||||
choices: SYSTEM.GENERAL.multiplierTypes,
|
choices: SYSTEM.GENERAL.multiplierTypes,
|
||||||
initial: 'proficiency',
|
initial: 'prof',
|
||||||
label: 'Multiplier'
|
label: 'Multiplier'
|
||||||
}),
|
}),
|
||||||
flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }),
|
flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }),
|
||||||
|
|
@ -22,10 +72,13 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
getFormula(actor) {
|
getFormula(actor) {
|
||||||
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : actor.system[this.multiplier]?.total;
|
/* const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : actor.system[this.multiplier]?.total;
|
||||||
return this.custom.enabled
|
return this.custom.enabled
|
||||||
? this.custom.formula
|
? this.custom.formula
|
||||||
: `${multiplier ?? 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}`) : ''}`; */
|
||||||
|
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : `@${this.multiplier}`,
|
||||||
|
bonus = this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : '';
|
||||||
|
return this.custom.enabled ? this.custom.formula : `${multiplier ?? 1}${this.dice}${bonus}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,9 +106,12 @@ export class DHDamageData extends foundry.abstract.DataModel {
|
||||||
nullable: false,
|
nullable: false,
|
||||||
required: true
|
required: true
|
||||||
}),
|
}),
|
||||||
resultBased: new fields.BooleanField({ initial: false, label: "DAGGERHEART.Actions.Settings.ResultBased.label" }),
|
resultBased: new fields.BooleanField({
|
||||||
|
initial: false,
|
||||||
|
label: 'DAGGERHEART.Actions.Settings.ResultBased.label'
|
||||||
|
}),
|
||||||
value: new fields.EmbeddedDataField(DHActionDiceData),
|
value: new fields.EmbeddedDataField(DHActionDiceData),
|
||||||
valueAlt: new fields.EmbeddedDataField(DHActionDiceData),
|
valueAlt: new fields.EmbeddedDataField(DHActionDiceData)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,4 +77,8 @@ export default class DhpAdversary extends BaseDataActor {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get attackBonus() {
|
||||||
|
return this.attack.roll.bonus;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
}),
|
}),
|
||||||
experiences: new fields.TypedObjectField(
|
experiences: new fields.TypedObjectField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
description: new fields.StringField({}),
|
name: new fields.StringField(),
|
||||||
value: new fields.NumberField({ integer: true, initial: 0 }),
|
value: new fields.NumberField({ integer: true, initial: 0 }),
|
||||||
bonus: new fields.NumberField({ integer: true, initial: 0 })
|
bonus: new fields.NumberField({ integer: true, initial: 0 })
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ export default class DHDualityRoll extends foundry.abstract.TypeDataModel {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
costs: new fields.ArrayField(new fields.ObjectField()),
|
||||||
hasDamage: new fields.BooleanField({ initial: false }),
|
hasDamage: new fields.BooleanField({ initial: false }),
|
||||||
hasHealing: new fields.BooleanField({ initial: false }),
|
hasHealing: new fields.BooleanField({ initial: false }),
|
||||||
hasEffect: new fields.BooleanField({ initial: false }),
|
hasEffect: new fields.BooleanField({ initial: false }),
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ export default class DHArmor extends BaseDataItem {
|
||||||
label: 'TYPES.Item.armor',
|
label: 'TYPES.Item.armor',
|
||||||
type: 'armor',
|
type: 'armor',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true
|
isQuantifiable: true,
|
||||||
|
isInventoryItem: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { actionsTypes } from '../action/_module.mjs';
|
||||||
* @property {string} type - The system type that this data model represents.
|
* @property {string} type - The system type that this data model represents.
|
||||||
* @property {boolean} hasDescription - Indicates whether items of this type have description field
|
* @property {boolean} hasDescription - Indicates whether items of this type have description field
|
||||||
* @property {boolean} isQuantifiable - Indicates whether items of this type have quantity field
|
* @property {boolean} isQuantifiable - Indicates whether items of this type have quantity field
|
||||||
|
* @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
@ -18,7 +19,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
label: 'Base Item',
|
label: 'Base Item',
|
||||||
type: 'base',
|
type: 'base',
|
||||||
hasDescription: false,
|
hasDescription: false,
|
||||||
isQuantifiable: false
|
isQuantifiable: false,
|
||||||
|
isInventoryItem: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,9 +54,9 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
const data = { ...actorRollData, item: { ...this } };
|
const data = { ...actorRollData, item: { ...this } };
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
if(!this.constructor.metadata.hasInitialAction || !foundry.utils.isEmpty(this.actions)) return;
|
if (!this.constructor.metadata.hasInitialAction || !foundry.utils.isEmpty(this.actions)) return;
|
||||||
const actionType = {
|
const actionType = {
|
||||||
weapon: 'attack'
|
weapon: 'attack'
|
||||||
}[this.constructor.metadata.type],
|
}[this.constructor.metadata.type],
|
||||||
|
|
@ -70,6 +72,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
parent: this.parent
|
parent: this.parent
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.updateSource({actions: [action]});
|
this.updateSource({ actions: [action] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ export default class DHConsumable extends BaseDataItem {
|
||||||
label: 'TYPES.Item.consumable',
|
label: 'TYPES.Item.consumable',
|
||||||
type: 'consumable',
|
type: 'consumable',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true
|
isQuantifiable: true,
|
||||||
|
isInventoryItem: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ export default class DHMiscellaneous extends BaseDataItem {
|
||||||
label: 'TYPES.Item.miscellaneous',
|
label: 'TYPES.Item.miscellaneous',
|
||||||
type: 'miscellaneous',
|
type: 'miscellaneous',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true
|
isQuantifiable: true,
|
||||||
|
isInventoryItem: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import BaseDataItem from './base.mjs';
|
||||||
import FormulaField from '../fields/formulaField.mjs';
|
import FormulaField from '../fields/formulaField.mjs';
|
||||||
import ActionField from '../fields/actionField.mjs';
|
import ActionField from '../fields/actionField.mjs';
|
||||||
import { weaponFeatures } from '../../config/itemConfig.mjs';
|
import { weaponFeatures } from '../../config/itemConfig.mjs';
|
||||||
import { actionsTypes } from '../action/_module.mjs';
|
import { actionsTypes } from '../action/_module.mjs';
|
||||||
|
|
||||||
export default class DHWeapon extends BaseDataItem {
|
export default class DHWeapon extends BaseDataItem {
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
|
|
@ -12,9 +12,7 @@ export default class DHWeapon extends BaseDataItem {
|
||||||
type: 'weapon',
|
type: 'weapon',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true,
|
isQuantifiable: true,
|
||||||
embedded: {
|
isInventoryItem: true,
|
||||||
feature: 'featureTest'
|
|
||||||
},
|
|
||||||
hasInitialAction: true
|
hasInitialAction: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,38 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
||||||
traitArray: new fields.ArrayField(new fields.NumberField({ required: true, integer: true }), {
|
traitArray: new fields.ArrayField(new fields.NumberField({ required: true, integer: true }), {
|
||||||
initial: () => [2, 1, 1, 0, 0, -1]
|
initial: () => [2, 1, 1, 0, 0, -1]
|
||||||
}),
|
}),
|
||||||
|
currency: new fields.SchemaField({
|
||||||
|
enabled: new fields.BooleanField({
|
||||||
|
required: true,
|
||||||
|
initial: false,
|
||||||
|
label: 'DAGGERHEART.Settings.Homebrew.Currency.enabled'
|
||||||
|
}),
|
||||||
|
title: new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
initial: 'Gold',
|
||||||
|
label: 'DAGGERHEART.Settings.Homebrew.Currency.currencyName'
|
||||||
|
}),
|
||||||
|
coins: new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
initial: 'Coins',
|
||||||
|
label: 'DAGGERHEART.Settings.Homebrew.Currency.coinName'
|
||||||
|
}),
|
||||||
|
handfulls: new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
initial: 'Handfulls',
|
||||||
|
label: 'DAGGERHEART.Settings.Homebrew.Currency.handfullName'
|
||||||
|
}),
|
||||||
|
bags: new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
initial: 'Bags',
|
||||||
|
label: 'DAGGERHEART.Settings.Homebrew.Currency.bagName'
|
||||||
|
}),
|
||||||
|
chests: new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
initial: 'Chests',
|
||||||
|
label: 'DAGGERHEART.Settings.Homebrew.Currency.chestName'
|
||||||
|
})
|
||||||
|
}),
|
||||||
restMoves: new fields.SchemaField({
|
restMoves: new fields.SchemaField({
|
||||||
longRest: new fields.SchemaField({
|
longRest: new fields.SchemaField({
|
||||||
nrChoices: new fields.NumberField({ required: true, integer: true, min: 1, initial: 2 }),
|
nrChoices: new fields.NumberField({ required: true, integer: true, min: 1, initial: 2 }),
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
export default class D20RollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
export default class D20RollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
constructor(config = {}, options = {}) {
|
constructor(roll, config = {}, options = {}) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
|
this.roll = roll;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.config.experiences = [];
|
this.config.experiences = [];
|
||||||
|
|
||||||
|
|
@ -59,22 +60,26 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
context.advantage = this.config.advantage;
|
context.advantage = this.config.advantage;
|
||||||
/* context.diceOptions = this.diceOptions; */
|
/* context.diceOptions = this.diceOptions; */
|
||||||
context.canRoll = true;
|
context.canRoll = true;
|
||||||
|
context.isLite = this.config.roll?.lite;
|
||||||
if (this.config.costs?.length) {
|
if (this.config.costs?.length) {
|
||||||
const updatedCosts = this.action.calcCosts(this.config.costs);
|
const updatedCosts = this.action.calcCosts(this.config.costs);
|
||||||
context.costs = updatedCosts;
|
context.costs = updatedCosts;
|
||||||
context.canRoll = this.action.hasCost(updatedCosts);
|
context.canRoll = this.action.hasCost(updatedCosts);
|
||||||
|
this.config.data.scale = this.config.costs[0].total;
|
||||||
}
|
}
|
||||||
if (this.config.uses?.max) {
|
if (this.config.uses?.max) {
|
||||||
context.uses = this.action.calcUses(this.config.uses);
|
context.uses = this.action.calcUses(this.config.uses);
|
||||||
context.canRoll = context.canRoll && this.action.hasUses(context.uses);
|
context.canRoll = context.canRoll && this.action.hasUses(context.uses);
|
||||||
}
|
}
|
||||||
console.log(context, _options)
|
context.formula = this.roll.constructFormula(this.config);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateRollConfiguration(event, _, formData) {
|
static updateRollConfiguration(event, _, formData) {
|
||||||
const { ...rest } = foundry.utils.expandObject(formData.object);
|
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.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);
|
if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses);
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
@ -86,11 +91,15 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
}
|
}
|
||||||
|
|
||||||
static selectExperience(_, button) {
|
static selectExperience(_, button) {
|
||||||
if (this.config.experiences.find(x => x === button.dataset.key)) {
|
/* if (this.config.experiences.find(x => x === button.dataset.key)) {
|
||||||
this.config.experiences = this.config.experiences.filter(x => x !== button.dataset.key);
|
this.config.experiences = this.config.experiences.filter(x => x !== button.dataset.key);
|
||||||
} else {
|
} else {
|
||||||
this.config.experiences = [...this.config.experiences, button.dataset.key];
|
this.config.experiences = [...this.config.experiences, button.dataset.key];
|
||||||
}
|
} */
|
||||||
|
this.config.experiences =
|
||||||
|
this.config.experiences.indexOf(button.dataset.key) > -1
|
||||||
|
? this.config.experiences.filter(x => x !== button.dataset.key)
|
||||||
|
: [...this.config.experiences, button.dataset.key];
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,9 +112,9 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
if (!options.submitted) this.config = false;
|
if (!options.submitted) this.config = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async configure(config = {}, options={}) {
|
static async configure(roll, config = {}, options = {}) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const app = new this(config, options);
|
const app = new this(roll, config, options);
|
||||||
app.addEventListener('close', () => resolve(app.config), { once: true });
|
app.addEventListener('close', () => resolve(app.config), { once: true });
|
||||||
app.render({ force: true });
|
app.render({ force: true });
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
export default class DamageDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
export default class DamageDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
constructor(config={}, options={}) {
|
constructor(roll, config = {}, options = {}) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
|
this.roll = roll;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,24 +37,24 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
context.title = this.config.title;
|
context.title = this.config.title;
|
||||||
context.formula = this.config.formula;
|
context.formula = this.config.roll.formula;
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async submitRoll() {
|
static async submitRoll() {
|
||||||
await this.close({ submitted: true });
|
await this.close({ submitted: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
_onClose(options={}) {
|
_onClose(options = {}) {
|
||||||
if ( !options.submitted ) this.config = false;
|
if (!options.submitted) this.config = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async configure(config={}) {
|
static async configure(roll, config = {}) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const app = new this(config);
|
const app = new this(roll, config);
|
||||||
app.addEventListener("close", () => resolve(app.config), { once: true });
|
app.addEventListener('close', () => resolve(app.config), { once: true });
|
||||||
app.render({ force: true });
|
app.render({ force: true });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export { default as DhpActor } from './actor.mjs';
|
export { default as DhpActor } from './actor.mjs';
|
||||||
export { default as DhpItem } from './item.mjs';
|
export { default as DHItem } from './item.mjs';
|
||||||
export { default as DhpCombat } from './combat.mjs';
|
export { default as DhpCombat } from './combat.mjs';
|
||||||
export { default as DhActiveEffect } from './activeEffect.mjs';
|
export { default as DhActiveEffect } from './activeEffect.mjs';
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,6 @@ export default class DhpActor extends Actor {
|
||||||
this.updateSource({ prototypeToken });
|
this.updateSource({ prototypeToken });
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareData() {
|
|
||||||
super.prepareData();
|
|
||||||
}
|
|
||||||
|
|
||||||
async _preUpdate(changed, options, user) {
|
|
||||||
super._preUpdate(changed, options, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateLevel(newLevel) {
|
async updateLevel(newLevel) {
|
||||||
if (this.type !== 'character' || newLevel === this.system.levelData.level.changed) return;
|
if (this.type !== 'character' || newLevel === this.system.levelData.level.changed) return;
|
||||||
|
|
||||||
|
|
@ -274,9 +266,10 @@ export default class DhpActor extends Actor {
|
||||||
* @param {object} [config.costs]
|
* @param {object} [config.costs]
|
||||||
*/
|
*/
|
||||||
async diceRoll(config) {
|
async diceRoll(config) {
|
||||||
config.source = {...(config.source ?? {}), actor: this.uuid};
|
config.source = { ...(config.source ?? {}), actor: this.uuid };
|
||||||
config.data = this.getRollData();
|
config.data = this.getRollData();
|
||||||
return await this.rollClass.build(config);
|
const rollClass = config.roll.lite ? CONFIG.Dice.daggerheart['DHRoll'] : this.rollClass;
|
||||||
|
return await rollClass.build(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
get rollClass() {
|
get rollClass() {
|
||||||
|
|
@ -429,13 +422,17 @@ export default class DhpActor extends Actor {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
updates.actor.resources[`system.resources.${r.type}.value`] = Math.max(
|
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),
|
Math.min(
|
||||||
|
this.system.resources[r.type].value + r.value,
|
||||||
|
this.system.resources[r.type].maxTotal ?? this.system.resources[r.type].max
|
||||||
|
),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Object.values(updates).forEach(async u => {
|
Object.values(updates).forEach(async u => {
|
||||||
|
console.log(updates, u);
|
||||||
if (Object.keys(u.resources).length > 0) {
|
if (Object.keys(u.resources).length > 0) {
|
||||||
if (game.user.isGM) {
|
if (game.user.isGM) {
|
||||||
await u.target.update(u.resources);
|
await u.target.update(u.resources);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,8 @@
|
||||||
export default class DhpItem extends Item {
|
/**
|
||||||
/** @inheritdoc */
|
* Override and extend the basic Item implementation.
|
||||||
getEmbeddedDocument(embeddedName, id, { invalid = false, strict = false } = {}) {
|
* @extends {foundry.documents.Item}
|
||||||
const systemEmbeds = this.system.constructor.metadata.embedded ?? {};
|
*/
|
||||||
if (embeddedName in systemEmbeds) {
|
export default class DHItem extends foundry.documents.Item {
|
||||||
const path = `system.${systemEmbeds[embeddedName]}`;
|
|
||||||
return foundry.utils.getProperty(this, path).get(id) ?? null;
|
|
||||||
}
|
|
||||||
return super.getEmbeddedDocument(embeddedName, id, { invalid, strict });
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
prepareEmbeddedDocuments() {
|
prepareEmbeddedDocuments() {
|
||||||
super.prepareEmbeddedDocuments();
|
super.prepareEmbeddedDocuments();
|
||||||
|
|
@ -35,67 +29,51 @@ export default class DhpItem extends Item {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
isInventoryItem() {
|
/**
|
||||||
return ['weapon', 'armor', 'miscellaneous', 'consumable'].includes(this.type);
|
* Determine if this item is classified as an inventory item based on its metadata.
|
||||||
|
* @returns {boolean} Returns `true` if the item is an inventory item.
|
||||||
|
*/
|
||||||
|
get isInventoryItem() {
|
||||||
|
return this.system.constructor.metadata.isInventoryItem ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async createDialog(data = {}, { parent = null, pack = null, ...options } = {}) {
|
/** @inheritdoc */
|
||||||
const documentName = this.metadata.name;
|
static async createDialog(data = {}, createOptions = {}, options = {}) {
|
||||||
const types = game.documentTypes[documentName].filter(t => t !== CONST.BASE_DOCUMENT_TYPE);
|
const { folders, types, template, context = {}, ...dialogOptions } = options;
|
||||||
let collection;
|
|
||||||
if (!parent) {
|
|
||||||
if (pack) collection = game.packs.get(pack);
|
|
||||||
else collection = game.collections.get(documentName);
|
|
||||||
}
|
|
||||||
const folders = collection?._formatFolderSelectOptions() ?? [];
|
|
||||||
const label = game.i18n.localize(this.metadata.label);
|
|
||||||
const title = game.i18n.format('DOCUMENT.Create', { type: label });
|
|
||||||
const typeObjects = types.reduce((obj, t) => {
|
|
||||||
const label = CONFIG[documentName]?.typeLabels?.[t] ?? t;
|
|
||||||
obj[t] = { value: t, label: game.i18n.has(label) ? game.i18n.localize(label) : t };
|
|
||||||
return obj;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
// Render the document creation form
|
if (types?.length === 0) {
|
||||||
const html = await foundry.applications.handlebars.renderTemplate(
|
throw new Error('The array of sub-types to restrict to must not be empty.');
|
||||||
'systems/daggerheart/templates/sidebar/documentCreate.hbs',
|
}
|
||||||
{
|
|
||||||
folders,
|
const documentTypes = this.TYPES.filter(type => type !== 'base' && (!types || types.includes(type))).map(
|
||||||
name: data.name || game.i18n.format('DOCUMENT.New', { type: label }),
|
type => {
|
||||||
folder: data.folder,
|
const labelKey = CONFIG.Item?.typeLabels?.[type];
|
||||||
hasFolders: folders.length >= 1,
|
const label = labelKey && game.i18n.has(labelKey) ? game.i18n.localize(labelKey) : type;
|
||||||
type: data.type || CONFIG[documentName]?.defaultType || typeObjects.armor,
|
|
||||||
types: {
|
const isInventoryItem = CONFIG.Item.dataModels[type]?.metadata?.isInventoryItem;
|
||||||
Items: [typeObjects.armor, typeObjects.weapon, typeObjects.consumable, typeObjects.miscellaneous],
|
const group =
|
||||||
Character: [
|
isInventoryItem === true
|
||||||
typeObjects.class,
|
? 'Inventory Items'
|
||||||
typeObjects.subclass,
|
: isInventoryItem === false
|
||||||
typeObjects.ancestry,
|
? 'Character Items'
|
||||||
typeObjects.community,
|
: 'Other';
|
||||||
typeObjects.feature,
|
|
||||||
typeObjects.domainCard
|
return { value: type, label, group };
|
||||||
]
|
|
||||||
},
|
|
||||||
hasTypes: types.length > 1
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Render the confirmation dialog window
|
if (!documentTypes.length) {
|
||||||
return Dialog.prompt({
|
throw new Error('No document types were permitted to be created.');
|
||||||
title: title,
|
}
|
||||||
content: html,
|
|
||||||
label: title,
|
const sortedTypes = documentTypes.sort((a, b) => a.label.localeCompare(b.label, game.i18n.lang));
|
||||||
callback: html => {
|
|
||||||
const form = html[0].querySelector('form');
|
return await super.createDialog(data, createOptions, {
|
||||||
const fd = new FormDataExtended(form);
|
folders,
|
||||||
foundry.utils.mergeObject(data, fd.object, { inplace: true });
|
types,
|
||||||
if (!data.folder) delete data.folder;
|
template,
|
||||||
if (types.length === 1) data.type = types[0];
|
context: { types: sortedTypes, ...context },
|
||||||
if (!data.name?.trim()) data.name = this.defaultName();
|
...dialogOptions
|
||||||
return this.create(data, { parent, pack, renderSheet: true });
|
|
||||||
},
|
|
||||||
rejectClose: false,
|
|
||||||
options
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,20 +82,16 @@ export default class DhpItem extends Item {
|
||||||
'systems/daggerheart/templates/views/actionSelect.hbs',
|
'systems/daggerheart/templates/views/actionSelect.hbs',
|
||||||
{ actions: this.system.actions }
|
{ actions: this.system.actions }
|
||||||
),
|
),
|
||||||
title = 'Select Action',
|
title = 'Select Action';
|
||||||
type = 'div',
|
|
||||||
data = {};
|
return foundry.applications.api.DialogV2.prompt({
|
||||||
return Dialog.prompt({
|
window: { title },
|
||||||
title,
|
|
||||||
// label: title,
|
|
||||||
content,
|
content,
|
||||||
type,
|
ok: {
|
||||||
callback: html => {
|
label: title,
|
||||||
const form = html[0].querySelector('form'),
|
callback: (event, button, dialog) =>
|
||||||
fd = new foundry.applications.ux.FormDataExtended(form);
|
this.system.actions.find(a => a._id === button.form.elements.actionId.value)
|
||||||
return this.system.actions.find(a => a._id === fd.object.actionId);
|
}
|
||||||
},
|
|
||||||
rejectClose: false
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -225,11 +225,10 @@ export const getDeleteKeys = (property, innerProperty, innerPropertyDefaultValue
|
||||||
|
|
||||||
// Fix on Foundry native formula replacement for DH
|
// Fix on Foundry native formula replacement for DH
|
||||||
const nativeReplaceFormulaData = Roll.replaceFormulaData;
|
const nativeReplaceFormulaData = Roll.replaceFormulaData;
|
||||||
Roll.replaceFormulaData = function (formula, data, { missing, warn = false } = {}) {
|
Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false } = {}) {
|
||||||
const terms = [
|
const terms = Object.keys(SYSTEM.GENERAL.multiplierTypes).map(type => {
|
||||||
{ term: 'prof', default: 1 },
|
return { term: type, default: 1 };
|
||||||
{ term: 'cast', default: 1 }
|
});
|
||||||
];
|
|
||||||
formula = terms.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula);
|
formula = terms.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula);
|
||||||
return nativeReplaceFormulaData(formula, data, { missing, warn });
|
return nativeReplaceFormulaData(formula, data, { missing, warn });
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -154,15 +154,16 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
? message.system.targets.map(target => game.canvas.tokens.get(target.id))
|
? message.system.targets.map(target => game.canvas.tokens.get(target.id))
|
||||||
: Array.from(game.user.targets);
|
: Array.from(game.user.targets);
|
||||||
|
|
||||||
if(message.system.onSave && event.currentTarget.dataset.targetHit) {
|
if (message.system.onSave && event.currentTarget.dataset.targetHit) {
|
||||||
console.log(message.system.targets)
|
const pendingingSaves = message.system.targets.filter(
|
||||||
const pendingingSaves = message.system.targets.filter(target => target.hit && target.saved.success === null);
|
target => target.hit && target.saved.success === null
|
||||||
if(pendingingSaves.length) {
|
);
|
||||||
|
if (pendingingSaves.length) {
|
||||||
const confirm = await foundry.applications.api.DialogV2.confirm({
|
const confirm = await foundry.applications.api.DialogV2.confirm({
|
||||||
window: {title: "Pending Reaction Rolls found"},
|
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>`
|
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 (!confirm) return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,7 +171,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
||||||
for (let target of targets) {
|
for (let target of targets) {
|
||||||
let damage = message.system.roll.total;
|
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));
|
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);
|
await target.actor.takeDamage(damage, message.system.roll.type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -181,7 +184,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
|
|
||||||
if (targets.length === 0)
|
if (targets.length === 0)
|
||||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
|
||||||
|
|
||||||
for (var target of targets) {
|
for (var target of targets) {
|
||||||
await target.actor.takeHealing([{ value: message.system.roll.total, type: message.system.roll.type }]);
|
await target.actor.takeHealing([{ value: message.system.roll.total, type: message.system.roll.type }]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
pull_request_template.md
Normal file
48
pull_request_template.md
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Please include a summary of the change and which issue is fixed (if applicable). Also include relevant context or motivation for the change.
|
||||||
|
|
||||||
|
- Fixes #(issue)
|
||||||
|
- Closes #(issue)
|
||||||
|
|
||||||
|
## Type of Change
|
||||||
|
|
||||||
|
Please check the relevant options:
|
||||||
|
|
||||||
|
- [ ] Bug fix
|
||||||
|
- [ ] New feature
|
||||||
|
- [ ] Code cleanup/refactor
|
||||||
|
- [ ] Documentation update
|
||||||
|
- [ ] Test coverage
|
||||||
|
- [ ] Dependency update
|
||||||
|
- [ ] Configuration change
|
||||||
|
- [ ] Other (please describe):
|
||||||
|
|
||||||
|
## How Has This Been Tested?
|
||||||
|
|
||||||
|
Please describe the tests you ran to verify your changes:
|
||||||
|
|
||||||
|
- [ ] Manual testing
|
||||||
|
- [ ] Other:
|
||||||
|
|
||||||
|
## Screenshots (if applicable)
|
||||||
|
|
||||||
|
Include screenshots or GIFs to help explain your changes visually.
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] My code follows the project style guidelines
|
||||||
|
- [ ] I have performed a self-review of my code
|
||||||
|
- [ ] I have commented my code where necessary
|
||||||
|
- [ ] I have made corresponding changes to the documentation
|
||||||
|
- [ ] My changes generate no new warnings or errors
|
||||||
|
- [ ] I have added tests that prove my fix or feature works
|
||||||
|
- [ ] New and existing tests pass locally with my changes
|
||||||
|
|
||||||
|
## Additional Comments
|
||||||
|
|
||||||
|
Add any other context or questions here.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> Thank you for your contribution! 🎉
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
@import './less/utils/colors.less';
|
||||||
|
|
||||||
.theme-light .daggerheart.dh-style.dialog.character-creation {
|
.theme-light .daggerheart.dh-style.dialog.character-creation {
|
||||||
.tab-navigation nav a .descriptor {
|
.tab-navigation nav a .descriptor {
|
||||||
background: red;
|
|
||||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
}
|
}
|
||||||
.main-selections-container {
|
.main-selections-container {
|
||||||
|
|
@ -15,6 +16,10 @@
|
||||||
.daggerheart.dh-style.dialog.character-creation {
|
.daggerheart.dh-style.dialog.character-creation {
|
||||||
.window-content {
|
.window-content {
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-navigation {
|
.tab-navigation {
|
||||||
|
|
|
||||||
|
|
@ -2570,7 +2570,6 @@ div.daggerheart.views.multiclass {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
.theme-light .daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .descriptor {
|
.theme-light .daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .descriptor {
|
||||||
background: red;
|
|
||||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
}
|
}
|
||||||
.theme-light .daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .suggested-traits-container .suggested-trait-container,
|
.theme-light .daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .suggested-traits-container .suggested-trait-container,
|
||||||
|
|
@ -2581,6 +2580,9 @@ div.daggerheart.views.multiclass {
|
||||||
.daggerheart.dh-style.dialog.character-creation .window-content {
|
.daggerheart.dh-style.dialog.character-creation .window-content {
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
.daggerheart.dh-style.dialog.character-creation .window-content .tab {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
.daggerheart.dh-style.dialog.character-creation .tab-navigation nav {
|
.daggerheart.dh-style.dialog.character-creation .tab-navigation nav {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
@ -4088,6 +4090,10 @@ div.daggerheart.views.multiclass {
|
||||||
.application.sheet.daggerheart.actor.dh-style.character .tab.inventory .search-section .search-bar input:placeholder {
|
.application.sheet.daggerheart.actor.dh-style.character .tab.inventory .search-section .search-bar input:placeholder {
|
||||||
color: light-dark(#18162e50, #efe6d850);
|
color: light-dark(#18162e50, #efe6d850);
|
||||||
}
|
}
|
||||||
|
.application.sheet.daggerheart.actor.dh-style.character .tab.inventory .search-section .search-bar input::-webkit-search-cancel-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.application.sheet.daggerheart.actor.dh-style.character .tab.inventory .search-section .search-bar .icon {
|
.application.sheet.daggerheart.actor.dh-style.character .tab.inventory .search-section .search-bar .icon {
|
||||||
align-content: center;
|
align-content: center;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,11 @@
|
||||||
&:placeholder {
|
&:placeholder {
|
||||||
color: light-dark(@dark-blue-50, @beige-50);
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&::-webkit-search-cancel-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<div>
|
<div class="scrollable">
|
||||||
{{formGroup settingFields.schema.fields.maxFear value=settingFields._source.maxFear localize=true}}
|
{{formGroup settingFields.schema.fields.maxFear value=settingFields._source.maxFear localize=true}}
|
||||||
|
|
||||||
<h4>{{localize "DAGGERHEART.Settings.Homebrew.FIELDS.traitArray.label"}}</h4>
|
<h4>{{localize "DAGGERHEART.Settings.Homebrew.FIELDS.traitArray.label"}}</h4>
|
||||||
|
|
@ -9,7 +9,20 @@
|
||||||
<input type="text" data-dtype="Number" name="{{concat "traitArray." index}}" value="{{this}}" />
|
<input type="text" data-dtype="Number" name="{{concat "traitArray." index}}" value="{{this}}" />
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
{{localize "DAGGERHEART.Settings.Homebrew.Currency.title"}}
|
||||||
|
</legend>
|
||||||
|
{{formGroup settingFields.schema.fields.currency.fields.enabled value=settingFields._source.currency.enabled localize=true}}
|
||||||
|
{{formGroup settingFields.schema.fields.currency.fields.title value=settingFields._source.currency.title localize=true}}
|
||||||
|
{{formGroup settingFields.schema.fields.currency.fields.coins value=settingFields._source.currency.coins localize=true}}
|
||||||
|
{{formGroup settingFields.schema.fields.currency.fields.handfulls value=settingFields._source.currency.handfulls localize=true}}
|
||||||
|
{{formGroup settingFields.schema.fields.currency.fields.bags value=settingFields._source.currency.bags localize=true}}
|
||||||
|
{{formGroup settingFields.schema.fields.currency.fields.chests value=settingFields._source.currency.chests localize=true}}
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<fieldset class="two-columns even">
|
<fieldset class="two-columns even">
|
||||||
<legend>{{localize "DAGGERHEART.Settings.Homebrew.DowntimeMoves"}}</legend>
|
<legend>{{localize "DAGGERHEART.Settings.Homebrew.DowntimeMoves"}}</legend>
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i class="fa-solid fa-magnifying-glass"></i>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" name="" id="" placeholder="Search...">
|
<input type="search" name="search" class="search-inventory" placeholder="Search...">
|
||||||
</div>
|
</div>
|
||||||
<a><i class="fa-solid fa-filter"></i></a>
|
<a><i class="fa-solid fa-filter"></i></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="items-section">
|
<div class="items-section">
|
||||||
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'TYPES.Item.weapon') type='weapon' isGlassy=true}}
|
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'TYPES.Item.weapon') type='weapon' isGlassy=true}}
|
||||||
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'TYPES.Item.armor') type='armor' isGlassy=true}}
|
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'TYPES.Item.armor') type='armor' isGlassy=true}}
|
||||||
|
|
@ -20,23 +20,23 @@
|
||||||
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'TYPES.Item.miscellaneous') type='miscellaneous' isGlassy=true}}
|
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'TYPES.Item.miscellaneous') type='miscellaneous' isGlassy=true}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="currency-section">
|
<div class="currency-section">
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<span>{{localize "DAGGERHEART.Sheets.PC.Gold.Coins"}}</span>
|
<span>{{localize this.inventory.currency.coins}}</span>
|
||||||
{{formInput systemFields.gold.fields.coins value=source.system.gold.coins enriched=source.system.gold.coins localize=true toggled=true}}
|
{{formInput systemFields.gold.fields.coins value=source.system.gold.coins enriched=source.system.gold.coins localize=true toggled=true}}
|
||||||
</div>
|
</div>
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<span>{{localize "DAGGERHEART.Sheets.PC.Gold.Handfulls"}}</span>
|
<span>{{localize this.inventory.currency.handfulls}}</span>
|
||||||
{{formInput systemFields.gold.fields.handfulls value=source.system.gold.handfulls enriched=source.system.gold.handfulls localize=true toggled=true}}
|
{{formInput systemFields.gold.fields.handfulls value=source.system.gold.handfulls enriched=source.system.gold.handfulls localize=true toggled=true}}
|
||||||
</div>
|
</div>
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<span>{{localize "DAGGERHEART.Sheets.PC.Gold.Bags"}}</span>
|
<span>{{localize this.inventory.currency.bags}}</span>
|
||||||
{{formInput systemFields.gold.fields.bags value=source.system.gold.bags enriched=source.system.gold.bags localize=true toggled=true}}
|
{{formInput systemFields.gold.fields.bags value=source.system.gold.bags enriched=source.system.gold.bags localize=true toggled=true}}
|
||||||
</div>
|
</div>
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<span>{{localize "DAGGERHEART.Sheets.PC.Gold.Chests"}}</span>
|
<span>{{localize this.inventory.currency.chests}}</span>
|
||||||
{{formInput systemFields.gold.fields.chests value=source.system.gold.chests enriched=source.system.gold.chests localize=true toggled=true}}
|
{{formInput systemFields.gold.fields.chests value=source.system.gold.chests enriched=source.system.gold.chests localize=true toggled=true}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i class="fa-solid fa-magnifying-glass"></i>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" name="" id="" placeholder="Search...">
|
<input type="search" name="search" class="search-loadout" placeholder="Search...">
|
||||||
</div>
|
</div>
|
||||||
<a><i class="fa-solid fa-filter"></i></a>
|
<a><i class="fa-solid fa-filter"></i></a>
|
||||||
<button class="btn-toggle-view" data-action="toggleLoadoutView" data-value="{{this.abilities.loadout.listView}}">
|
<button class="btn-toggle-view" data-action="toggleLoadoutView" data-value="{{this.abilities.loadout.listView}}">
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<li class="card-item" data-item-id="{{item.id}}">
|
<li class="card-item" data-item-id="{{item.id}}" data-item-id="{{item.id}}">
|
||||||
<img src="{{item.img}}" data-action="useItem" class="card-img" />
|
<img src="{{item.img}}" data-action="useItem" class="card-img" />
|
||||||
<div class="card-label">
|
<div class="card-label">
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<fieldset class="left-main-container" style="padding-top: 0; margin-top: 8px;">
|
<fieldset class="left-main-container" style="padding-top: 0; margin-top: 8px;">
|
||||||
<legend class="legend" style="line-height: 2px;">{{localize "DAGGERHEART.Sheets.PC.Gold.Title"}}</legend>
|
<legend class="legend" style="line-height: 2px;">{{localize this.inventory.currency.title}}</legend>
|
||||||
|
|
||||||
<div class="gold-section">
|
<div class="gold-section">
|
||||||
<fieldset class="gold-fieldset">
|
<fieldset class="gold-fieldset">
|
||||||
<legend>
|
<legend>
|
||||||
{{localize "DAGGERHEART.Sheets.PC.Gold.Coins"}}
|
{{localize this.inventory.currency.coins}}
|
||||||
</legend>
|
</legend>
|
||||||
<div class="gold-column">
|
<div class="gold-column">
|
||||||
<div class="gold-row">
|
<div class="gold-row">
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="gold-fieldset">
|
<fieldset class="gold-fieldset">
|
||||||
<legend>
|
<legend>
|
||||||
{{localize "DAGGERHEART.Sheets.PC.Gold.Handfulls"}}
|
{{localize this.inventory.currency.handfulls}}
|
||||||
</legend>
|
</legend>
|
||||||
<div class="gold-column">
|
<div class="gold-column">
|
||||||
<div class="gold-row">
|
<div class="gold-row">
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="gold-fieldset">
|
<fieldset class="gold-fieldset">
|
||||||
<legend>
|
<legend>
|
||||||
{{localize "DAGGERHEART.Sheets.PC.Gold.Bags"}}
|
{{localize this.inventory.currency.bags}}
|
||||||
</legend>
|
</legend>
|
||||||
<div class="gold-column">
|
<div class="gold-column">
|
||||||
<div class="gold-row">
|
<div class="gold-row">
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="gold-fieldset" style="min-width: 56px;">
|
<fieldset class="gold-fieldset" style="min-width: 56px;">
|
||||||
<legend>
|
<legend>
|
||||||
{{localize "DAGGERHEART.Sheets.PC.Gold.Chests"}}
|
{{localize this.inventory.currency.chests}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<div class="gold-column">
|
<div class="gold-column">
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
<form id="document-create" autocomplete="off">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "Name"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<input type="text" name="name" placeholder="{{name}}" autofocus>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if hasTypes}}
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{localize "Type"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<select name="type">
|
|
||||||
{{#select type}}
|
|
||||||
{{#each types as |values key|}}
|
|
||||||
<optgroup label="{{key}}">
|
|
||||||
{{#each values}}
|
|
||||||
<option value="{{this.value}}">{{this.label}}</option>
|
|
||||||
{{/each}}
|
|
||||||
</optgroup>
|
|
||||||
{{/each}}
|
|
||||||
{{/select}}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if hasFolders}}
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{ localize "DOCUMENT.Folder" }}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<select name="folder">
|
|
||||||
{{#select folder}}
|
|
||||||
<option value=""></option>
|
|
||||||
{{#each folders}}
|
|
||||||
<option value="{{ this.id }}">{{ this.name }}</option>
|
|
||||||
{{/each}}
|
|
||||||
{{/select}}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{{content}}}
|
|
||||||
</form>
|
|
||||||
|
|
@ -7,8 +7,22 @@
|
||||||
{{formField fields.bonus label="Bonus" name="roll.bonus" value=source.bonus}}
|
{{formField fields.bonus label="Bonus" name="roll.bonus" value=source.bonus}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{formField fields.type label="Type" name="roll.type" value=source.type localize=true}}
|
{{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)}}
|
{{#if (eq source.type "diceSet")}}
|
||||||
{{formField fields.difficulty label="Difficulty" name="roll.difficulty" value=source.difficulty disabled=(not source.type)}}
|
<div class="multi-display">
|
||||||
|
{{formField fields.diceRolling.fields.multiplier name="roll.diceRolling.multiplier" value=source.diceRolling.multiplier localize=true}}
|
||||||
|
{{#if (eq source.diceRolling.multiplier 'flat')}}{{formField fields.diceRolling.fields.flatMultiplier value=source.diceRolling.flatMultiplier name="roll.diceRolling.flatMultiplier" }}{{/if}}
|
||||||
|
{{formField fields.diceRolling.fields.dice name="roll.diceRolling.dice" value=source.diceRolling.dice}}
|
||||||
|
</div>
|
||||||
|
<div class="multi-display">
|
||||||
|
{{formField fields.diceRolling.fields.compare name="roll.diceRolling.compare" value=source.diceRolling.compare localize=true}}
|
||||||
|
{{formField fields.diceRolling.fields.treshold name="roll.diceRolling.treshold" value=source.diceRolling.treshold localize=true}}
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="multi-display">
|
||||||
|
{{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)}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
@ -2,11 +2,12 @@
|
||||||
{{#if @root.hasRoll}}
|
{{#if @root.hasRoll}}
|
||||||
<div class="roll-dialog-container">
|
<div class="roll-dialog-container">
|
||||||
<div class="flexcol">
|
<div class="flexcol">
|
||||||
|
{{#unless @root.isLite}}
|
||||||
<div class="roll-dialog-experience-container">
|
<div class="roll-dialog-experience-container">
|
||||||
{{#each experiences}}
|
{{#each experiences}}
|
||||||
{{#if description}}
|
{{#if name}}
|
||||||
<div class="roll-dialog-chip {{#if (includes ../selectedExperiences id)}}selected{{/if}}" data-action="selectExperience" data-key="{{id}}">
|
<div class="roll-dialog-chip {{#if (includes ../selectedExperiences id)}}selected{{/if}}" data-action="selectExperience" data-key="{{id}}">
|
||||||
<span>{{description}}</span>
|
<span>{{name}}</span>
|
||||||
<span>+{{value}}</span>
|
<span>+{{value}}</span>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
@ -16,6 +17,10 @@
|
||||||
<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.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>
|
<button class="disadvantage flex1 {{#if (eq advantage -1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="-1">{{localize "DAGGERHEART.General.Disadvantage.Full"}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
<div>
|
||||||
|
<input type="text" value="{{@root.formula}}" disabled>
|
||||||
|
</div>
|
||||||
{{!-- {{#if (not isNpc)}} --}}
|
{{!-- {{#if (not isNpc)}} --}}
|
||||||
{{!-- <div class="form-group">
|
{{!-- <div class="form-group">
|
||||||
<label>Hope</label>
|
<label>Hope</label>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue