Merged with data-models branch

This commit is contained in:
WBHarry 2025-06-09 13:35:57 +02:00
commit 2534cc473f
23 changed files with 575 additions and 1040 deletions

View file

@ -263,46 +263,54 @@
"Description": "When an effect makes a creature Restrained, it means they cannot move until this condition is cleared.\nThey can still take actions from their current position." "Description": "When an effect makes a creature Restrained, it means they cannot move until this condition is cleared.\nThey can still take actions from their current position."
} }
}, },
"Tiers": {
"tier1": "Tier 1",
"tier2": "Tier 2",
"tier3": "Tier 3",
"tier4": "Tier 4"
},
"Adversary": { "Adversary": {
"Bruiser": { "Type": {
"Name": "Bruiser", "Bruiser": {
"Description": "Tough adversaries with powerful attacks." "label": "Bruiser",
}, "Description": "Tough adversaries with powerful attacks."
"Horde": { },
"Name": "Horde", "Horde": {
"Description": "A Horde represents a number of foes working in a group." "label": "Horde",
}, "Description": "A Horde represents a number of foes working in a group."
"Leader": { },
"Name": "Leader", "Leader": {
"Description": "Adversaries that command and summon other adversaries." "label": "Leader",
}, "Description": "Adversaries that command and summon other adversaries."
"Minion": { },
"Name": "Minion", "Minion": {
"Description": "Basic enemies that are easily dispatched but dangerous in numbers." "label": "Minion",
}, "Description": "Basic enemies that are easily dispatched but dangerous in numbers."
"Ranged": { },
"Name": "Ranged", "Ranged": {
"Description": "Adversaries that attack from a distance." "label": "Ranged",
}, "Description": "Adversaries that attack from a distance."
"Skulker": { },
"Name": "Skulker", "Skulk": {
"Description": "Adversaries that maneuver and exploit opportunities to ambush their opponents." "label": "Skulk",
}, "Description": "Adversaries that maneuver and exploit opportunities to ambush their opponents."
"Social": { },
"Name": "Social", "Social": {
"Description": "Adversaries that are primarily interpersonal threats or challenges." "label": "Social",
}, "Description": "Adversaries that are primarily interpersonal threats or challenges."
"Solo": { },
"Name": "Solo", "Solo": {
"Description": "Designed to present a challenge to a whole party." "label": "Solo",
}, "Description": "Designed to present a challenge to a whole party."
"Standard": { },
"Name": "Standard", "Standard": {
"Description": "Rank and File adversaries." "label": "Standard",
}, "Description": "Rank and File adversaries."
"Support": { },
"Name": "Support", "Support": {
"Description": "Enemies that enhance their allies and/or disrupt their opponents." "label": "Support",
"Description": "Enemies that enhance their allies and/or disrupt their opponents."
}
}, },
"Trait": { "Trait": {
"Relentless": { "Relentless": {
@ -322,6 +330,26 @@
} }
} }
}, },
"Environment": {
"Type": {
"Exploration": {
"label": "Exploration",
"description": ""
},
"Social": {
"label": "Social",
"description": ""
},
"Traversal": {
"label": "Traversal",
"description": ""
},
"Event": {
"label": "Event",
"description": ""
}
}
},
"Domains": { "Domains": {
"Arcana": { "Arcana": {
"Description": "This is the domain of the innate or instinctual use of magic. Those who walk this path tap into the raw, enigmatic forces of the realms to manipulate both the elements and their own energy. Arcana offers wielders a volatile power, but it is incredibly potent when correctly channeled." "Description": "This is the domain of the innate or instinctual use of magic. Those who walk this path tap into the raw, enigmatic forces of the realms to manipulate both the elements and their own energy. Arcana offers wielders a volatile power, but it is incredibly potent when correctly channeled."
@ -1001,40 +1029,81 @@
} }
}, },
"Adversary": { "Adversary": {
"Description": "Description", "FIELDS": {
"MotivesAndTactics": "Motives & Tactics", "tier": { "label": "Tier" },
"Tier": "Tier", "type": { "label": "Type" },
"Type": "Type", "description": { "label": "Description" },
"Attack": { "motivesAndTactics": { "label": "Motives & Tactics" },
"Title": "Attack", "difficulty": { "label": "Difficulty" },
"Modifier": "Attack Modifier", "damageThresholds": {
"Name": "Name", "major": { "label": "Major" },
"Range": "Range", "severe": { "label": "Severe" }
"Damage": { },
"Title": "Damage", "resources": {
"Value": "Value", "hitPoints": {
"Type": "Type" "value": { "label": "Current" },
"max": { "label": "Max" }
},
"stress": {
"value": { "label": "Current" },
"max": { "label": "Max" }
}
},
"experiences": {
"element": {
"name": { "label": "Name" },
"value": { "label": "Modifier" }
}
},
"attack": {
"name": { "label": "Name" },
"modifier": { "label": "Modifier" },
"range": { "label": "Range" },
"damage": {
"value": { "label": "Damage" },
"type": { "label": "Damage Type" }
}
} }
}, },
"Difficulty": "Difficulty", "Tabs": {
"Reaction": "Reaction Roll", "Main": "Data",
"DamageThresholds": { "Information": "Information"
"Title": "Damage Thresholds",
"Minor": "Minor",
"Major": "Major",
"Severe": "Severe"
}, },
"HP": "HP", "General": "General",
"DamageThresholds": "Damage Thresholds",
"HitPoints": "Hit Points",
"Stress": "Stress", "Stress": "Stress",
"Experience": "Experience",
"Experiences": "Experiences", "Experiences": "Experiences",
"Features": "Features", "Attack": "Attack"
"NewFeature": "New Feature"
}, },
"Environment": { "Environment": {
"ToneAndFeel": "Tone And feel", "FIELDS": {
"PotentialAdversaries": "Potential Adversaries", "tier": {
"NewFeature": "New Feature" "label": "Tier"
},
"type": {
"label": "Type"
},
"difficulty": {
"label": "Difficulty"
}
},
"Tabs": {
"Main": "Data",
"Information": "Information"
},
"general": "General",
"newAdversary": "New Adversary",
"newFeature": "New feature",
"description": "Description",
"impulses": "Impulses",
"potentialAdversaries": {
"label": "Potential Adversaries",
"placeholder": "Optionally drag and drop adversaries here"
},
"features": {
"label": "Features"
}
}, },
"Armor": { "Armor": {
"baseScore": "Base Score", "baseScore": "Base Score",

View file

@ -1,219 +1,12 @@
// import DhpApplicationMixin from '../daggerheart-sheet.mjs';
// export class Teest extends DhpApplicationMixin(ActorSheet) {
// static documentType = "adversary";
// constructor(options){
// super(options);
// this.editMode = false;
// }
// /** @override */
// static get defaultOptions() {
// return foundry.utils.mergeObject(super.defaultOptions, {
// classes: ["daggerheart", "sheet", "adversary"],
// width: 600,
// height: 'auto',
// resizable: false,
// });
// }
// async getData() {
// const context = super.getData();
// context.config = SYSTEM;
// context.editMode = this.editMode;
// context.title = `${this.actor.name} - ${game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.actor.system.type].name)}`;
// context.data = {
// description: this.object.system.description,
// motivesAndTactics: this.object.system.motivesAndTactics.join(', '),
// tier: this.object.system.tier,
// type: game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.object.system.type].name),
// attack: {
// name: this.object.system.attack.name,
// attackModifier: this.object.system.attackModifier,
// range: this.object.system.attack.range ? game.i18n.localize(SYSTEM.GENERAL.range[this.object.system.attack.range].name) : null,
// damage: {
// value: this.object.system.attack.damage.value,
// type: this.object.system.attack.damage.type,
// typeName: this.object.system.attack.damage.type ? game.i18n.localize(SYSTEM.GENERAL.damageTypes[this.object.system.attack.damage.type].abbreviation).toLowerCase() : null,
// },
// },
// damageThresholds: this.object.system.damageThresholds,
// difficulty: this.object.system.difficulty,
// hp: { ...this.object.system.resources.health, lastRowIndex: Math.floor(this.object.system.resources.health.max/5)*5 },
// stress: { ...this.object.system.resources.stress, lastRowIndex: Math.floor(this.object.system.resources.stress.max/5)*5 },
// moves: this.object.system.moves,
// };
// return context;
// }
// async _handleAction(action, event, button) {
// switch(action){
// case 'viewMove':
// await this.viewMove(button);
// break;
// case 'addMove':
// this.addMove();
// break;
// case 'removeMove':
// await this.removeMove(button);
// break;
// case 'toggleSlider':
// this.toggleEditMode();
// break;
// case 'addMotive':
// await this.addMotive();
// break;
// case 'removeMotive':
// await this.removeMotive(button);
// break;
// case 'reactionRoll':
// await this.reactionRoll(event);
// break;
// case 'attackRoll':
// await this.attackRoll(event);
// break;
// case 'addExperience':
// await this.addExperience();
// break;
// case 'removeExperience':
// await this.removeExperience(button);
// break;
// case 'toggleHP':
// await this.toggleHP(button);
// break;
// case 'toggleStress':
// await this.toggleStress(button);
// break;
// }
// }
// async viewMove(button){
// const move = await fromUuid(button.dataset.move);
// move.sheet.render(true);
// }
// async addMove(){
// const result = await this.object.createEmbeddedDocuments("Item", [{
// name: game.i18n.localize('DAGGERHEART.Sheets.Adversary.NewMove'),
// type: 'feature',
// }]);
// await result[0].sheet.render(true);
// }
// async removeMove(button){
// await this.object.items.find(x => x.uuid === button.dataset.move).delete();
// }
// toggleEditMode(){
// this.editMode = !this.editMode;
// this.render();
// }
// async addMotive(){
// await this.object.update({ "system.motivesAndTactics": [...this.object.system.motivesAndTactics, ''] });
// }
// async removeMotive(button){
// await this.object.update({ "system.motivesAndTactics": this.object.system.motivesAndTactics.filter((_, index) => index !== Number.parseInt(button.dataset.motive) )});
// }
// async reactionRoll(event){
// const { roll, diceResults, modifiers } = await this.actor.diceRoll({ title: `${this.actor.name} - Reaction Roll`, value: 0 }, event.shiftKey);
// const cls = getDocumentClass("ChatMessage");
// const msg = new cls({
// type: 'adversaryRoll',
// system: {
// roll: roll._formula,
// total: roll._total,
// modifiers: modifiers,
// diceResults: diceResults,
// },
// content: "systems/daggerheart/templates/chat/adversary-roll.hbs",
// rolls: [roll]
// });
// cls.create(msg.toObject());
// }
// async attackRoll(event){
// const modifier = Number.parseInt(event.currentTarget.dataset.value);
// const { roll, diceResults, modifiers } = await this.actor.diceRoll({ title: `${this.actor.name} - Attack Roll`, value: modifier }, event.shiftKey);
// const 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,
// }));
// const cls = getDocumentClass("ChatMessage");
// const msg = new cls({
// type: 'adversaryRoll',
// system: {
// roll: roll._formula,
// total: roll._total,
// modifiers: modifiers,
// diceResults: diceResults,
// targets: targets,
// damage: { value: event.currentTarget.dataset.damage, type: event.currentTarget.dataset.damageType },
// },
// content: "systems/daggerheart/templates/chat/adversary-attack-roll.hbs",
// rolls: [roll]
// });
// cls.create(msg.toObject());
// }
// async addExperience(){
// await this.object.update({ "system.experiences": [...this.object.system.experiences, { name: 'Experience', value: 1 }] });
// }
// async removeExperience(button){
// await this.object.update({ "system.experiences": this.object.system.experiences.filter((_, index) => index !== Number.parseInt(button.dataset.experience) )});
// }
// async toggleHP(button){
// const index = Number.parseInt(button.dataset.index);
// const newHP = index < this.object.system.resources.health.value ? index : index+1;
// await this.object.update({ "system.resources.health.value": newHP });
// }
// async toggleStress(button){
// const index = Number.parseInt(button.dataset.index);
// const newStress = index < this.object.system.resources.stress.value ? index : index+1;
// await this.object.update({ "system.resources.stress.value": newStress });
// }
// }
import DaggerheartSheet from './daggerheart-sheet.mjs'; import DaggerheartSheet from './daggerheart-sheet.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) {
constructor(options = {}) {
super(options);
this.editMode = false;
}
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
tag: 'form', tag: 'form',
classes: ['daggerheart', 'sheet', 'adversary'], classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'adversary'],
position: { width: 600 }, position: { width: 450, height: 1000 },
actions: { actions: {
viewMove: this.viewMove,
addMove: this.addMove,
removeMove: this.removeMove,
toggleSlider: this.toggleEditMode,
addMotive: this.addMotive,
removeMotive: this.removeMotive,
reactionRoll: this.reactionRoll, reactionRoll: this.reactionRoll,
attackRoll: this.attackRoll, attackRoll: this.attackRoll,
addExperience: this.addExperience, addExperience: this.addExperience,
@ -229,54 +22,35 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
}; };
static PARTS = { static PARTS = {
form: { header: { template: 'systems/daggerheart/templates/sheets/actors/adversary/header.hbs' },
id: 'feature', tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
template: 'systems/daggerheart/templates/sheets/adversary.hbs' main: { template: 'systems/daggerheart/templates/sheets/actors/adversary/main.hbs' },
information: { template: 'systems/daggerheart/templates/sheets/actors/adversary/information.hbs' }
};
static TABS = {
main: {
active: true,
cssClass: '',
group: 'primary',
id: 'main',
icon: null,
label: 'DAGGERHEART.Sheets.Adversary.Tabs.Main'
},
information: {
active: false,
cssClass: '',
group: 'primary',
id: 'information',
icon: null,
label: 'DAGGERHEART.Sheets.Adversary.Tabs.Information'
} }
}; };
async _prepareContext(_options) { async _prepareContext(_options) {
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.document = this.document; context.document = this.document;
context.config = SYSTEM; context.tabs = super._getTabs(this.constructor.TABS);
context.editMode = this.editMode;
context.title = `${this.actor.name} - ${game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.actor.system.type].name)}`;
context.data = {
description: this.document.system.description,
motivesAndTactics: this.document.system.motivesAndTactics.join(', '),
tier: this.document.system.tier,
type: game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.document.system.type].name),
attack: {
name: this.document.system.attack.name,
attackModifier: this.document.system.attackModifier,
range: this.document.system.attack.range
? game.i18n.localize(SYSTEM.GENERAL.range[this.document.system.attack.range].name)
: null,
damage: {
value: this.document.system.attack.damage.value,
type: this.document.system.attack.damage.type,
typeName: this.document.system.attack.damage.type
? game.i18n
.localize(
SYSTEM.GENERAL.damageTypes[this.document.system.attack.damage.type].abbreviation
)
.toLowerCase()
: null
}
},
damageThresholds: this.document.system.damageThresholds,
difficulty: this.document.system.difficulty,
hp: {
...this.document.system.resources.health,
lastRowIndex: Math.floor(this.document.system.resources.health.max / 5) * 5
},
stress: {
...this.document.system.resources.stress,
lastRowIndex: Math.floor(this.document.system.resources.stress.max / 5) * 5
},
moves: this.document.system.moves
};
return context; return context;
} }
@ -286,43 +60,6 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
this.render(); this.render();
} }
static async viewMove(_, button) {
const move = await fromUuid(button.dataset.move);
move.sheet.render(true);
}
static async addMove() {
const result = await this.document.createEmbeddedDocuments('Item', [
{
name: game.i18n.localize('DAGGERHEART.Sheets.Adversary.NewMove'),
type: 'feature'
}
]);
await result[0].sheet.render(true);
}
static async removeMove(_, button) {
await this.document.items.find(x => x.uuid === button.dataset.move).delete();
}
static toggleEditMode() {
this.editMode = !this.editMode;
this.render();
}
static async addMotive() {
await this.document.update({ 'system.motivesAndTactics': [...this.document.system.motivesAndTactics, ''] });
}
static async removeMotive(button) {
await this.document.update({
'system.motivesAndTactics': this.document.system.motivesAndTactics.filter(
(_, index) => index !== Number.parseInt(button.dataset.motive)
)
});
}
static async reactionRoll(event) { static async reactionRoll(event) {
const { roll, diceResults, modifiers } = await this.actor.diceRoll( const { roll, diceResults, modifiers } = await this.actor.diceRoll(
{ title: `${this.actor.name} - Reaction Roll`, value: 0 }, { title: `${this.actor.name} - Reaction Roll`, value: 0 },
@ -349,9 +86,8 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
cls.create(msg.toObject()); cls.create(msg.toObject());
} }
static async attackRoll(event, button) { static async attackRoll() {
const modifier = Number.parseInt(button.dataset.value); const { modifier, damage, name: attackName } = this.actor.system.attack;
const { roll, dice, advantageState, modifiers } = await this.actor.diceRoll( const { roll, dice, advantageState, modifiers } = await this.actor.diceRoll(
{ title: `${this.actor.name} - Attack Roll`, value: modifier }, { title: `${this.actor.name} - Attack Roll`, value: modifier },
event.shiftKey event.shiftKey
@ -367,7 +103,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
const cls = getDocumentClass('ChatMessage'); const cls = getDocumentClass('ChatMessage');
const systemData = { const systemData = {
title: button.dataset.name, title: attackName,
origin: this.document.id, origin: this.document.id,
roll: roll._formula, roll: roll._formula,
advantageState, advantageState,
@ -375,7 +111,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
modifiers: modifiers, modifiers: modifiers,
dice: dice, dice: dice,
targets: targets, targets: targets,
damage: { value: button.dataset.damage, type: button.dataset.damageType } damage: { value: damage.value, type: damage.type }
}; };
const msg = new cls({ const msg = new cls({
type: 'adversaryRoll', type: 'adversaryRoll',

View file

@ -1,78 +1,60 @@
import DaggerheartSheet from './daggerheart-sheet.mjs'; import DaggerheartSheet from './daggerheart-sheet.mjs';
const { DocumentSheetV2 } = foundry.applications.api; const { ActorSheetV2 } = foundry.applications.sheets;
export default class DhpEnvironment extends DaggerheartSheet(DocumentSheetV2) { export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) {
constructor(options) {
super(options);
this.editMode = false;
}
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
tag: 'form', tag: 'form',
classes: ['daggerheart', 'sheet', 'adversary', 'environment'], classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'environment'],
position: { position: {
width: 600, width: 450,
height: 'auto' height: 1000
}, },
actions: { actions: {
toggleSlider: this.toggleSlider, addAdversary: this.addAdversary,
viewFeature: this.viewFeature,
addFeature: this.addFeature, addFeature: this.addFeature,
removeFeature: this.removeFeature, deleteProperty: this.deleteProperty,
addTone: this.addTone, viewAdversary: this.viewAdversary
removeTone: this.removeTone,
useFeature: this.useFeature
}, },
form: { form: {
handler: this._updateForm, handler: this._updateForm,
closeOnSubmit: false, submitOnChange: true,
submitOnChange: true closeOnSubmit: false
} },
dragDrop: [{ dragSelector: null, dropSelector: '.adversary-container' }]
}; };
/** @override */
static PARTS = { static PARTS = {
form: { header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' },
id: 'form', tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
template: 'systems/daggerheart/templates/sheets/environment.hbs' main: { template: 'systems/daggerheart/templates/sheets/actors/environment/main.hbs' },
} information: { template: 'systems/daggerheart/templates/sheets/actors/environment/information.hbs' }
}; };
/* -------------------------------------------- */ static TABS = {
main: {
/** @inheritDoc */ active: true,
get title() { cssClass: '',
return `${game.i18n.localize('Environment')} - ${this.document.name}`; group: 'primary',
} id: 'main',
icon: null,
label: 'DAGGERHEART.Sheets.Environment.Tabs.Main'
},
information: {
active: false,
cssClass: '',
group: 'primary',
id: 'information',
icon: null,
label: 'DAGGERHEART.Sheets.Environment.Tabs.Information'
}
};
async _prepareContext(_options) { async _prepareContext(_options) {
return { const context = await super._prepareContext(_options);
title: `${this.document.name} - ${game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.document.system.type].name)}`, context.document = this.document;
user: this.document, context.tabs = super._getTabs(this.constructor.TABS);
source: this.document.toObject(),
fields: this.document.schema.fields,
data: {
type: game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.document.system.type].name),
features: this.document.items.reduce((acc, x) => {
if (x.type === 'feature') {
const feature = x.toObject();
acc.push({
...feature,
system: {
...feature.system,
actionType: game.i18n.localize(SYSTEM.ITEM.actionTypes[feature.system.actionType].name)
},
uuid: x.uuid
});
}
return acc; return context;
}, [])
},
editMode: this.editMode,
config: SYSTEM
};
} }
static async _updateForm(event, _, formData) { static async _updateForm(event, _, formData) {
@ -80,60 +62,41 @@ export default class DhpEnvironment extends DaggerheartSheet(DocumentSheetV2) {
this.render(); this.render();
} }
static toggleSlider() { static async addAdversary() {
this.editMode = !this.editMode; await this.document.update({
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
'DAGGERHEART.Sheets.Environment.newAdversary'
)
});
this.render(); this.render();
} }
static async viewFeature(_, button) {
const move = await fromUuid(button.dataset.feature);
move.sheet.render(true);
}
static async addFeature() { static async addFeature() {
const result = await this.document.createEmbeddedDocuments('Item', [ ui.notifications.error('Not Implemented yet. Awaiting datamodel rework');
{
name: game.i18n.localize('DAGGERHEART.Sheets.Environment.NewFeature'),
type: 'feature'
}
]);
await result[0].sheet.render(true);
} }
static async removeFeature(_, button) { static async deleteProperty(_, target) {
await this.document.items.find(x => x.uuid === button.dataset.feature).delete(); await this.document.update({ [`${target.dataset.path}.-=${target.id}`]: null });
this.render();
} }
static async addTone() { static async viewAdversary(_, button) {
await this.document.update({ 'system.toneAndFeel': [...this.document.system.toneAndFeel, ''] }); const adversary = foundry.utils.getProperty(
this.document.system.potentialAdversaries,
`${button.dataset.potentialAdversary}.adversaries.${button.dataset.adversary}`
);
adversary.sheet.render(true);
} }
static async removeTone(button) { async _onDrop(event) {
await this.document.update({ const data = TextEditor.getDragEventData(event);
'system.toneAndFeel': this.document.system.toneAndFeel.filter( const item = await fromUuid(data.uuid);
(_, index) => index !== Number.parseInt(button.dataset.tone) if (item.type === 'adversary') {
) const target = event.target.closest('.adversary-container');
}); const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries.${item.id}`;
} await this.document.update({
[path]: item.uuid
static async useFeature(_, button) { });
const item = this.document.items.find(x => x.uuid === button.dataset.feature); }
const cls = getDocumentClass('ChatMessage');
const msg = new cls({
user: game.user.id,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/chat/ability-use.hbs',
{
title: game.i18n.format('DAGGERHEART.Chat.EnvironmentTitle', {
actionType: button.dataset.actionType
}),
card: { name: item.name, img: item.img, description: item.system.description }
}
)
});
cls.create(msg.toObject());
} }
} }

View file

@ -82,47 +82,76 @@ export const featureProperties = {
export const adversaryTypes = { export const adversaryTypes = {
bruiser: { bruiser: {
name: 'DAGGERHEART.Adversary.Bruiser.Name', id: 'bruiser',
label: 'DAGGERHEART.Adversary.Type.Bruiser.label',
description: 'DAGGERHEART.Adversary.Bruiser.Description' description: 'DAGGERHEART.Adversary.Bruiser.Description'
}, },
horde: { horde: {
name: 'DAGGERHEART.Adversary.Horde.Name', id: 'horde',
label: 'DAGGERHEART.Adversary.Type.Horde.label',
description: 'DAGGERHEART.Adversary.Horde.Description' description: 'DAGGERHEART.Adversary.Horde.Description'
}, },
leader: { leader: {
name: 'DAGGERHEART.Adversary.Leader.Name', id: 'leader',
label: 'DAGGERHEART.Adversary.Type.Leader.label',
description: 'DAGGERHEART.Adversary.Leader.Description' description: 'DAGGERHEART.Adversary.Leader.Description'
}, },
minion: { minion: {
name: 'DAGGERHEART.Adversary.Minion.Name', id: 'minion',
label: 'DAGGERHEART.Adversary.Type.Minion.label',
description: 'DAGGERHEART.Adversary.Minion.Description' description: 'DAGGERHEART.Adversary.Minion.Description'
}, },
ranged: { ranged: {
name: 'DAGGERHEART.Adversary.Ranged.Name', id: 'ranged',
label: 'DAGGERHEART.Adversary.Type.Ranged.label',
description: 'DAGGERHEART.Adversary.Ranged.Description' description: 'DAGGERHEART.Adversary.Ranged.Description'
}, },
skulker: { skulk: {
name: 'DAGGERHEART.Adversary.Skulker.Name', id: 'skulk',
description: 'DAGGERHEART.Adversary.Skulker.Description' label: 'DAGGERHEART.Adversary.Type.Skulk.label',
description: 'DAGGERHEART.Adversary.Skulk.Description'
}, },
social: { social: {
name: 'DAGGERHEART.Adversary.Social.Name', id: 'social',
label: 'DAGGERHEART.Adversary.Type.Social.label',
description: 'DAGGERHEART.Adversary.Social.Description' description: 'DAGGERHEART.Adversary.Social.Description'
}, },
solo: { solo: {
name: 'DAGGERHEART.Adversary.Solo.Name', id: 'solo',
label: 'DAGGERHEART.Adversary.Type.Solo.label',
description: 'DAGGERHEART.Adversary.Solo.Description' description: 'DAGGERHEART.Adversary.Solo.Description'
}, },
standard: { standard: {
name: 'DAGGERHEART.Adversary.Standard.Name', id: 'standard',
label: 'DAGGERHEART.Adversary.Type.Standard.label',
description: 'DAGGERHEART.Adversary.Standard.Description' description: 'DAGGERHEART.Adversary.Standard.Description'
}, },
support: { support: {
name: 'DAGGERHEART.Adversary.Support.Name', id: 'support',
label: 'DAGGERHEART.Adversary.Type.Support.label',
description: 'DAGGERHEART.Adversary.Support.Description' description: 'DAGGERHEART.Adversary.Support.Description'
} }
}; };
export const environmentTypes = {
exploration: {
label: 'DAGGERHEART.Environment.Type.Exploration.label',
description: 'DAGGERHEART.Environment.Type.Exploration.description'
},
social: {
label: 'DAGGERHEART.Environment.Type.Social.label',
description: 'DAGGERHEART.Environment.Type.Social.description'
},
traversal: {
label: 'DAGGERHEART.Environment.Type.Traversal.label',
description: 'DAGGERHEART.Environment.Type.Traversal.description'
},
event: {
label: 'DAGGERHEART.Environment.Type.Event.label',
description: 'DAGGERHEART.Environment.Type.Event.description'
}
};
export const adversaryTraits = { export const adversaryTraits = {
relentless: { relentless: {
name: 'DAGGERHEART.Adversary.Trait..Name', name: 'DAGGERHEART.Adversary.Trait..Name',

View file

@ -1,25 +1,30 @@
export const range = { export const range = {
melee: { melee: {
id: 'melee',
label: 'DAGGERHEART.Range.melee.name', label: 'DAGGERHEART.Range.melee.name',
description: 'DAGGERHEART.Range.melee.description', description: 'DAGGERHEART.Range.melee.description',
distance: 1 distance: 1
}, },
veryClose: { veryClose: {
id: 'veryClose',
label: 'DAGGERHEART.Range.veryClose.name', label: 'DAGGERHEART.Range.veryClose.name',
description: 'DAGGERHEART.Range.veryClose.description', description: 'DAGGERHEART.Range.veryClose.description',
distance: 3 distance: 3
}, },
close: { close: {
id: 'close',
label: 'DAGGERHEART.Range.close.name', label: 'DAGGERHEART.Range.close.name',
description: 'DAGGERHEART.Range.close.description', description: 'DAGGERHEART.Range.close.description',
distance: 10 distance: 10
}, },
far: { far: {
id: 'far',
label: 'DAGGERHEART.Range.far.name', label: 'DAGGERHEART.Range.far.name',
description: 'DAGGERHEART.Range.far.description', description: 'DAGGERHEART.Range.far.description',
distance: 20 distance: 20
}, },
veryFar: { veryFar: {
id: 'veryFar',
label: 'DAGGERHEART.Range.veryFar.name', label: 'DAGGERHEART.Range.veryFar.name',
description: 'DAGGERHEART.Range.veryFar.description', description: 'DAGGERHEART.Range.veryFar.description',
distance: 30 distance: 30
@ -175,25 +180,21 @@ export const deathMoves = {
}; };
export const tiers = { export const tiers = {
0: { tier1: {
key: 0,
id: 'tier0',
name: 'DAGGERHEART.General.Tier.0'
},
1: {
key: 1,
id: 'tier1', id: 'tier1',
name: 'DAGGERHEART.General.Tier.1' label: 'DAGGERHEART.Tiers.tier1'
}, },
2: { tier2: {
key: 2,
id: 'tier2', id: 'tier2',
name: 'DAGGERHEART.General.Tier.2' label: 'DAGGERHEART.Tiers.tier2'
}, },
3: { tier3: {
key: 3,
id: 'tier3', id: 'tier3',
name: 'DAGGERHEART.General.Tier.3' label: 'DAGGERHEART.Tiers.tier3'
},
tier4: {
id: 'tier4',
label: 'DAGGERHEART.Tiers.tier4'
} }
}; };

View file

@ -1,52 +1,60 @@
const resourceField = () =>
new foundry.data.fields.SchemaField({
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
max: new foundry.data.fields.NumberField({ initial: 0, integer: true })
});
export default class DhpAdversary extends foundry.abstract.TypeDataModel { export default class DhpAdversary extends foundry.abstract.TypeDataModel {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.Sheets.Adversary'];
static defineSchema() { static defineSchema() {
const fields = foundry.data.fields; const fields = foundry.data.fields;
return { return {
resources: new fields.SchemaField({ tier: new fields.StringField({
health: new fields.SchemaField({ required: true,
value: new fields.NumberField({ initial: 0, integer: true }), choices: SYSTEM.GENERAL.tiers,
min: new fields.NumberField({ initial: 0, integer: true }), initial: SYSTEM.GENERAL.tiers.tier1.id
max: new fields.NumberField({ initial: 0, integer: true })
}),
stress: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true }),
min: new fields.NumberField({ initial: 0, integer: true }),
max: new fields.NumberField({ initial: 0, integer: true })
})
}), }),
tier: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.tiers), integer: false }),
type: new fields.StringField({ type: new fields.StringField({
choices: Object.keys(SYSTEM.ACTOR.adversaryTypes), required: true,
integer: false, choices: SYSTEM.ACTOR.adversaryTypes,
initial: Object.keys(SYSTEM.ACTOR.adversaryTypes).find(x => x === 'standard') initial: SYSTEM.ACTOR.adversaryTypes.standard.id
}),
description: new fields.HTMLField(),
motivesAndTactics: new fields.HTMLField(),
difficulty: new fields.NumberField({ required: true, initial: 1, integer: true }),
damageThresholds: new fields.SchemaField({
major: new fields.NumberField({ required: true, initial: 0, integer: true }),
severe: new fields.NumberField({ required: true, initial: 0, integer: true })
}),
resources: new fields.SchemaField({
hitPoints: resourceField(),
stress: resourceField()
}), }),
description: new fields.StringField({}),
motivesAndTactics: new fields.ArrayField(new fields.StringField({})),
attackModifier: new fields.NumberField({ integer: true, nullabe: true, initial: null }),
attack: new fields.SchemaField({ attack: new fields.SchemaField({
name: new fields.StringField({}), name: new fields.StringField({}),
range: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.range), integer: false }), modifier: new fields.NumberField({ required: true, integer: true, initial: 0 }),
range: new fields.StringField({
required: true,
choices: SYSTEM.GENERAL.range,
initial: SYSTEM.GENERAL.range.melee.id
}),
damage: new fields.SchemaField({ damage: new fields.SchemaField({
value: new fields.StringField({}), value: new fields.StringField(),
type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false }) type: new fields.StringField({
required: true,
choices: SYSTEM.GENERAL.damageTypes,
initial: SYSTEM.GENERAL.damageTypes.physical.id
})
}) })
}), }),
difficulty: new fields.NumberField({ initial: 1, integer: true }),
damageThresholds: new fields.SchemaField({
major: new fields.NumberField({ initial: 0, integer: true }),
severe: new fields.NumberField({ initial: 0, integer: true })
}),
experiences: new fields.TypedObjectField( experiences: new fields.TypedObjectField(
new fields.SchemaField({ new fields.SchemaField({
id: new fields.StringField({ required: true }),
name: new fields.StringField(), name: new fields.StringField(),
value: new fields.NumberField({ integer: true, nullable: true, initial: null }) value: new fields.NumberField({ required: true, integer: true, initial: 1 })
}) })
) )
/* Features waiting on pseudo-document data model addition */
}; };
} }
get features() {
return this.parent.items.filter(x => x.type === 'feature');
}
} }

View file

@ -1,22 +1,28 @@
export default class DhpEnvironment extends foundry.abstract.TypeDataModel { import { environmentTypes } from '../config/actorConfig.mjs';
import ForeignDocumentUUIDField from './fields/foreignDocumentUUIDField.mjs';
export default class DhEnvironment extends foundry.abstract.TypeDataModel {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.Sheets.Environment'];
static defineSchema() { static defineSchema() {
const fields = foundry.data.fields; const fields = foundry.data.fields;
return { return {
resources: new fields.SchemaField({}), tier: new fields.StringField({
tier: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.tiers), integer: false }), required: true,
type: new fields.StringField({ choices: SYSTEM.GENERAL.tiers,
choices: Object.keys(SYSTEM.ACTOR.adversaryTypes), initial: SYSTEM.GENERAL.tiers.tier1.id
integer: false,
initial: Object.keys(SYSTEM.ACTOR.adversaryTypes).find(x => x === 'standard')
}), }),
description: new fields.StringField({}), type: new fields.StringField({ choices: environmentTypes }),
toneAndFeel: new fields.StringField({}), description: new fields.HTMLField(),
difficulty: new fields.NumberField({ initial: 1, integer: true }), impulses: new fields.HTMLField(),
potentialAdversaries: new fields.StringField({}) difficulty: new fields.NumberField({ required: true, initial: 11, integer: true }),
potentialAdversaries: new fields.TypedObjectField(
new fields.SchemaField({
label: new fields.StringField(),
adversaries: new fields.TypedObjectField(new ForeignDocumentUUIDField({ type: 'Actor' }))
})
)
/* Features pending datamodel rework */
}; };
} }
get features() {
return this.parent.items.filter(x => x.type === 'feature');
}
} }

View file

@ -2370,229 +2370,6 @@ div.daggerheart.views.multiclass {
align-items: center; align-items: center;
gap: 5px; gap: 5px;
} }
.daggerheart.sheet.adversary .adversary-header-container {
position: relative;
background-color: grey;
display: flex;
}
.daggerheart.sheet.adversary .adversary-header-container .adversary-header {
flex: 1;
}
.daggerheart.sheet.adversary .adversary-header-container .adversary-header img {
height: 60px;
width: 60px;
}
.daggerheart.sheet.adversary .adversary-header-container .adversary-header .adversary-title {
display: flex;
align-items: center;
text-align: center;
font-size: 28px;
}
.daggerheart.sheet.adversary .adversary-header-container .adversary-header .adversary-title .title-text {
width: 100%;
}
.daggerheart.sheet.adversary .adversary-header-container .adversary-header .adversary-title input {
font-size: 28px;
border: 0;
height: 100%;
}
.daggerheart.sheet.adversary .adversary-header-container .adversary-toggle {
position: absolute;
top: 0;
right: 0;
background-color: white;
color: black;
flex: 0;
}
.daggerheart.sheet.adversary .motive-container {
background: lightgrey;
margin-bottom: 8px;
padding-bottom: 4px;
}
.daggerheart.sheet.adversary .motive-container .motive-title {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
.daggerheart.sheet.adversary .motive-container .motive-title .motive-title-base {
font-size: 21px;
}
.daggerheart.sheet.adversary .motive-container .motive-title .motive-title-value {
font-style: italic;
position: relative;
top: 2px;
}
.daggerheart.sheet.adversary .motive-container .motive-title i {
margin-left: 4px;
cursor: pointer;
}
.daggerheart.sheet.adversary .motive-container .motive-title i:hover {
filter: drop-shadow(0 0 3px red);
}
.daggerheart.sheet.adversary .adversary-content-container {
display: flex;
align-items: baseline;
}
.daggerheart.sheet.adversary .adversary-statistics-container {
flex: 1;
margin-right: 24px;
display: flex;
flex-direction: column;
gap: 12px;
}
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-title {
flex: 0;
white-space: nowrap;
font-weight: bold;
}
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-row {
display: flex;
align-items: center;
}
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-row .statistic-value {
flex: 0;
white-space: nowrap;
margin-left: 4px;
}
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-row .adversary-roll {
border: 0;
width: 16px;
margin-left: 4px;
align-self: baseline;
transition: transform 0.2s;
}
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-row .adversary-roll:hover {
transform: rotate(30deg);
filter: drop-shadow(0px 0px 3px red);
cursor: pointer;
}
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-resource-container {
display: flex;
align-items: center;
}
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-resource-container label {
min-width: 44px;
}
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-resource-container .statistic-resource-inner-container {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 4px;
}
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-resource-container .resource-title {
align-self: center;
font-weight: bold;
}
.daggerheart.sheet.adversary .adversary-statistics-container .statistic-resource-container .statistic-resource-input {
margin: 0;
flex: 0;
min-width: 16px;
}
.daggerheart.sheet.adversary .adversary-statistics-container .attack-container {
border: 1px solid black dotted;
}
.daggerheart.sheet.adversary .adversary-statistics-container .experience-row {
display: flex;
}
.daggerheart.sheet.adversary .adversary-statistics-container .experience-row * {
flex: 0;
white-space: nowrap;
}
.daggerheart.sheet.adversary .adversary-statistics-container .experience-container i {
margin-left: 4px;
cursor: pointer;
}
.daggerheart.sheet.adversary .adversary-statistics-container .experience-container i:hover {
filter: drop-shadow(0 0 3px red);
}
.daggerheart.sheet.adversary .adversary-statistics-container .experience-chip {
border: 2px solid #708090;
border-radius: 6px;
display: flex;
align-items: center;
padding: 4px;
margin-bottom: 6px;
}
.daggerheart.sheet.adversary .adversary-statistics-container .experience-chip .experience-text {
flex: 1;
}
.daggerheart.sheet.adversary .adversary-statistics-container .experience-chip .experience-value {
flex: 0;
min-width: 26px;
margin: 0 4px;
}
.daggerheart.sheet.adversary .adversary-statistics-container .experience-chip .experience-button {
flex: 0;
border-radius: 50%;
height: 20px;
width: 20px;
display: flex;
align-items: center;
justify-content: center;
padding: 12px;
}
.daggerheart.sheet.adversary .adversary-damage-threshold-container input {
min-width: 26px;
}
.daggerheart.sheet.adversary .adversary-moves-container {
flex: 2.5;
}
.daggerheart.sheet.adversary .adversary-moves-container .moves-title {
text-decoration: underline;
font-weight: bold;
}
.daggerheart.sheet.adversary .adversary-moves-container .move-container {
cursor: pointer;
}
.daggerheart.sheet.adversary .adversary-moves-container .move-container:hover {
background: #2f4f4f40;
}
.daggerheart.sheet.adversary .adversary-moves-container .move-container .moves-name {
font-weight: bold;
text-decoration: none;
}
.daggerheart.sheet.adversary .adversary-moves-container .move-container .move-description p {
margin-top: 0;
}
.daggerheart.sheet.adversary .adversary-moves-container .moves-edit-container i {
margin-left: 4px;
cursor: pointer;
}
.daggerheart.sheet.adversary .adversary-moves-container .moves-edit-container i:hover {
filter: drop-shadow(0 0 3px red);
}
.daggerheart.sheet.adversary .chip-container {
display: flex;
align-items: center;
justify-content: space-between;
background: #778899;
padding: 8px;
border: 2px solid black;
border-radius: 6px;
}
.daggerheart.sheet.adversary .chip-container:not(:last-child) {
margin-bottom: 8px;
}
.daggerheart.sheet.adversary .chip-container .chip-inner-container {
display: flex;
align-items: center;
}
.daggerheart.sheet.adversary .chip-container .chip-inner-container img {
height: 40px;
width: 40px;
margin-right: 8px;
}
.daggerheart.sheet.adversary .chip-container .chip-inner-container .chip-title {
font-size: 22px;
font-weight: bold;
font-style: italic;
}
.daggerheart.sheet.adversary .chip-container button {
height: 40px;
width: 40px;
background: white;
}
.daggerheart.sheet .title-container { .daggerheart.sheet .title-container {
display: flex; display: flex;
gap: 8px; gap: 8px;
@ -3078,6 +2855,31 @@ div.daggerheart.views.multiclass {
#resources:has(.fear-bar) { #resources:has(.fear-bar) {
min-width: 200px; min-width: 200px;
} }
.application.sheet.daggerheart.actor.dh-style.adversary .window-content {
overflow: auto;
}
.daggerheart.sheet.actor.environment .potential-adversary-container {
width: 100%;
height: 50px;
}
.daggerheart.sheet.actor.environment .potential-adversary-container .adversary-placeholder {
font-style: italic;
text-align: center;
opacity: 0.6;
}
.daggerheart.sheet.actor.environment .potential-adversary-container .adversaries-container {
display: flex;
gap: 8px;
}
.daggerheart.sheet.actor.environment .potential-adversary-container .adversaries-container .adversary-container {
border: 1px solid var(--color-dark-5);
border-radius: 6px;
padding: 0 2px;
font-weight: bold;
cursor: pointer;
background-image: url(../assets/parchments/dh-parchment-dark.png);
color: var(--color-light-3);
}
.application.sheet.daggerheart.dh-style.feature .item-sheet-header { .application.sheet.daggerheart.dh-style.feature .item-sheet-header {
display: flex; display: flex;
} }
@ -3354,6 +3156,12 @@ div.daggerheart.views.multiclass {
grid-template-columns: 1fr 2fr; grid-template-columns: 1fr 2fr;
gap: 10px; gap: 10px;
} }
.application.sheet.dh-style fieldset.two-columns.even {
grid-template-columns: 1fr 1fr;
}
.application.sheet.dh-style fieldset.two-columns .full-width {
grid-column: span 2;
}
.application.sheet.dh-style fieldset legend { .application.sheet.dh-style fieldset legend {
font-family: 'Montserrat', sans-serif; font-family: 'Montserrat', sans-serif;
font-weight: bold; font-weight: bold;

View file

@ -13,6 +13,10 @@
@import './resources.less'; @import './resources.less';
// new styles imports // new styles imports
@import './less/actors/character.less';
@import './less/actors/adversary.less';
@import './less/actors/environment.less';
@import './less/items/feature.less'; @import './less/items/feature.less';
@import './less/items/domainCard.less'; @import './less/items/domainCard.less';
@import './less/items/class.less'; @import './less/items/class.less';

View file

@ -0,0 +1,5 @@
.application.sheet.daggerheart.actor.dh-style.adversary {
.window-content {
overflow: auto;
}
}

View file

View file

@ -0,0 +1,27 @@
.daggerheart.sheet.actor.environment {
.potential-adversary-container {
width: 100%;
height: 50px;
.adversary-placeholder {
font-style: italic;
text-align: center;
opacity: 0.6;
}
.adversaries-container {
display: flex;
gap: 8px;
.adversary-container {
border: 1px solid var(--color-dark-5);
border-radius: 6px;
padding: 0 2px;
font-weight: bold;
cursor: pointer;
background-image: url(../assets/parchments/dh-parchment-dark.png);
color: var(--color-light-3);
}
}
}
}

View file

@ -103,6 +103,14 @@
display: grid; display: grid;
grid-template-columns: 1fr 2fr; grid-template-columns: 1fr 2fr;
gap: 10px; gap: 10px;
&.even {
grid-template-columns: 1fr 1fr;
}
.full-width {
grid-column: span 2;
}
} }
legend { legend {

View file

@ -1,280 +0,0 @@
.daggerheart.sheet.adversary {
.adversary-header-container {
position: relative;
background-color: grey;
display: flex;
.adversary-header {
flex: 1;
img {
height: 60px;
width: 60px;
}
.adversary-title {
display: flex;
align-items: center;
text-align: center;
font-size: 28px;
.title-text {
width: 100%;
}
input {
font-size: 28px;
border: 0;
height: 100%;
}
}
}
.adversary-toggle {
position: absolute;
top: 0;
right: 0;
background-color: white;
color: black;
flex: 0;
}
}
.motive-container {
background: lightgrey;
margin-bottom: @fullMargin;
padding-bottom: @fullPadding;
.motive-title {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
.motive-title-base {
font-size: 21px;
}
.motive-title-value {
font-style: italic;
position: relative;
top: 2px;
}
i {
margin-left: 4px;
cursor: pointer;
&:hover {
filter: drop-shadow(0 0 3px red);
}
}
}
}
.adversary-content-container {
display: flex;
align-items: baseline;
}
.adversary-statistics-container {
flex: 1;
margin-right: 24px;
display: flex;
flex-direction: column;
gap: @mediumMargin;
.statistic-title {
flex: 0;
white-space: nowrap;
font-weight: bold;
}
.statistic-row {
display: flex;
align-items: center;
.statistic-value {
flex: 0;
white-space: nowrap;
margin-left: 4px;
}
.adversary-roll {
border: 0;
width: 16px;
margin-left: 4px;
align-self: baseline;
transition: transform 0.2s;
&:hover {
transform: rotate(30deg);
filter: drop-shadow(0px 0px 3px red);
cursor: pointer;
}
}
}
.statistic-resource-container {
display: flex;
align-items: center;
label {
min-width: 44px;
}
.statistic-resource-inner-container {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: @halfMargin;
}
.resource-title {
align-self: center;
font-weight: bold;
}
.statistic-resource-input {
margin: 0;
flex: 0;
min-width: 16px;
}
}
.attack-container {
border: 1px solid black dotted;
}
.experience-row {
display: flex;
* {
flex: 0;
white-space: nowrap;
}
}
.experience-container {
i {
margin-left: 4px;
cursor: pointer;
&:hover {
filter: drop-shadow(0 0 3px red);
}
}
}
.experience-chip {
border: 2px solid @secondaryAccent;
border-radius: 6px;
display: flex;
align-items: center;
padding: 4px;
margin-bottom: 6px;
.experience-text {
flex: 1;
}
.experience-value {
flex: 0;
min-width: @inputSingleMinWidth;
margin: 0 4px;
}
.experience-button {
flex: 0;
border-radius: 50%;
height: 20px;
width: 20px;
display: flex;
align-items: center;
justify-content: center;
padding: 12px;
}
}
}
.adversary-damage-threshold-container {
input {
min-width: @inputSingleMinWidth;
}
}
.adversary-moves-container {
flex: 2.5;
.moves-title {
text-decoration: underline;
font-weight: bold;
}
.move-container {
cursor: pointer;
&:hover {
background: @hoverBackground;
}
.moves-name {
font-weight: bold;
text-decoration: none;
}
.move-description {
p {
margin-top: 0;
}
}
}
.moves-edit-container {
i {
margin-left: 4px;
cursor: pointer;
&:hover {
filter: drop-shadow(0 0 3px red);
}
}
}
}
.chip-container {
display: flex;
align-items: center;
justify-content: space-between;
background: @primaryAccent;
padding: 8px;
border: 2px solid black;
border-radius: 6px;
&:not(:last-child) {
margin-bottom: 8px;
}
.chip-inner-container {
display: flex;
align-items: center;
img {
height: 40px;
width: 40px;
margin-right: 8px;
}
.chip-title {
font-size: 22px;
font-weight: bold;
font-style: italic;
}
}
button {
height: 40px;
width: 40px;
background: white;
}
}
}

View file

@ -1,6 +1,5 @@
@import './heritage.less'; @import './heritage.less';
@import './class.less'; @import './class.less';
@import './adversary.less';
.daggerheart.sheet { .daggerheart.sheet {
.title-container { .title-container {

View file

@ -206,8 +206,12 @@
"character": { "character": {
"htmlFields": ["story", "description", "scars.*.description"] "htmlFields": ["story", "description", "scars.*.description"]
}, },
"adversary": {}, "adversary": {
"environment": {} "htmlFields": ["description", "motivesAndTactics"]
},
"environment": {
"htmlFields": ["description", "impulses"]
}
}, },
"Item": { "Item": {
"ancestry": { "ancestry": {

View file

@ -39,7 +39,7 @@
</div> </div>
{{/if}} {{/if}}
<div class="flexrow"> <div class="flexrow">
<button class="roll-damage-button" data-value="{{this.total.normal}}" data-damage="{{this.damage.value}}" data-damage-type="{{this.damage.type}}" {{#if this.damage.disabled}}disabled{{/if}}><span>Roll Damage</span></button> <button class="duality-action" data-value="{{this.total.normal}}" data-damage="{{this.damage.value}}" data-damage-type="{{this.damage.type}}" {{#if this.damage.disabled}}disabled{{/if}}><span>Roll Damage</span></button>
</div> </div>
</div> </div>
</div> </div>

View file

@ -0,0 +1,9 @@
<header class='item-card-header'>
<img class='profile' src='{{source.img}}' data-action='editImage' data-edit='img' />
<div class='item-info'>
<h1 class='item-name'><input type='text' name='name' value='{{source.name}}' /></h1>
<div class='item-description'>
<h3>{{localize 'TYPES.Actor.adversary'}}</h3>
</div>
</div>
</header>

View file

@ -0,0 +1,17 @@
<section
class='tab {{tabs.information.cssClass}} {{tabs.information.id}}'
data-tab='{{tabs.information.id}}'
data-group='{{tabs.information.group}}'
>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Description" }}</legend>
{{formGroup systemFields.description value=source.system.description}}
</fieldset>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Adversary.MotivesAndTactics" }}</legend>
{{formGroup systemFields.motivesAndTactics value=source.system.motivesAndTactics}}
</fieldset>
</section>

View file

@ -0,0 +1,60 @@
<section
class='tab {{tabs.main.cssClass}} {{tabs.main.id}}'
data-tab='{{tabs.main.id}}'
data-group='{{tabs.main.group}}'
>
<div class="adversary-container">
<fieldset class="two-columns even">
<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>
<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>
<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.max value=source.system.resources.hitPoints.max}}
</fieldset>
<fieldset>
<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.max value=source.system.resources.stress.max}}
</fieldset>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Experiences"}} <a><i class="fa-solid fa-plus icon-button" data-action="addExperience"></i></a></legend>
{{#each source.system.experiences}}
<fieldset class="one-column">
<legend>{{this.name}} <a><i class="fa-solid fa-trash icon-button" data-action="removeExperience" data-experience="{{@key}}"></i></a></legend>
{{formGroup @root.systemFields.experiences.element.fields.name name=(concat "system.experiences." @key ".name") value=this.name }}
{{formGroup @root.systemFields.experiences.element.fields.value name=(concat "system.experiences." @key ".value") value=this.value }}
</fieldset>
{{/each}}
</fieldset>
</fieldset>
<fieldset class="two-columns even">
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Attack"}}</legend>
{{formGroup systemFields.attack.fields.name value=source.system.attack.name}}
<button data-action="attackRoll">Attack</button>
{{formGroup systemFields.attack.fields.modifier value=source.system.attack.modifier}}
{{formGroup systemFields.attack.fields.range value=source.system.attack.range localize=true}}
{{formGroup systemFields.attack.fields.damage.fields.value value=source.system.attack.damage.value}}
{{formGroup systemFields.attack.fields.damage.fields.type value=source.system.attack.damage.type localize=true}}
</fieldset>
</div>
</section>

View file

@ -0,0 +1,9 @@
<header class='item-card-header'>
<img class='profile' src='{{source.img}}' data-action='editImage' data-edit='img' />
<div class='item-info'>
<h1 class='item-name'><input type='text' name='name' value='{{source.name}}' /></h1>
<div class='item-description'>
<h3>{{localize 'TYPES.Actor.environment'}}</h3>
</div>
</div>
</header>

View file

@ -0,0 +1,16 @@
<section
class='tab {{tabs.information.cssClass}} {{tabs.information.id}}'
data-tab='{{tabs.information.id}}'
data-group='{{tabs.information.group}}'
>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Environment.description"}}</legend>
{{formInput systemFields.description value=source.system.description }}
</fieldset>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Environment.impulses"}}</legend>
{{formInput systemFields.impulses value=source.system.impulses }}
</fieldset>
</section>

View file

@ -0,0 +1,37 @@
<section
class='tab {{tabs.main.cssClass}} {{tabs.main.id}}'
data-tab='{{tabs.main.id}}'
data-group='{{tabs.main.group}}'
>
<fieldset class="two-columns even">
<legend>{{localize "DAGGERHEART.Sheets.Environment.general"}}</legend>
{{formGroup systemFields.tier value=source.system.tier localize=true }}
{{formGroup systemFields.type value=source.system.type localize=true }}
{{formGroup systemFields.difficulty value=source.system.difficulty localize=true }}
</fieldset>
<fieldset class="one-column">
<legend>{{localize "DAGGERHEART.Sheets.Environment.potentialAdversaries.label"}}<a><i class="fa-solid fa-plus icon-button" data-action="addAdversary"></i></a></legend>
{{#each source.system.potentialAdversaries}}
<fieldset class="potential-adversary-container" data-potential-adversary="{{@key}}">
<legend><input name="{{concat "system.potentialAdversaries." id ".label" }}" value="{{this.label}}" /><i class="fa-solid fa-trash" data-action="deleteProperty" data-path="system.potentialAdversaries" id={{@key}}></i></legend>
{{#if (eq (length this.adversaries) 0)}}
<div class="adversary-placeholder">{{localize "DAGGERHEART.Sheets.Environment.potentialAdversaries.placeholder"}}</div>
{{else}}
<div class="adversaries-container">
{{#each this.adversaries as |adversary id|}}
<div class="adversary-container" data-action="viewAdversary" data-potential-adversary="{{@../key}}" data-adversary="{{id}}">{{adversary.name}}</div>
{{/each}}
</div>
{{/if}}
</fieldset>
{{/each}}
</fieldset>
<fieldset class="one-column">
<legend>{{localize "DAGGERHEART.Sheets.Environment.features.label"}}<a><i class="fa-solid fa-plus icon-button" data-action="addFeature"></i></a></legend>
</fieldset>
</section>