diff --git a/lang/en.json b/lang/en.json
index 0b43ee8f..0732f34e 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -531,6 +531,14 @@
"Stress": {
"Name": "Stress",
"Stress": "STR"
+ },
+ "Hope": {
+ "Name": "Hope",
+ "Abbreviation": "HO"
+ },
+ "ArmorStack": {
+ "Name": "Armor Stack",
+ "Stress": "AS"
}
},
"ArmorFeature": {
diff --git a/module/applications/config/Action.mjs b/module/applications/config/Action.mjs
index 5c992ae8..75127b60 100644
--- a/module/applications/config/Action.mjs
+++ b/module/applications/config/Action.mjs
@@ -60,7 +60,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
context.tabs = this._getTabs();
context.config = SYSTEM;
if (!!this.action.effects) context.effects = this.action.effects.map(e => this.action.item.effects.get(e._id));
- if (this.action.damage?.hasOwnProperty('includeBase')) context.hasBaseDamage = !!this.action.parent.damage;
+ if (this.action.damage?.hasOwnProperty('includeBase') && this.action.type === 'attack') context.hasBaseDamage = !!this.action.parent.damage;
context.getRealIndex = this.getRealIndex.bind(this);
console.log(context)
return context;
diff --git a/module/applications/roll.mjs b/module/applications/roll.mjs
new file mode 100644
index 00000000..0d4d2a05
--- /dev/null
+++ b/module/applications/roll.mjs
@@ -0,0 +1,34 @@
+
+export default class DHRoll extends Roll {
+ static async build(config={}, message={}) {
+ const roll = await this.buildConfigure();
+ await this.buildEvaluate(config, message={});
+ await this.buildPost(config, message={});
+ return roll;
+ }
+
+ static async buildConfigure(config={}, message={}) {
+ config.hooks = [...(config.hooks ?? []), ""];
+ for ( const hook of config.hooks ) {
+ if ( Hooks.call(`dnd5e.preRoll${hook.capitalize()}`, config, message) === false ) return null;
+ }
+ }
+
+ static async buildEvaluate(roll, config={}, message={}) {
+ if(config.evaluate !== false) await roll.evalutate();
+ }
+
+ static async buildPost(config, message) {
+ for ( const hook of config.hooks ) {
+ if ( Hooks.call(`dnd5e.postRoll${hook.capitalize()}`, config, message) === false ) return null;
+ }
+ // Create Chat Message
+ await this.toMessage(roll, message.data);
+ }
+
+ static async toMessage(roll, data) {
+
+ const cls = getDocumentClass("ChatMessage");
+ const msg = new cls(data);
+ }
+}
\ No newline at end of file
diff --git a/module/config/actionConfig.mjs b/module/config/actionConfig.mjs
index 8c67de17..663a29f4 100644
--- a/module/config/actionConfig.mjs
+++ b/module/config/actionConfig.mjs
@@ -4,21 +4,21 @@ export const actionTypes = {
name: 'DAGGERHEART.Actions.Types.Attack.Name',
icon: 'fa-swords'
},
- spellcast: {
- id: 'spellcast',
- name: 'DAGGERHEART.Actions.Types.Spellcast.Name',
- icon: 'fa-book-sparkles'
- },
+ // spellcast: {
+ // id: 'spellcast',
+ // name: 'DAGGERHEART.Actions.Types.Spellcast.Name',
+ // icon: 'fa-book-sparkles'
+ // },
healing: {
id: 'healing',
name: 'DAGGERHEART.Actions.Types.Healing.Name',
icon: 'fa-kit-medical'
},
- resource: {
- id: 'resource',
- name: 'DAGGERHEART.Actions.Types.Resource.Name',
- icon: 'fa-honey-pot'
- },
+ // resource: {
+ // id: 'resource',
+ // name: 'DAGGERHEART.Actions.Types.Resource.Name',
+ // icon: 'fa-honey-pot'
+ // },
damage: {
id: 'damage',
name: 'DAGGERHEART.Actions.Types.Damage.Name',
diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs
index cea1fe55..6e2103c5 100644
--- a/module/config/generalConfig.mjs
+++ b/module/config/generalConfig.mjs
@@ -70,6 +70,16 @@ export const healingTypes = {
id: 'stress',
label: 'DAGGERHEART.HealingType.Stress.Name',
abbreviation: 'DAGGERHEART.HealingType.Stress.Abbreviation'
+ },
+ hope: {
+ id: 'hope',
+ label: 'DAGGERHEART.HealingType.Hope.Name',
+ abbreviation: 'DAGGERHEART.HealingType.Hope.Abbreviation'
+ },
+ armorStack: {
+ id: 'armorStack',
+ label: 'DAGGERHEART.HealingType.ArmorStack.Name',
+ abbreviation: 'DAGGERHEART.HealingType.ArmorStack.Abbreviation'
}
};
diff --git a/module/data/action/_module.mjs b/module/data/action/_module.mjs
index c9088886..23d4e3c1 100644
--- a/module/data/action/_module.mjs
+++ b/module/data/action/_module.mjs
@@ -5,16 +5,16 @@ import {
DHEffectAction,
DHHealingAction,
DHMacroAction,
- DHResourceAction,
- DHSpellCastAction,
+ // DHResourceAction,
+ // DHSpellCastAction,
DHSummonAction
} from './action.mjs';
export const actionsTypes = {
base: DHBaseAction,
attack: DHAttackAction,
- spellcast: DHSpellCastAction,
- resource: DHResourceAction,
+ // spellcast: DHSpellCastAction,
+ // resource: DHResourceAction,
damage: DHDamageAction,
healing: DHHealingAction,
summon: DHSummonAction,
diff --git a/module/data/action/action.mjs b/module/data/action/action.mjs
index 7a976f29..274f4041 100644
--- a/module/data/action/action.mjs
+++ b/module/data/action/action.mjs
@@ -14,6 +14,16 @@ const fields = foundry.data.fields;
- Range Check
- Area of effect and measurement placement
- Auto use costs and action
+
+ Activity Types List
+ - Attack => Weapon Attack, Spell Attack, etc...
+ - Effects => Like Attack without damage
+ - Damage => Like Attack without roll
+ - Healing
+ - Resource => Merge Healing & Resource ?
+ - Summon
+ - Sequencer => Trigger a list of Activities set on the item one by one
+ - Macro
*/
export class DHBaseAction extends foundry.abstract.DataModel {
@@ -49,9 +59,9 @@ export class DHBaseAction extends foundry.abstract.DataModel {
}),
range: new fields.StringField({
choices: SYSTEM.GENERAL.range,
- required: true,
- blank: false,
- initial: 'self'
+ required: false,
+ blank: true,
+ initial: null
})
};
}
@@ -74,7 +84,7 @@ export class DHBaseAction extends foundry.abstract.DataModel {
return 'systems/daggerheart/templates/chat/attack-roll.hbs';
}
- static getRollType() {
+ static getRollType(parent) {
return 'ability';
}
@@ -83,17 +93,36 @@ export class DHBaseAction extends foundry.abstract.DataModel {
updateSource.img ??= parent?.img ?? parent?.system?.img;
if (parent?.system?.trait) {
updateSource['roll'] = {
- type: this.getRollType(),
+ type: this.getRollType(parent),
trait: parent.system.trait
};
}
+ if(parent?.type === 'weapon' && !!this.schema.fields.damage) {
+ updateSource['damage'] = {includeBase: true};
+ }
if (parent?.system?.range) {
updateSource['range'] = parent?.system?.range;
}
return updateSource;
}
- async use(event) {
+ getRollData() {
+ const actorData = this.actor.getRollData(false);
+ return {
+ ...actorData.toObject(),
+ prof: actorData.proficiency?.value ?? 1,
+ cast: actorData.spellcast?.value ?? 1,
+ scale: this.cost.length ? this.cost.reduce((a,c) => {a[c.type] = c.value; return a},{}) : 1
+ }
+ }
+
+ async use(event, ...args) {
+ // throw new Error("Activity must implement a 'use' method.");
+ const data = {
+ itemUUID: this.item,
+ activityId: this.id
+ };
+
if(this.cost?.length) {
const hasCost = await this.checkCost();
if(!hasCost) return ui.notifications.warn("You don't have the resources to use that action.");
@@ -104,7 +133,7 @@ export class DHBaseAction extends foundry.abstract.DataModel {
if(this.range) {
const hasRange = await this.checkRange();
}
- if (this.roll.type && this.roll.trait) {
+ if (this.roll?.type && this.roll?.trait) {
const modifierValue = this.actor.system.traits[this.roll.trait].value;
const config = {
event: event,
@@ -129,8 +158,13 @@ export class DHBaseAction extends foundry.abstract.DataModel {
if (this.effects.length) {
// Apply Active Effects. In Chat Message ?
}
- return this.actor.diceRoll(config);
+ data.roll = await this.actor.diceRoll(config);
}
+ if(this.withMessage || true) {
+
+ }
+
+ return data;
}
async checkCost() {
@@ -174,22 +208,22 @@ export class DHBaseAction extends foundry.abstract.DataModel {
}
}
-const tmpTargetObject = () => {
-
-}
-
const extraDefineSchema = (field, option) => {
return {
[field]: {
// damage: new fields.SchemaField({
// parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData))
// }),
- damage: new DHDamageField(option),
+ damage: new DHDamageField(),
roll: new fields.SchemaField({
type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }),
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 })
}),
+ save: new fields.SchemaField({
+ trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
+ difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 })
+ }),
target: new fields.SchemaField({
type: new fields.StringField({
choices: SYSTEM.ACTIONS.targetTypes,
@@ -207,72 +241,61 @@ const extraDefineSchema = (field, option) => {
};
};
-export class DHAttackAction extends DHBaseAction {
- static defineSchema() {
- return {
- ...super.defineSchema(),
- ...extraDefineSchema('damage', true),
- ...extraDefineSchema('roll'),
- ...extraDefineSchema('target'),
- ...extraDefineSchema('effects')
- };
- }
+export class DHDamageAction extends DHBaseAction {
+ directDamage = true;
- static getRollType() {
- return 'weapon';
- }
-
- prepareData() {
- super.prepareData();
- if (this.damage.includeBase && !!this.item?.system?.damage) {
- const baseDamage = this.getParentDamage();
- this.damage.parts.unshift(new DHDamageData(baseDamage));
- }
- }
-
- getParentDamage() {
- return {
- multiplier: 'proficiency',
- dice: this.item?.system?.damage.value,
- bonus: this.item?.system?.damage.bonus ?? 0,
- type: this.item?.system?.damage.type,
- base: true
- };
- }
-
- // Temporary until full formula parser
- // getDamageFormula() {
- // return this.damage.parts.map(p => p.formula).join(' + ');
- // }
-}
-
-export class DHSpellCastAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema(),
...extraDefineSchema('damage'),
- ...extraDefineSchema('roll'),
...extraDefineSchema('target'),
...extraDefineSchema('effects')
};
}
- static getRollType() {
- return 'spellcast';
- }
-}
+ async use(event, ...args) {
+ const messageData = await super.use(event, args);
+ if(!this.directDamage) return;
+ const roll = await this.rollDamage();
+ if(!roll) return;
+ const cls = getDocumentClass('ChatMessage'),
+ msg = new cls({
+ user: game.user.id,
+ content: await foundry.applications.handlebars.renderTemplate(
+ this.chatTemplate,
+ {
+ ...roll,
+ ...messageData
+ }
+ )
+ });
-export class DHDamageAction extends DHBaseAction {
- static defineSchema() {
+ cls.create(msg.toObject());
+ }
+
+ async rollDamage() {
+ const formula = this.damage.parts.map(p => p.getFormula(this.actor)).join(' + ');
+ console.log(this, formula)
+ if (!formula || formula == '') return;
+
+ let roll = { formula: formula, total: formula };
+ if (isNaN(formula)) {
+ roll = await new Roll(formula, this.getRollData()).evaluate();
+ }
+ console.log(roll)
return {
- ...super.defineSchema(),
- ...extraDefineSchema('damage', false),
- ...extraDefineSchema('target'),
- ...extraDefineSchema('effects')
- };
+ roll: roll.formula,
+ total: roll.total,
+ dice: roll.dice,
+ type: this.damage.parts.map(p => p.type)
+ }
}
- async use(event) {
+ get chatTemplate() {
+ return 'systems/daggerheart/templates/chat/damage-roll.hbs';
+ }
+
+ /* async use(event, ...args) {
const formula = this.damage.parts.map(p => p.getFormula(this.actor)).join(' + ');
if (!formula || formula == '') return;
@@ -295,9 +318,87 @@ export class DHDamageAction extends DHBaseAction {
});
cls.create(msg.toObject());
- }
+ } */
+
+ /* async applyDamage(targets, value) {
+ const promises = [];
+ for(let t of targets) {
+ if(!t) continue;
+ promises.push(new Promise(async (resolve, reject) => {
+ await t.takeDamage(value, 'physical'); // Apply one instance of damage per parts ?
+ resolve();
+ })
+ )
+ }
+ return Promise.all(promises).then((values) => {
+ return values;
+ });
+ } */
}
+export class DHAttackAction extends DHDamageAction {
+ directDamage = false;
+
+ static defineSchema() {
+ return {
+ ...super.defineSchema(),
+ ...extraDefineSchema('roll'),
+ ...extraDefineSchema('save')
+ };
+ }
+
+ static getRollType(parent) {
+ return parent.type === 'weapon' ? 'weapon' : 'spellcast';
+ }
+
+ get chatTemplate() {
+ return 'systems/daggerheart/templates/chat/attack-roll.hbs';
+ }
+
+ prepareData() {
+ super.prepareData();
+ if (this.damage.includeBase && !!this.item?.system?.damage) {
+ const baseDamage = this.getParentDamage();
+ this.damage.parts.unshift(new DHDamageData(baseDamage));
+ }
+ }
+
+ getParentDamage() {
+ return {
+ multiplier: 'proficiency',
+ dice: this.item?.system?.damage.value,
+ bonus: this.item?.system?.damage.bonus ?? 0,
+ type: this.item?.system?.damage.type,
+ base: true
+ };
+ }
+
+ /* async use(event, ...args) {
+
+ } */
+
+ // Temporary until full formula parser
+ // getDamageFormula() {
+ // return this.damage.parts.map(p => p.formula).join(' + ');
+ // }
+}
+
+/* export class DHSpellCastAction extends DHBaseAction {
+ static defineSchema() {
+ return {
+ ...super.defineSchema(),
+ ...extraDefineSchema('damage'),
+ ...extraDefineSchema('roll'),
+ ...extraDefineSchema('target'),
+ ...extraDefineSchema('effects')
+ };
+ }
+
+ static getRollType(parent) {
+ return 'spellcast';
+ }
+} */
+
export class DHHealingAction extends DHBaseAction {
static defineSchema() {
return {
@@ -317,30 +418,38 @@ export class DHHealingAction extends DHBaseAction {
};
}
- async use(event) {
+ async use(event, ...args) {
+ const messageData = await super.use(event, args),
+ roll = await this.rollHealing(),
+ cls = getDocumentClass('ChatMessage'),
+ msg = new cls({
+ user: game.user.id,
+ content: await foundry.applications.handlebars.renderTemplate(
+ this.chatTemplate,
+ {
+ ...roll,
+ ...messageData
+ }
+ )
+ });
+
+ cls.create(msg.toObject());
+ }
+
+ async rollHealing() {
const formula = this.healing.value.getFormula(this.actor);
if (!formula || formula == '') return;
- // const roll = await super.use(event);
let roll = { formula: formula, total: formula };
if (isNaN(formula)) {
- roll = await new Roll(formula).evaluate();
+ roll = await new Roll(formula, this.getRollData()).evaluate();
+ }
+ return {
+ roll: roll.formula,
+ total: roll.total,
+ dice: roll.dice,
+ type: this.healing.type
}
-
- const cls = getDocumentClass('ChatMessage');
- const msg = new cls({
- user: game.user.id,
- content: await foundry.applications.handlebars.renderTemplate(
- 'systems/daggerheart/templates/chat/healing-roll.hbs',
- {
- roll: roll.formula,
- total: roll.total,
- type: this.healing.type
- }
- )
- });
-
- cls.create(msg.toObject());
}
get chatTemplate() {
@@ -348,7 +457,7 @@ export class DHHealingAction extends DHBaseAction {
}
}
-export class DHResourceAction extends DHBaseAction {
+/* export class DHResourceAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema(),
@@ -367,7 +476,7 @@ export class DHResourceAction extends DHBaseAction {
})
};
}
-}
+} */
export class DHSummonAction extends DHBaseAction {
static defineSchema() {
@@ -395,7 +504,7 @@ export class DHMacroAction extends DHBaseAction {
};
}
- async use(event) {
+ async use(event, ...args) {
const fixUUID = !this.documentUUID.includes('Macro.') ? `Macro.${this.documentUUID}` : this.documentUUID,
macro = await fromUuid(fixUUID);
try {
diff --git a/module/data/action/actionDice.mjs b/module/data/action/actionDice.mjs
index 9fd445cc..591a2baf 100644
--- a/module/data/action/actionDice.mjs
+++ b/module/data/action/actionDice.mjs
@@ -23,16 +23,17 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
getFormula(actor) {
return this.custom.enabled
? this.custom.formula
- : `${actor.system[this.multiplier] ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`;
+ : `${actor.system[this.multiplier].value ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`;
}
}
export class DHDamageField extends fields.SchemaField {
- constructor(hasBase, options, context = {}) {
+ constructor(options, context = {}) {
const damageFields = {
- parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData))
+ parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)),
+ includeBase: new fields.BooleanField({ initial: false })
};
- if (hasBase) damageFields.includeBase = new fields.BooleanField({ initial: true });
+ // if (hasBase) damageFields.includeBase = new fields.BooleanField({ initial: true });
super(damageFields, options, context);
}
}
diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs
index 7391a075..b8982cf5 100644
--- a/module/documents/actor.mjs
+++ b/module/documents/actor.mjs
@@ -494,8 +494,8 @@ export default class DhpActor extends Actor {
: damage >= this.system.damageThresholds.minor
? 1
: 0;
-
- const update = {
+ await this.modifyResource(hpDamage, type);
+ /* const update = {
'system.resources.hitPoints.value': Math.min(
this.system.resources.hitPoints.value + hpDamage,
this.system.resources.hitPoints.max
@@ -513,10 +513,39 @@ export default class DhpActor extends Actor {
update: update
}
});
+ } */
+ }
+
+ async modifyResource(value, type) {
+ let resource, target, update;
+ switch (type) {
+ case 'armorStrack':
+ resource = 'system.stacks.value';
+ target = this.armor;
+ update = Math.min(this.marks.value + value, this.marks.max);
+ break;
+ default:
+ resource = `system.resources.${type}`;
+ target = this;
+ update = Math.min(this.resources[type].value + value, this.resources[type].max);
+ break;
+ }
+ if(!resource || !target || !update) return;
+ if (game.user.isGM) {
+ await target.update(update);
+ } else {
+ await game.socket.emit(`system.${SYSTEM.id}`, {
+ action: socketEvent.GMUpdate,
+ data: {
+ action: GMUpdateEvent.UpdateDocument,
+ uuid: target.uuid,
+ update: update
+ }
+ });
}
}
- async takeHealing(healing, type) {
+ /* async takeHealing(healing, type) {
let update = {};
switch (type) {
case SYSTEM.GENERAL.healingTypes.health.id:
@@ -549,7 +578,7 @@ export default class DhpActor extends Actor {
}
});
}
- }
+ } */
//Move to action-scope?
/* async useAction(action) {
diff --git a/module/ui/chatLog.mjs b/module/ui/chatLog.mjs
index 1b575aa8..3339b92a 100644
--- a/module/ui/chatLog.mjs
+++ b/module/ui/chatLog.mjs
@@ -107,7 +107,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
for (var target of targets) {
- await target.actor.takeHealing(healing, event.currentTarget.dataset.type);
+ await target.actor.modifyResource(healing, event.currentTarget.dataset.type);
}
};
diff --git a/templates/chat/damage-roll.hbs b/templates/chat/damage-roll.hbs
index b57a8f4e..8bd65834 100644
--- a/templates/chat/damage-roll.hbs
+++ b/templates/chat/damage-roll.hbs
@@ -11,7 +11,7 @@