From ad0acd62cdce8e2a151737e62eb8be955a15316f Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 9 Jun 2025 13:25:43 +0200
Subject: [PATCH 1/2] Fixed up data model and a basic placeholder template
(#117)
---
lang/en.json | 56 +++++-
module/applications/sheets/environment.mjs | 165 +++++++-----------
module/config/actorConfig.mjs | 19 ++
module/config/generalConfig.mjs | 24 ++-
module/data/adversary.mjs | 5 +-
module/data/environment.mjs | 36 ++--
module/helpers/handlebarsHelper.mjs | 5 +
styles/daggerheart.css | 25 +++
styles/less/global/elements.less | 4 +
styles/sheets/environment.less | 27 +++
styles/sheets/sheets.less | 1 +
system.json | 16 +-
.../sheets/actors/environment/header.hbs | 9 +
.../sheets/actors/environment/information.hbs | 16 ++
templates/sheets/actors/environment/main.hbs | 37 ++++
15 files changed, 300 insertions(+), 145 deletions(-)
create mode 100644 styles/sheets/environment.less
create mode 100644 templates/sheets/actors/environment/header.hbs
create mode 100644 templates/sheets/actors/environment/information.hbs
create mode 100644 templates/sheets/actors/environment/main.hbs
diff --git a/lang/en.json b/lang/en.json
index eb5ecd62..26cedccb 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -261,6 +261,12 @@
"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": {
"Bruiser": {
"Name": "Bruiser",
@@ -320,6 +326,26 @@
}
}
},
+ "Environment": {
+ "Type": {
+ "Exploration": {
+ "label": "Exploration",
+ "description": ""
+ },
+ "Social": {
+ "label": "Social",
+ "description": ""
+ },
+ "Traversal": {
+ "label": "Traversal",
+ "description": ""
+ },
+ "Event": {
+ "label": "Event",
+ "description": ""
+ }
+ }
+ },
"Domains": {
"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."
@@ -1030,9 +1056,33 @@
"NewFeature": "New Feature"
},
"Environment": {
- "ToneAndFeel": "Tone And feel",
- "PotentialAdversaries": "Potential Adversaries",
- "NewFeature": "New Feature"
+ "FIELDS": {
+ "tier": {
+ "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": {
"baseScore": "Base Score",
diff --git a/module/applications/sheets/environment.mjs b/module/applications/sheets/environment.mjs
index 8799d41a..7c59fd55 100644
--- a/module/applications/sheets/environment.mjs
+++ b/module/applications/sheets/environment.mjs
@@ -1,78 +1,60 @@
import DaggerheartSheet from './daggerheart-sheet.mjs';
-const { DocumentSheetV2 } = foundry.applications.api;
-export default class DhpEnvironment extends DaggerheartSheet(DocumentSheetV2) {
- constructor(options) {
- super(options);
-
- this.editMode = false;
- }
-
+const { ActorSheetV2 } = foundry.applications.sheets;
+export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) {
static DEFAULT_OPTIONS = {
tag: 'form',
- classes: ['daggerheart', 'sheet', 'adversary', 'environment'],
+ classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'environment'],
position: {
- width: 600,
- height: 'auto'
+ width: 450,
+ height: 1000
},
actions: {
- toggleSlider: this.toggleSlider,
- viewFeature: this.viewFeature,
+ addAdversary: this.addAdversary,
addFeature: this.addFeature,
- removeFeature: this.removeFeature,
- addTone: this.addTone,
- removeTone: this.removeTone,
- useFeature: this.useFeature
+ deleteProperty: this.deleteProperty,
+ viewAdversary: this.viewAdversary
},
form: {
handler: this._updateForm,
- closeOnSubmit: false,
- submitOnChange: true
- }
+ submitOnChange: true,
+ closeOnSubmit: false
+ },
+ dragDrop: [{ dragSelector: null, dropSelector: '.adversary-container' }]
};
- /** @override */
static PARTS = {
- form: {
- id: 'form',
- template: 'systems/daggerheart/templates/sheets/environment.hbs'
- }
+ header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' },
+ tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
+ main: { template: 'systems/daggerheart/templates/sheets/actors/environment/main.hbs' },
+ information: { template: 'systems/daggerheart/templates/sheets/actors/environment/information.hbs' }
};
- /* -------------------------------------------- */
-
- /** @inheritDoc */
- get title() {
- return `${game.i18n.localize('Environment')} - ${this.document.name}`;
- }
+ static TABS = {
+ main: {
+ active: true,
+ cssClass: '',
+ 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) {
- return {
- title: `${this.document.name} - ${game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.document.system.type].name)}`,
- user: this.document,
- 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
- });
- }
+ const context = await super._prepareContext(_options);
+ context.document = this.document;
+ context.tabs = super._getTabs(this.constructor.TABS);
- return acc;
- }, [])
- },
- editMode: this.editMode,
- config: SYSTEM
- };
+ return context;
}
static async _updateForm(event, _, formData) {
@@ -80,60 +62,41 @@ export default class DhpEnvironment extends DaggerheartSheet(DocumentSheetV2) {
this.render();
}
- static toggleSlider() {
- this.editMode = !this.editMode;
+ static async addAdversary() {
+ await this.document.update({
+ [`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
+ 'DAGGERHEART.Sheets.Environment.newAdversary'
+ )
+ });
this.render();
}
- static async viewFeature(_, button) {
- const move = await fromUuid(button.dataset.feature);
- move.sheet.render(true);
- }
-
static async addFeature() {
- const result = await this.document.createEmbeddedDocuments('Item', [
- {
- name: game.i18n.localize('DAGGERHEART.Sheets.Environment.NewFeature'),
- type: 'feature'
- }
- ]);
-
- await result[0].sheet.render(true);
+ ui.notifications.error('Not Implemented yet. Awaiting datamodel rework');
}
- static async removeFeature(_, button) {
- await this.document.items.find(x => x.uuid === button.dataset.feature).delete();
+ static async deleteProperty(_, target) {
+ await this.document.update({ [`${target.dataset.path}.-=${target.id}`]: null });
+ this.render();
}
- static async addTone() {
- await this.document.update({ 'system.toneAndFeel': [...this.document.system.toneAndFeel, ''] });
+ static async viewAdversary(_, button) {
+ const adversary = foundry.utils.getProperty(
+ this.document.system.potentialAdversaries,
+ `${button.dataset.potentialAdversary}.adversaries.${button.dataset.adversary}`
+ );
+ adversary.sheet.render(true);
}
- static async removeTone(button) {
- await this.document.update({
- 'system.toneAndFeel': this.document.system.toneAndFeel.filter(
- (_, index) => index !== Number.parseInt(button.dataset.tone)
- )
- });
- }
-
- 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());
+ async _onDrop(event) {
+ const data = TextEditor.getDragEventData(event);
+ const item = await fromUuid(data.uuid);
+ 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
+ });
+ }
}
}
diff --git a/module/config/actorConfig.mjs b/module/config/actorConfig.mjs
index 4db5ca9c..bcb6ee8b 100644
--- a/module/config/actorConfig.mjs
+++ b/module/config/actorConfig.mjs
@@ -123,6 +123,25 @@ export const adversaryTypes = {
}
};
+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 = {
relentless: {
name: 'DAGGERHEART.Adversary.Trait..Name',
diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs
index fb596347..47736284 100644
--- a/module/config/generalConfig.mjs
+++ b/module/config/generalConfig.mjs
@@ -175,25 +175,21 @@ export const deathMoves = {
};
export const tiers = {
- 0: {
- key: 0,
- id: 'tier0',
- name: 'DAGGERHEART.General.Tier.0'
- },
- 1: {
- key: 1,
+ tier1: {
id: 'tier1',
- name: 'DAGGERHEART.General.Tier.1'
+ label: 'DAGGERHEART.Tiers.tier1'
},
- 2: {
- key: 2,
+ tier2: {
id: 'tier2',
- name: 'DAGGERHEART.General.Tier.2'
+ label: 'DAGGERHEART.Tiers.tier2'
},
- 3: {
- key: 3,
+ tier3: {
id: 'tier3',
- name: 'DAGGERHEART.General.Tier.3'
+ label: 'DAGGERHEART.Tiers.tier3'
+ },
+ tier4: {
+ id: 'tier4',
+ label: 'DAGGERHEART.Tiers.tier4'
}
};
diff --git a/module/data/adversary.mjs b/module/data/adversary.mjs
index 3e8cdaf6..0f25b62d 100644
--- a/module/data/adversary.mjs
+++ b/module/data/adversary.mjs
@@ -14,7 +14,10 @@ export default class DhpAdversary extends foundry.abstract.TypeDataModel {
max: new fields.NumberField({ initial: 0, integer: true })
})
}),
- tier: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.tiers), integer: false }),
+ tier: new fields.StringField({
+ choices: Object.keys(SYSTEM.GENERAL.tiers),
+ initial: SYSTEM.GENERAL.tiers.tier1.id
+ }),
type: new fields.StringField({
choices: Object.keys(SYSTEM.ACTOR.adversaryTypes),
integer: false,
diff --git a/module/data/environment.mjs b/module/data/environment.mjs
index 23e9cd25..a2cd7529 100644
--- a/module/data/environment.mjs
+++ b/module/data/environment.mjs
@@ -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() {
const fields = foundry.data.fields;
return {
- resources: new fields.SchemaField({}),
- tier: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.tiers), integer: false }),
- type: new fields.StringField({
- choices: Object.keys(SYSTEM.ACTOR.adversaryTypes),
- integer: false,
- initial: Object.keys(SYSTEM.ACTOR.adversaryTypes).find(x => x === 'standard')
+ tier: new fields.StringField({
+ required: true,
+ choices: SYSTEM.GENERAL.tiers,
+ initial: SYSTEM.GENERAL.tiers.tier1.id
}),
- description: new fields.StringField({}),
- toneAndFeel: new fields.StringField({}),
- difficulty: new fields.NumberField({ initial: 1, integer: true }),
- potentialAdversaries: new fields.StringField({})
+ type: new fields.StringField({ choices: environmentTypes }),
+ description: new fields.HTMLField(),
+ impulses: new fields.HTMLField(),
+ 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');
- }
}
diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs
index 87d1fb7f..25dd0e5e 100644
--- a/module/helpers/handlebarsHelper.mjs
+++ b/module/helpers/handlebarsHelper.mjs
@@ -11,6 +11,7 @@ export default class RegisterHandlebarsHelpers {
includes: this.includes,
debug: this.debug,
signedNumber: this.signedNumber,
+ length: this.length,
switch: this.switch,
case: this.case
});
@@ -82,6 +83,10 @@ export default class RegisterHandlebarsHelpers {
return number >= 0 ? `+${number}` : number;
}
+ static length(obj) {
+ return Object.keys(obj).length;
+ }
+
static switch(value, options) {
this.switch_value = value;
this.switch_break = false;
diff --git a/styles/daggerheart.css b/styles/daggerheart.css
index 78af5ecc..cb4ede19 100755
--- a/styles/daggerheart.css
+++ b/styles/daggerheart.css
@@ -2593,6 +2593,28 @@ div.daggerheart.views.multiclass {
width: 40px;
background: white;
}
+.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);
+}
.daggerheart.sheet .title-container {
display: flex;
gap: 8px;
@@ -3354,6 +3376,9 @@ div.daggerheart.views.multiclass {
grid-template-columns: 1fr 2fr;
gap: 10px;
}
+.application.sheet.dh-style fieldset.two-columns.even {
+ grid-template-columns: 1fr 1fr;
+}
.application.sheet.dh-style fieldset legend {
font-family: 'Montserrat', sans-serif;
font-weight: bold;
diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less
index 077d2226..14345ca6 100755
--- a/styles/less/global/elements.less
+++ b/styles/less/global/elements.less
@@ -103,6 +103,10 @@
display: grid;
grid-template-columns: 1fr 2fr;
gap: 10px;
+
+ &.even {
+ grid-template-columns: 1fr 1fr;
+ }
}
legend {
diff --git a/styles/sheets/environment.less b/styles/sheets/environment.less
new file mode 100644
index 00000000..d534de38
--- /dev/null
+++ b/styles/sheets/environment.less
@@ -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);
+ }
+ }
+ }
+}
diff --git a/styles/sheets/sheets.less b/styles/sheets/sheets.less
index 19c76980..3ce23be8 100644
--- a/styles/sheets/sheets.less
+++ b/styles/sheets/sheets.less
@@ -1,6 +1,7 @@
@import './heritage.less';
@import './class.less';
@import './adversary.less';
+@import './environment.less';
.daggerheart.sheet {
.title-container {
diff --git a/system.json b/system.json
index f90f53c8..1717e6e6 100644
--- a/system.json
+++ b/system.json
@@ -163,10 +163,7 @@
"name": "Daggerheart",
"sorting": "m",
"color": "#08718c",
- "packs": [
- "adversaries",
- "environments"
- ],
+ "packs": ["adversaries", "environments"],
"folders": [
{
"name": "Character Options",
@@ -186,12 +183,7 @@
"name": "Items",
"sorting": "m",
"color": "#000000",
- "packs": [
- "weapons",
- "armors",
- "consumables",
- "general-items"
- ]
+ "packs": ["weapons", "armors", "consumables", "general-items"]
}
]
}
@@ -213,7 +205,9 @@
"Actor": {
"pc": {},
"adversary": {},
- "environment": {}
+ "environment": {
+ "htmlFields": ["description", "impulses"]
+ }
},
"Item": {
"ancestry": {
diff --git a/templates/sheets/actors/environment/header.hbs b/templates/sheets/actors/environment/header.hbs
new file mode 100644
index 00000000..a21f5f64
--- /dev/null
+++ b/templates/sheets/actors/environment/header.hbs
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/templates/sheets/actors/environment/information.hbs b/templates/sheets/actors/environment/information.hbs
new file mode 100644
index 00000000..3b49a933
--- /dev/null
+++ b/templates/sheets/actors/environment/information.hbs
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/templates/sheets/actors/environment/main.hbs b/templates/sheets/actors/environment/main.hbs
new file mode 100644
index 00000000..a973cdc9
--- /dev/null
+++ b/templates/sheets/actors/environment/main.hbs
@@ -0,0 +1,37 @@
+
+
+ {{localize "DAGGERHEART.Sheets.Environment.general"}}
+
+ {{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 }}
+
+
+
+ {{localize "DAGGERHEART.Sheets.Environment.potentialAdversaries.label"}}
+
+ {{#each source.system.potentialAdversaries}}
+
+
+ {{#if (eq (length this.adversaries) 0)}}
+ {{localize "DAGGERHEART.Sheets.Environment.potentialAdversaries.placeholder"}}
+ {{else}}
+
+ {{#each this.adversaries as |adversary id|}}
+
{{adversary.name}}
+ {{/each}}
+
+ {{/if}}
+
+ {{/each}}
+
+
+
+ {{localize "DAGGERHEART.Sheets.Environment.features.label"}}
+
+
+
\ No newline at end of file
From 02f16f7363fa3d2d360e9a96ea3d355c90977e31 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 9 Jun 2025 13:33:33 +0200
Subject: [PATCH 2/2] 118 - adversary data model (#119)
* Fixed datamodel and set up basic template in new style
* Added in a temp attack button, because why not
* Restored HitPoints counting up
---
lang/en.json | 145 ++++----
module/applications/chatMessage.mjs | 6 +-
module/applications/sheets/adversary.mjs | 322 ++----------------
module/config/actorConfig.mjs | 34 +-
module/config/generalConfig.mjs | 5 +
module/data/adversary.mjs | 71 ++--
styles/daggerheart.css | 273 ++-------------
styles/daggerheart.less | 3 +
styles/less/actors/adversary.less | 5 +
.../{sheets => less/actors}/environment.less | 0
styles/less/global/elements.less | 4 +
styles/sheets/adversary.less | 280 ---------------
styles/sheets/sheets.less | 2 -
system.json | 4 +-
templates/chat/adversary-attack-roll.hbs | 2 +-
templates/sheets/actors/adversary/header.hbs | 9 +
.../sheets/actors/adversary/information.hbs | 17 +
templates/sheets/actors/adversary/main.hbs | 60 ++++
18 files changed, 307 insertions(+), 935 deletions(-)
create mode 100644 styles/less/actors/adversary.less
rename styles/{sheets => less/actors}/environment.less (100%)
delete mode 100644 styles/sheets/adversary.less
create mode 100644 templates/sheets/actors/adversary/header.hbs
create mode 100644 templates/sheets/actors/adversary/information.hbs
create mode 100644 templates/sheets/actors/adversary/main.hbs
diff --git a/lang/en.json b/lang/en.json
index 26cedccb..78532b49 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -268,45 +268,47 @@
"tier4": "Tier 4"
},
"Adversary": {
- "Bruiser": {
- "Name": "Bruiser",
- "Description": "Tough adversaries with powerful attacks."
- },
- "Horde": {
- "Name": "Horde",
- "Description": "A Horde represents a number of foes working in a group."
- },
- "Leader": {
- "Name": "Leader",
- "Description": "Adversaries that command and summon other adversaries."
- },
- "Minion": {
- "Name": "Minion",
- "Description": "Basic enemies that are easily dispatched but dangerous in numbers."
- },
- "Ranged": {
- "Name": "Ranged",
- "Description": "Adversaries that attack from a distance."
- },
- "Skulker": {
- "Name": "Skulker",
- "Description": "Adversaries that maneuver and exploit opportunities to ambush their opponents."
- },
- "Social": {
- "Name": "Social",
- "Description": "Adversaries that are primarily interpersonal threats or challenges."
- },
- "Solo": {
- "Name": "Solo",
- "Description": "Designed to present a challenge to a whole party."
- },
- "Standard": {
- "Name": "Standard",
- "Description": "Rank and File adversaries."
- },
- "Support": {
- "Name": "Support",
- "Description": "Enemies that enhance their allies and/or disrupt their opponents."
+ "Type": {
+ "Bruiser": {
+ "label": "Bruiser",
+ "Description": "Tough adversaries with powerful attacks."
+ },
+ "Horde": {
+ "label": "Horde",
+ "Description": "A Horde represents a number of foes working in a group."
+ },
+ "Leader": {
+ "label": "Leader",
+ "Description": "Adversaries that command and summon other adversaries."
+ },
+ "Minion": {
+ "label": "Minion",
+ "Description": "Basic enemies that are easily dispatched but dangerous in numbers."
+ },
+ "Ranged": {
+ "label": "Ranged",
+ "Description": "Adversaries that attack from a distance."
+ },
+ "Skulk": {
+ "label": "Skulk",
+ "Description": "Adversaries that maneuver and exploit opportunities to ambush their opponents."
+ },
+ "Social": {
+ "label": "Social",
+ "Description": "Adversaries that are primarily interpersonal threats or challenges."
+ },
+ "Solo": {
+ "label": "Solo",
+ "Description": "Designed to present a challenge to a whole party."
+ },
+ "Standard": {
+ "label": "Standard",
+ "Description": "Rank and File adversaries."
+ },
+ "Support": {
+ "label": "Support",
+ "Description": "Enemies that enhance their allies and/or disrupt their opponents."
+ }
},
"Trait": {
"Relentless": {
@@ -1025,35 +1027,52 @@
}
},
"Adversary": {
- "Description": "Description",
- "MotivesAndTactics": "Motives & Tactics",
- "Tier": "Tier",
- "Type": "Type",
- "Attack": {
- "Title": "Attack",
- "Modifier": "Attack Modifier",
- "Name": "Name",
- "Range": "Range",
- "Damage": {
- "Title": "Damage",
- "Value": "Value",
- "Type": "Type"
+ "FIELDS": {
+ "tier": { "label": "Tier" },
+ "type": { "label": "Type" },
+ "description": { "label": "Description" },
+ "motivesAndTactics": { "label": "Motives & Tactics" },
+ "difficulty": { "label": "Difficulty" },
+ "damageThresholds": {
+ "major": { "label": "Major" },
+ "severe": { "label": "Severe" }
+ },
+ "resources": {
+ "hitPoints": {
+ "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",
- "Reaction": "Reaction Roll",
- "DamageThresholds": {
- "Title": "Damage Thresholds",
- "Minor": "Minor",
- "Major": "Major",
- "Severe": "Severe"
+ "Tabs": {
+ "Main": "Data",
+ "Information": "Information"
},
- "HP": "HP",
+ "General": "General",
+ "DamageThresholds": "Damage Thresholds",
+ "HitPoints": "Hit Points",
"Stress": "Stress",
- "Experience": "Experience",
"Experiences": "Experiences",
- "Features": "Features",
- "NewFeature": "New Feature"
+ "Attack": "Attack"
},
"Environment": {
"FIELDS": {
diff --git a/module/applications/chatMessage.mjs b/module/applications/chatMessage.mjs
index 30bd0a27..aef05bae 100644
--- a/module/applications/chatMessage.mjs
+++ b/module/applications/chatMessage.mjs
@@ -1,12 +1,8 @@
import { DualityRollColor } from '../data/settings/Appearance.mjs';
-import DHDualityRoll from "../data/chat-message/dualityRoll.mjs";
+import DHDualityRoll from '../data/chat-message/dualityRoll.mjs';
export default class DhpChatMessage extends foundry.documents.ChatMessage {
async renderHTML() {
- if (this.type === 'dualityRoll' || this.type === 'adversaryRoll' || this.type === 'abilityUse') {
- this.content = await foundry.applications.handlebars.renderTemplate(this.content, this.system);
- }
-
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
const html = await super.renderHTML();
diff --git a/module/applications/sheets/adversary.mjs b/module/applications/sheets/adversary.mjs
index b9180bf3..8345b532 100644
--- a/module/applications/sheets/adversary.mjs
+++ b/module/applications/sheets/adversary.mjs
@@ -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';
const { ActorSheetV2 } = foundry.applications.sheets;
export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
- constructor(options = {}) {
- super(options);
-
- this.editMode = false;
- }
-
static DEFAULT_OPTIONS = {
tag: 'form',
- classes: ['daggerheart', 'sheet', 'adversary'],
- position: { width: 600 },
+ classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'adversary'],
+ position: { width: 450, height: 1000 },
actions: {
- viewMove: this.viewMove,
- addMove: this.addMove,
- removeMove: this.removeMove,
- toggleSlider: this.toggleEditMode,
- addMotive: this.addMotive,
- removeMotive: this.removeMotive,
reactionRoll: this.reactionRoll,
attackRoll: this.attackRoll,
addExperience: this.addExperience,
@@ -229,54 +22,35 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
};
static PARTS = {
- form: {
- id: 'feature',
- template: 'systems/daggerheart/templates/sheets/adversary.hbs'
+ header: { template: 'systems/daggerheart/templates/sheets/actors/adversary/header.hbs' },
+ tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.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) {
const context = await super._prepareContext(_options);
context.document = this.document;
- 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.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
- };
+ context.tabs = super._getTabs(this.constructor.TABS);
return context;
}
@@ -286,43 +60,6 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
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) {
const { roll, diceResults, modifiers } = await this.actor.diceRoll(
{ title: `${this.actor.name} - Reaction Roll`, value: 0 },
@@ -349,9 +86,8 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
cls.create(msg.toObject());
}
- static async attackRoll(event, button) {
- const modifier = Number.parseInt(button.dataset.value);
-
+ static async attackRoll() {
+ const { modifier, damage, name: attackName } = this.actor.system.attack;
const { roll, dice, advantageState, modifiers } = await this.actor.diceRoll(
{ title: `${this.actor.name} - Attack Roll`, value: modifier },
event.shiftKey
@@ -367,7 +103,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
const cls = getDocumentClass('ChatMessage');
const systemData = {
- title: button.dataset.name,
+ title: attackName,
origin: this.document.id,
roll: roll._formula,
advantageState,
@@ -375,7 +111,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
modifiers: modifiers,
dice: dice,
targets: targets,
- damage: { value: button.dataset.damage, type: button.dataset.damageType }
+ damage: { value: damage.value, type: damage.type }
};
const msg = new cls({
type: 'adversaryRoll',
diff --git a/module/config/actorConfig.mjs b/module/config/actorConfig.mjs
index bcb6ee8b..dcd85cdb 100644
--- a/module/config/actorConfig.mjs
+++ b/module/config/actorConfig.mjs
@@ -82,43 +82,53 @@ export const featureProperties = {
export const adversaryTypes = {
bruiser: {
- name: 'DAGGERHEART.Adversary.Bruiser.Name',
+ id: 'bruiser',
+ label: 'DAGGERHEART.Adversary.Type.Bruiser.label',
description: 'DAGGERHEART.Adversary.Bruiser.Description'
},
horde: {
- name: 'DAGGERHEART.Adversary.Horde.Name',
+ id: 'horde',
+ label: 'DAGGERHEART.Adversary.Type.Horde.label',
description: 'DAGGERHEART.Adversary.Horde.Description'
},
leader: {
- name: 'DAGGERHEART.Adversary.Leader.Name',
+ id: 'leader',
+ label: 'DAGGERHEART.Adversary.Type.Leader.label',
description: 'DAGGERHEART.Adversary.Leader.Description'
},
minion: {
- name: 'DAGGERHEART.Adversary.Minion.Name',
+ id: 'minion',
+ label: 'DAGGERHEART.Adversary.Type.Minion.label',
description: 'DAGGERHEART.Adversary.Minion.Description'
},
ranged: {
- name: 'DAGGERHEART.Adversary.Ranged.Name',
+ id: 'ranged',
+ label: 'DAGGERHEART.Adversary.Type.Ranged.label',
description: 'DAGGERHEART.Adversary.Ranged.Description'
},
- skulker: {
- name: 'DAGGERHEART.Adversary.Skulker.Name',
- description: 'DAGGERHEART.Adversary.Skulker.Description'
+ skulk: {
+ id: 'skulk',
+ label: 'DAGGERHEART.Adversary.Type.Skulk.label',
+ description: 'DAGGERHEART.Adversary.Skulk.Description'
},
social: {
- name: 'DAGGERHEART.Adversary.Social.Name',
+ id: 'social',
+ label: 'DAGGERHEART.Adversary.Type.Social.label',
description: 'DAGGERHEART.Adversary.Social.Description'
},
solo: {
- name: 'DAGGERHEART.Adversary.Solo.Name',
+ id: 'solo',
+ label: 'DAGGERHEART.Adversary.Type.Solo.label',
description: 'DAGGERHEART.Adversary.Solo.Description'
},
standard: {
- name: 'DAGGERHEART.Adversary.Standard.Name',
+ id: 'standard',
+ label: 'DAGGERHEART.Adversary.Type.Standard.label',
description: 'DAGGERHEART.Adversary.Standard.Description'
},
support: {
- name: 'DAGGERHEART.Adversary.Support.Name',
+ id: 'support',
+ label: 'DAGGERHEART.Adversary.Type.Support.label',
description: 'DAGGERHEART.Adversary.Support.Description'
}
};
diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs
index 47736284..fecb939e 100644
--- a/module/config/generalConfig.mjs
+++ b/module/config/generalConfig.mjs
@@ -1,25 +1,30 @@
export const range = {
melee: {
+ id: 'melee',
label: 'DAGGERHEART.Range.melee.name',
description: 'DAGGERHEART.Range.melee.description',
distance: 1
},
veryClose: {
+ id: 'veryClose',
label: 'DAGGERHEART.Range.veryClose.name',
description: 'DAGGERHEART.Range.veryClose.description',
distance: 3
},
close: {
+ id: 'close',
label: 'DAGGERHEART.Range.close.name',
description: 'DAGGERHEART.Range.close.description',
distance: 10
},
far: {
+ id: 'far',
label: 'DAGGERHEART.Range.far.name',
description: 'DAGGERHEART.Range.far.description',
distance: 20
},
veryFar: {
+ id: 'veryFar',
label: 'DAGGERHEART.Range.veryFar.name',
description: 'DAGGERHEART.Range.veryFar.description',
distance: 30
diff --git a/module/data/adversary.mjs b/module/data/adversary.mjs
index 0f25b62d..7e4c28f1 100644
--- a/module/data/adversary.mjs
+++ b/module/data/adversary.mjs
@@ -1,55 +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 {
+ static LOCALIZATION_PREFIXES = ['DAGGERHEART.Sheets.Adversary'];
+
static defineSchema() {
const fields = foundry.data.fields;
return {
- resources: new fields.SchemaField({
- health: 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 })
- }),
- 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),
+ required: true,
+ choices: SYSTEM.GENERAL.tiers,
initial: SYSTEM.GENERAL.tiers.tier1.id
}),
type: new fields.StringField({
- choices: Object.keys(SYSTEM.ACTOR.adversaryTypes),
- integer: false,
- initial: Object.keys(SYSTEM.ACTOR.adversaryTypes).find(x => x === 'standard')
+ required: true,
+ choices: SYSTEM.ACTOR.adversaryTypes,
+ 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({
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({
- value: new fields.StringField({}),
- type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false })
+ value: new fields.StringField(),
+ 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(
new fields.SchemaField({
- id: new fields.StringField({ required: true }),
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');
- }
}
diff --git a/styles/daggerheart.css b/styles/daggerheart.css
index cb4ede19..96b949fc 100755
--- a/styles/daggerheart.css
+++ b/styles/daggerheart.css
@@ -2370,251 +2370,6 @@ div.daggerheart.views.multiclass {
align-items: center;
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.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);
-}
.daggerheart.sheet .title-container {
display: flex;
gap: 8px;
@@ -3100,6 +2855,31 @@ div.daggerheart.views.multiclass {
#resources:has(.fear-bar) {
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 {
display: flex;
}
@@ -3379,6 +3159,9 @@ div.daggerheart.views.multiclass {
.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 {
font-family: 'Montserrat', sans-serif;
font-weight: bold;
diff --git a/styles/daggerheart.less b/styles/daggerheart.less
index 6869e316..8ec94054 100755
--- a/styles/daggerheart.less
+++ b/styles/daggerheart.less
@@ -13,6 +13,9 @@
@import './resources.less';
// new styles imports
+@import './less/actors/adversary.less';
+@import './less/actors/environment.less';
+
@import './less/items/feature.less';
@import './less/items/domainCard.less';
@import './less/items/class.less';
diff --git a/styles/less/actors/adversary.less b/styles/less/actors/adversary.less
new file mode 100644
index 00000000..5b4feb26
--- /dev/null
+++ b/styles/less/actors/adversary.less
@@ -0,0 +1,5 @@
+.application.sheet.daggerheart.actor.dh-style.adversary {
+ .window-content {
+ overflow: auto;
+ }
+}
diff --git a/styles/sheets/environment.less b/styles/less/actors/environment.less
similarity index 100%
rename from styles/sheets/environment.less
rename to styles/less/actors/environment.less
diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less
index 14345ca6..797b2328 100755
--- a/styles/less/global/elements.less
+++ b/styles/less/global/elements.less
@@ -107,6 +107,10 @@
&.even {
grid-template-columns: 1fr 1fr;
}
+
+ .full-width {
+ grid-column: span 2;
+ }
}
legend {
diff --git a/styles/sheets/adversary.less b/styles/sheets/adversary.less
deleted file mode 100644
index 657ce218..00000000
--- a/styles/sheets/adversary.less
+++ /dev/null
@@ -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;
- }
- }
-}
diff --git a/styles/sheets/sheets.less b/styles/sheets/sheets.less
index 3ce23be8..5a49a010 100644
--- a/styles/sheets/sheets.less
+++ b/styles/sheets/sheets.less
@@ -1,7 +1,5 @@
@import './heritage.less';
@import './class.less';
-@import './adversary.less';
-@import './environment.less';
.daggerheart.sheet {
.title-container {
diff --git a/system.json b/system.json
index 1717e6e6..c402b837 100644
--- a/system.json
+++ b/system.json
@@ -204,7 +204,9 @@
"documentTypes": {
"Actor": {
"pc": {},
- "adversary": {},
+ "adversary": {
+ "htmlFields": ["description", "motivesAndTactics"]
+ },
"environment": {
"htmlFields": ["description", "impulses"]
}
diff --git a/templates/chat/adversary-attack-roll.hbs b/templates/chat/adversary-attack-roll.hbs
index 3e805de9..d7cd9ecc 100644
--- a/templates/chat/adversary-attack-roll.hbs
+++ b/templates/chat/adversary-attack-roll.hbs
@@ -39,7 +39,7 @@
{{/if}}
- Roll Damage
+ Roll Damage
\ No newline at end of file
diff --git a/templates/sheets/actors/adversary/header.hbs b/templates/sheets/actors/adversary/header.hbs
new file mode 100644
index 00000000..e3914c32
--- /dev/null
+++ b/templates/sheets/actors/adversary/header.hbs
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/templates/sheets/actors/adversary/information.hbs b/templates/sheets/actors/adversary/information.hbs
new file mode 100644
index 00000000..6d6efb21
--- /dev/null
+++ b/templates/sheets/actors/adversary/information.hbs
@@ -0,0 +1,17 @@
+
diff --git a/templates/sheets/actors/adversary/main.hbs b/templates/sheets/actors/adversary/main.hbs
new file mode 100644
index 00000000..788af5ea
--- /dev/null
+++ b/templates/sheets/actors/adversary/main.hbs
@@ -0,0 +1,60 @@
+
+
+
+ {{localize "DAGGERHEART.Sheets.Adversary.General"}}
+
+ {{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 "DAGGERHEART.Sheets.Adversary.DamageThresholds"}}
+
+ {{formGroup systemFields.damageThresholds.fields.major value=source.system.damageThresholds.major}}
+ {{formGroup systemFields.damageThresholds.fields.severe value=source.system.damageThresholds.severe}}
+
+
+
+ {{localize "DAGGERHEART.Sheets.Adversary.HitPoints"}}
+
+ {{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}}
+
+
+
+ {{localize "DAGGERHEART.Sheets.Adversary.Stress"}}
+
+ {{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}}
+
+
+
+ {{localize "DAGGERHEART.Sheets.Adversary.Experiences"}}
+
+ {{#each source.system.experiences}}
+
+ {{this.name}}
+
+ {{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 }}
+
+ {{/each}}
+
+
+
+
+ {{localize "DAGGERHEART.Sheets.Adversary.Attack"}}
+
+ {{formGroup systemFields.attack.fields.name value=source.system.attack.name}}
+ Attack
+ {{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}}
+
+
+