[Feature] 155 - New Adversary Sheet (#228)

* rework adversary templates, add setting dialog to adversary and fix erratas

* fix errata in adversary data model

* developing experience setting page

* Experience settings update

* finish actions setting tab

* Fixed Notes enriched path and adversary-settings form id

* Fixed UseItem and ToChat

* Fixed Firefox progress bar (HP/Stress) (#230)

* insert prose-mirror style and inicial implement to damage settings

* fix character import relative paths and remove effects from standard adversary attack

---------

Co-authored-by: WBHarry <williambjrklund@gmail.com>
Co-authored-by: GyroFalc <43814421+GyroFalc@users.noreply.github.com>
This commit is contained in:
Murilo Brito 2025-07-01 17:07:12 -03:00 committed by GitHub
parent 8b834036fa
commit 5ffb22fcc5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 2123 additions and 254 deletions

View file

@ -223,12 +223,20 @@
"Major": "Major", "Major": "Major",
"Minor": "Minor", "Minor": "Minor",
"None": "None" "None": "None"
} },
"tabs": {
"details": "Details",
"attack": "Attack",
"experiences": "Experiences",
"features": "Features",
"actions": "Actions"
},
"basics": "Basics"
}, },
"ActionType": { "ActionType": {
"Passive": "Passive", "passive": "Passive",
"Action": "Action", "action": "Action",
"Reaction": "Reaction" "reaction": "Reaction"
}, },
"Abilities": { "Abilities": {
"agility": { "agility": {
@ -312,45 +320,45 @@
}, },
"Adversary": { "Adversary": {
"Type": { "Type": {
"Bruiser": { "bruiser": {
"label": "Bruiser", "label": "Bruiser",
"Description": "Tough adversaries with powerful attacks." "description": "Tough adversaries with powerful attacks."
}, },
"Horde": { "horde": {
"label": "Horde", "label": "Horde",
"Description": "A Horde represents a number of foes working in a group." "description": "A Horde represents a number of foes working in a group."
}, },
"Leader": { "leader": {
"label": "Leader", "label": "Leader",
"Description": "Adversaries that command and summon other adversaries." "description": "Adversaries that command and summon other adversaries."
}, },
"Minion": { "minion": {
"label": "Minion", "label": "Minion",
"Description": "Basic enemies that are easily dispatched but dangerous in numbers." "description": "Basic enemies that are easily dispatched but dangerous in numbers."
}, },
"Ranged": { "ranged": {
"label": "Ranged", "label": "Ranged",
"Description": "Adversaries that attack from a distance." "description": "Adversaries that attack from a distance."
}, },
"Skulk": { "skulk": {
"label": "Skulk", "label": "Skulk",
"Description": "Adversaries that maneuver and exploit opportunities to ambush their opponents." "description": "Adversaries that maneuver and exploit opportunities to ambush their opponents."
}, },
"Social": { "social": {
"label": "Social", "label": "Social",
"Description": "Adversaries that are primarily interpersonal threats or challenges." "description": "Adversaries that are primarily interpersonal threats or challenges."
}, },
"Solo": { "solo": {
"label": "Solo", "label": "Solo",
"Description": "Designed to present a challenge to a whole party." "description": "Designed to present a challenge to a whole party."
}, },
"Standard": { "standard": {
"label": "Standard", "label": "Standard",
"Description": "Rank and File adversaries." "description": "Rank and File adversaries."
}, },
"Support": { "support": {
"label": "Support", "label": "Support",
"Description": "Enemies that enhance their allies and/or disrupt their opponents." "description": "Enemies that enhance their allies and/or disrupt their opponents."
} }
}, },
"Trait": { "Trait": {
@ -1333,14 +1341,18 @@
}, },
"Tabs": { "Tabs": {
"Main": "Data", "Main": "Data",
"Information": "Information" "Information": "Information",
"features": "Features",
"notes": "Notes",
"effects": "Effects"
}, },
"General": "General", "General": "General",
"DamageThresholds": "Damage Thresholds", "DamageThresholds": "Damage Thresholds",
"HitPoints": "Hit Points", "HitPoints": "Hit Points",
"Stress": "Stress", "Stress": "Stress",
"Experiences": "Experiences", "Experiences": "Experiences",
"Attack": "Attack" "Attack": "Attack",
"horderHp": "Horde/HP"
}, },
"Environment": { "Environment": {
"FIELDS": { "FIELDS": {
@ -1568,6 +1580,7 @@
"sendToChat": "Send to Chat", "sendToChat": "Send to Chat",
"moreOptions": "More Options", "moreOptions": "More Options",
"equip": "Equip", "equip": "Equip",
"edit": "Edit",
"unequip": "Unequip", "unequip": "Unequip",
"delete": "Delete", "delete": "Delete",
"sendToVault": "Send to Vault", "sendToVault": "Send to Vault",
@ -1575,29 +1588,29 @@
}, },
"Actions": { "Actions": {
"Types": { "Types": {
"Attack": { "attack": {
"Name": "Attack" "name": "Attack"
}, },
"Spellcast": { "spellcast": {
"Name": "Spellcast" "name": "Spellcast"
}, },
"Resource": { "resource": {
"Name": "Resource" "name": "Resource"
}, },
"Damage": { "damage": {
"Name": "Damage" "name": "Damage"
}, },
"Healing": { "healing": {
"Name": "Healing" "name": "Healing"
}, },
"Summon": { "summon": {
"Name": "Summon" "name": "Summon"
}, },
"Effect": { "effect": {
"Name": "Effect" "name": "Effect"
}, },
"Macro": { "macro": {
"Name": "Macro" "name": "Macro"
} }
}, },
"Settings": { "Settings": {

View file

@ -1,6 +1,7 @@
export { default as DhCharacterSheet } from './sheets/character.mjs'; export { default as DhCharacterSheet } from './sheets/actors/character.mjs';
export { default as DhpAdversarySheet } from './sheets/actors/adversary.mjs';
export { default as DhpAdversarySheet } from './sheets/actors/adversary.mjs';
export { default as DhCompanionSheet } from './sheets/companion.mjs'; export { default as DhCompanionSheet } from './sheets/companion.mjs';
export { default as DhpAdversarySheet } from './sheets/adversary.mjs';
export { default as DhpClassSheet } from './sheets/items/class.mjs'; export { default as DhpClassSheet } from './sheets/items/class.mjs';
export { default as DhpSubclass } from './sheets/items/subclass.mjs'; export { default as DhpSubclass } from './sheets/items/subclass.mjs';
export { default as DhpFeatureSheet } from './sheets/items/feature.mjs'; export { default as DhpFeatureSheet } from './sheets/items/feature.mjs';

View file

@ -1,20 +1,23 @@
import DHActionConfig from '../config/Action.mjs'; import DHActionConfig from '../../config/Action.mjs';
import DaggerheartSheet from './daggerheart-sheet.mjs'; import DaggerheartSheet from '../daggerheart-sheet.mjs';
import DHAdversarySettings from '../applications/adversary-settings.mjs';
const { ActorSheetV2 } = foundry.applications.sheets; const { ActorSheetV2 } = foundry.applications.sheets;
export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) { export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
tag: 'form', tag: 'form',
classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'adversary'], classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'adversary'],
position: { width: 450, height: 1000 }, position: { width: 660, height: 766 },
actions: { actions: {
reactionRoll: this.reactionRoll, reactionRoll: this.reactionRoll,
attackRoll: this.attackRoll, useItem: this.useItem,
toChat: this.toChat,
attackConfigure: this.attackConfigure, attackConfigure: this.attackConfigure,
addExperience: this.addExperience, addExperience: this.addExperience,
removeExperience: this.removeExperience, removeExperience: this.removeExperience,
toggleHP: this.toggleHP, toggleHP: this.toggleHP,
toggleStress: this.toggleStress toggleStress: this.toggleStress,
openSettings: this.openSettings
}, },
form: { form: {
handler: this.updateForm, handler: this.updateForm,
@ -24,28 +27,37 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
}; };
static PARTS = { static PARTS = {
sidebar: { template: 'systems/daggerheart/templates/sheets/actors/adversary/sidebar.hbs' },
header: { template: 'systems/daggerheart/templates/sheets/actors/adversary/header.hbs' }, header: { template: 'systems/daggerheart/templates/sheets/actors/adversary/header.hbs' },
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, actions: { template: 'systems/daggerheart/templates/sheets/actors/adversary/actions.hbs' },
main: { template: 'systems/daggerheart/templates/sheets/actors/adversary/main.hbs' }, notes: { template: 'systems/daggerheart/templates/sheets/actors/adversary/notes.hbs' },
information: { template: 'systems/daggerheart/templates/sheets/actors/adversary/information.hbs' } effects: { template: 'systems/daggerheart/templates/sheets/actors/adversary/effects.hbs' }
}; };
static TABS = { static TABS = {
main: { actions: {
active: true, active: true,
cssClass: '', cssClass: '',
group: 'primary', group: 'primary',
id: 'main', id: 'actions',
icon: null, icon: null,
label: 'DAGGERHEART.Sheets.Adversary.Tabs.Main' label: 'DAGGERHEART.General.tabs.actions'
}, },
information: { notes: {
active: false, active: false,
cssClass: '', cssClass: '',
group: 'primary', group: 'primary',
id: 'information', id: 'notes',
icon: null, icon: null,
label: 'DAGGERHEART.Sheets.Adversary.Tabs.Information' label: 'DAGGERHEART.Sheets.Adversary.Tabs.notes'
},
effects: {
active: false,
cssClass: '',
group: 'primary',
id: 'effects',
icon: null,
label: 'DAGGERHEART.Sheets.Adversary.Tabs.effects'
} }
}; };
@ -56,10 +68,15 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
context.systemFields.attack.fields = this.document.system.attack.schema.fields; context.systemFields.attack.fields = this.document.system.attack.schema.fields;
context.getEffectDetails = this.getEffectDetails.bind(this); context.getEffectDetails = this.getEffectDetails.bind(this);
context.isNPC = true; context.isNPC = true;
console.log(context)
return context; return context;
} }
getAction(element) {
const itemId = (element.target ?? element).closest('[data-item-id]').dataset.itemId,
item = this.document.system.actions.find(x => x.id === itemId);
return item;
}
static async updateForm(event, _, formData) { static async updateForm(event, _, formData) {
await this.document.update(formData.object); await this.document.update(formData.object);
this.render(); this.render();
@ -86,8 +103,40 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
return {}; return {};
} }
static async attackRoll(event) { static async openSettings() {
this.actor.system.attack.use(event); await new DHAdversarySettings(this.document).render(true);
}
static async useItem(event) {
const action = this.getAction(event) ?? this.actor.system.attack;
action.use(event);
}
static async toChat(event, button) {
if (button?.dataset?.type === 'experience') {
const experience = this.document.system.experiences[button.dataset.uuid];
const cls = getDocumentClass('ChatMessage');
const systemData = {
name: game.i18n.localize('DAGGERHEART.General.Experience.Single'),
description: `${experience.name} ${
experience.modifier < 0 ? experience.modifier : `+${experience.modifier}`
}`
};
const msg = new cls({
type: 'abilityUse',
user: game.user.id,
system: systemData,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/chat/ability-use.hbs',
systemData
)
});
cls.create(msg.toObject());
} else {
const item = this.getAction(event) ?? this.document.system.attack;
item.toChat(this.document.id);
}
} }
static async attackConfigure(event) { static async attackConfigure(event) {

View file

@ -1,11 +1,11 @@
import { capitalize } from '../../helpers/utils.mjs'; import { capitalize } from '../../../helpers/utils.mjs';
import DhpDeathMove from '../deathMove.mjs'; import DhpDeathMove from '../../deathMove.mjs';
import DhpDowntime from '../downtime.mjs'; import DhpDowntime from '../../downtime.mjs';
import AncestrySelectionDialog from '../ancestrySelectionDialog.mjs'; import AncestrySelectionDialog from '../../ancestrySelectionDialog.mjs';
import DaggerheartSheet from './daggerheart-sheet.mjs'; import DaggerheartSheet from '.././daggerheart-sheet.mjs';
import { abilities } from '../../config/actorConfig.mjs'; import { abilities } from '../../../config/actorConfig.mjs';
import DhCharacterlevelUp from '../levelup/characterLevelup.mjs'; import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs';
import DhCharacterCreation from '../characterCreation.mjs'; import DhCharacterCreation from '../../characterCreation.mjs';
const { ActorSheetV2 } = foundry.applications.sheets; const { ActorSheetV2 } = foundry.applications.sheets;
const { TextEditor } = foundry.applications.ux; const { TextEditor } = foundry.applications.ux;
@ -727,9 +727,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
const cls = getDocumentClass('ChatMessage'); const cls = getDocumentClass('ChatMessage');
const systemData = { const systemData = {
name: game.i18n.localize('DAGGERHEART.General.Experience.Single'), name: game.i18n.localize('DAGGERHEART.General.Experience.Single'),
description: `${experience.description} ${ description: `${experience.name} ${experience.total < 0 ? experience.total : `+${experience.total}`}`
experience.total < 0 ? experience.total : `+${experience.total}`
}`
}; };
const msg = new cls({ const msg = new cls({
type: 'abilityUse', type: 'abilityUse',

View file

@ -0,0 +1,181 @@
import DHActionConfig from '../../config/Action.mjs';
import DHBaseItemSheet from '../api/base-item.mjs';
import { actionsTypes } from '../../../data/_module.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class DHAdversarySettings extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(actor) {
super({});
this.actor = actor;
}
get title() {
return `${game.i18n.localize('DAGGERHEART.Sheets.TABS.settings')}`;
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'dh-style', 'dialog', 'adversary-settings'],
window: {
icon: 'fa-solid fa-wrench',
resizable: false
},
position: { width: 455, height: 'auto' },
actions: {
addExperience: this.#addExperience,
removeExperience: this.#removeExperience,
addAction: this.#addAction,
editAction: this.#editAction,
removeAction: this.#removeAction
},
form: {
handler: this.updateForm,
submitOnChange: true,
closeOnSubmit: false
}
};
static PARTS = {
header: {
id: 'header',
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/header.hbs'
},
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
details: {
id: 'details',
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/details.hbs'
},
attack: {
id: 'attack',
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/attack.hbs'
},
experiences: {
id: 'experiences',
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/experiences.hbs'
},
actions: {
id: 'actions',
template: 'systems/daggerheart/templates/sheets/applications/adversary-settings/actions.hbs'
}
};
static TABS = {
details: {
active: true,
cssClass: '',
group: 'primary',
id: 'details',
icon: null,
label: 'DAGGERHEART.General.tabs.details'
},
attack: {
active: false,
cssClass: '',
group: 'primary',
id: 'attack',
icon: null,
label: 'DAGGERHEART.General.tabs.attack'
},
experiences: {
active: false,
cssClass: '',
group: 'primary',
id: 'experiences',
icon: null,
label: 'DAGGERHEART.General.tabs.experiences'
},
actions: {
active: false,
cssClass: '',
group: 'primary',
id: 'actions',
icon: null,
label: 'DAGGERHEART.General.tabs.actions'
}
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.document = this.actor;
context.tabs = this._getTabs(this.constructor.TABS);
context.systemFields = this.actor.system.schema.fields;
context.systemFields.attack.fields = this.actor.system.attack.schema.fields;
context.isNPC = true;
return context;
}
_getTabs(tabs) {
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active;
v.cssClass = v.active ? 'active' : '';
}
return tabs;
}
static async #addExperience() {
const newExperience = {
name: 'Experience',
modifier: 0
};
await this.actor.update({ [`system.experiences.${foundry.utils.randomID()}`]: newExperience });
this.render();
}
static async #removeExperience(_, target) {
await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null });
this.render();
}
static async #addAction(_event, _button) {
const actionType = await DHBaseItemSheet.selectActionType();
if (!actionType) return;
try {
const cls = actionsTypes[actionType] ?? actionsTypes.attack,
action = new cls(
{
_id: foundry.utils.randomID(),
type: actionType,
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType].name),
...cls.getSourceConfig(this.actor)
},
{
parent: this.actor
}
);
console.log(action);
await this.actor.update({ 'system.actions': [...this.actor.system.actions, action] });
await new DHActionConfig(this.actor.system.actions[this.actor.system.actions.length - 1]).render({
force: true
});
this.render();
} catch (error) {
console.log(error);
}
}
static async #editAction(event, target) {
event.stopPropagation();
const actionIndex = target.dataset.index;
await new DHActionConfig(this.actor.system.actions[actionIndex]).render({
force: true
});
}
static async #removeAction(event, target) {
event.stopPropagation();
const actionIndex = target.dataset.index;
await this.actor.update({
'system.actions': this.actor.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex))
});
this.render();
}
static async updateForm(event, _, formData) {
await this.actor.update(formData.object);
this.render();
}
}

View file

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

View file

@ -83,53 +83,53 @@ export const featureProperties = {
export const adversaryTypes = { export const adversaryTypes = {
bruiser: { bruiser: {
id: 'bruiser', id: 'bruiser',
label: 'DAGGERHEART.Adversary.Type.Bruiser.label', label: 'DAGGERHEART.Adversary.Type.bruiser.label',
description: 'DAGGERHEART.Adversary.Bruiser.Description' description: 'DAGGERHEART.Adversary.bruiser.description'
}, },
horde: { horde: {
id: 'horde', id: 'horde',
label: 'DAGGERHEART.Adversary.Type.Horde.label', label: 'DAGGERHEART.Adversary.Type.horde.label',
description: 'DAGGERHEART.Adversary.Horde.Description' description: 'DAGGERHEART.Adversary.horde.description'
}, },
leader: { leader: {
id: 'leader', id: 'leader',
label: 'DAGGERHEART.Adversary.Type.Leader.label', label: 'DAGGERHEART.Adversary.Type.leader.label',
description: 'DAGGERHEART.Adversary.Leader.Description' description: 'DAGGERHEART.Adversary.leader.description'
}, },
minion: { minion: {
id: 'minion', id: 'minion',
label: 'DAGGERHEART.Adversary.Type.Minion.label', label: 'DAGGERHEART.Adversary.Type.minion.label',
description: 'DAGGERHEART.Adversary.Minion.Description' description: 'DAGGERHEART.Adversary.minion.description'
}, },
ranged: { ranged: {
id: 'ranged', id: 'ranged',
label: 'DAGGERHEART.Adversary.Type.Ranged.label', label: 'DAGGERHEART.Adversary.Type.ranged.label',
description: 'DAGGERHEART.Adversary.Ranged.Description' description: 'DAGGERHEART.Adversary.ranged.description'
}, },
skulk: { skulk: {
id: 'skulk', id: 'skulk',
label: 'DAGGERHEART.Adversary.Type.Skulk.label', label: 'DAGGERHEART.Adversary.Type.skulk.label',
description: 'DAGGERHEART.Adversary.Skulk.Description' description: 'DAGGERHEART.Adversary.skulk.description'
}, },
social: { social: {
id: 'social', id: 'social',
label: 'DAGGERHEART.Adversary.Type.Social.label', label: 'DAGGERHEART.Adversary.Type.social.label',
description: 'DAGGERHEART.Adversary.Social.Description' description: 'DAGGERHEART.Adversary.social.description'
}, },
solo: { solo: {
id: 'solo', id: 'solo',
label: 'DAGGERHEART.Adversary.Type.Solo.label', label: 'DAGGERHEART.Adversary.Type.solo.label',
description: 'DAGGERHEART.Adversary.Solo.Description' description: 'DAGGERHEART.Adversary.solo.description'
}, },
standard: { standard: {
id: 'standard', id: 'standard',
label: 'DAGGERHEART.Adversary.Type.Standard.label', label: 'DAGGERHEART.Adversary.Type.standard.label',
description: 'DAGGERHEART.Adversary.Standard.Description' description: 'DAGGERHEART.Adversary.standard.description'
}, },
support: { support: {
id: 'support', id: 'support',
label: 'DAGGERHEART.Adversary.Type.Support.label', label: 'DAGGERHEART.Adversary.Type.support.label',
description: 'DAGGERHEART.Adversary.Support.Description' description: 'DAGGERHEART.Adversary.support.description'
} }
}; };

View file

@ -712,14 +712,14 @@ export const valueTypes = {
export const actionTypes = { export const actionTypes = {
passive: { passive: {
id: 'passive', id: 'passive',
label: 'DAGGERHEART.ActionType.Passive' label: 'DAGGERHEART.ActionType.passive'
}, },
action: { action: {
id: 'action', id: 'action',
label: 'DAGGERHEART.ActionType.Action' label: 'DAGGERHEART.ActionType.action'
}, },
reaction: { reaction: {
id: 'reaction', id: 'reaction',
label: 'DAGGERHEART.ActionType.Reaction' label: 'DAGGERHEART.ActionType.reaction'
} }
}; };

View file

@ -531,6 +531,29 @@ export class DHBaseAction extends foundry.abstract.DataModel {
} }
} }
/* SAVE */ /* SAVE */
async toChat(origin) {
const cls = getDocumentClass('ChatMessage');
const systemData = {
title: game.i18n.localize('DAGGERHEART.ActionType.action'),
origin: origin,
img: this.img,
name: this.name,
description: this.description,
actions: []
};
const msg = new cls({
type: 'abilityUse',
user: game.user.id,
system: systemData,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/chat/ability-use.hbs',
systemData
)
});
cls.create(msg.toObject());
}
} }
export class DHDamageAction extends DHBaseAction { export class DHDamageAction extends DHBaseAction {

View file

@ -30,8 +30,11 @@ export default class DhpAdversary extends BaseDataActor {
choices: SYSTEM.ACTOR.adversaryTypes, choices: SYSTEM.ACTOR.adversaryTypes,
initial: SYSTEM.ACTOR.adversaryTypes.standard.id initial: SYSTEM.ACTOR.adversaryTypes.standard.id
}), }),
motivesAndTactics: new fields.HTMLField(), description: new fields.StringField(),
motivesAndTactics: new fields.StringField(),
notes: new fields.HTMLField(),
difficulty: new fields.NumberField({ required: true, initial: 1, integer: true }), difficulty: new fields.NumberField({ required: true, initial: 1, integer: true }),
hordeHp: new fields.NumberField({ required: true, initial: 1, integer: true }),
damageThresholds: new fields.SchemaField({ damageThresholds: new fields.SchemaField({
major: new fields.NumberField({ required: true, initial: 0, integer: true }), major: new fields.NumberField({ required: true, initial: 0, integer: true }),
severe: new fields.NumberField({ required: true, initial: 0, integer: true }) severe: new fields.NumberField({ required: true, initial: 0, integer: true })
@ -40,6 +43,7 @@ export default class DhpAdversary extends BaseDataActor {
hitPoints: resourceField(), hitPoints: resourceField(),
stress: resourceField() stress: resourceField()
}), }),
actions: new fields.ArrayField(new ActionField()),
attack: new ActionField({ attack: new ActionField({
initial: { initial: {
name: 'Attack', name: 'Attack',
@ -66,7 +70,7 @@ export default class DhpAdversary extends BaseDataActor {
experiences: new fields.TypedObjectField( experiences: new fields.TypedObjectField(
new fields.SchemaField({ new fields.SchemaField({
name: new fields.StringField(), name: new fields.StringField(),
value: new fields.NumberField({ required: true, integer: true, initial: 1 }) modifier: new fields.NumberField({ required: true, integer: true, initial: 1 })
}) })
), ),
bonuses: new fields.SchemaField({ bonuses: new fields.SchemaField({

File diff suppressed because it is too large Load diff

View file

@ -25,6 +25,14 @@
@import './less/actors/character/biography.less'; @import './less/actors/character/biography.less';
@import './less/actors/character/features.less'; @import './less/actors/character/features.less';
@import './less/actors/adversary/header.less';
@import './less/actors/adversary/sheet.less';
@import './less/actors/adversary/sidebar.less';
@import './less/applications/header.less';
@import './less/applications/adversary-settings/sheet.less';
@import './less/applications/adversary-settings/experiences.less';
@import './less/applications/adversary-settings/actions.less';
@import './less/actors/companion/sheet.less'; @import './less/actors/companion/sheet.less';
@import './less/actors/adversary.less'; @import './less/actors/adversary.less';
@ -38,6 +46,7 @@
@import './less/utils/fonts.less'; @import './less/utils/fonts.less';
@import './less/global/sheet.less'; @import './less/global/sheet.less';
@import './less/global/dialog.less';
@import './less/global/elements.less'; @import './less/global/elements.less';
@import './less/global/tab-navigation.less'; @import './less/global/tab-navigation.less';
@import './less/global/tab-form-footer.less'; @import './less/global/tab-form-footer.less';
@ -47,6 +56,7 @@
@import './less/global/feature-section.less'; @import './less/global/feature-section.less';
@import './less/global/inventory-item.less'; @import './less/global/inventory-item.less';
@import './less/global/inventory-fieldset-items.less'; @import './less/global/inventory-fieldset-items.less';
@import './less/global/prose-mirror.less';
@import '../node_modules/@yaireo/tagify/dist/tagify.css'; @import '../node_modules/@yaireo/tagify/dist/tagify.css';
.daggerheart { .daggerheart {

View file

@ -0,0 +1,20 @@
@import '../../utils/colors.less';
@import '../../utils/fonts.less';
.application.sheet.daggerheart.actor.dh-style.adversary {
.tab.actions {
.action-section {
display: flex;
flex-direction: column;
gap: 10px;
overflow-y: auto;
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
padding: 20px 0;
padding-top: 10px;
height: 95%;
scrollbar-width: thin;
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
}
}
}

View file

@ -0,0 +1,152 @@
@import '../../utils/colors.less';
@import '../../utils/fonts.less';
.application.sheet.daggerheart.actor.dh-style.adversary {
.adversary-header-sheet {
padding: 0 15px;
padding-top: 36px;
width: 100%;
.name-row {
display: flex;
gap: 5px;
align-items: center;
justify-content: space-between;
padding: 0;
padding-top: 5px;
padding-bottom: 8px;
flex: 1;
input[type='text'] {
font-size: 32px;
height: 42px;
text-align: start;
border: 1px solid transparent;
outline: 2px solid transparent;
transition: all 0.3s ease;
&:hover {
outline: 2px solid light-dark(@dark, @golden);
}
}
.level-div {
white-space: nowrap;
display: flex;
justify-content: end;
.label {
display: flex;
align-items: center;
gap: 4px;
}
input {
width: 40px;
padding: 0;
text-align: center;
}
.level-button {
color: light-dark(@dark, @beige);
font-size: 18px;
line-height: 1;
min-height: unset;
height: min-content;
padding: 4px;
font-family: 'Cinzel', serif;
margin: 0;
font-weight: normal;
border-color: light-dark(@dark-blue, @golden);
background-color: light-dark(transparent, @deep-black);
&:hover {
background-image: none;
background-color: var(--color-warm-2);
filter: drop-shadow(0 0 3px lightgray);
}
}
}
}
.tags {
display: flex;
gap: 10px;
padding-bottom: 16px;
.tag {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 3px 5px;
font-size: 12px;
font: @font-body;
background: light-dark(@dark-15, @beige-15);
border: 1px solid light-dark(@dark, @beige);
border-radius: 3px;
}
.label {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
font-size: 12px;
}
}
.adversary-info {
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px 0;
.description,
.motives-and-tatics {
font-family: @font-body;
}
}
.adversary-navigation {
display: flex;
gap: 8px;
align-items: center;
}
.character-details {
display: flex;
justify-content: space-between;
padding: 5px 0;
margin-bottom: 10px;
font-size: 12px;
color: light-dark(@dark-blue, @golden);
span {
padding: 3px;
border-radius: 3px;
transition: all 0.3s ease;
cursor: pointer;
&:hover {
background: light-dark(@dark-blue-40, @golden-40);
}
&.dot {
background: transparent;
cursor: default;
}
}
}
.character-row {
display: flex;
gap: 20px;
align-items: center;
justify-content: space-between;
padding: 0;
margin-bottom: 15px;
}
}
}

View file

@ -0,0 +1,28 @@
@import '../../utils/colors.less';
@import '../../utils/fonts.less';
.application.sheet.daggerheart.actor.dh-style.adversary {
.window-content {
display: grid;
grid-template-columns: 275px 1fr;
grid-template-rows: 283px 1fr;
gap: 15px 0;
height: 100%;
width: 100%;
.adversary-sidebar-sheet {
grid-row: 1 / span 2;
grid-column: 1;
}
.adversary-header-sheet {
grid-row: 1;
grid-column: 2;
}
.tab {
grid-row: 2;
grid-column: 2;
}
}
}

View file

@ -0,0 +1,341 @@
@import '../../utils/colors.less';
@import '../../utils/fonts.less';
.application.sheet.daggerheart.actor.dh-style.adversary {
.adversary-sidebar-sheet {
width: 275px;
min-width: 275px;
border-right: 1px solid light-dark(@dark-blue, @golden);
background-image: url('../assets/parchments/dh-parchment-dark.png');
.theme-light & {
background: transparent;
}
.portrait {
position: relative;
border-bottom: 1px solid light-dark(@dark-blue, @golden);
cursor: pointer;
img {
height: 235px;
width: 275px;
object-fit: cover;
}
.death-roll-btn {
display: none;
}
&.death-roll {
filter: grayscale(1);
.death-roll-btn {
display: flex;
position: absolute;
top: 30%;
right: 30%;
font-size: 6rem;
color: @beige;
&:hover {
text-shadow: 0 0 8px @beige;
}
}
}
}
.threshold-section {
position: relative;
display: flex;
gap: 10px;
background-color: light-dark(transparent, @dark-blue);
color: light-dark(@dark-blue, @golden);
padding: 5px 10px;
border: 1px solid light-dark(@dark-blue, @golden);
border-radius: 6px;
align-items: center;
width: fit-content;
height: 30px;
margin-top: 16px;
h4 {
font-size: 14px;
font-weight: bold;
text-transform: uppercase;
color: light-dark(@dark-blue, @golden);
&.threshold-value {
color: light-dark(@dark, @beige);
}
}
.threshold-legend {
position: absolute;
bottom: -21px;
color: light-dark(@golden, @dark-blue);
background-color: light-dark(@dark-blue, @golden);
padding: 3px;
justify-self: anchor-center;
border-radius: 0 0 3px 3px;
text-transform: capitalize;
}
.hope-value {
display: flex;
cursor: pointer;
}
}
.info-section {
position: relative;
display: flex;
flex-direction: column;
top: -20px;
gap: 16px;
margin-bottom: -10px;
.resources-section {
display: flex;
justify-content: space-evenly;
.status-bar {
position: relative;
width: 100px;
height: 40px;
justify-items: center;
.status-label {
position: relative;
top: 40px;
height: 22px;
width: 79px;
clip-path: path('M0 0H79L74 16.5L39 22L4 16.5L0 0Z');
background: light-dark(@dark-blue, @golden);
h4 {
font-weight: bold;
text-align: center;
line-height: 18px;
color: light-dark(@beige, @dark-blue);
}
}
.status-value {
position: absolute;
display: flex;
padding: 0 6px;
font-size: 1.5rem;
align-items: center;
width: 100px;
height: 40px;
justify-content: center;
text-align: center;
z-index: 2;
color: @beige;
input[type='number'] {
background: transparent;
font-size: 1.5rem;
width: 40px;
height: 30px;
text-align: center;
border: none;
outline: 2px solid transparent;
color: @beige;
&.bar-input {
padding: 0;
color: @beige;
backdrop-filter: none;
background: transparent;
transition: all 0.3s ease;
&:hover,
&:focus {
background: @semi-transparent-dark-blue;
backdrop-filter: blur(9.5px);
}
}
}
.bar-label {
width: 40px;
}
}
.progress-bar {
position: absolute;
appearance: none;
width: 100px;
height: 40px;
border: 1px solid light-dark(@dark-blue, @golden);
border-radius: 6px;
z-index: 1;
background: @dark-blue;
&::-webkit-progress-bar {
border: none;
background: @dark-blue;
border-radius: 6px;
}
&::-webkit-progress-value {
background: @gradient-hp;
border-radius: 6px;
}
&.stress-color::-webkit-progress-value {
background: @gradient-stress;
border-radius: 6px;
}
&::-moz-progress-bar {
background: @gradient-hp;
border-radius: 6px;
}
&.stress-color::-moz-progress-bar {
background: @gradient-stress;
border-radius: 6px;
}
}
}
}
.status-section {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
.status-number {
justify-items: center;
.status-value {
position: relative;
display: flex;
width: 50px;
height: 30px;
border: 1px solid light-dark(@dark-blue, @golden);
border-bottom: none;
border-radius: 6px 6px 0 0;
padding: 0 6px;
font-size: 1.2rem;
align-items: center;
justify-content: center;
background: light-dark(transparent, @dark-blue);
z-index: 2;
&.armor-slots {
width: 80px;
height: 30px;
}
}
.status-label {
padding: 2px 10px;
width: 100%;
border-radius: 3px;
background: light-dark(@dark-blue, @golden);
h4 {
font-weight: bold;
text-align: center;
line-height: 18px;
font-size: 12px;
color: light-dark(@beige, @dark-blue);
}
}
}
}
}
.items-sidebar-list {
display: flex;
flex-direction: column;
gap: 5px;
.inventory-item {
padding: 0 10px;
}
}
.attack-section {
.title {
display: flex;
gap: 15px;
align-items: center;
h3 {
font-size: 20px;
}
}
.items-list {
display: flex;
flex-direction: column;
gap: 10px;
align-items: center;
}
}
.experience-section {
margin-bottom: 20px;
.title {
display: flex;
gap: 15px;
align-items: center;
h3 {
font-size: 20px;
}
}
.experience-list {
display: flex;
flex-direction: column;
gap: 5px;
width: 100%;
margin-top: 10px;
align-items: center;
.experience-row {
display: flex;
gap: 5px;
width: 250px;
align-items: center;
justify-content: space-between;
.experience-name {
width: 180px;
text-align: start;
font-size: 14px;
font-family: @font-body;
color: light-dark(@dark, @beige);
}
}
.experience-value {
height: 25px;
width: 35px;
font-size: 14px;
font-family: @font-body;
color: light-dark(@dark, @beige);
align-content: center;
text-align: center;
background: url(../assets/svg/experience-shield.svg) no-repeat;
.theme-light & {
background: url('../assets/svg/experience-shield-light.svg') no-repeat;
}
}
}
}
.reaction-section {
display: flex;
padding: 0 10px;
margin-top: 20px;
width: 100%;
button {
width: 100%;
}
}
}
}

View file

@ -24,9 +24,5 @@
grid-row: 2; grid-row: 2;
grid-column: 2; grid-column: 2;
} }
.old-sheet {
width: 500px;
}
} }
} }

View file

@ -14,8 +14,6 @@
.portrait { .portrait {
position: relative; position: relative;
height: 235px;
width: 275px;
border-bottom: 1px solid light-dark(@dark-blue, @golden); border-bottom: 1px solid light-dark(@dark-blue, @golden);
cursor: pointer; cursor: pointer;
@ -130,6 +128,7 @@
border: 1px solid light-dark(@dark-blue, @golden); border: 1px solid light-dark(@dark-blue, @golden);
border-radius: 6px; border-radius: 6px;
z-index: 1; z-index: 1;
background: @dark-blue;
&::-webkit-progress-bar { &::-webkit-progress-bar {
border: none; border: none;
@ -144,13 +143,9 @@
background: @gradient-stress; background: @gradient-stress;
border-radius: 6px; border-radius: 6px;
} }
&::-moz-progress-value,
&::-moz-progress-bar {
border-radius: 6px;
}
&::-moz-progress-bar { &::-moz-progress-bar {
background: @gradient-hp; background: @gradient-hp;
border-radius: 6px;
} }
&.stress-color::-moz-progress-bar { &.stress-color::-moz-progress-bar {
background: @gradient-stress; background: @gradient-stress;

View file

@ -0,0 +1,62 @@
@import '../../utils/colors.less';
@import '../../utils/fonts.less';
.application.daggerheart.dh-style.dialog {
.tab.actions {
.add-action-btn {
width: 100%;
margin-bottom: 12px;
}
.action-list {
display: flex;
flex-direction: column;
gap: 10px;
.action-item {
display: grid;
grid-template-columns: 40px 1fr auto;
align-items: center;
gap: 5px;
.label {
font-family: @font-body;
.tags {
display: flex;
gap: 10px;
.tag {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 3px 5px;
font-size: 12px;
background: light-dark(@dark-15, @beige-15);
border: 1px solid light-dark(@dark, @beige);
border-radius: 3px;
}
.label {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
font-size: 12px;
}
}
}
.controls {
display: flex;
gap: 5px;
a {
text-align: center;
}
}
}
}
}
}

View file

@ -0,0 +1,28 @@
@import '../../utils/colors.less';
@import '../../utils/fonts.less';
.application.daggerheart.dh-style.dialog {
.tab.experiences {
.add-experience-btn {
width: 100%;
margin-bottom: 12px;
}
.experience-list {
display: flex;
flex-direction: column;
gap: 10px;
.experience-item {
display: grid;
grid-template-columns: 3fr 1fr 30px;
align-items: center;
gap: 5px;
a {
text-align: center;
}
}
}
}
}

View file

@ -0,0 +1,18 @@
@import '../../utils/colors.less';
@import '../../utils/fonts.less';
.application.daggerheart.dh-style.dialog {
.tab {
&.details.active,
&.attack.active {
display: flex;
flex-direction: column;
gap: 16px;
}
.fieldsets-section {
display: flex;
gap: 16px;
}
}
}

View file

@ -0,0 +1,20 @@
@import '../utils/colors.less';
@import '../utils/fonts.less';
.application.daggerheart.dh-style.dialog {
.window-content {
.dialog-header {
width: 100%;
padding-bottom: 16px;
h1 {
font-family: @font-subtitle;
font-style: normal;
font-weight: 700;
font-size: 24px;
margin: 0;
text-align: center;
color: light-dark(@dark-blue, @golden);
}
}
}
}

View file

@ -0,0 +1,45 @@
@import '../utils/colors.less';
@import '../utils/fonts.less';
.theme-light {
.application.dialog.dh-style {
background-image: url('../assets/parchments/dh-parchment-light.png');
background-repeat: no-repeat;
background-position: center;
}
}
.theme-dark {
.application.dialog.dh-style {
background-image: url('../assets/parchments/dh-parchment-dark.png');
background-repeat: no-repeat;
background-position: center;
}
}
.application.dialog.dh-style {
border: none;
.window-header {
background: transparent;
border-bottom: none;
color: light-dark(@dark-blue, @beige);
h1 {
color: light-dark(@dark-blue, @beige);
font-family: @font-body;
}
button {
color: light-dark(@dark-blue, @beige);
background: light-dark(transparent, @deep-black);
border: 1px solid light-dark(@dark-blue, transparent);
padding: 0;
&:hover {
border: 1px solid light-dark(@dark-blue, @golden);
color: light-dark(@dark-blue, @golden);
}
}
}
}

View file

@ -1,7 +1,7 @@
@import '../utils/colors.less'; @import '../utils/colors.less';
@import '../utils/fonts.less'; @import '../utils/fonts.less';
.application.sheet.dh-style { .application.dh-style {
border: 1px solid light-dark(@dark-blue, @golden); border: 1px solid light-dark(@dark-blue, @golden);
input[type='text'], input[type='text'],
@ -90,6 +90,7 @@
a:hover, a:hover,
a.active { a.active {
font-weight: bold;
text-shadow: 0 0 8px light-dark(@dark-blue, @golden); text-shadow: 0 0 8px light-dark(@dark-blue, @golden);
} }
@ -111,6 +112,10 @@
} }
} }
&.fit-height {
height: 95%;
}
&.flex { &.flex {
display: flex; display: flex;
gap: 20px; gap: 20px;
@ -160,10 +165,12 @@
.nest-inputs { .nest-inputs {
display: flex; display: flex;
align-items: center; align-items: center;
width: 100%;
gap: 5px; gap: 5px;
} }
.form-group { .form-group {
width: 100%;
label { label {
font-family: @font-body; font-family: @font-body;
font-weight: bold; font-weight: bold;

View file

@ -1,7 +1,7 @@
@import '../utils/colors.less'; @import '../utils/colors.less';
@import '../utils/fonts.less'; @import '../utils/fonts.less';
.application.sheet.daggerheart.actor.dh-style.character { .application.daggerheart.dh-style {
.items-list { .items-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View file

@ -1,7 +1,7 @@
@import '../utils/colors.less'; @import '../utils/colors.less';
@import '../utils/fonts.less'; @import '../utils/fonts.less';
.application.sheet.daggerheart.actor.dh-style.character { .application.sheet.daggerheart.actor.dh-style {
.inventory-item { .inventory-item {
display: grid; display: grid;
grid-template-columns: 40px 1fr 60px; grid-template-columns: 40px 1fr 60px;

View file

@ -0,0 +1,24 @@
@import '../utils/colors.less';
.application {
prose-mirror {
height: 100% !important;
.editor-menu {
background-color: transparent;
}
.editor-content {
scrollbar-width: thin;
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
h1 {
font-size: 36px;
}
h2 {
font-size: 32px;
}
h3 {
font-size: 24px;
}
}
}
}

View file

@ -1,10 +1,11 @@
@import '../utils/colors.less'; @import '../utils/colors.less';
@import '../utils/fonts.less'; @import '../utils/fonts.less';
.sheet.daggerheart.dh-style { .daggerheart.dh-style {
.tab-navigation { .tab-navigation {
margin: 5px 0; margin: 5px 0;
height: 40px; height: 40px;
width: 100%;
.feature-tab { .feature-tab {
border: none; border: none;

View file

@ -0,0 +1,11 @@
<section
class='tab {{tabs.actions.cssClass}} {{tabs.actions.id}}'
data-tab='{{tabs.actions.id}}'
data-group='{{tabs.actions.group}}'
>
<div class="action-section">
{{#if document.system.actions}}
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize tabs.actions.label) type='action'}}
{{/if}}
</div>
</section>

View file

@ -0,0 +1,8 @@
<section
class='tab {{tabs.effects.cssClass}} {{tabs.effects.id}}'
data-tab='{{tabs.effects.id}}'
data-group='{{tabs.effects.group}}'
>
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.Sheets.Global.activeEffects') type='effect'}}
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.Sheets.Global.inativeEffects') type='effect'}}
</section>

View file

@ -1,9 +1,43 @@
<header class='item-card-header'> <header class='adversary-header-sheet'>
<img class='profile' src='{{source.img}}' data-action='editImage' data-edit='img' /> <line-div></line-div>
<div class='item-info'> <div class="name-row">
<h1 class='item-name'><input type='text' name='name' value='{{source.name}}' /></h1> <h1 class='actor-name'>
<div class='item-description'> <input type='text' name='name' value='{{source.name}}' placeholder='Actor Name'
<h3>{{localize 'TYPES.Actor.adversary'}}</h3> />
</h1>
</div>
<div class="tags">
<div class="tag">
<span>
{{localize (concat 'DAGGERHEART.Tiers.' source.system.tier)}}
</span>
</div>
<div class="tag">
<span>
{{localize (concat 'DAGGERHEART.Adversary.Type.' source.system.type '.label')}}
</span>
</div>
{{#if (eq source.system.type 'horde')}}
<div class="tag">
<span>{{source.system.hordeHp}}</span>
<span>/HP</span>
</div>
{{/if}}
</div>
<line-div></line-div>
<div class="adversary-info">
<div class="description">
<i>{{source.system.description}}</i>
</div>
<div class="motives-and-tatics">
<b>{{localize 'DAGGERHEART.Sheets.Adversary.FIELDS.motivesAndTactics.label'}}: </b>{{{source.system.motivesAndTactics}}}
</div> </div>
</div> </div>
<div class="adversary-navigation">
{{> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}}
<button data-action="openSettings">
<i class="fa-solid fa-wrench"></i>
</button>
</div>
</header> </header>

View file

@ -4,33 +4,26 @@
data-group='{{tabs.main.group}}' data-group='{{tabs.main.group}}'
> >
<div class="adversary-container"> <div class="adversary-container">
<button data-action="reactionRoll">Reaction Test</button>
<fieldset class="two-columns even"> <fieldset class="two-columns even">
<legend>{{localize "DAGGERHEART.Sheets.Adversary.General"}}</legend> <legend>{{localize "DAGGERHEART.Sheets.Adversary.General"}}</legend>
{{formGroup systemFields.tier value=source.system.tier localize=true}}
{{formGroup systemFields.type value=source.system.type localize=true}}
<div class="full-width">{{formGroup systemFields.difficulty value=source.system.difficulty}}</div> <div class="full-width">{{formGroup systemFields.difficulty value=source.system.difficulty}}</div>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Adversary.DamageThresholds"}}</legend>
{{formGroup systemFields.damageThresholds.fields.major value=source.system.damageThresholds.major}}
{{formGroup systemFields.damageThresholds.fields.severe value=source.system.damageThresholds.severe}}
</fieldset>
<fieldset> <fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Adversary.HitPoints"}}</legend> <legend>{{localize "DAGGERHEART.Sheets.Adversary.HitPoints"}}</legend>
<!--
{{formGroup systemFields.resources.fields.hitPoints.fields.value value=source.system.resources.hitPoints.value}} {{formGroup systemFields.resources.fields.hitPoints.fields.value value=source.system.resources.hitPoints.value}}
{{formGroup systemFields.resources.fields.hitPoints.fields.max value=source.system.resources.hitPoints.max}} {{formGroup systemFields.resources.fields.hitPoints.fields.max value=source.system.resources.hitPoints.max}}
-->
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Stress"}}</legend> <legend>{{localize "DAGGERHEART.Sheets.Adversary.Stress"}}</legend>
<!--
{{formGroup systemFields.resources.fields.stress.fields.value value=source.system.resources.stress.value}} {{formGroup systemFields.resources.fields.stress.fields.value value=source.system.resources.stress.value}}
{{formGroup systemFields.resources.fields.stress.fields.max value=source.system.resources.stress.max}} {{formGroup systemFields.resources.fields.stress.fields.max value=source.system.resources.stress.max}}
-->
</fieldset> </fieldset>
<fieldset> <fieldset>
@ -46,35 +39,5 @@
{{/each}} {{/each}}
</fieldset> </fieldset>
</fieldset> </fieldset>
<fieldset class="two-columns even">
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Attack"}}</legend>
<button data-action="attackConfigure">Configure</button>
<button data-action="attackRoll">Attack</button>
<fieldset class="action-category" style="grid-column: 1 / -1;">
<legend class="action-category-label" data-action="toggleSection" data-section="range">
<div>Name</div>
</legend>
<div class="action-category-data open">
{{formGroup systemFields.attack.fields.name value=source.system.attack.name name="system.attack.name"}}
{{formGroup systemFields.attack.fields.img value=source.img label="Icon" name="system.attack.img"}}
</div>
</fieldset>
<div>
<fieldset class="action-category">
<legend class="action-category-label" data-action="toggleSection" data-section="range">
<div>Bonus to Hit</div>
</legend>
<div class="action-category-data open">
{{formField systemFields.attack.fields.roll.fields.bonus value=source.system.attack.roll.bonus name="system.attack.roll.bonus"}}
</div>
</fieldset>
{{> 'systems/daggerheart/templates/views/actionTypes/range-target.hbs' fields=(object range=systemFields.attack.fields.range target=systemFields.attack.fields.target.fields) source=(object target=source.system.attack.target range=source.system.attack.range) path="system.attack."}}
</div>
{{> 'systems/daggerheart/templates/views/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=source.system.attack.damage path="system.attack."}}
<div style="grid-column: 1 / -1;">
{{> 'systems/daggerheart/templates/views/actionTypes/effect.hbs' fields=systemFields.attack.fields.effects.element.fields source=source.system.attack.effects}}
</div>
</fieldset>
</div> </div>
</section> </section>

View file

@ -0,0 +1,10 @@
<section
class='tab {{tabs.notes.cssClass}} {{tabs.notes.id}}'
data-tab='{{tabs.notes.id}}'
data-group='{{tabs.notes.group}}'
>
<fieldset class="fit-height">
<legend>{{localize tabs.notes.label}}</legend>
{{formInput systemFields.notes value=document.system.notes enriched=document.system.notes localize=true toggled=true}}
</fieldset>
</section>

View file

@ -0,0 +1,111 @@
<aside class="adversary-sidebar-sheet">
<div class="portrait {{#if (gte source.system.resources.hitPoints.value source.system.resources.hitPoints.maxTotal)}}death-roll{{/if}}">
<img src="{{source.img}}" alt="{{source.name}}" data-action='editImage' data-edit="img">
<a class="death-roll-btn" data-tooltip="{{localize "DAGGERHEART.Sheets.PC.Health.DeathMoveTooltip"}}" data-action="makeDeathMove"><i class="fas fa-skull death-save" ></i></a>
</div>
<div class="info-section">
<div class="resources-section">
<div class="status-bar">
<div class='status-value'>
<p><input class="bar-input" name="system.resources.hitPoints.value" value="{{source.system.resources.hitPoints.value}}" type="number"></p>
<p>/</p>
<p class="bar-label">{{source.system.resources.hitPoints.max}}</p>
</div>
<progress
class='progress-bar'
value='{{source.system.resources.hitPoints.value}}'
max='{{source.system.resources.hitPoints.max}}'
></progress>
<div class="status-label">
<h4>HP</h4>
</div>
</div>
<div class="status-bar">
<div class='status-value'>
<p><input class="bar-input" name="system.resources.stress.value" value="{{source.system.resources.stress.value}}" type="number"></p>
<p>/</p>
<p class="bar-label">{{source.system.resources.stress.max}}</p>
</div>
<progress
class='progress-bar stress-color'
value='{{source.system.resources.stress.value}}'
max='{{source.system.resources.stress.max}}'
></progress>
<div class="status-label">
<h4>Stress</h4>
</div>
</div>
</div>
<div class="status-section">
<div class="threshold-section">
<h4 class="threshold-label">{{localize "DAGGERHEART.Sheets.PC.Health.Minor"}}</h4>
<h4 class="threshold-value">{{document.system.damageThresholds.major}}</h4>
<h4 class="threshold-label">{{localize "DAGGERHEART.Sheets.PC.Health.Major"}}</h4>
<h4 class="threshold-value">{{document.system.damageThresholds.severe}}</h4>
<h4 class="threshold-label">{{localize "DAGGERHEART.Sheets.PC.Health.Severe"}}</h4>
</div>
</div>
<div class="status-section">
<div class="status-number">
<div class='status-value armor-slots'>
{{#if source.system.difficulty}}
<p>{{source.system.difficulty}}</p>
{{else}}
<p>-</p>
{{/if}}
</div>
<div class="status-label">
<h4>Difficulty</h4>
</div>
</div>
<div class="status-number">
<div class='status-value armor-slots'>
{{#if source.system.attack.target.amount}}
<p>{{source.system.attack.target.amount}}</p>
{{else}}
<p>-</p>
{{/if}}
</div>
<div class="status-label">
<h4>Attack</h4>
</div>
</div>
</div>
</div>
<div class="attack-section">
<div class="title">
<side-line-div class="invert"></side-line-div>
<h3>Attack</h3>
<side-line-div></side-line-div>
</div>
<ul class="items-sidebar-list">
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=source.system.attack type=source.system.attack.systemPath isSidebar=true}}
</ul>
</div>
<div class="experience-section">
<div class="title">
<side-line-div class="invert"></side-line-div>
<h3>Experience</h3>
<side-line-div></side-line-div>
</div>
<div class="experience-list">
{{#each source.system.experiences as |experience id|}}
<div class="experience-row">
<div class="experience-value">
+{{experience.modifier}}
</div>
<span class="experience-name">{{experience.name}}</span>
<div class="controls">
<a data-action="toChat" data-type="experience" data-uuid="{{id}}"><i class="fa-regular fa-message"></i></a>
</div>
</div>
{{/each}}
</div>
</div>
<line-div></line-div>
<div class="reaction-section">
<button data-action="reactionRoll">Reaction Test</button>
</div>
</aside>

View file

@ -18,7 +18,7 @@
max='{{document.system.resources.hitPoints.maxTotal}}' max='{{document.system.resources.hitPoints.maxTotal}}'
></progress> ></progress>
<div class="status-label"> <div class="status-label">
<h4>Health</h4> <h4>HP</h4>
</div> </div>
</div> </div>

View file

@ -0,0 +1,34 @@
<section
class='tab {{tabs.actions.cssClass}} {{tabs.actions.id}}'
data-tab='{{tabs.actions.id}}'
data-group='{{tabs.actions.group}}'
>
<button class="add-action-btn" data-action="addAction">
New Action
</button>
<fieldset>
<legend>{{localize tabs.actions.label}}</legend>
<ul class="action-list">
{{#each document.system.actions as |action index|}}
<li class="action-item">
<img src="{{action.img}}" alt="">
<div class="label">
<span>{{action.name}}</span>
<div class="tags">
<div class="tag">
{{localize (concat 'DAGGERHEART.Actions.Types.' action.type '.name')}}
</div>
<div class="tag">
{{localize (concat 'DAGGERHEART.ActionType.' action.actionType)}}
</div>
</div>
</div>
<div class="controls">
<a data-action="editAction" data-index="{{index}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.edit'}}"><i class="fa-solid fa-pen-to-square"></i></a>
<a data-action="removeAction" data-index="{{index}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.delete'}}"><i class="fa-solid fa-trash"></i></a>
</div>
</li>
{{/each}}
</ul>
</fieldset>
</section>

View file

@ -0,0 +1,23 @@
<section
class='tab {{tabs.attack.cssClass}} {{tabs.attack.id}}'
data-tab='{{tabs.attack.id}}'
data-group='{{tabs.attack.group}}'
>
<fieldset class="one-column">
<legend>{{localize 'DAGGERHEART.General.basics'}}</legend>
{{formGroup systemFields.attack.fields.img value=document.img label="Image Path" name="system.attack.img"}}
{{formGroup systemFields.attack.fields.name value=document.system.attack.name label="Attack Name" name="system.attack.name"}}
</fieldset>
<fieldset class="flex">
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Attack"}}</legend>
{{formField systemFields.attack.fields.roll.fields.bonus value=document.system.attack.roll.bonus label="Attack Bonus" name="system.attack.roll.bonus"}}
{{formField systemFields.attack.fields.range value=document.system.attack.range label="Range" name=(concat path "range") localize=true}}
{{#if systemFields.attack.fields.target.fields}}
{{#if (and document.system.target.type (not (eq document.system.target.type 'self')))}}
{{ formField systemFields.attack.fields.target.fields.amount value=document.system.target.amount label="Amount" name=(concat path "target.amount") }}
{{/if}}
{{ formField systemFields.attack.fields.target.fields.type value=document.system.target.type label="Target" name=(concat path "target.type") localize=true }}
{{/if}}
</fieldset>
{{> 'systems/daggerheart/templates/views/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=document.system.attack.damage path="system.attack."}}
</section>

View file

@ -0,0 +1,38 @@
<section
class='tab {{tabs.details.cssClass}} {{tabs.details.id}}'
data-tab='{{tabs.details.id}}'
data-group='{{tabs.details.group}}'
>
<fieldset class="one-column">
<legend>{{localize 'DAGGERHEART.General.basics'}}</legend>
<div class="nest-inputs">
{{formGroup systemFields.tier value=document.system.tier localize=true}}
{{formGroup systemFields.type value=document.system.type localize=true}}
{{#if (eq document.system.type 'horde')}}
{{formGroup systemFields.hordeHp value=document.system.hordeHp label=(localize "DAGGERHEART.Sheets.Adversary.horderHp")}}
{{/if}}
{{formGroup systemFields.difficulty value=document.system.difficulty localize=true}}
</div>
{{formField systemFields.description value=document.system.description label=(localize "DAGGERHEART.Sheets.Adversary.FIELDS.description.label")}}
{{formField systemFields.motivesAndTactics value=document.system.motivesAndTactics label=(localize "DAGGERHEART.Sheets.Adversary.FIELDS.motivesAndTactics.label")}}
</fieldset>
<div class="fieldsets-section">
<fieldset class="flex">
<legend>{{localize "DAGGERHEART.Sheets.Adversary.HitPoints"}}</legend>
{{formGroup systemFields.resources.fields.hitPoints.fields.value value=document.system.resources.hitPoints.value}}
{{formGroup systemFields.resources.fields.hitPoints.fields.max value=document.system.resources.hitPoints.max}}
</fieldset>
<fieldset class="flex">
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Stress"}}</legend>
{{formGroup systemFields.resources.fields.stress.fields.value value=document.system.resources.stress.value}}
{{formGroup systemFields.resources.fields.stress.fields.max value=document.system.resources.stress.max}}
</fieldset>
</div>
<fieldset class="flex">
<legend>{{localize "DAGGERHEART.Sheets.Adversary.DamageThresholds"}}</legend>
{{formGroup systemFields.damageThresholds.fields.major value=document.system.damageThresholds.major}}
{{formGroup systemFields.damageThresholds.fields.severe value=document.system.damageThresholds.severe}}
</fieldset>
</section>

View file

@ -0,0 +1,23 @@
<section
class='tab {{tabs.experiences.cssClass}} {{tabs.experiences.id}}'
data-tab='{{tabs.experiences.id}}'
data-group='{{tabs.experiences.group}}'
>
<button class="add-experience-btn" type="button" data-action="addExperience">
<span>New Experience</span>
</button>
<fieldset>
<legend>{{localize tabs.experiences.label}}</legend>
<ul class="experience-list">
{{#each document.system.experiences as |experience key|}}
<li class="experience-item">
<input class="name" type="text" name="system.experiences.{{key}}.name" value="{{experience.name}}" />
<input class="modifier" type="text" name="system.experiences.{{key}}.modifier" value="{{experience.modifier}}" data-dtype="Number" />
<a data-action="removeExperience" data-experience="{{key}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.delete'}}"><i class="fa-solid fa-trash"></i></a>
</li>
{{/each}}
</ul>
</fieldset>
</section>

View file

@ -0,0 +1,3 @@
<header class="dialog-header">
<h1>{{document.name}}</h1>
</header>

View file

@ -34,6 +34,11 @@
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=effect type=../type}} {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=effect type=../type}}
{{/if}} {{/if}}
{{/each}} {{/each}}
{{#each document.system.actions as |action|}}
{{#if (or (eq ../type 'action'))}}
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=action type=../type}}
{{/if}}
{{/each}}
{{#each document.system.features as |feature|}} {{#each document.system.features as |feature|}}
{{#if (or (eq ../type 'features'))}} {{#if (or (eq ../type 'features'))}}
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=feature}} {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=feature}}

View file

@ -100,6 +100,16 @@
{{/each}} {{/each}}
</div> </div>
{{/if}} {{/if}}
{{#if (eq type 'action')}}
<div class="item-tags">
<div class="tag">
{{localize (concat 'DAGGERHEART.Actions.Types.' item.type '.name')}}
</div>
<div class="tag">
{{localize (concat 'DAGGERHEART.ActionType.' item.actionType)}}
</div>
</div>
{{/if}}
</div> </div>
<div class="controls"> <div class="controls">
{{#if (eq type 'weapon')}} {{#if (eq type 'weapon')}}