actions types - attack roll

This commit is contained in:
Dapoolp 2025-06-10 23:41:25 +02:00
parent 4c7f3a02c4
commit edaf5df9e0
35 changed files with 1015 additions and 165 deletions

View file

@ -4,9 +4,10 @@ const { ApplicationV2 } = foundry.applications.api;
export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
constructor(action) {
super({});
this.action = action;
this.openSection = null;
// console.log(this.action)
}
// get title(){
@ -19,11 +20,19 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
classes: ['daggerheart', 'views', 'action'],
position: { width: 600, height: 'auto' },
actions: {
toggleSection: this.toggleSection
toggleSection: this.toggleSection,
addEffect: this.addEffect,
removeEffect: this.removeEffect,
addElement: this.addElement,
removeElement: this.removeElement,
editEffect: this.editEffect,
addDamage: this.addDamage,
removeDamage: this.removeDamage
},
form: {
handler: this.updateForm,
closeOnSubmit: true
submitOnChange: true,
closeOnSubmit: false
}
};
@ -36,16 +45,19 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
_getTabs() {
const tabs = {
effects: { active: true, cssClass: '', group: 'primary', id: 'effects', icon: null, label: 'Effects' },
useage: { active: false, cssClass: '', group: 'primary', id: 'useage', icon: null, label: 'Useage' },
conditions: {
active: false,
cssClass: '',
group: 'primary',
id: 'conditions',
icon: null,
label: 'Conditions'
}
base: { active: true, cssClass: '', group: 'primary', id: 'base', icon: null, label: 'Base' },
config: { active: false, cssClass: '', group: 'primary', id: 'config', icon: null, label: 'Configuration' },
effect: { active: false, cssClass: '', group: 'primary', id: 'effect', icon: null, label: 'Effect' },
// effects: { active: true, cssClass: '', group: 'primary', id: 'effects', icon: null, label: 'Effects' },
// useage: { active: false, cssClass: '', group: 'primary', id: 'useage', icon: null, label: 'Useage' },
// conditions: {
// active: false,
// cssClass: '',
// group: 'primary',
// id: 'conditions',
// icon: null,
// label: 'Conditions'
// }
};
for (const v of Object.values(tabs)) {
@ -58,9 +70,15 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
async _prepareContext(_options) {
const context = await super._prepareContext(_options, 'action');
context.source = this.action.toObject(false);
context.openSection = this.openSection;
context.tabs = this._getTabs();
context.config = SYSTEM;
context.effects = this.action.effects.map(e => this.action.item.effects.get(e._id));
context.hasBaseDamage = !!this.action.parent.damage;
context.getRealIndex = this.getRealIndex.bind(this);
console.log(context, this.action)
return context;
}
@ -69,15 +87,103 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
this.render(true);
}
static async updateForm(event, _, formData) {
const data = foundry.utils.expandObject(
foundry.utils.mergeObject(this.action.toObject(), foundry.utils.expandObject(formData.object))
);
const newActions = this.action.parent.actions.map(x => x.toObject());
if (!newActions.findSplice(x => x.id === data.id, data)) {
newActions.push(data);
}
getRealIndex(index) {
const data = this.action.toObject(false);
return data.damage.parts.find(d => d.base) ? index - 1 : index;
}
_prepareSubmitData(event, formData) {
const submitData = foundry.utils.expandObject(formData.object);
// this.element.querySelectorAll("fieldset[disabled] :is(input, select)").forEach(input => {
// foundry.utils.setProperty(submitData, input.name, input.value);
// });
return submitData;
}
static async updateForm(event, _, formData) {
const submitData = this._prepareSubmitData(event, formData),
data = foundry.utils.expandObject(foundry.utils.mergeObject(this.action.toObject(), submitData)),
newActions = this.action.parent.actions.map(x => x.toObject()); // Find better way
if (!newActions.findSplice(x => x._id === data._id, data)) newActions.push(data);
const updates = await this.action.parent.parent.update({ 'system.actions': newActions });
if(!updates) return;
this.action = updates.system.actions[this.action.index];
this.render();
}
/* cleanData(data) {
for(let k in data) {
if(typeof data[k] === 'object' && !Array.isArray(data[k])) {
if(!isNaN(Object.keys(data[k])[0])) data[k] = Object.values(data[k]);
}
}
return data;
} */
static addElement(event) {
const data = this.action.toObject(),
key = $(event.target).closest('.action-category-data').data('key');
if ( !this.action[key] ) return;
data[key].push({});
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
}
static removeElement(event) {
const data = this.action.toObject(),
key = $(event.target).closest('.action-category-data').data('key'),
index = $(event.target).data('index');
data[key].splice(index, 1);
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
}
static addDamage(event) {
if ( !this.action.damage.parts ) return;
const data = this.action.toObject();
data.damage.parts.push({});
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
}
static removeDamage(event) {
if ( !this.action.damage.parts ) return;
const data = this.action.toObject(),
index = $(event.target).data('index');
data.damage.parts.splice(index, 1);
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
}
static async addEffect(event) {
if ( !this.action.effects ) return;
// console.log(this, this.action.item)
const effectData = this._addEffectData.bind(this)(),
[created] = await this.action.item.createEmbeddedDocuments("ActiveEffect", [effectData], { render: false }),
data = this.action.toObject();
data.effects.push( { '_id': created._id } )
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
}
/**
* The data for a newly created applied effect.
* @returns {object}
* @protected
*/
_addEffectData() {
return {
name: this.action.item.name,
img: this.action.item.img,
origin: this.action.item.uuid,
transfer: false
};
}
static removeEffect(event) {
if ( !this.action.effects ) return;
const index = $(event.target).data('index'),
effectId = this.action.effects[index]._id;
this.constructor.removeElement.bind(this)(event);
this.action.item.deleteEmbeddedDocuments("ActiveEffect", [effectId]);
}
static editEffect(event) {
await this.action.parent.parent.update({ 'system.actions': newActions });
}
}

View file

@ -27,6 +27,7 @@ export default function DhpApplicationMixin(Base) {
async _prepareContext(_options, objectPath = 'document') {
const context = await super._prepareContext(_options);
console.log(this, objectPath)
context.source = this[objectPath].toObject();
context.fields = this[objectPath].schema.fields;
context.systemFields = this[objectPath].system ? this[objectPath].system.schema.fields : {};

View file

@ -1,4 +1,4 @@
import DHAction from '../../../data/action.mjs';
import DHAction from '../../../data/action/action.mjs';
import DHActionConfig from '../../config/Action.mjs';
import DaggerheartSheet from '../daggerheart-sheet.mjs';
@ -79,6 +79,7 @@ export default class DomainCardSheet extends DaggerheartSheet(ItemSheetV2) {
const action = await new DHAction(
{
id: `${this.document.id}-Action-${actionIndexes.length > 0 ? actionIndexes[0] + 1 : 1}`
// id: foundry.utils.randomID()
},
{
parent: this.document

View file

@ -1,4 +1,4 @@
import DHAction from '../../../data/action.mjs';
import DHAction from '../../../data/action/action.mjs';
import DHActionConfig from '../../config/Action.mjs';
import DaggerheartSheet from '../daggerheart-sheet.mjs';
@ -132,7 +132,10 @@ export default class FeatureSheet extends DaggerheartSheet(ItemSheetV2) {
}
static async addAction() {
const action = await new DHAction({ img: this.document.img }, { parent: this.document });
const action = new DHAction({
id: foundry.utils.randomID(),
// img: this.document.img
}, { parent: this.document });
await this.document.update({ 'system.actions': [...this.document.system.actions, action] });
await new DHActionConfig(this.document.system.actions[this.document.system.actions.length - 1]).render(
true

View file

@ -1,4 +1,6 @@
import DHActionConfig from '../../config/Action.mjs';
import DaggerheartSheet from '../daggerheart-sheet.mjs';
import { actionsTypes } from '../../../data/_module.mjs';
const { ItemSheetV2 } = foundry.applications.sheets;
export default class WeaponSheet extends DaggerheartSheet(ItemSheetV2) {
@ -6,6 +8,11 @@ export default class WeaponSheet extends DaggerheartSheet(ItemSheetV2) {
tag: 'form',
classes: ['daggerheart', 'sheet', 'item', 'dh-style', 'weapon'],
position: { width: 600 },
actions: {
addAction: this.addAction,
editAction: this.editAction,
removeAction: this.removeAction
},
form: {
handler: this.updateForm,
submitOnChange: true,
@ -17,6 +24,10 @@ export default class WeaponSheet extends DaggerheartSheet(ItemSheetV2) {
header: { template: 'systems/daggerheart/templates/sheets/items/weapon/header.hbs' },
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
actions: {
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs',
scrollable: ['.actions']
},
settings: {
template: 'systems/daggerheart/templates/sheets/items/weapon/settings.hbs',
scrollable: ['.settings']
@ -32,6 +43,14 @@ export default class WeaponSheet extends DaggerheartSheet(ItemSheetV2) {
icon: null,
label: 'DAGGERHEART.Sheets.Feature.Tabs.Description'
},
actions: {
active: false,
cssClass: '',
group: 'primary',
id: 'actions',
icon: null,
label: 'DAGGERHEART.Sheets.Feature.Tabs.Actions'
},
settings: {
active: false,
cssClass: '',
@ -55,4 +74,67 @@ export default class WeaponSheet extends DaggerheartSheet(ItemSheetV2) {
await this.document.update(formData.object);
this.render();
}
static async selectActionType() {
const content = await foundry.applications.handlebars.renderTemplate(
"systems/daggerheart/templates/views/actionType.hbs",
{types: SYSTEM.ACTIONS.actionTypes}
),
title = 'Select Action Type',
type = 'form',
data = {};
return Dialog.prompt({
title,
label: title,
content, type,
callback: html => {
const form = html[0].querySelector("form"),
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
})
}
static async addAction() {
// const actionType = await WeaponSheet.selectActionType();
const actionType = await WeaponSheet.selectActionType(),
actionIndexes = this.document.system.actions.map(x => x._id.split('-')[2]).sort((a, b) => a - b)
try {
// const cls = DHAction,
const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack,
action = new cls(
{
// id: `${this.document.id}-Action-${actionIndexes.length > 0 ? actionIndexes[0] + 1 : 1}`
_id: foundry.utils.randomID(),
type: actionType.type,
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name),
...cls.getSourceConfig(this.document)
},
{
parent: this.document
}
);
await this.document.update({ 'system.actions': [...this.document.system.actions, action] });
await new DHActionConfig(this.document.system.actions[this.document.system.actions.length - 1]).render(true);
} catch (error) {
console.log(error)
}
}
static async editAction(_, button) {
const action = this.document.system.actions[button.dataset.index];
await new DHActionConfig(action).render(true);
}
static async removeAction(event, button) {
event.stopPropagation();
await this.document.update({
'system.actions': this.document.system.actions.filter(
(_, index) => index !== Number.parseInt(button.dataset.index)
)
});
}
}

View file

@ -46,6 +46,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
selectAncestry: this.selectAncestry,
selectCommunity: this.selectCommunity,
viewObject: this.viewObject,
useItem: this.useItem,
useFeature: this.useFeature,
takeShortRest: this.takeShortRest,
takeLongRest: this.takeLongRest,
@ -184,6 +185,9 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
.querySelectorAll('.experience-value')
.forEach(element => element.addEventListener('change', this.experienceValueChange.bind(this)));
htmlElement.querySelector('.level-value').addEventListener('change', this.onLevelChange.bind(this));
htmlElement
.querySelectorAll('[data-item-id]')
.forEach(element => element.addEventListener('contextmenu', this.editItem.bind(this)));
}
async _prepareContext(_options) {
@ -738,6 +742,12 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
(await game.packs.get('daggerheart.communities'))?.render(true);
}
static useItem(event) {
const uuid = event.target.closest('[data-item-id]').dataset.itemId,
item = this.document.items.find(i => i.uuid === uuid);
item.use(event);
}
static async viewObject(_, button) {
const object = await fromUuid(button.dataset.value);
if (!object) return;
@ -750,6 +760,16 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
object.sheet.render(true);
}
editItem(event) {
const uuid = event.target.closest('[data-item-id]').dataset.itemId,
item = this.document.items.find(i => i.uuid === uuid);
if (!item) return;
if (item.sheet.editMode) item.sheet.editMode = false;
item.sheet.render(true);
}
static async takeShortRest() {
await new DhpDowntime(this.document, true).render(true);
await this.minimize();

View file

@ -1,7 +1,43 @@
export const actionTypes = {
attack: {
id: 'attack',
name: 'DAGGERHEART.Actions.Types.Attack.Name',
icon: "fa-swords"
},
spellcast: {
id: 'spellcast',
name: 'DAGGERHEART.Actions.Types.Spellcast.Name',
icon: "fa-book-sparkles"
},
resource: {
id: 'resource',
name: 'DAGGERHEART.Actions.Types.Resource.Name',
icon: "fa-honey-pot"
},
damage: {
id: 'damage',
name: 'DAGGERHEART.Effects.Types.Health.Name'
name: 'DAGGERHEART.Actions.Types.Damage.Name',
icon: "fa-bone-break"
},
healing: {
id: 'healing',
name: 'DAGGERHEART.Actions.Types.Healing.Name',
icon: "fa-kit-medical"
},
summon: {
id: 'summon',
name: 'DAGGERHEART.Actions.Types.Summon.Name',
icon: "fa-ghost"
},
effect: {
id: 'effect',
name: 'DAGGERHEART.Actions.Types.Effect.Name',
icon: "fa-person-rays"
},
macro: {
id: 'macro',
name: 'DAGGERHEART.Actions.Types.Macro.Name',
icon: "fa-scroll"
}
};

View file

@ -1,4 +1,9 @@
export const range = {
self: {
label: 'DAGGERHEART.Range.self.name',
description: 'DAGGERHEART.Range.self.description',
distance: 0
},
melee: {
label: 'DAGGERHEART.Range.melee.name',
description: 'DAGGERHEART.Range.melee.description',
@ -247,6 +252,11 @@ export const diceTypes = {
d20: 'd20'
};
export const multiplierTypes = {
proficiency: 'Proficiency',
spellcast: 'Spellcast'
};
export const getDiceSoNicePresets = () => {
const { diceSoNice } = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance);
@ -311,3 +321,18 @@ export const abilityCosts = {
label: 'Stress'
}
};
export const rollTypes = {
weapon: {
id: 'weapon',
label: 'DAGGERHEART.RollTypes.weapon.name'
},
spellcast: {
id: 'spellcast',
label: 'DAGGERHEART.RollTypes.spellcast.name'
},
ability: {
id: 'ability',
label: 'DAGGERHEART.RollTypes.ability.name'
}
}

View file

@ -7,4 +7,5 @@ export { default as DhpAdversary } from './adversary.mjs';
export { default as DhpEnvironment } from './environment.mjs';
export * as items from './item/_module.mjs';
export { actionsTypes } from './action/_module.mjs';
export * as messages from './chat-message/_modules.mjs';

View file

@ -1,123 +0,0 @@
export default class DHAction extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
id: new fields.DocumentIdField(),
name: new fields.StringField({ initial: 'New Action' }),
damage: new fields.SchemaField({
type: new fields.StringField({ choices: SYSTEM.GENERAL.damageTypes, nullable: true, initial: null }),
value: new fields.StringField({})
}),
healing: new fields.SchemaField({
type: new fields.StringField({ choices: SYSTEM.GENERAL.healingTypes, nullable: true, initial: null }),
value: new fields.StringField()
}),
conditions: new fields.ArrayField(
new fields.SchemaField({
name: new fields.StringField(),
icon: new fields.StringField(),
description: new fields.StringField()
})
),
cost: new fields.SchemaField({
type: new fields.StringField({ choices: SYSTEM.GENERAL.abilityCosts, nullable: true, initial: null }),
value: new fields.NumberField({ nullable: true, initial: null })
}),
target: new fields.SchemaField({
type: new fields.StringField({
choices: SYSTEM.ACTIONS.targetTypes,
initial: SYSTEM.ACTIONS.targetTypes.other.id
})
})
};
}
}
const fields = foundry.data.fields;
export class DHBaseAction extends foundry.abstract.DataModel {
static defineSchema() {
return {
_id: new fields.DocumentIdField(),
type: new fields.StringField({ blank: false, required: true, readOnly: true, initial: () => '' }),
name: new fields.StringField({ initial: 'New Action' }),
// description: new fields.StringField({}),
// shortDescription: new fields.StringField({}),
cost: new fields.SchemaField({
type: new fields.StringField({ choices: SYSTEM.GENERAL.abilityCosts, nullable: true, initial: null }),
value: new fields.NumberField({ nullable: true, initial: null })
}),
uses: new fields.SchemaField({
value: new fields.NumberField({ nullable: true, initial: null }),
max: new fields.NumberField({ nullable: true, initial: null }),
recovery: new fields.StringField({
// choices: SYSTEM.ACTIONS.targetTypes,
// initial: SYSTEM.ACTIONS.targetTypes.other.id
})
}),
duration: new fields.SchemaField({
value: new fields.NumberField({ nullable: true, initial: null }),
units: new fields.StringField({required: true, blank: false, initial: "instant"})
}),
target: new fields.SchemaField({
type: new fields.StringField({
choices: SYSTEM.ACTIONS.targetTypes,
initial: SYSTEM.ACTIONS.targetTypes.other.id
})
})
}
}
}
export class DHAttackAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema(),
attack: new fields.SchemaField({}),
damage: new fields.SchemaField({})
}
}
}
export class DHDamageAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema(),
damage: new fields.SchemaField({})
}
}
}
export class DHHealingAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema(),
healing: new fields.SchemaField({})
}
}
}
export class DHSummonAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema(),
healing: new fields.SchemaField({})
}
}
}
export class DHEffectAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema()
}
}
}
export class DHMacroAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema()
}
}
}

View file

@ -0,0 +1,13 @@
import { DHAttackAction, DHBaseAction, DHDamageAction, DHEffectAction, DHHealingAction, DHMacroAction, DHResourceAction, DHSpellCastAction, DHSummonAction } from "./action.mjs";
export const actionsTypes = {
base: DHBaseAction,
attack: DHAttackAction,
spellcast: DHSpellCastAction,
resource: DHResourceAction,
damage: DHDamageAction,
healing: DHHealingAction,
summon: DHSummonAction,
effect: DHEffectAction,
macro: DHMacroAction
}

View file

@ -0,0 +1,306 @@
import DHDamageData from "./damage.mjs";
import { abilities } from "../../config/actorConfig.mjs";
// import DHWeapon from "../item/weapon.mjs";
export default class DHAction extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
id: new fields.DocumentIdField(),
name: new fields.StringField({ initial: 'New Action' }),
damage: new fields.SchemaField({
type: new fields.StringField({ choices: SYSTEM.GENERAL.damageTypes, nullable: true, initial: null }),
value: new fields.StringField({})
}),
healing: new fields.SchemaField({
type: new fields.StringField({ choices: SYSTEM.GENERAL.healingTypes, nullable: true, initial: null }),
value: new fields.StringField()
}),
conditions: new fields.ArrayField(
new fields.SchemaField({
name: new fields.StringField(),
icon: new fields.StringField(),
description: new fields.StringField()
})
),
cost: new fields.SchemaField({
type: new fields.StringField({ choices: SYSTEM.GENERAL.abilityCosts, nullable: true, initial: null }),
value: new fields.NumberField({ nullable: true, initial: null })
}),
target: new fields.SchemaField({
type: new fields.StringField({
choices: SYSTEM.ACTIONS.targetTypes,
initial: SYSTEM.ACTIONS.targetTypes.other.id
})
})
};
}
}
const fields = foundry.data.fields;
// Create Roll Field
// Create Damage Field
export class DHBaseAction extends foundry.abstract.DataModel {
static defineSchema() {
return {
_id: new fields.DocumentIdField(),
type: new fields.StringField({ initial: undefined, readonly: true, required: true }),
name: new fields.StringField({ initial: undefined }),
img: new fields.FilePathField({ initial: undefined, categories: ["IMAGE"], base64: false }),
actionType: new fields.StringField({ choices: SYSTEM.ITEM.actionTypes, initial: 'action', nullable: true }),
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 })
}),
cost: new fields.ArrayField(
new fields.SchemaField({
type: new fields.StringField({ choices: SYSTEM.GENERAL.abilityCosts, nullable: false, required: true, initial: 'hope' }),
value: new fields.NumberField({ nullable: true, initial: 1 }),
scalable: new fields.BooleanField({ initial: false }),
step: new fields.NumberField({ nullable: true, initial: null }),
})
/* ,
{ initial: [
{ type: "hope", value: 1, scalable: false, step: null },
{ type: "stress", value: 2, scalable: true, step: 2 }
]} */
),
uses: new fields.SchemaField({
value: new fields.NumberField({ nullable: true, initial: null }),
max: new fields.NumberField({ nullable: true, initial: null }),
recovery: new fields.StringField({ choices: SYSTEM.GENERAL.refreshTypes, initial: null, nullable: true })
}),
/* duration: new fields.SchemaField({
value: new fields.NumberField({ nullable: true, initial: null }),
units: new fields.StringField({ required: true, blank: false, initial: "instant" })
}), */
target: new fields.SchemaField({
type: new fields.StringField({ choices: SYSTEM.ACTIONS.targetTypes, initial: SYSTEM.ACTIONS.targetTypes.other.id })
}),
range: new fields.StringField({ choices: SYSTEM.GENERAL.range, required: true, blank: false, initial: "self" }),
effects: new fields.ArrayField( // ActiveEffect
new fields.SchemaField({
'_id': new fields.DocumentIdField()
})
)
}
}
prepareData() {}
get index() {
return this.parent.actions.indexOf(this);
}
get item() {
return this.parent.parent;
}
get actor() {
return this.item?.actor;
}
static getRollType() {
return 'ability';
}
static getSourceConfig(parent) {
const updateSource = {};
updateSource.img ??= parent?.img ?? parent?.system?.img;
if(parent?.system?.trait) {
updateSource['roll'] = {
type: this.getRollType(),
trait: parent.system.trait
};
}
if(parent?.system?.range) {
updateSource['range'] = parent?.system?.range;
}
return updateSource;
}
async use(event) {
console.log(this)
// console.log(this.item.getRollData(), this.item.actor.getRollData())
// const weapon = await fromUuid(button.dataset.weapon);
let damage, modifier, roll, hope, fear, advantage, disadvantage, modifiers, bonusDamageString, targets;
if(this.damage.parts.length) {
damage = {
value: `${this.actor.system[this.damage.parts[0].multiplier].value}${this.damage.parts[0].dice}`,
type: this.damage.parts[0].type,
bonusDamage: [this.damage.parts[0].bonus ?? 0, ...this.actor.system.bonuses.damage]
};
damage.value = damage.value.concat(bonusDamageString);
}
if(this.roll.type && this.roll.trait) {
modifier = this.actor.system.traits[this.roll.trait].value;
({roll, hope, fear, advantage, disadvantage, modifiers, bonusDamageString} =
await this.actor.dualityRoll(
{ title: game.i18n.localize(abilities[this.roll.trait].label), value: modifier },
event.shiftKey,
damage?.bonusDamage ?? 0
));
}
console.log(roll, hope, fear, advantage, disadvantage, modifiers, bonusDamageString)
// if(this.target?.type) {
targets = Array.from(game.user.targets).map(x => ({
id: x.id,
name: x.actor.name,
img: x.actor.img,
difficulty: x.actor.system.difficulty,
evasion: x.actor.system.evasion.value
}));
// }
const systemData = {
title: this.item.name,
origin: this.actor.id,
roll: roll._formula,
modifiers: modifiers,
hope: hope,
fear: fear,
advantage: advantage,
disadvantage: disadvantage,
damage: damage,
targets: targets
};
const cls = getDocumentClass('ChatMessage');
const msg = new cls({
type: 'dualityRoll',
sound: CONFIG.sounds.dice,
system: systemData,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/chat/attack-roll.hbs',
systemData
),
rolls: [roll]
});
await cls.create(msg.toObject());
}
}
export class DHAttackAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema(),
/* attack: new fields.SchemaField({
trait: new fields.StringField({ required: true, choices: SYSTEM.ACTOR.abilities, initial: 'agility' }),
bonus: new fields.NumberField({ nullable: true, initial: null })
}), */
/* damage: new fields.SchemaField({
baseDamage: new fields.BooleanField({ initial: true }), // Add damage from source item ?
parts: new fields.ArrayField(
new fields.SchemaField({ // Create DamageField
type: new fields.StringField({
choices: SYSTEM.GENERAL.damageTypes,
initial: 'physical'
}),
value: new FormulaField({ initial: 'd6' }),
bonus: new fields.NumberField({ nullable: true, initial: null }),
base: new fields.BooleanField({ initial: false, readonly: true })
})
)
}) */
damage: new fields.SchemaField({
parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)),
includeBase: new fields.BooleanField({ initial: 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
};
}
}
export class DHSpellCastAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema(),
}
}
static getRollType() {
return 'spellcast';
}
}
export class DHResourceAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema(),
resource: new fields.SchemaField({
target: new fields.StringField({ choices: [], required: true, blank: false, initial: "" }),
value: new fields.NumberField({ initial: 0 })
})
}
}
}
export class DHDamageAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema(),
damage: new fields.SchemaField({})
}
}
}
export class DHHealingAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema(),
type: new fields.StringField({ choices: SYSTEM.GENERAL.healingTypes, required: true, blan: false, initial: SYSTEM.GENERAL.healingTypes.health.id }),
healing: new fields.SchemaField({})
}
}
}
export class DHSummonAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema(),
healing: new fields.SchemaField({})
}
}
}
export class DHEffectAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema()
}
}
}
export class DHMacroAction extends DHBaseAction {
static defineSchema() {
return {
...super.defineSchema()
}
}
}

View file

@ -0,0 +1,26 @@
import FormulaField from "../fields/formulaField.mjs";
const fields = foundry.data.fields;
export default class DHDamageData extends foundry.abstract.DataModel {
/** @override */
static defineSchema() {
return {
multiplier: new fields.StringField({ choices: SYSTEM.GENERAL.multiplierTypes, initial: 'proficiency', label: 'Multiplier' }),
dice: new fields.StringField({ choices: SYSTEM.GENERAL.diceTypes, initial: 'd6', label: 'Formula' }),
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }),
base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }),
type: new fields.StringField({
choices: SYSTEM.GENERAL.damageTypes,
initial: 'physical',
label: 'Type',
nullable: false,
required: true
}),
custom: new fields.SchemaField({
enabled: new fields.BooleanField({ label: 'Custom Formula' }),
formula: new FormulaField( { label: 'Formula' } )
})
}
}
}

View file

@ -0,0 +1,11 @@
import { actionsTypes } from "../action/_module.mjs";
// Temporary Solution
export default class ActionField extends foundry.data.fields.EmbeddedDataField {
/** @override */
initialize(value, model, options={}) {
this.model = actionsTypes[value?.type] ?? actionsTypes.attack;
this.fields = this._initialize(this.model.defineSchema());
return super.initialize(value, model, options)
}
}

View file

@ -24,7 +24,7 @@ export default class ForeignDocumentUUIDField extends foundry.data.fields.Docume
/**@override */
initialize(value, _model, _options = {}) {
if (this.idOnly) return value;
return () => {
return (() => {
try {
const doc = fromUuidSync(value);
return doc;
@ -32,7 +32,7 @@ export default class ForeignDocumentUUIDField extends foundry.data.fields.Docume
console.error(error);
return value ?? null;
}
};
})();
}
/**@override */

View file

@ -19,7 +19,7 @@ export {
DHFeature,
DHMiscellaneous,
DHSubclass,
DHWeapon,
DHWeapon
}
export const config = {

View file

@ -1,4 +1,4 @@
import DHAction from "../action.mjs";
import DHAction from "../action/action.mjs";
import BaseDataItem from "./base.mjs";
export default class DHDomainCard extends BaseDataItem {

View file

@ -1,5 +1,5 @@
import { getTier } from '../../helpers/utils.mjs';
import DHAction from '../action.mjs';
import DHAction from '../action/action.mjs';
import BaseDataItem from './base.mjs';
export default class DHFeature extends BaseDataItem {

View file

@ -1,5 +1,7 @@
import BaseDataItem from "./base.mjs";
import FormulaField from "../fields/formulaField.mjs";
import ActionField from "../fields/actionField.mjs"
import { DHBaseAction, DHAttackAction } from "../action/action.mjs";
export default class DHWeapon extends BaseDataItem {
/** @inheritDoc */
@ -33,8 +35,9 @@ export default class DHWeapon extends BaseDataItem {
initial: 'physical'
})
}),
feature: new fields.StringField({ choices: SYSTEM.ITEM.weaponFeatures, blank: true }),
actions: new fields.ArrayField(new fields.EmbeddedDataField(DHAttackAction))
// actions: new fields.ArrayField(new ActionField(DHBaseAction))
};
}
}

View file

@ -11,6 +11,13 @@ export default class DhpItem extends Item {
// this.system.domains = CONFIG.daggerheart.DOMAIN.classDomainMap[Object.keys(CONFIG.daggerheart.DOMAIN.classDomainMap).find(x => x === this.name.toLowerCase())];
}
}
/** @inheritDoc */
prepareEmbeddedDocuments() {
super.prepareEmbeddedDocuments();
for ( const action of this.system.actions ?? [] ) action.prepareData();
// for ( const action of this.system.actions ?? [] ) console.log(action);
}
/**
* @inheritdoc
@ -99,4 +106,46 @@ export default class DhpItem extends Item {
options
});
}
async selectActionDialog() {
const content = await foundry.applications.handlebars.renderTemplate(
"systems/daggerheart/templates/views/actionSelect.hbs",
{actions: this.system.actions}
),
title = 'Select Action',
type = 'div',
data = {};
return Dialog.prompt({
title,
// label: title,
content, type,
callback: html => {
const form = html[0].querySelector("form"),
fd = new foundry.applications.ux.FormDataExtended(form);
return this.system.actions.find(a => a._id === fd.object.actionId);
},
rejectClose: false
})
}
async use(event) {
const actions = this.system.actions;
if(actions?.length) {
let action = actions[0];
if(actions.length > 1 && !event?.shiftKey) {
// Actions Choice Dialog
action = await this.selectActionDialog();
}
if(!action) return;
// Check Target
// If action.roll => Roll Dialog
// Else If action.cost => Cost Dialog
// Then
// Apply Cost
// Apply Effect
return action.use(event);
}
// Display Item Card in chat
}
}