mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-04-21 23:13:39 +02:00
Merge branch 'v14-dev'
This commit is contained in:
commit
c6335980ba
22 changed files with 261 additions and 306 deletions
|
|
@ -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 — we’re 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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
21
lang/en.json
21
lang/en.json
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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: [
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
{
|
||||
...actor.prototypeToken,
|
||||
displayName: 50,
|
||||
...tokenData
|
||||
},
|
||||
{ renderSheet: false, actor }
|
||||
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.toObject(),
|
||||
actorId: actor.id,
|
||||
displayName: 50,
|
||||
...tokenData
|
||||
}
|
||||
],
|
||||
{ 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 } } });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the movement of the token preview on mousedrag.
|
||||
* @param {mousemove Event} event
|
||||
* Creates new tokens on the canvas by placing previews.
|
||||
* @param {object} tokenData
|
||||
* @param {object} options
|
||||
*/
|
||||
#onDragMouseMove(event) {
|
||||
event.stopPropagation();
|
||||
const { moveTime, object } = this.#activePreview;
|
||||
const update = {};
|
||||
async createTokensWithPreview(tokensData, { elevation } = {}) {
|
||||
const scene = game.scenes.get(game.user.viewedScene);
|
||||
if (!scene) return;
|
||||
|
||||
const now = Date.now();
|
||||
if (now - (moveTime || 0) <= 16) return;
|
||||
this.#activePreview.moveTime = now;
|
||||
const level = scene.levels.get(game.user.viewedLevel);
|
||||
if (!level) return;
|
||||
|
||||
let cursor = event.getLocalPosition(canvas.templates);
|
||||
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;
|
||||
|
||||
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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,31 +68,33 @@
|
|||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
},
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.resistance.physical.resistance",
|
||||
"type": "override",
|
||||
"value": 1,
|
||||
"priority": null,
|
||||
"phase": "initial"
|
||||
},
|
||||
{
|
||||
"key": "system.disadvantageSources",
|
||||
"type": "add",
|
||||
"value": "Action rolls",
|
||||
"priority": null,
|
||||
"phase": "initial"
|
||||
}
|
||||
],
|
||||
"duration": {
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.resistance.physical.resistance",
|
||||
"mode": 5,
|
||||
"value": "1",
|
||||
"priority": null
|
||||
},
|
||||
{
|
||||
"key": "system.disadvantageSources",
|
||||
"mode": 2,
|
||||
"value": "Retract",
|
||||
"priority": null
|
||||
}
|
||||
],
|
||||
"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 can’t 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"
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -29,39 +29,28 @@
|
|||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
},
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.advantageSources",
|
||||
"type": "add",
|
||||
"value": "Rolls to hide, investigate, or perceive details in low light",
|
||||
"priority": null,
|
||||
"phase": "initial"
|
||||
}
|
||||
],
|
||||
"duration": {
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"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
|
||||
}
|
||||
],
|
||||
"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 you’re 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"
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -132,27 +132,29 @@
|
|||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
},
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.advantageSources",
|
||||
"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>"
|
||||
}
|
||||
},
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.advantageSources",
|
||||
"mode": 2,
|
||||
"value": "1",
|
||||
"priority": null
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -4,17 +4,25 @@
|
|||
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-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 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>
|
||||
|
||||
<textarea name="{{concat "experiences." id ".description"}}">{{experience.description}}</textarea>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
<footer>
|
||||
<button data-action="finish">{{localize "Save Settings"}}</button>
|
||||
<button data-action="finish">{{localize "DAGGERHEART.GENERAL.saveSettings"}}</button>
|
||||
</footer>
|
||||
|
|
@ -26,11 +26,11 @@
|
|||
|
||||
{{formGroup systemFields.duration.fields.type value=source.system.duration.type localize=true }}
|
||||
|
||||
<div class="form-group slim duration-description">
|
||||
<div class="form-fields">
|
||||
{{formInput systemFields.duration.fields.description value=source.system.duration.description localize=true }}
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="custom-duration-section">
|
||||
{{formGroup fields.start.fields.time value=source.start.time localize=true }}
|
||||
|
|
|
|||
|
|
@ -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')}}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 |}}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue