Compare commits

..

14 commits

Author SHA1 Message Date
WBHarry
c6335980ba Merge branch 'v14-dev' 2026-04-14 20:55:14 +02:00
WBHarry
1176328f62 Updated deploy.yml 2026-04-14 20:55:10 +02:00
a62d28cd96
updated contributing guidelines (#1800) 2026-04-14 18:51:28 +02:00
WBHarry
8d8dea81fe
[Fix] Compendium Advantage Sources (#1796)
* Duration Description field didn't show initially for type temporary

* Corrected SRD
2026-04-13 20:41:28 +02:00
Nikhil Nagarajan
fb07938e54
container fix (#1795) 2026-04-12 19:10:51 +02:00
WBHarry
c337338c8b Merge branch 'v14-dev' of https://github.com/Foundryborne/daggerheart into v14-dev 2026-04-12 13:58:51 +02:00
WBHarry
f900011510 Fixed trait counting in CharacterCreation. Fixed description layout and labels in CharacterCreation. Fixed drag/drop images from Compendium Browser 2026-04-12 13:58:43 +02:00
WBHarry
56a6613a73
Fixed more missing translations (#1792) 2026-04-12 11:38:15 +02:00
WBHarry
e003db3ec1 Corrected updateActorsRangeDependentEffects when token is null 2026-04-12 11:22:00 +02:00
WBHarry
66c90d69e3 Added saefety to updateActorsRangeDepenedentEffects 2026-04-12 11:10:02 +02:00
WBHarry
a839ca0066 Corrected deploy.yml for new branch 2026-04-12 00:36:24 +02:00
WBHarry
6ed975f5b7 Merge branch 'v14-dev' of https://github.com/Foundryborne/daggerheart into v14-dev 2026-04-12 00:33:04 +02:00
WBHarry
e2c97a7b61 Raised version 2026-04-12 00:32:59 +02:00
WBHarry
3ec013ff50
Reworked summon action and clowncar functionality to work with levels (#1791) 2026-04-12 00:25:43 +02:00
23 changed files with 262 additions and 307 deletions

View file

@ -35,7 +35,7 @@ jobs:
env:
version: ${{steps.get_version.outputs.version-without-v}}
url: https://github.com/${{github.repository}}
manifest: https://raw.githubusercontent.com/${{github.repository}}/main/system.json
manifest: https://raw.githubusercontent.com/${{github.repository}}/v14/system.json
download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/system.zip
# Create a zip file with all files required by the module to add to the release

View file

@ -1,78 +1,9 @@
# Contributing to Foundryborne
# Contributing to Daggerheart
Welcome! This is a community-driven project to bring [Daggerheart](https://www.daggerheart.com/) to [FoundryVTT](https://foundryvtt.com/) as a full system. We're excited to have you here and appreciate your interest in contributing.
Thank you for your interest in contributing to the Foundryborne project!
---
To ensure that all contributions align with our project goals and architectural standards, we ask that you **do not submit outside contributions without first receiving feedback from the development team.**
## 🤝 How to Contribute
If you have an idea or a fix you'd like to contribute, please start a discussion or open an issue first. We'd love to hear from you and collaborate on the best way to move forward!
We welcome contributions of all kinds:
- Bug reports
- Feature suggestions
- Code contributions
- UI/UX mockups
- Documentation improvements
- Questions and discussions
Please be respectful and collaborative — were all here to build something great together.
### Community Translations
Please note that we are not accepting community translations in the main project. Instead, community translations should be published as a module.
---
## 🧭 General Guidelines
- **Use GitHub Issues** to report bugs or propose features
- **Start a Discussion** for larger ideas or questions
- **Open a Pull Request** once you've confirmed your work aligns with project direction
- **Keep things modular and maintainable** — if you're not sure how to structure something, ask!
- **Orient your code on existing examples**, and feel free to suggest a standard if it makes things clearer
---
## 🗂️ Project Structure
Please try to follow the general logic of the existing code when submitting PRs.
We encourage contributors to leave comments or open Discussions when proposing structural or organizational changes.
---
## 🧾 Issue & PR Best Practices
**For Issues:**
- Use clear, descriptive titles
- Provide a concise explanation of the problem or idea
- Include reproduction steps or example scenarios if it's a bug
- Add screenshots or logs if helpful
**For Pull Requests:**
- Use a clear title summarizing the change
- Provide a brief description of what your code does and why
- Link to any related Issues
- Keep PRs focused — smaller is better
---
## 🔖 Labels and Boards
We use GitHub labels to help organize contributions. If your issue or PR relates to a specific category, feel free to tag it. There is also a GitHub Project Board to help track active work and priorities.
---
## 📣 Communication
Discussions are currently happening on GitHub — in Issues, PRs, and [GitHub Discussions](https://github.com/Foundryborne/daggerheart/discussions). You're welcome to use any of these, though we may consolidate to one in the future.
---
## 🤗 Thank You!
Whether you're fixing a typo or designing entire mechanics — every contribution matters. Thank you for helping bring _Daggerheart_ to life in FoundryVTT through **Foundryborne**!
🐸🛠️
Thank you for your understanding and support.

View file

@ -355,6 +355,8 @@ Hooks.on(CONFIG.DH.HOOKS.hooksConfig.groupRollStart, async data => {
});
const updateActorsRangeDependentEffects = async token => {
if (!token) return;
const rangeMeasurement = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.variantRules

View file

@ -74,9 +74,7 @@
"name": "Summon",
"tooltip": "Create tokens in the scene.",
"error": "You do not have permission to summon tokens or there is no active scene.",
"invalidDrop": "You can only drop Actor entities to summon.",
"chatMessageTitle": "Test2",
"chatMessageHeaderTitle": "Summoning"
"invalidDrop": "You can only drop Actor entities to summon."
},
"transform": {
"name": "Transform",
@ -2016,6 +2014,10 @@
"hint": "Multiply any damage dealt to you by this number"
}
},
"Battlepoints": {
"full": "Battlepoints",
"short": "BP"
},
"Bonuses": {
"rest": {
"downtimeAction": "Downtime Action",
@ -2431,6 +2433,7 @@
"next": "Next",
"none": "None",
"noTarget": "No current target",
"optionalThing": "Optional {thing}",
"partner": "Partner",
"player": {
"single": "Player",
@ -2457,6 +2460,7 @@
"rollDamage": "Roll Damage",
"rollWith": "{roll} Roll",
"save": "Save",
"saveSettings": "Save Settings",
"scalable": "Scalable",
"scars": "Scars",
"situationalBonus": "Situational Bonus",
@ -2611,8 +2615,14 @@
},
"Weapon": {
"weaponType": "Weapon Type",
"primaryWeapon": "Primary Weapon",
"secondaryWeapon": "Secondary Weapon"
"primaryWeapon": {
"full": "Primary Weapon",
"short": "Primary"
},
"secondaryWeapon": {
"full": "Secondary Weapon",
"short": "Secondary"
}
}
},
"MACROS": {
@ -3067,6 +3077,7 @@
},
"ItemBrowser": {
"title": "Daggerheart Compendium Browser",
"windowTitle": "Compendium Browser",
"hint": "Select a Folder in sidebar to start browsing through the compendium",
"browserSettings": "Browser Settings",
"columnName": "Name",

View file

@ -11,7 +11,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
this.character = character;
this.setup = {
traits: this.character.system.traits,
traits: Object.keys(this.character.system.traits).reduce((acc, key) => {
acc[key] = { value: null };
return acc;
}, {}),
ancestryName: {
primary: '',
secondary: ''
@ -377,8 +380,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
];
return Object.values(this.setup.traits).reduce((acc, x) => {
const index = traitCompareArray.indexOf(x.value);
if (index === -1) return acc;
traitCompareArray.splice(index, 1);
acc += index !== -1;
acc += 1;
return acc;
}, 0);
}

View file

@ -122,15 +122,14 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
async toggleClowncar(actors) {
const animationDuration = 500;
const activeTokens = actors.flatMap(member => member.getActiveTokens());
const scene = game.scenes.get(game.user.viewedScene);
/* getDependentTokens returns already removed tokens with id = null. Need to filter that until it's potentially fixed from Foundry */
const activeTokens = actors.flatMap(member => member.getDependentTokens({ scenes: scene }).filter(x => x._id));
const { x: actorX, y: actorY } = this.document;
if (activeTokens.length > 0) {
for (let token of activeTokens) {
await token.document.update(
{ x: actorX, y: actorY, alpha: 0 },
{ animation: { duration: animationDuration } }
);
setTimeout(() => token.document.delete(), animationDuration);
await token.update({ x: actorX, y: actorY, alpha: 0 }, { animation: { duration: animationDuration } });
setTimeout(() => token.delete(), animationDuration);
}
} else {
const activeScene = game.scenes.find(x => x.id === game.user.viewedScene);
@ -140,11 +139,16 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
tokenData.push(data.toObject());
}
const viewedLevel = game.scenes.get(game.user.viewedScene).levels.get(game.user.viewedLevel);
const elevation = this.actor.token?.elevation ?? viewedLevel.elevation.bottom;
const newTokens = await activeScene.createEmbeddedDocuments(
'Token',
tokenData.map(tokenData => ({
...tokenData,
alpha: 0,
level: viewedLevel,
elevation: elevation,
x: actorX,
y: actorY
}))

View file

@ -37,7 +37,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
tag: 'div',
window: {
frame: true,
title: 'Compendium Browser',
title: 'DAGGERHEART.UI.ItemBrowser.windowTitle',
icon: 'fa-solid fa-book-atlas',
positioned: true,
resizable: true
@ -583,7 +583,9 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
const { itemUuid } = event.target.closest('[data-item-uuid]').dataset,
item = await foundry.utils.fromUuid(itemUuid),
dragData = item.toDragData();
event.dataTransfer.setData('text/plain', JSON.stringify(dragData));
event.dataTransfer.setDragImage(event.target.querySelector('img'), 0, 0);
}
_canDragStart() {

View file

@ -1,16 +1 @@
export default class DhTokenLayer extends foundry.canvas.layers.TokenLayer {
async _createPreview(createData, options) {
if (options.actor) {
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
if (options.actor?.system.metadata.usesSize) {
const tokenSize = tokenSizes[options.actor.system.size];
if (tokenSize && options.actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) {
createData.width = tokenSize;
createData.height = tokenSize;
}
}
}
return super._createPreview(createData, options);
}
}
export default class DhTokenLayer extends foundry.canvas.layers.TokenLayer {}

View file

@ -74,12 +74,13 @@ export const typeConfig = {
columns: [
{
key: 'type',
label: 'DAGGERHEART.GENERAL.type'
label: 'DAGGERHEART.GENERAL.type',
format: type => type ? `TYPES.Item.${type}` : '-'
},
{
key: 'system.secondary',
label: 'DAGGERHEART.UI.ItemBrowser.subtype',
format: isSecondary => (isSecondary ? 'secondary' : isSecondary === false ? 'primary' : '-')
format: isSecondary => (isSecondary ? 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon.short' : isSecondary === false ? 'DAGGERHEART.ITEMS.Weapon.primaryWeapon.short' : '-')
},
{
key: 'system.tier',
@ -99,8 +100,8 @@ export const typeConfig = {
key: 'system.secondary',
label: 'DAGGERHEART.UI.ItemBrowser.subtype',
choices: [
{ value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon' },
{ value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon' }
{ value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon.full' },
{ value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full' }
]
},
{
@ -258,11 +259,13 @@ export const typeConfig = {
columns: [
{
key: 'system.type',
label: 'DAGGERHEART.GENERAL.type'
label: 'DAGGERHEART.GENERAL.type',
format: type => type ? `DAGGERHEART.CONFIG.DomainCardTypes.${type}` : '-'
},
{
key: 'system.domain',
label: 'DAGGERHEART.GENERAL.Domain.single'
label: 'DAGGERHEART.GENERAL.Domain.single',
format: domain => domain ? CONFIG.DH.DOMAIN.allDomains()[domain].label : '-'
},
{
key: 'system.level',
@ -374,7 +377,7 @@ export const typeConfig = {
columns: [
{
key: 'system.linkedClass',
label: 'Class',
label: 'TYPES.Item.class',
format: linkedClass => linkedClass?.name ?? 'DAGGERHEART.UI.ItemBrowser.missing'
},
{
@ -386,7 +389,7 @@ export const typeConfig = {
filters: [
{
key: 'system.linkedClass.uuid',
label: 'Class',
label: 'TYPES.Item.class',
choices: items => {
const list = items
.filter(item => item.system.linkedClass)
@ -410,7 +413,8 @@ export const typeConfig = {
},
{
key: 'system.mainTrait',
label: 'DAGGERHEART.GENERAL.Trait.single'
label: 'DAGGERHEART.GENERAL.Trait.single',
format: trait => (trait ? `DAGGERHEART.CONFIG.Traits.${trait}.name` : '-')
}
],
filters: [

View file

@ -44,12 +44,18 @@ export default class DHSummonField extends fields.ArrayField {
count = roll.total;
}
const actor = DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID));
const actor = await DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID));
/* Extending summon data in memory so it's available in actionField.toChat. Think it's harmless, but ugly. Could maybe find a better way. */
summon.rolledCount = count;
summon.actor = actor.toObject();
summonData.push({ actor, count: count });
const countNumber = Number.parseInt(count);
for (let i = 0; i < countNumber; i++) {
const remaining = countNumber - i;
summonData.push({
actor,
tokenPreviewName: `${actor.prototypeToken.name}${remaining > 1 ? ` (${remaining}x)` : ''}`
});
}
}
if (rolls.length) await Promise.all(rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true)));
@ -58,32 +64,22 @@ export default class DHSummonField extends fields.ArrayField {
DHSummonField.handleSummon(summonData, this.actor);
}
/* Check for any available instances of the actor present in the world if we're missing artwork in the compendium */
static getWorldActor(baseActor) {
/* Check for any available instances of the actor present in the world if we're missing artwork in the compendium. If none exists, create one. */
static async getWorldActor(baseActor) {
const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`];
if (baseActor.inCompendium && dataType && baseActor.img === dataType.DEFAULT_ICON) {
const worldActorCopy = game.actors.find(x => x.name === baseActor.name);
return worldActorCopy ?? baseActor;
if (worldActorCopy) return worldActorCopy;
return await game.system.api.documents.DhpActor.create(baseActor.toObject());
}
return baseActor;
}
static async handleSummon(summonData, actionActor, summonIndex = 0) {
const summon = summonData[summonIndex];
const result = await CONFIG.ux.TokenManager.createPreviewAsync(summon.actor, {
name: `${summon.actor.prototypeToken.name}${summon.count > 1 ? ` (${summon.count}x)` : ''}`
});
static async handleSummon(summonData, actionActor) {
await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: actionActor.token?.elevation });
if (!result) return actionActor.sheet?.maximize();
summon.actor = result.actor;
summon.count--;
if (summon.count <= 0) {
summonIndex++;
if (summonIndex === summonData.length) return actionActor.sheet?.maximize();
}
DHSummonField.handleSummon(summonData, actionActor, summonIndex);
return actionActor.sheet?.maximize();
}
}

View file

@ -1,104 +1,68 @@
/**
* A singleton class that handles preview tokens.
* A singleton class that handles creating tokens.
*/
export default class DhTokenManager {
#activePreview;
#actor;
#resolve;
/**
* Create a template preview, deactivating any existing ones.
* @param {object} data
* Create a token previer
* @param {Actor} actor
* @param {object} tokenData
*/
async createPreview(actor, tokenData) {
this.#actor = actor;
const token = await canvas.tokens._createPreview(
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
if (actor?.system.metadata.usesSize) {
const tokenSize = tokenSizes[actor.system.size];
if (tokenSize && actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) {
tokenData.width = tokenSize;
tokenData.height = tokenSize;
}
}
return await canvas.tokens.placeTokens(
[
{
...actor.prototypeToken,
...actor.prototypeToken.toObject(),
actorId: actor.id,
displayName: 50,
...tokenData
},
{ renderSheet: false, actor }
}
],
{ create: false }
);
this.#activePreview = {
document: token.document,
object: token,
origin: { x: token.document.x, y: token.document.y }
};
this.#activePreview.events = {
contextmenu: this.#cancelTemplate.bind(this),
mousedown: this.#confirmTemplate.bind(this),
mousemove: this.#onDragMouseMove.bind(this)
};
canvas.stage.on('mousemove', this.#activePreview.events.mousemove);
canvas.stage.on('mousedown', this.#activePreview.events.mousedown);
canvas.app.view.addEventListener('contextmenu', this.#activePreview.events.contextmenu);
}
/* Currently intended for using as a preview of where to create a token. (note the flag) */
async createPreviewAsync(actor, tokenData = {}) {
return new Promise(resolve => {
this.#resolve = resolve;
this.createPreview(actor, { ...tokenData, flags: { daggerheart: { createPlacement: true } } });
/**
* Creates new tokens on the canvas by placing previews.
* @param {object} tokenData
* @param {object} options
*/
async createTokensWithPreview(tokensData, { elevation } = {}) {
const scene = game.scenes.get(game.user.viewedScene);
if (!scene) return;
const level = scene.levels.get(game.user.viewedLevel);
if (!level) return;
const createElevation = elevation ?? level.elevation.bottom;
for (const tokenData of tokensData) {
const previewTokens = await this.createPreview(tokenData.actor, {
name: tokenData.tokenPreviewName,
level: game.user.viewedLevel,
elevation: createElevation,
flags: { daggerheart: { createPlacement: true } }
});
}
if (!previewTokens?.length) return null;
/**
* Handles the movement of the token preview on mousedrag.
* @param {mousemove Event} event
*/
#onDragMouseMove(event) {
event.stopPropagation();
const { moveTime, object } = this.#activePreview;
const update = {};
const now = Date.now();
if (now - (moveTime || 0) <= 16) return;
this.#activePreview.moveTime = now;
let cursor = event.getLocalPosition(canvas.templates);
Object.assign(update, canvas.grid.getTopLeftPoint(cursor));
object.document.updateSource(update);
object.renderFlags.set({ refresh: true });
}
/**
* Cancels the preview token on right-click.
* @param {contextmenu Event} event
*/
#cancelTemplate(_event, resolved) {
const { mousemove, mousedown, contextmenu } = this.#activePreview.events;
this.#activePreview.object.destroy();
canvas.stage.off('mousemove', mousemove);
canvas.stage.off('mousedown', mousedown);
canvas.app.view.removeEventListener('contextmenu', contextmenu);
if (this.#resolve && !resolved) this.#resolve(false);
}
/**
* Creates a real Actor and token at the preview location and cancels the preview.
* @param {click Event} event
*/
async #confirmTemplate(event) {
event.stopPropagation();
this.#cancelTemplate(event, true);
const actor = this.#actor.inCompendium
? await game.system.api.documents.DhpActor.create(this.#actor.toObject())
: this.#actor;
const tokenData = await actor.getTokenDocument();
const result = await canvas.scene.createEmbeddedDocuments('Token', [
{ ...tokenData.toObject(), x: this.#activePreview.document.x, y: this.#activePreview.document.y }
]);
this.#activePreview = undefined;
if (this.#resolve && result.length) this.#resolve(result[0]);
await canvas.scene.createEmbeddedDocuments(
'Token',
previewTokens.map(x => ({
...x.toObject(),
name: tokenData.actor.prototypeToken.name,
displayName: tokenData.actor.prototypeToken.displayName,
flags: tokenData.actor.prototypeToken.flags
})),
{ controlObject: true, parent: canvas.scene }
);
}
}
}

View file

@ -68,31 +68,33 @@
"type": "withinRange",
"target": "hostile",
"range": "melee"
}
},
"changes": [
{
"key": "system.resistance.physical.resistance",
"mode": 5,
"value": "1",
"priority": null
"type": "override",
"value": 1,
"priority": null,
"phase": "initial"
},
{
"key": "system.disadvantageSources",
"mode": 2,
"value": "Retract",
"priority": null
"type": "add",
"value": "Action rolls",
"priority": null,
"phase": "initial"
}
],
"duration": {
"type": ""
}
},
"disabled": true,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
"value": null,
"units": "seconds",
"expiry": null,
"expired": false
},
"description": "<p>While in your shell, you have resistance to physical damage, you have disadvantage on action rolls, and you cant move.</p>",
"tint": "#ffffff",
@ -102,6 +104,16 @@
"_stats": {
"compendiumSource": null
},
"start": {
"time": 0,
"combat": null,
"combatant": null,
"initiative": null,
"round": null,
"turn": null
},
"showIcon": 1,
"folder": null,
"_key": "!items.effects!UFR67BUOhNGLFyg9.3V4FPoyjJUnFP9WS"
}
],

View file

@ -29,39 +29,28 @@
"type": "withinRange",
"target": "hostile",
"range": "melee"
}
},
"changes": [
{
"key": "system.advantageSources",
"mode": 2,
"value": "In an area with low light or heavy shadow: hide, investigate, or perceive",
"priority": null
},
{
"key": "system.advantageSources",
"mode": 2,
"value": "",
"priority": null
},
{
"key": "",
"mode": 2,
"value": "",
"priority": null
"type": "add",
"value": "Rolls to hide, investigate, or perceive details in low light",
"priority": null,
"phase": "initial"
}
],
"duration": {
"type": ""
}
},
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
"value": null,
"units": "seconds",
"expiry": null,
"expired": false
},
"description": "",
"description": "<p>When youre in an area with low light or heavy shadow, you have advantage on rolls to hide, investigate, or perceive details within that area.</p>",
"origin": null,
"tint": "#ffffff",
"transfer": true,
@ -71,6 +60,16 @@
"_stats": {
"compendiumSource": null
},
"start": {
"time": 0,
"combat": null,
"combatant": null,
"initiative": null,
"round": null,
"turn": null
},
"showIcon": 1,
"folder": null,
"_key": "!items.effects!aMla3xQuCHEwORGD.pCp32u7UwqxCI4WW"
}
],

View file

@ -132,27 +132,29 @@
"type": "withinRange",
"target": "hostile",
"range": "melee"
}
},
"changes": [
{
"key": "system.advantageSources",
"mode": 2,
"value": "1",
"priority": null
"type": "add",
"value": "On Attacks",
"priority": null,
"phase": "initial"
}
],
"duration": {
"type": "temporary",
"description": "<p>Until you or an ally rolls a failure with Fear.</p>"
}
},
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
"value": null,
"units": "seconds",
"expiry": null,
"expired": false
},
"description": "",
"description": "<p>You gain advantage on attack rolls until you or an ally rolls a failure with Fear.</p>",
"tint": "#ffffff",
"statuses": [],
"sort": 0,
@ -160,6 +162,16 @@
"_stats": {
"compendiumSource": null
},
"start": {
"time": 0,
"combat": null,
"combatant": null,
"initiative": null,
"round": null,
"turn": null
},
"showIcon": 1,
"folder": null,
"_key": "!items.effects!Ef1JsUG50LIoKx2F.s7ma4TNgAvt0ZgEW"
}
],

View file

@ -175,6 +175,11 @@
opacity: 0.2;
}
&.no-horizontal-padding {
padding-left: 0;
padding-right: 0;
}
legend {
margin-left: auto;
margin-right: auto;
@ -250,6 +255,11 @@
height: 65px;
background: url(../assets/svg/trait-shield.svg) no-repeat;
background-size: 100%;
padding-top: 3px;
display: flex;
flex-direction: column;
align-items: center;
row-gap: 3px;
div {
filter: drop-shadow(0 0 3px black);
@ -278,6 +288,15 @@
flex-direction: column;
gap: 5px;
&.separated {
border-bottom: 2px solid;
padding-bottom: 8px;
}
.form-group {
padding: 0 0.75rem;
}
.experience-inner-container {
position: relative;
display: flex;

View file

@ -27,10 +27,11 @@
height: 65px;
background: url(../assets/svg/trait-shield.svg) no-repeat;
background-size: 100%;
padding-top: 4px;
padding-top: 3px;
display: flex;
flex-direction: column;
align-items: center;
row-gap: 3px;
span {
font-size: var(--font-size-10);

View file

@ -2,7 +2,7 @@
"id": "daggerheart",
"title": "Daggerheart",
"description": "An unofficial implementation of the Daggerheart system",
"version": "2.1.1",
"version": "2.1.2",
"compatibility": {
"minimum": "14.359",
"verified": "14.360",
@ -10,7 +10,7 @@
},
"url": "https://github.com/Foundryborne/daggerheart",
"manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json",
"download": "https://github.com/Foundryborne/daggerheart/releases/download/2.1.1/system.zip",
"download": "https://github.com/Foundryborne/daggerheart/releases/download/2.1.2/system.zip",
"authors": [
{
"name": "WBHarry"

View file

@ -4,18 +4,26 @@
data-group='{{tabs.experience.group}}'
>
<div class="main-selections-container">
<fieldset class="section-container">
<fieldset class="section-container no-horizontal-padding">
<legend>{{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.initialExperiences"}} {{experience.nrSelected}}/{{experience.nrTotal}}</legend>
<div class="experiences-inner-container">
{{#each experience.values as |experience id|}}
<div class="experience-container">
<div class="experience-container {{#unless @last}}separated{{/unless}}">
<div class="form-group">
<label>{{localize "DAGGERHEART.GENERAL.label"}}</label>
<div class="experience-inner-container">
<input class="experience-description" type="text" name="{{concat "experiences." id ".name" }}" value="{{experience.name}}" placeholder="{{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.newExperience"}}" />
<span class="experience-value">{{numberFormat this.value sign=true}}</span>
</div>
</div>
<div class="form-group">
<label>{{localize "DAGGERHEART.GENERAL.optionalThing" thing=(localize "DAGGERHEART.GENERAL.description")}}</label>
<div class="form-fields">
<textarea name="{{concat "experiences." id ".description"}}">{{experience.description}}</textarea>
</div>
</div>
</div>
{{/each}}
</div>
</fieldset>

View file

@ -1,3 +1,3 @@
<footer>
<button data-action="finish">{{localize "Save Settings"}}</button>
<button data-action="finish">{{localize "DAGGERHEART.GENERAL.saveSettings"}}</button>
</footer>

View file

@ -26,7 +26,7 @@
{{formGroup systemFields.duration.fields.type value=source.system.duration.type localize=true }}
<div class="form-group slim duration-description">
<div class="form-group slim duration-description {{#if (eq source.system.duration.type 'temporary')}}visible{{/if}}">
<div class="form-fields">
{{formInput systemFields.duration.fields.description value=source.system.duration.description localize=true }}
</div>

View file

@ -5,9 +5,9 @@
<h1 class='item-name'><input type='text' name='name' value='{{source.name}}' /></h1>
<div class='item-description'>
{{#if source.system.secondary}}
<h3>{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon"}}</h3>
<h3>{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full"}}</h3>
{{else}}
<h3>{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon"}}</h3>
<h3>{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon.full"}}</h3>
{{/if}}
<h3>
{{localize (concat 'DAGGERHEART.CONFIG.Traits.' source.system.attack.roll.trait '.short')}}

View file

@ -7,9 +7,9 @@
{{#each category as |grouping index|}}
<div class="battlepoint-grouping-container">
{{#if grouping.nr}}
<label>{{key}} BP: {{concat (localize grouping.description) ' ' '('grouping.nr 'x)'}}</label>
<label>{{key}} {{localize "DAGGERHEART.GENERAL.Battlepoints.short"}} - {{concat (localize grouping.description) ' ' '('grouping.nr 'x)'}}</label>
{{else}}
<label class="unselected-grouping">{{key}} BP - {{localize grouping.description}}</label>
<label class="unselected-grouping">{{key}} {{localize "DAGGERHEART.GENERAL.Battlepoints.short"}} - {{localize grouping.description}}</label>
{{/if}}
</div>
{{/each}}
@ -26,7 +26,7 @@
{{else}}
<input type="checkbox" data-combat-id="{{@root.combatId}}" data-category="{{toggle.categoryKey}}" data-grouping="{{toggle.toggleKey}}" {{checked toggle.checked}} />
{{/if}}
<label class="unselected-grouping">{{toggle.categoryKey}} BP: {{localize toggle.description}}</label>
<label class="unselected-grouping">{{toggle.categoryKey}} {{localize "DAGGERHEART.GENERAL.Battlepoints.short"}}: {{localize toggle.description}}</label>
</div>
{{/each}}
</div>

View file

@ -3,7 +3,7 @@
<h2 class="tooltip-title">{{item.name}}</h2>
<div class="tags">
<div class="tag">
<span>{{#if item.system.secondary}}{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon"}}{{else}}{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon"}}{{/if}}</span>
<span>{{#if item.system.secondary}}{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full"}}{{else}}{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon.full"}}{{/if}}</span>
</div>
<div class="tag">
{{#with (lookup config.GENERAL.burden item.system.burden) as | burden |}}