Merged with development

This commit is contained in:
WBHarry 2025-08-23 18:17:32 +02:00
commit bd76e22e8d
1096 changed files with 11080 additions and 5102 deletions

2
.env.example Normal file
View file

@ -0,0 +1,2 @@
FOUNDRY_MAIN_PATH=/path/to/foundry/resources/app/main.js
FOUNDRY_DATA_PATH=/path/to/foundry/data

View file

@ -1,10 +1,10 @@
--- ---
name: Bug report name: Bug report
about: Create a report to help us improve about: Create a bug report to help us identify issues and resolve them
title: "[BUG] - " title: "[Bug] <Insert Title here> "
labels: bug labels: bug
type: bug
assignees: '' assignees: ''
--- ---
**Describe the bug** **Describe the bug**
@ -24,10 +24,10 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.
**Setup Information:** **Setup Information:**
- OS: [e.g. iOS] - OS: [e.g. iOS, Windows]
- Browser [e.g. chrome, safari] - Browser [e.g. chrome, safari]
- Foundry Version [e.g. v13 b342] - Foundry Version [e.g. v13 b342]
- System Version [e.g. main-3593f44] - System Version [e.g. v.1.0, v.1.0.1]
**Additional context** **Additional context**

View file

@ -0,0 +1,14 @@
---
name: Feature report
about: Create a feature report for suggestions on improving the system
title: "[Feature] <Insert Title here> "
labels: enhancement, discussion, maybe
type: feature
assignees: ''
---
**Description**
A clear and concise description of what feature needs to be implemented.
**Screenshots**
If applicable, add screenshots to help explain the feature that needs to be implemented.

9
.github/ISSUE_TEMPLATE/typo_report.md vendored Normal file
View file

@ -0,0 +1,9 @@
---
name: Typo report
about: Create a new issue to report a compendium typo
title: "[TYPO] - "
labels: compendium, typo
type: bug
assignees: ''
---

View file

@ -0,0 +1,55 @@
---
name: Pull Request
about: Create a new pull request
title: "[Community PR] <Insert Title here>"
labels: community pr
assignees: ''
---
## Description
Please include a summary of the change and which issue is fixed (if applicable). Also include relevant context or motivation for the change.
- Fixes #(issue)
- Closes #(issue)
## Type of Change
Please check the relevant options:
- [ ] Bug fix
- [ ] New feature
- [ ] Code cleanup/refactor
- [ ] Documentation update
- [ ] Test coverage
- [ ] Dependency update
- [ ] Configuration change
- [ ] Other (please describe):
## How Has This Been Tested?
Please describe the tests you ran to verify your changes:
- [ ] Manual testing
- [ ] Other:
## Screenshots (if applicable)
Include screenshots or GIFs to help explain your changes visually.
## Checklist
- [ ] My code follows the project style guidelines
- [ ] I have performed a self-review of my code
- [ ] I have commented my code where necessary
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings or errors
- [ ] I have added tests that prove my fix or feature works
- [ ] New and existing tests pass locally with my changes
## Additional Comments
Add any other context or questions here.
---
> Thank you for your contribution! 🎉

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
.vscode .vscode
.env
node_modules node_modules
/packs /packs
Build Build

View file

@ -24,24 +24,41 @@ You can find the documentation here: https://github.com/Foundryborne/daggerheart
## Development Setup ## Development Setup
- Open a terminal in the directory with the repo `cd <path>/<to>/<repo>` 1. **Navigate to the repo directory:**
- NOTE: The repo should be placed in the system files are or somewhere else and a link (if on linux) is placed in the system directory
- NOTE: Linux link can be made using `ln -snf <path to development folder> daggerheart` inside the system folder
- Install npm `npm install`
- Update package.json to match your profile
``` ```bash
"start": "concurrently \"rollup -c --watch\" \"node C:/FoundryDev/resources/app/main.js --dataPath=C:/FoundryDevFiles --noupnp\" \"gulp\"", cd <path>/<to>/<repo>
"start-test": "node C:/FoundryDev/resources/app/main.js --dataPath=C:/FoundryDevFiles && rollup -c --watch && gulp", ```
``` 2. **Install dependencies:**
- Replace `C:/FoundryDev/resources/app/main.js` with `<your>/<path>/<to>/<foundry>/<main.js>` ```bash
- The main is likely in `<Foundry Install Location>/resouces/app/main.js` npm install
- Replace `--dataPath=C:/FoundryDevFiles` with `<your>/<path>/<to>/<foundry>/<data>` ```
Now you should be able to build the app using `npm start` 3. **Configure your Foundry paths:**
[Foundry VTT Website][1]
```bash
npm run setup:dev -- --foundry-path="/path/to/foundry/main.js" --data-path="/path/to/data"
```
4. **Start developing:**
```bash
npm start
```
### Available Scripts
- `npm start` - Start development with file watching and Foundry launching
- `npm run build` - One-time build
- `npm run setup:dev -- --foundry-path="<path>" --data-path="<path>"` - Configure development environment
### Notes
- The repo should be placed in your Foundry `Data/systems/` directory or symlinked there
- Linux symlink can be made using `ln -snf <path to development folder> daggerheart` inside the systems folder
- Your `.env` file is ignored by git, so each developer can have their own configuration
[Foundry VTT Website][1]
[1]: https://foundryvtt.com/ [1]: https://foundryvtt.com/

View file

@ -8,11 +8,12 @@ import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs';
import { enricherConfig, enricherRenderSetup } from './module/enrichers/_module.mjs'; import { enricherConfig, enricherRenderSetup } from './module/enrichers/_module.mjs';
import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs'; import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs';
import { NarrativeCountdowns } from './module/applications/ui/countdowns.mjs'; import { NarrativeCountdowns } from './module/applications/ui/countdowns.mjs';
import { DHRoll, DualityRoll, D20Roll, DamageRoll } from './module/dice/_module.mjs'; import { BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll } from './module/dice/_module.mjs';
import { enrichedDualityRoll } from './module/enrichers/DualityRollEnricher.mjs'; import { enrichedDualityRoll } from './module/enrichers/DualityRollEnricher.mjs';
import { registerCountdownHooks } from './module/data/countdowns.mjs'; import { registerCountdownHooks } from './module/data/countdowns.mjs';
import { import {
handlebarsRegistration, handlebarsRegistration,
runMigrations,
settingsRegistration, settingsRegistration,
socketRegistration socketRegistration
} from './module/systemRegistration/_module.mjs'; } from './module/systemRegistration/_module.mjs';
@ -49,9 +50,7 @@ Hooks.once('init', () => {
DamageRoll: DamageRoll DamageRoll: DamageRoll
}; };
CONFIG.Dice.rolls = [...CONFIG.Dice.rolls, DHRoll, DualityRoll, D20Roll, DamageRoll]; CONFIG.Dice.rolls = [BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll];
Roll.CHAT_TEMPLATE = 'systems/daggerheart/templates/ui/chat/foundryRoll.hbs';
Roll.TOOLTIP_TEMPLATE = 'systems/daggerheart/templates/ui/chat/foundryRollTooltip.hbs';
CONFIG.MeasuredTemplate.objectClass = placeables.DhMeasuredTemplate; CONFIG.MeasuredTemplate.objectClass = placeables.DhMeasuredTemplate;
const { DocumentSheetConfig } = foundry.applications.apps; const { DocumentSheetConfig } = foundry.applications.apps;
@ -149,6 +148,11 @@ Hooks.once('init', () => {
// Make Compendium Dialog resizable // Make Compendium Dialog resizable
foundry.applications.sidebar.apps.Compendium.DEFAULT_OPTIONS.window.resizable = true; foundry.applications.sidebar.apps.Compendium.DEFAULT_OPTIONS.window.resizable = true;
DocumentSheetConfig.registerSheet(foundry.documents.Scene, SYSTEM.id, applications.scene.DhSceneConfigSettings, {
makeDefault: true,
label: 'Daggerheart'
});
settingsRegistration.registerDHSettings(); settingsRegistration.registerDHSettings();
RegisterHandlebarsHelpers.registerHelpers(); RegisterHandlebarsHelpers.registerHelpers();
@ -172,6 +176,8 @@ Hooks.on('ready', async () => {
game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.welcomeMessage, true); game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.welcomeMessage, true);
} }
} }
runMigrations();
}); });
Hooks.once('dicesoniceready', () => {}); Hooks.once('dicesoniceready', () => {});

View file

@ -26,6 +26,14 @@
"CONTROLS": { "CONTROLS": {
"inFront": "In Front" "inFront": "In Front"
}, },
"SCENE": {
"TABS": {
"SHEET": {
"dh": "Daggerheart"
}
}
},
"DAGGERHEART": { "DAGGERHEART": {
"ACTIONS": { "ACTIONS": {
"TYPES": { "TYPES": {
@ -64,6 +72,14 @@
"exactHint": "The Character's Tier is used if empty", "exactHint": "The Character's Tier is used if empty",
"label": "Beastform" "label": "Beastform"
}, },
"damage": {
"multiplier": "Multiplier",
"flatMultiplier": "Flat Multiplier"
},
"general": {
"customFormula": "Custom Formula",
"formula": "Formula"
},
"displayInChat": "Display in chat" "displayInChat": "Display in chat"
}, },
"RollField": { "RollField": {
@ -193,7 +209,10 @@
"companionLevelup": { "companionLevelup": {
"confirmTitle": "Companion Levelup", "confirmTitle": "Companion Levelup",
"confirmText": "Would you like to level up your companion {name} by {levelChange} levels at this time? (You can do it manually later)" "confirmText": "Would you like to level up your companion {name} by {levelChange} levels at this time? (You can do it manually later)"
} },
"viewLevelups": "View Levelups",
"InvalidOldCharacterImportTitle": "Old Character Import",
"InvalidOldCharacterImportText": "Character data exported prior to system version 1.1 will not generate a complete character. Do you wish to continue?"
}, },
"Companion": { "Companion": {
"FIELDS": { "FIELDS": {
@ -236,6 +255,9 @@
} }
}, },
"APPLICATIONS": { "APPLICATIONS": {
"Attribution": {
"title": "Attribution"
},
"CharacterCreation": { "CharacterCreation": {
"tabs": { "tabs": {
"ancestry": "Ancestry", "ancestry": "Ancestry",
@ -490,7 +512,8 @@
"pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.", "pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.",
"posttext": "Take an additional domain card of your level or lower from a domain you have access to." "posttext": "Take an additional domain card of your level or lower from a domain you have access to."
}, },
"title": "{actor} Level Up" "title": "{actor} Level Up",
"viewModeTitle": "{actor} Level Up (View Mode)"
}, },
"MulticlassChoice": { "MulticlassChoice": {
"title": "Multiclassing - {actor}", "title": "Multiclassing - {actor}",
@ -1885,7 +1908,8 @@
"tier4": "tier 4", "tier4": "tier 4",
"domains": "Domains", "domains": "Domains",
"downtime": "Downtime", "downtime": "Downtime",
"rules": "Rules" "rules": "Rules",
"types": "Types"
}, },
"Tiers": { "Tiers": {
"singular": "Tier", "singular": "Tier",
@ -1905,6 +1929,7 @@
"armorScore": "Armor Score", "armorScore": "Armor Score",
"activeEffects": "Active Effects", "activeEffects": "Active Effects",
"armorSlots": "Armor Slots", "armorSlots": "Armor Slots",
"artistAttribution": "Artwork By: {artist}",
"attack": "Attack", "attack": "Attack",
"basics": "Basics", "basics": "Basics",
"bonus": "Bonus", "bonus": "Bonus",
@ -1912,6 +1937,7 @@
"continue": "Continue", "continue": "Continue",
"criticalSuccess": "Critical Success", "criticalSuccess": "Critical Success",
"criticalShort": "Critical", "criticalShort": "Critical",
"custom": "Custom",
"d20Roll": "D20 Roll", "d20Roll": "D20 Roll",
"damage": "Damage", "damage": "Damage",
"damageRoll": "Damage Roll", "damageRoll": "Damage Roll",
@ -1995,7 +2021,7 @@
"true": "True", "true": "True",
"type": "Type", "type": "Type",
"unarmed": "Unarmed", "unarmed": "Unarmed",
"unarmedStrike": "Unarmed Strike", "unarmedAttack": "Unarmed Attack",
"unarmored": "Unarmored", "unarmored": "Unarmored",
"use": "Use", "use": "Use",
"used": "Used", "used": "Used",
@ -2005,6 +2031,11 @@
}, },
"ITEMS": { "ITEMS": {
"FIELDS": { "FIELDS": {
"attribution": {
"source": { "label": "Source" },
"page": { "label": "Page" },
"artist": { "label": "Artist" }
},
"resource": { "resource": {
"amount": { "label": "Amount" }, "amount": { "label": "Amount" },
"dieFaces": { "label": "Die Faces" }, "dieFaces": { "label": "Die Faces" },
@ -2102,7 +2133,7 @@
"FIELDS": { "FIELDS": {
"displayFear": { "label": "Fear Display" }, "displayFear": { "label": "Fear Display" },
"dualityColorScheme": { "label": "Chat Style" }, "dualityColorScheme": { "label": "Chat Style" },
"showGenericStatusEffects": { "label": "Show Foundry Status Effects" }, "hideAttribution": { "label": "Hide Attribution" },
"expandedTitle": "Auto-expand Descriptions", "expandedTitle": "Auto-expand Descriptions",
"extendCharacterDescriptions": { "label": "Characters" }, "extendCharacterDescriptions": { "label": "Characters" },
"extendAdversaryDescriptions": { "label": "Adversaries" }, "extendAdversaryDescriptions": { "label": "Adversaries" },
@ -2112,7 +2143,8 @@
"expandRollMessageDesc": { "label": "Description" }, "expandRollMessageDesc": { "label": "Description" },
"expandRollMessageRoll": { "label": "Formula" }, "expandRollMessageRoll": { "label": "Formula" },
"expandRollMessageDamage": { "label": "Damage/Healing" }, "expandRollMessageDamage": { "label": "Damage/Healing" },
"expandRollMessageTarget": { "label": "Target" } "expandRollMessageTarget": { "label": "Target" },
"showGenericStatusEffects": { "label": "Show Foundry Status Effects" }
}, },
"fearDisplay": { "fearDisplay": {
"token": "Tokens", "token": "Tokens",
@ -2208,6 +2240,10 @@
"deleteDomain": "Delete Domain", "deleteDomain": "Delete Domain",
"deleteDomainText": "Are you sure you want to delete the {name} domain? It will be immediately removed from all Actors in this world where it's currently used. Compendiums are not cleared.", "deleteDomainText": "Are you sure you want to delete the {name} domain? It will be immediately removed from all Actors in this world where it's currently used. Compendiums are not cleared.",
"duplicateDomain": "There is already a domain with this identification." "duplicateDomain": "There is already a domain with this identification."
},
"adversaryType": {
"title": "Custom Adversary Types",
"newType": "Adversary Type"
} }
}, },
"Menu": { "Menu": {
@ -2271,6 +2307,9 @@
"ResetSettings": { "ResetSettings": {
"resetConfirmationTitle": "Reset Settings", "resetConfirmationTitle": "Reset Settings",
"resetConfirmationText": "Are you sure you want to reset the {settings}?" "resetConfirmationText": "Are you sure you want to reset the {settings}?"
},
"Scene": {
"rangeMeasurementOverride": "Override Global Range Measurement Settings"
} }
}, },
"UI": { "UI": {
@ -2326,6 +2365,42 @@
"playerMessage": "{user} rerolled their {name}" "playerMessage": "{user} rerolled their {name}"
} }
}, },
"ItemBrowser": {
"title": "Daggerheart Compendium Browser",
"hint": "Select a Folder in sidebar to start browsing through the compendium",
"searchPlaceholder": "Search...",
"columnName": "Name",
"tooltipFilters": "Filters",
"tooltipErase": "Erase",
"difficultyMin": "Difficulty (Min)",
"difficultyMax": "Difficulty (Max)",
"hitPointsMin": "Hit Points (Min)",
"hitPointsMax": "Hit Points (Max)",
"stressMin": "Stress (Min)",
"stressMax": "Stress (Max)",
"armorScoreMin": "Armor Score (Min)",
"armorScoreMax": "Armor Score (Max)",
"levelMin": "Level (Min)",
"levelMax": "Level (Max)",
"recallCostMin": "Recall Cost (Min)",
"recallCostMax": "Recall Cost (Max)",
"evasionMin": "Evasion (Min)",
"evasionMax": "Evasion (Max)",
"subtype": "Subtype",
"folders": {
"adversaries": "Adversaries",
"ancestries": "Ancestries",
"equipment": "Equipment",
"classes": "Classes",
"subclasses": "Subclasses",
"domainCards": "Domain Cards",
"communities": "Communities",
"environments": "Environments",
"beastforms": "Beastforms",
"features": "Features",
"items": "Items"
}
},
"Notifications": { "Notifications": {
"adversaryMissing": "The linked adversary doesn't exist in the world.", "adversaryMissing": "The linked adversary doesn't exist in the world.",
"beastformInapplicable": "A beastform can only be applied to a Character.", "beastformInapplicable": "A beastform can only be applied to a Character.",
@ -2385,7 +2460,8 @@
"beastformEquipWeapon": "You cannot use weapons while in a Beastform.", "beastformEquipWeapon": "You cannot use weapons while in a Beastform.",
"loadoutMaxReached": "You've reached maximum loadout. Move atleast one domain card to the vault, or increase the limit in homebrew settings if desired.", "loadoutMaxReached": "You've reached maximum loadout. Move atleast one domain card to the vault, or increase the limit in homebrew settings if desired.",
"domainMaxReached": "You've reached the maximum domains for the class. Increase the limit in homebrew settings if desired.", "domainMaxReached": "You've reached the maximum domains for the class. Increase the limit in homebrew settings if desired.",
"insufficientResources": "You have insufficient resources", "insufficientResources": "You don't have enough resources to use that action.",
"actionNoUsesRemaining": "That action doesn't have remaining uses.",
"multiclassAlreadyPresent": "You already have a class and multiclass", "multiclassAlreadyPresent": "You already have a class and multiclass",
"subclassesAlreadyPresent": "You already have a class and multiclass subclass", "subclassesAlreadyPresent": "You already have a class and multiclass subclass",
"noDiceSystem": "Your selected dice {system} does not have a {faces} dice", "noDiceSystem": "Your selected dice {system} does not have a {faces} dice",
@ -2423,7 +2499,8 @@
"rulesOff": "Rules Off", "rulesOff": "Rules Off",
"remainingUses": "Uses refresh on {type}", "remainingUses": "Uses refresh on {type}",
"rightClickExtand": "Right-Click to extand", "rightClickExtand": "Right-Click to extand",
"companionPartnerLevelBlock": "The companion needs an assigned partner to level up." "companionPartnerLevelBlock": "The companion needs an assigned partner to level up.",
"configureAttribution": "Configure Attribution"
} }
} }
} }

View file

@ -2,6 +2,7 @@ export * as characterCreation from './characterCreation/_module.mjs';
export * as dialogs from './dialogs/_module.mjs'; export * as dialogs from './dialogs/_module.mjs';
export * as hud from './hud/_module.mjs'; export * as hud from './hud/_module.mjs';
export * as levelup from './levelup/_module.mjs'; export * as levelup from './levelup/_module.mjs';
export * as scene from './scene/_module.mjs';
export * as settings from './settings/_module.mjs'; export * as settings from './settings/_module.mjs';
export * as sheets from './sheets/_module.mjs'; export * as sheets from './sheets/_module.mjs';
export * as sheetConfigs from './sheets-configs/_module.mjs'; export * as sheetConfigs from './sheets-configs/_module.mjs';

View file

@ -1,3 +1,4 @@
export { default as AttributionDialog } from './attributionDialog.mjs';
export { default as BeastformDialog } from './beastformDialog.mjs'; export { default as BeastformDialog } from './beastformDialog.mjs';
export { default as d20RollDialog } from './d20RollDialog.mjs'; export { default as d20RollDialog } from './d20RollDialog.mjs';
export { default as DamageDialog } from './damageDialog.mjs'; export { default as DamageDialog } from './damageDialog.mjs';

View file

@ -0,0 +1,93 @@
import autocomplete from 'autocompleter';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class AttriubtionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(item) {
super({});
this.item = item;
this.sources = Object.keys(CONFIG.DH.GENERAL.attributionSources).flatMap(groupKey => {
const group = CONFIG.DH.GENERAL.attributionSources[groupKey];
return group.values.map(x => ({ group: group.label, ...x }));
});
}
get title() {
return game.i18n.localize('DAGGERHEART.APPLICATIONS.Attribution.title');
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'dh-style', 'dialog', 'views', 'attribution'],
position: { width: 'auto', height: 'auto' },
window: { icon: 'fa-solid fa-signature' },
form: { handler: this.updateData, submitOnChange: false, closeOnSubmit: true }
};
static PARTS = {
main: { template: 'systems/daggerheart/templates/dialogs/attribution.hbs' }
};
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
const sources = this.sources;
htmlElement.querySelectorAll('.attribution-input').forEach(element => {
autocomplete({
input: element,
fetch: function (text, update) {
if (!text) {
update(sources);
} else {
text = text.toLowerCase();
var suggestions = sources.filter(n => n.label.toLowerCase().includes(text));
update(suggestions);
}
},
render: function (item, search) {
const label = game.i18n.localize(item.label);
const matchIndex = label.toLowerCase().indexOf(search);
const beforeText = label.slice(0, matchIndex);
const matchText = label.slice(matchIndex, matchIndex + search.length);
const after = label.slice(matchIndex + search.length, label.length);
const element = document.createElement('li');
element.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
if (item.hint) {
element.dataset.tooltip = game.i18n.localize(item.hint);
}
return element;
},
renderGroup: function (label) {
const itemElement = document.createElement('div');
itemElement.textContent = game.i18n.localize(label);
return itemElement;
},
onSelect: function (item) {
element.value = item.label;
},
click: e => e.fetch(),
customize: function (_input, _inputRect, container) {
container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ;
},
minLength: 0
});
});
}
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.item = this.item;
context.data = this.item.system.attribution;
return context;
}
static async updateData(_event, _element, formData) {
await this.item.update({ 'system.attribution': formData.object });
this.item.sheet.refreshFrame();
}
}

View file

@ -1,3 +1,4 @@
export { default as CharacterLevelup } from './characterLevelup.mjs'; export { default as CharacterLevelup } from './characterLevelup.mjs';
export { default as CompanionLevelup } from './companionLevelup.mjs'; export { default as CompanionLevelup } from './companionLevelup.mjs';
export { default as Levelup } from './levelup.mjs'; export { default as Levelup } from './levelup.mjs';
export { default as LevelupViewMode } from './levelupViewMode.mjs';

View file

@ -0,0 +1,95 @@
import { chunkify } from '../../helpers/utils.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class DhlevelUpViewMode extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(actor) {
super({});
this.actor = actor;
}
get title() {
return game.i18n.format('DAGGERHEART.APPLICATIONS.Levelup.viewModeTitle', { actor: this.actor.name });
}
static DEFAULT_OPTIONS = {
classes: ['daggerheart', 'dialog', 'dh-style', 'levelup'],
position: { width: 1000, height: 'auto' },
window: {
resizable: true,
icon: 'fa-solid fa-arrow-turn-up'
}
};
static PARTS = {
main: { template: 'systems/daggerheart/templates/levelup/tabs/viewMode.hbs' }
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
const { tiers } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers);
const tierKeys = Object.keys(tiers);
const selections = Object.keys(this.actor.system.levelData.levelups).reduce(
(acc, key) => {
const level = this.actor.system.levelData.levelups[key];
Object.keys(level.selections).forEach(optionKey => {
const choice = level.selections[optionKey];
if (!acc[choice.tier][choice.optionKey]) acc[choice.tier][choice.optionKey] = {};
acc[choice.tier][choice.optionKey][choice.checkboxNr] = choice;
});
return acc;
},
tierKeys.reduce((acc, key) => {
acc[key] = {};
return acc;
}, {})
);
context.tiers = tierKeys.map((tierKey, tierIndex) => {
const tier = tiers[tierKey];
return {
name: tier.name,
active: true,
groups: Object.keys(tier.options).map(optionKey => {
const option = tier.options[optionKey];
const checkboxes = [...Array(option.checkboxSelections).keys()].flatMap(index => {
const checkboxNr = index + 1;
const checkboxData = selections[tierKey]?.[optionKey]?.[checkboxNr];
const checkbox = { ...option, checkboxNr, tier: tierKey, disabled: true };
if (checkboxData) {
checkbox.level = checkboxData.level;
checkbox.selected = true;
}
return checkbox;
});
let label = game.i18n.localize(option.label);
return {
label: label,
checkboxGroups: chunkify(checkboxes, option.minCost, chunkedBoxes => {
const anySelected = chunkedBoxes.some(x => x.selected);
const anyDisabled = chunkedBoxes.some(x => x.disabled);
return {
multi: option.minCost > 1,
checkboxes: chunkedBoxes.map(x => ({
...x,
selected: anySelected,
disabled: anyDisabled
}))
};
})
};
})
};
});
return context;
}
}

View file

@ -0,0 +1 @@
export { default as DhSceneConfigSettings } from './sceneConfigSettings.mjs';

View file

@ -0,0 +1,24 @@
export default class DhSceneConfigSettings extends foundry.applications.sheets.SceneConfig {
constructor(options, ...args) {
super(options, ...args);
}
static buildParts() {
const { footer, ...parts } = super.PARTS;
const tmpParts = {
...parts,
dh: { template: 'systems/daggerheart/templates/scene/dh-config.hbs' },
footer
};
return tmpParts;
}
static PARTS = DhSceneConfigSettings.buildParts();
static buildTabs() {
super.TABS.sheet.tabs.push({ id: 'dh', icon: 'fa-solid' });
return super.TABS;
}
static TABS = DhSceneConfigSettings.buildTabs();
}

View file

@ -1,5 +1,6 @@
import { DhHomebrew } from '../../data/settings/_module.mjs'; import { DhHomebrew } from '../../data/settings/_module.mjs';
import { slugify } from '../../helpers/utils.mjs'; import { slugify } from '../../helpers/utils.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class DhHomebrewSettings extends HandlebarsApplicationMixin(ApplicationV2) { export default class DhHomebrewSettings extends HandlebarsApplicationMixin(ApplicationV2) {
@ -10,11 +11,14 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).toObject() game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).toObject()
); );
this.selected = { this.selected = this.#getDefaultAdversaryType();
domain: null
};
} }
#getDefaultAdversaryType = () => ({
domain: null,
adversaryType: null
});
get title() { get title() {
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title'); return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
} }
@ -35,6 +39,9 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
addDomain: this.addDomain, addDomain: this.addDomain,
toggleSelectedDomain: this.toggleSelectedDomain, toggleSelectedDomain: this.toggleSelectedDomain,
deleteDomain: this.deleteDomain, deleteDomain: this.deleteDomain,
addAdversaryType: this.addAdversaryType,
deleteAdversaryType: this.deleteAdversaryType,
selectAdversaryType: this.selectAdversaryType,
save: this.save, save: this.save,
reset: this.reset reset: this.reset
}, },
@ -45,6 +52,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
settings: { template: 'systems/daggerheart/templates/settings/homebrew-settings/settings.hbs' }, settings: { template: 'systems/daggerheart/templates/settings/homebrew-settings/settings.hbs' },
domains: { template: 'systems/daggerheart/templates/settings/homebrew-settings/domains.hbs' }, domains: { template: 'systems/daggerheart/templates/settings/homebrew-settings/domains.hbs' },
types: { template: 'systems/daggerheart/templates/settings/homebrew-settings/types.hbs' },
downtime: { template: 'systems/daggerheart/templates/settings/homebrew-settings/downtime.hbs' }, downtime: { template: 'systems/daggerheart/templates/settings/homebrew-settings/downtime.hbs' },
footer: { template: 'systems/daggerheart/templates/settings/homebrew-settings/footer.hbs' } footer: { template: 'systems/daggerheart/templates/settings/homebrew-settings/footer.hbs' }
}; };
@ -52,12 +60,19 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
/** @inheritdoc */ /** @inheritdoc */
static TABS = { static TABS = {
main: { main: {
tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'downtime' }], tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'types' }, { id: 'downtime' }],
initial: 'settings', initial: 'settings',
labelPrefix: 'DAGGERHEART.GENERAL.Tabs' labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
} }
}; };
changeTab(tab, group, options) {
super.changeTab(tab, group, options);
this.selected = this.#getDefaultAdversaryType();
this.render();
}
async _prepareContext(_options) { async _prepareContext(_options) {
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.settingFields = this.settings; context.settingFields = this.settings;
@ -79,6 +94,11 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
context.configDomains = CONFIG.DH.DOMAIN.domains; context.configDomains = CONFIG.DH.DOMAIN.domains;
context.homebrewDomains = this.settings.domains; context.homebrewDomains = this.settings.domains;
break; break;
case 'types':
context.selectedAdversaryType = this.selected.adversaryType
? { id: this.selected.adversaryType, ...this.settings.adversaryTypes[this.selected.adversaryType] }
: null;
break;
} }
return context; return context;
@ -301,6 +321,32 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
this.render(); this.render();
} }
static async addAdversaryType(_, target) {
const newId = foundry.utils.randomID();
await this.settings.updateSource({
[`adversaryTypes.${newId}`]: {
id: newId,
label: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.adversaryType.newType')
}
});
this.selected.adversaryType = newId;
this.render();
}
static async deleteAdversaryType(_, target) {
const { key } = target.dataset;
await this.settings.updateSource({ [`adversaryTypes.-=${key}`]: null });
this.selected.adversaryType = this.selected.adversaryType === key ? null : this.selected.adversaryType;
this.render();
}
static async selectAdversaryType(_, target) {
this.selected.adversaryType = this.selected.adversaryType === target.dataset.type ? null : target.dataset.type;
this.render();
}
static async save() { static async save() {
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject()); await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
this.close(); this.close();

View file

@ -4,6 +4,7 @@ import DHBaseActorSheet from '../api/base-actor.mjs';
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ /**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
export default class AdversarySheet extends DHBaseActorSheet { export default class AdversarySheet extends DHBaseActorSheet {
/** @inheritDoc */
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
classes: ['adversary'], classes: ['adversary'],
position: { width: 660, height: 766 }, position: { width: 660, height: 766 },
@ -12,16 +13,34 @@ export default class AdversarySheet extends DHBaseActorSheet {
reactionRoll: AdversarySheet.#reactionRoll reactionRoll: AdversarySheet.#reactionRoll
}, },
window: { window: {
resizable: true resizable: true,
controls: [
{
icon: 'fa-solid fa-signature',
label: 'DAGGERHEART.UI.Tooltip.configureAttribution',
action: 'editAttribution'
}
]
} }
}; };
static PARTS = { static PARTS = {
sidebar: { template: 'systems/daggerheart/templates/sheets/actors/adversary/sidebar.hbs' }, sidebar: {
template: 'systems/daggerheart/templates/sheets/actors/adversary/sidebar.hbs',
scrollable: ['.shortcut-items-section']
},
header: { template: 'systems/daggerheart/templates/sheets/actors/adversary/header.hbs' }, header: { template: 'systems/daggerheart/templates/sheets/actors/adversary/header.hbs' },
features: { template: 'systems/daggerheart/templates/sheets/actors/adversary/features.hbs' }, features: {
notes: { template: 'systems/daggerheart/templates/sheets/actors/adversary/notes.hbs' }, template: 'systems/daggerheart/templates/sheets/actors/adversary/features.hbs',
effects: { template: 'systems/daggerheart/templates/sheets/actors/adversary/effects.hbs' } scrollable: ['.feature-section']
},
notes: {
template: 'systems/daggerheart/templates/sheets/actors/adversary/notes.hbs'
},
effects: {
template: 'systems/daggerheart/templates/sheets/actors/adversary/effects.hbs',
scrollable: ['.effects-sections']
}
}; };
/** @inheritdoc */ /** @inheritdoc */
@ -37,6 +56,7 @@ export default class AdversarySheet extends DHBaseActorSheet {
async _prepareContext(options) { async _prepareContext(options) {
const context = await super._prepareContext(options); const context = await super._prepareContext(options);
context.systemFields.attack.fields = this.document.system.attack.schema.fields; context.systemFields.attack.fields = this.document.system.attack.schema.fields;
return context; return context;
} }
@ -46,6 +66,9 @@ export default class AdversarySheet extends DHBaseActorSheet {
switch (partId) { switch (partId) {
case 'header': case 'header':
await this._prepareHeaderContext(context, options); await this._prepareHeaderContext(context, options);
const adversaryTypes = CONFIG.DH.ACTOR.allAdversaryTypes();
context.adversaryType = game.i18n.localize(adversaryTypes[this.document.system.type].label);
break; break;
case 'notes': case 'notes':
await this._prepareNotesContext(context, options); await this._prepareNotesContext(context, options);
@ -60,6 +83,7 @@ export default class AdversarySheet extends DHBaseActorSheet {
htmlElement.querySelectorAll('.inventory-item-resource').forEach(element => { htmlElement.querySelectorAll('.inventory-item-resource').forEach(element => {
element.addEventListener('change', this.updateItemResource.bind(this)); element.addEventListener('change', this.updateItemResource.bind(this));
element.addEventListener('click', e => e.stopPropagation());
}); });
} }

View file

@ -1,7 +1,7 @@
import DHBaseActorSheet from '../api/base-actor.mjs'; import DHBaseActorSheet from '../api/base-actor.mjs';
import DhpDeathMove from '../../dialogs/deathMove.mjs'; import DhpDeathMove from '../../dialogs/deathMove.mjs';
import { abilities } from '../../../config/actorConfig.mjs'; import { abilities } from '../../../config/actorConfig.mjs';
import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs'; import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs';
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs'; import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
import FilterMenu from '../../ux/filter-menu.mjs'; import FilterMenu from '../../ux/filter-menu.mjs';
import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs'; import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs';
@ -23,6 +23,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
openPack: CharacterSheet.#openPack, openPack: CharacterSheet.#openPack,
makeDeathMove: CharacterSheet.#makeDeathMove, makeDeathMove: CharacterSheet.#makeDeathMove,
levelManagement: CharacterSheet.#levelManagement, levelManagement: CharacterSheet.#levelManagement,
viewLevelups: CharacterSheet.#viewLevelups,
toggleEquipItem: CharacterSheet.#toggleEquipItem, toggleEquipItem: CharacterSheet.#toggleEquipItem,
toggleResourceDice: CharacterSheet.#toggleResourceDice, toggleResourceDice: CharacterSheet.#toggleResourceDice,
handleResourceDice: CharacterSheet.#handleResourceDice, handleResourceDice: CharacterSheet.#handleResourceDice,
@ -30,7 +31,14 @@ export default class CharacterSheet extends DHBaseActorSheet {
tempBrowser: CharacterSheet.#tempBrowser tempBrowser: CharacterSheet.#tempBrowser
}, },
window: { window: {
resizable: true resizable: true,
controls: [
{
icon: 'fa-solid fa-angles-up',
label: 'DAGGERHEART.ACTORS.Character.viewLevelups',
action: 'viewLevelups'
}
]
}, },
dragDrop: [ dragDrop: [
{ {
@ -70,6 +78,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
static PARTS = { static PARTS = {
sidebar: { sidebar: {
id: 'sidebar', id: 'sidebar',
scrollable: ['.shortcut-items-section'],
template: 'systems/daggerheart/templates/sheets/actors/character/sidebar.hbs' template: 'systems/daggerheart/templates/sheets/actors/character/sidebar.hbs'
}, },
header: { header: {
@ -78,22 +87,27 @@ export default class CharacterSheet extends DHBaseActorSheet {
}, },
features: { features: {
id: 'features', id: 'features',
scrollable: ['.features-sections'],
template: 'systems/daggerheart/templates/sheets/actors/character/features.hbs' template: 'systems/daggerheart/templates/sheets/actors/character/features.hbs'
}, },
loadout: { loadout: {
id: 'loadout', id: 'loadout',
scrollable: ['.items-section'],
template: 'systems/daggerheart/templates/sheets/actors/character/loadout.hbs' template: 'systems/daggerheart/templates/sheets/actors/character/loadout.hbs'
}, },
inventory: { inventory: {
id: 'inventory', id: 'inventory',
scrollable: ['.items-section'],
template: 'systems/daggerheart/templates/sheets/actors/character/inventory.hbs' template: 'systems/daggerheart/templates/sheets/actors/character/inventory.hbs'
}, },
biography: { biography: {
id: 'biography', id: 'biography',
scrollable: ['.items-section'],
template: 'systems/daggerheart/templates/sheets/actors/character/biography.hbs' template: 'systems/daggerheart/templates/sheets/actors/character/biography.hbs'
}, },
effects: { effects: {
id: 'effects', id: 'effects',
scrollable: ['.effects-sections'],
template: 'systems/daggerheart/templates/sheets/actors/character/effects.hbs' template: 'systems/daggerheart/templates/sheets/actors/character/effects.hbs'
} }
}; };
@ -114,6 +128,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
htmlElement.querySelectorAll('.inventory-item-resource').forEach(element => { htmlElement.querySelectorAll('.inventory-item-resource').forEach(element => {
element.addEventListener('change', this.updateItemResource.bind(this)); element.addEventListener('change', this.updateItemResource.bind(this));
element.addEventListener('click', e => e.stopPropagation());
}); });
htmlElement.querySelectorAll('.inventory-item-quantity').forEach(element => { htmlElement.querySelectorAll('.inventory-item-quantity').forEach(element => {
element.addEventListener('change', this.updateItemQuantity.bind(this)); element.addEventListener('change', this.updateItemQuantity.bind(this));
@ -585,7 +600,14 @@ export default class CharacterSheet extends DHBaseActorSheet {
if (!value || !subclass) if (!value || !subclass)
return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.missingClassOrSubclass')); return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.missingClassOrSubclass'));
new DhCharacterlevelUp(this.document).render({ force: true }); new CharacterLevelup(this.document).render({ force: true });
}
/**
* Opens the charater level management window in viewMode.
*/
static #viewLevelups() {
new LevelupViewMode(this.document).render({ force: true });
} }
/** /**

View file

@ -15,7 +15,10 @@ export default class DhCompanionSheet extends DHBaseActorSheet {
static PARTS = { static PARTS = {
header: { template: 'systems/daggerheart/templates/sheets/actors/companion/header.hbs' }, header: { template: 'systems/daggerheart/templates/sheets/actors/companion/header.hbs' },
details: { template: 'systems/daggerheart/templates/sheets/actors/companion/details.hbs' }, details: { template: 'systems/daggerheart/templates/sheets/actors/companion/details.hbs' },
effects: { template: 'systems/daggerheart/templates/sheets/actors/companion/effects.hbs' } effects: {
template: 'systems/daggerheart/templates/sheets/actors/companion/effects.hbs',
scrollable: ['.effects-sections']
}
}; };
/* -------------------------------------------- */ /* -------------------------------------------- */

View file

@ -8,10 +8,17 @@ export default class DhpEnvironment extends DHBaseActorSheet {
classes: ['environment'], classes: ['environment'],
position: { position: {
width: 500, width: 500,
height: 725 height: 740
}, },
window: { window: {
resizable: true resizable: true,
controls: [
{
icon: 'fa-solid fa-signature',
label: 'DAGGERHEART.UI.Tooltip.configureAttribution',
action: 'editAttribution'
}
]
}, },
actions: {}, actions: {},
dragDrop: [{ dragSelector: '.action-section .inventory-item', dropSelector: null }] dragDrop: [{ dragSelector: '.action-section .inventory-item', dropSelector: null }]
@ -20,9 +27,13 @@ export default class DhpEnvironment extends DHBaseActorSheet {
/**@override */ /**@override */
static PARTS = { static PARTS = {
header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' }, header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' },
features: { template: 'systems/daggerheart/templates/sheets/actors/environment/features.hbs' }, features: {
template: 'systems/daggerheart/templates/sheets/actors/environment/features.hbs',
scrollable: ['feature-section']
},
potentialAdversaries: { potentialAdversaries: {
template: 'systems/daggerheart/templates/sheets/actors/environment/potentialAdversaries.hbs' template: 'systems/daggerheart/templates/sheets/actors/environment/potentialAdversaries.hbs',
scrollable: ['items-sections']
}, },
notes: { template: 'systems/daggerheart/templates/sheets/actors/environment/notes.hbs' } notes: { template: 'systems/daggerheart/templates/sheets/actors/environment/notes.hbs' }
}; };
@ -42,6 +53,7 @@ export default class DhpEnvironment extends DHBaseActorSheet {
switch (partId) { switch (partId) {
case 'header': case 'header':
await this._prepareHeaderContext(context, options); await this._prepareHeaderContext(context, options);
break; break;
case 'notes': case 'notes':
await this._prepareNotesContext(context, options); await this._prepareNotesContext(context, options);

View file

@ -85,6 +85,8 @@ export default function DHApplicationMixin(Base) {
this._dragDrop = this._createDragDropHandlers(); this._dragDrop = this._createDragDropHandlers();
} }
#nonHeaderAttribution = ['environment', 'ancestry', 'community', 'domainCard'];
/** /**
* The default options for the sheet. * The default options for the sheet.
* @type {DHSheetV2Configuration} * @type {DHSheetV2Configuration}
@ -101,7 +103,8 @@ export default function DHApplicationMixin(Base) {
toggleEffect: DHSheetV2.#toggleEffect, toggleEffect: DHSheetV2.#toggleEffect,
toggleExtended: DHSheetV2.#toggleExtended, toggleExtended: DHSheetV2.#toggleExtended,
addNewItem: DHSheetV2.#addNewItem, addNewItem: DHSheetV2.#addNewItem,
browseItem: DHSheetV2.#browseItem browseItem: DHSheetV2.#browseItem,
editAttribution: DHSheetV2.#editAttribution
}, },
contextMenus: [ contextMenus: [
{ {
@ -121,10 +124,47 @@ export default function DHApplicationMixin(Base) {
} }
} }
], ],
dragDrop: [], dragDrop: [{ dragSelector: '.inventory-item[data-type="effect"]', dropSelector: null }],
tagifyConfigs: [] tagifyConfigs: []
}; };
/**@inheritdoc */
async _renderFrame(options) {
const frame = await super._renderFrame(options);
const hideAttribution = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.appearance
).hideAttribution;
const headerAttribution = !this.#nonHeaderAttribution.includes(this.document.type);
if (!hideAttribution && this.document.system.metadata.hasAttribution && headerAttribution) {
const { source, page } = this.document.system.attribution;
const attribution = [source, page ? `pg ${page}.` : null].filter(x => x).join('. ');
const element = `<label class="attribution-header-label">${attribution}</label>`;
this.window.controls.insertAdjacentHTML('beforebegin', element);
}
return frame;
}
/**
* Refresh the custom parts of the application frame
*/
refreshFrame() {
const hideAttribution = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.appearance
).hideAttribution;
const headerAttribution = !this.#nonHeaderAttribution.includes(this.document.type);
if (!hideAttribution && this.document.system.metadata.hasAttribution && headerAttribution) {
const { source, page } = this.document.system.attribution;
const attribution = [source, page ? `pg ${page}.` : null].filter(x => x).join('. ');
const label = this.window.header.querySelector('.attribution-header-label');
label.innerHTML = attribution;
}
}
/** /**
* Related documents that should cause a rerender of this application when updated. * Related documents that should cause a rerender of this application when updated.
*/ */
@ -249,14 +289,37 @@ export default function DHApplicationMixin(Base) {
* @param {DragEvent} event * @param {DragEvent} event
* @protected * @protected
*/ */
_onDragStart(event) {} async _onDragStart(event) {
const inventoryItem = event.currentTarget.closest('.inventory-item');
if (inventoryItem) {
const { type, itemUuid } = inventoryItem.dataset;
if (type === 'effect') {
const effect = await foundry.utils.fromUuid(itemUuid);
const effectData = {
type: 'ActiveEffect',
data: { ...effect.toObject(), _id: null },
fromInternal: this.document.uuid
};
event.dataTransfer.setData('text/plain', JSON.stringify(effectData));
event.dataTransfer.setDragImage(inventoryItem.querySelector('img'), 60, 0);
}
}
}
/** /**
* Handle drop event. * Handle drop event.
* @param {DragEvent} event * @param {DragEvent} event
* @protected * @protected
*/ */
_onDrop(event) {} _onDrop(event) {
event.stopPropagation();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
if (data.fromInternal === this.document.uuid) return;
if (data.type === 'ActiveEffect') {
this.document.createEmbeddedDocuments('ActiveEffect', [data.data]);
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Context Menu */ /* Context Menu */
@ -548,6 +611,14 @@ export default function DHApplicationMixin(Base) {
return new ItemBrowser({ presets }).render({ force: true }); return new ItemBrowser({ presets }).render({ force: true });
} }
/**
* Open the attribution dialog
* @type {ApplicationClickAction}
*/
static async #editAttribution() {
new game.system.api.applications.dialogs.AttributionDialog(this.document).render({ force: true });
}
/** /**
* Create an embedded document. * Create an embedded document.
* @type {ApplicationClickAction} * @type {ApplicationClickAction}
@ -568,7 +639,6 @@ export default function DHApplicationMixin(Base) {
if (featureOnCharacter) { if (featureOnCharacter) {
systemData = { systemData = {
originItemType: this.document.type, originItemType: this.document.type,
originId: this.document.id,
identifier: this.document.system.isMulticlass ? 'multiclass' : null identifier: this.document.system.isMulticlass ? 'multiclass' : null
}; };
} }

View file

@ -55,6 +55,9 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
async _prepareContext(_options) { async _prepareContext(_options) {
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.isNPC = this.document.isNPC; context.isNPC = this.document.isNPC;
context.showAttribution = !game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
.hideAttribution;
return context; return context;
} }
@ -195,6 +198,8 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
}; };
event.dataTransfer.setData('text/plain', JSON.stringify(attackData)); event.dataTransfer.setData('text/plain', JSON.stringify(attackData));
event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0); event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0);
} else if (this.document.type !== 'environment') {
super._onDragStart(event);
} }
} }
} }

View file

@ -13,7 +13,16 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
classes: ['item'], classes: ['item'],
position: { width: 600 }, position: { width: 600 },
window: { resizable: true }, window: {
resizable: true,
controls: [
{
icon: 'fa-solid fa-signature',
label: 'DAGGERHEART.UI.Tooltip.configureAttribution',
action: 'editAttribution'
}
]
},
form: { form: {
submitOnChange: true submitOnChange: true
}, },
@ -55,6 +64,15 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
/* Prepare Context */ /* Prepare Context */
/* -------------------------------------------- */ /* -------------------------------------------- */
/**@inheritdoc */
async _prepareContext(options) {
const context = super._prepareContext(options);
context.showAttribution = !game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
.hideAttribution;
return context;
}
/**@inheritdoc */ /**@inheritdoc */
async _preparePartContext(partId, context, options) { async _preparePartContext(partId, context, options) {
await super._preparePartContext(partId, context, options); await super._preparePartContext(partId, context, options);
@ -149,12 +167,12 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
const { type } = target.dataset; const { type } = target.dataset;
const cls = foundry.documents.Item.implementation; const cls = foundry.documents.Item.implementation;
const multiclass = this.document.system.isMulticlass ? 'multiclass' : null;
let systemData = {}; let systemData = {};
if (this.document.parent?.type === 'character') { if (this.document.parent?.type === 'character') {
systemData = { systemData = {
originItemType: this.document.type, originItemType: this.document.type,
originId: this.document.id, identifier: multiclass ?? type
identifier: this.document.system.isMulticlass ? 'multiclass' : null
}; };
} }
@ -252,6 +270,8 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
}; };
event.dataTransfer.setData('text/plain', JSON.stringify(actionData)); event.dataTransfer.setData('text/plain', JSON.stringify(actionData));
event.dataTransfer.setDragImage(actionItem.querySelector('img'), 60, 0); event.dataTransfer.setDragImage(actionItem.querySelector('img'), 60, 0);
} else {
super._onDragStart(event);
} }
} }
} }
@ -261,6 +281,8 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
* @param {DragEvent} event - The drag event * @param {DragEvent} event - The drag event
*/ */
async _onDrop(event) { async _onDrop(event) {
super._onDrop(event);
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
if (data.fromInternal) return; if (data.fromInternal) return;
@ -271,14 +293,15 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
if (this.document.parent?.type === 'character') { if (this.document.parent?.type === 'character') {
const itemData = item.toObject(); const itemData = item.toObject();
const multiclass = this.document.system.isMulticlass ? 'multiclass' : null;
item = await cls.create( item = await cls.create(
{ {
...itemData, ...itemData,
_stats: { compendiumSource: this.document.uuid },
system: { system: {
...itemData.system, ...itemData.system,
originItemType: this.document.type, originItemType: this.document.type,
originId: this.document.id, identifier: multiclass ?? target.dataset.type
identifier: this.document.system.isMulticlass ? 'multiclass' : null
} }
}, },
{ parent: this.document.parent } { parent: this.document.parent }

View file

@ -41,7 +41,7 @@ export default function ItemAttachmentSheet(Base) {
} }
async _onDrop(event) { async _onDrop(event) {
const data = TextEditor.getDragEventData(event); const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
const attachmentsSection = event.target.closest('.attachments-section'); const attachmentsSection = event.target.closest('.attachments-section');
if (!attachmentsSection) return super._onDrop(event); if (!attachmentsSection) return super._onDrop(event);

View file

@ -27,6 +27,9 @@ export default class AncestrySheet extends DHHeritageSheet {
* @param {DragEvent} event - The drag event * @param {DragEvent} event - The drag event
*/ */
async _onDrop(event) { async _onDrop(event) {
const data = TextEditor.getDragEventData(event);
if (data.type === 'ActiveEffect') return super._onDrop(event);
const target = event.target.closest('fieldset.drop-section'); const target = event.target.closest('fieldset.drop-section');
const typeField = const typeField =
this.document.system[target.dataset.type === 'primary' ? 'primaryFeature' : 'secondaryFeature']; this.document.system[target.dataset.type === 'primary' ? 'primaryFeature' : 'secondaryFeature'];

View file

@ -115,16 +115,17 @@ export default class ClassSheet extends DHBaseItemSheet {
async _onDrop(event) { async _onDrop(event) {
event.stopPropagation(); event.stopPropagation();
const data = TextEditor.getDragEventData(event); const data = TextEditor.getDragEventData(event);
const item = await fromUuid(data.uuid); const item = data.data ?? (await fromUuid(data.uuid));
const itemType = data.data ? data.type : item.type;
const target = event.target.closest('fieldset.drop-section'); const target = event.target.closest('fieldset.drop-section');
if (item.type === 'subclass') { if (itemType === 'subclass') {
await this.document.update({ await this.document.update({
'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid] 'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid]
}); });
} else if (item.type === 'feature') { } else if (['feature', 'ActiveEffect'].includes(itemType)) {
super._onDrop(event); super._onDrop(event);
} else if (this.document.parent?.type !== 'character') { } else if (this.document.parent?.type !== 'character') {
if (item.type === 'weapon') { if (itemType === 'weapon') {
if (target.classList.contains('primary-weapon-section')) { if (target.classList.contains('primary-weapon-section')) {
if (!item.system.secondary) if (!item.system.secondary)
await this.document.update({ await this.document.update({
@ -136,21 +137,21 @@ export default class ClassSheet extends DHBaseItemSheet {
'system.characterGuide.suggestedSecondaryWeapon': item.uuid 'system.characterGuide.suggestedSecondaryWeapon': item.uuid
}); });
} }
} else if (item.type === 'armor') { } else if (itemType === 'armor') {
if (target.classList.contains('armor-section')) { if (target.classList.contains('armor-section')) {
await this.document.update({ await this.document.update({
'system.characterGuide.suggestedArmor': item.uuid 'system.characterGuide.suggestedArmor': item.uuid
}); });
} }
} else if (target.classList.contains('choice-a-section')) { } else if (target.classList.contains('choice-a-section')) {
if (item.type === 'loot' || item.type === 'consumable') { if (itemType === 'loot' || itemType === 'consumable') {
const filteredChoiceA = this.document.system.inventory.choiceA; const filteredChoiceA = this.document.system.inventory.choiceA;
if (filteredChoiceA.length < 2) if (filteredChoiceA.length < 2)
await this.document.update({ await this.document.update({
'system.inventory.choiceA': [...filteredChoiceA.map(x => x.uuid), item.uuid] 'system.inventory.choiceA': [...filteredChoiceA.map(x => x.uuid), item.uuid]
}); });
} }
} else if (item.type === 'loot') { } else if (itemType === 'loot') {
if (target.classList.contains('take-section')) { if (target.classList.contains('take-section')) {
const filteredTake = this.document.system.inventory.take.filter(x => x); const filteredTake = this.document.system.inventory.take.filter(x => x);
if (filteredTake.length < 3) if (filteredTake.length < 3)

View file

@ -108,7 +108,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
async onRollDamage(event, message) { async onRollDamage(event, message) {
event.stopPropagation(); event.stopPropagation();
const actor = await this.getActor(message.system.source.actor); const actor = await this.getActor(message.system.source.actor);
if (game.user.character?.id !== actor.id && !game.user.isGM) return true; if (!actor.isOwner) return true;
if (message.system.source.item && message.system.source.action) { if (message.system.source.item && message.system.source.action) {
const action = this.getAction(actor, message.system.source.item, message.system.source.action); const action = this.getAction(actor, message.system.source.item, message.system.source.action);
if (!action || !action?.rollDamage) return; if (!action || !action?.rollDamage) return;

View file

@ -124,11 +124,11 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
_attachPartListeners(partId, htmlElement, options) { _attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options); super._attachPartListeners(partId, htmlElement, options);
htmlElement htmlElement.querySelectorAll('[data-action="selectFolder"]').forEach(element =>
.querySelectorAll('[data-action="selectFolder"]') element.addEventListener('contextmenu', event => {
.forEach(element => element.addEventListener("contextmenu", (event) => {
event.target.classList.toggle('expanded'); event.target.classList.toggle('expanded');
})) })
);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -154,7 +154,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
Object.values(config).forEach(c => { Object.values(config).forEach(c => {
const folder = { const folder = {
id: c.id, id: c.id,
label: c.label, label: game.i18n.localize(c.label),
selected: (!parent || parent.selected) && this.selectedMenu.path[depth] === c.id selected: (!parent || parent.selected) && this.selectedMenu.path[depth] === c.id
}; };
folder.folders = c.folders folder.folders = c.folders
@ -173,11 +173,16 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
folderPath = `${compendium}.folders.${folderId}`, folderPath = `${compendium}.folders.${folderId}`,
folderData = foundry.utils.getProperty(config, folderPath); folderData = foundry.utils.getProperty(config, folderPath);
const columns = ItemBrowser.getFolderConfig(folderData).map(col => ({
...col,
label: game.i18n.localize(col.label)
}));
this.selectedMenu = { this.selectedMenu = {
path: folderPath.split('.'), path: folderPath.split('.'),
data: { data: {
...folderData, ...folderData,
columns: ItemBrowser.getFolderConfig(folderData) columns: columns
} }
}; };
@ -190,8 +195,11 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
this.items = ItemBrowser.sortBy(items, 'name'); this.items = ItemBrowser.sortBy(items, 'name');
if(target) { if (target) {
target.closest('.compendium-sidebar').querySelectorAll('[data-action="selectFolder"]').forEach(element => element.classList.remove("is-selected")) target
.closest('.compendium-sidebar')
.querySelectorAll('[data-action="selectFolder"]')
.forEach(element => element.classList.remove('is-selected'));
target.classList.add('is-selected'); target.classList.add('is-selected');
} }
@ -199,7 +207,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
} }
_replaceHTML(result, content, options) { _replaceHTML(result, content, options) {
if(!options.isFirstRender) delete result.sidebar; if (!options.isFirstRender) delete result.sidebar;
super._replaceHTML(result, content, options); super._replaceHTML(result, content, options);
} }
@ -237,6 +245,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
else if (typeof f.choices === 'function') { else if (typeof f.choices === 'function') {
f.choices = f.choices(); f.choices = f.choices();
} }
// Clear field label so template uses our custom label parameter
if (f.field && f.label) {
f.field.label = undefined;
}
f.name ??= f.key; f.name ??= f.key;
f.value = this.presets?.filter?.[f.name]?.value ?? null; f.value = this.presets?.filter?.[f.name]?.value ?? null;
}); });

View file

@ -10,29 +10,41 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur
const splitRulerText = this.ruler.text.split(' '); const splitRulerText = this.ruler.text.split(' ');
if (splitRulerText.length > 0) { if (splitRulerText.length > 0) {
const rulerValue = Number(splitRulerText[0]); const rulerValue = Number(splitRulerText[0]);
const vagueLabel = this.constructor.getDistanceLabel(rulerValue, rangeMeasurementSettings); const result = DhMeasuredTemplate.getRangeLabels(rulerValue, rangeMeasurementSettings);
this.ruler.text = vagueLabel; this.ruler.text = result.distance + (result.units ? ' ' + result.units : '');
} }
} }
} }
static getDistanceLabel(distance, settings) { static getRangeLabels(distanceValue, settings) {
if (distance <= settings.melee) { let result = { distance: distanceValue, units: '' };
return game.i18n.localize('DAGGERHEART.CONFIG.Range.melee.name'); const rangeMeasurementOverride = canvas.scene.flags.daggerheart?.rangeMeasurementOverride;
if (rangeMeasurementOverride === true) {
result.distance = distanceValue;
result.units = canvas.scene?.grid?.units;
return result;
} }
if (distance <= settings.veryClose) { if (distanceValue <= settings.melee) {
return game.i18n.localize('DAGGERHEART.CONFIG.Range.veryClose.name'); result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.melee.name');
return result;
} }
if (distance <= settings.close) { if (distanceValue <= settings.veryClose) {
return game.i18n.localize('DAGGERHEART.CONFIG.Range.close.name'); result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.veryClose.name');
return result;
} }
if (distance <= settings.far) { if (distanceValue <= settings.close) {
return game.i18n.localize('DAGGERHEART.CONFIG.Range.far.name'); result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.close.name');
return result;
} }
if (distance > settings.far) { if (distanceValue <= settings.far) {
return game.i18n.localize('DAGGERHEART.CONFIG.Range.veryFar.name'); result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.far.name');
return result;
}
if (distanceValue > settings.far) {
result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.veryFar.name');
} }
return ''; return result;
} }
} }

View file

@ -8,9 +8,9 @@ export default class DhpRuler extends foundry.canvas.interaction.Ruler {
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement; const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement;
if (range.enabled) { if (range.enabled) {
const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range); const result = DhMeasuredTemplate.getRangeLabels(waypoint.measurement.distance.toNearest(0.01), range);
context.cost = { total: distance, units: null }; context.cost = { total: result.distance, units: result.units };
context.distance = { total: distance, units: null }; context.distance = { total: result.distance, units: result.units };
} }
return context; return context;

View file

@ -8,9 +8,9 @@ export default class DhpTokenRuler extends foundry.canvas.placeables.tokens.Toke
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement; const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement;
if (range.enabled) { if (range.enabled) {
const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range); const result = DhMeasuredTemplate.getRangeLabels(waypoint.measurement.distance.toNearest(0.01), range);
context.cost = { total: distance, units: null }; context.cost = { total: result.distance, units: result.units };
context.distance = { total: distance, units: null }; context.distance = { total: result.distance, units: result.units };
} }
return context; return context;

View file

@ -157,6 +157,11 @@ export const adversaryTypes = {
} }
}; };
export const allAdversaryTypes = () => ({
...adversaryTypes,
...game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).adversaryTypes
});
export const environmentTypes = { export const environmentTypes = {
exploration: { exploration: {
label: 'DAGGERHEART.CONFIG.EnvironmentType.exploration.label', label: 'DAGGERHEART.CONFIG.EnvironmentType.exploration.label',

View file

@ -624,6 +624,13 @@ export const rollTypes = {
} }
}; };
export const attributionSources = {
daggerheart: {
label: 'Daggerheart',
values: [{ label: 'Daggerheart SRD' }]
}
};
export const fearDisplay = { export const fearDisplay = {
token: { value: 'token', label: 'DAGGERHEART.SETTINGS.Appearance.fearDisplay.token' }, token: { value: 'token', label: 'DAGGERHEART.SETTINGS.Appearance.fearDisplay.token' },
bar: { value: 'bar', label: 'DAGGERHEART.SETTINGS.Appearance.fearDisplay.bar' }, bar: { value: 'bar', label: 'DAGGERHEART.SETTINGS.Appearance.fearDisplay.bar' },

View file

@ -2,270 +2,278 @@ export const typeConfig = {
adversaries: { adversaries: {
columns: [ columns: [
{ {
key: "system.tier", key: 'system.tier',
label: "Tier" label: 'DAGGERHEART.GENERAL.Tiers.singular'
}, },
{ {
key: "system.type", key: 'system.type',
label: "Type" label: 'DAGGERHEART.GENERAL.type'
} }
], ],
filters: [ filters: [
{ {
key: "system.tier", key: 'system.tier',
label: "Tier", label: 'DAGGERHEART.GENERAL.Tiers.singular',
field: 'system.api.models.actors.DhAdversary.schema.fields.tier' field: 'system.api.models.actors.DhAdversary.schema.fields.tier'
}, },
{ {
key: "system.type", key: 'system.type',
label: "Type", label: 'DAGGERHEART.GENERAL.type',
field: 'system.api.models.actors.DhAdversary.schema.fields.type' field: 'system.api.models.actors.DhAdversary.schema.fields.type'
}, },
{ {
key: "system.difficulty", key: 'system.difficulty',
name: "difficulty.min", name: 'difficulty.min',
label: "Difficulty (Min)", label: 'DAGGERHEART.UI.ItemBrowser.difficultyMin',
field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty', field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty',
operator: "gte" operator: 'gte'
}, },
{ {
key: "system.difficulty", key: 'system.difficulty',
name: "difficulty.max", name: 'difficulty.max',
label: "Difficulty (Max)", label: 'DAGGERHEART.UI.ItemBrowser.difficultyMax',
field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty', field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty',
operator: "lte" operator: 'lte'
}, },
{ {
key: "system.resources.hitPoints.max", key: 'system.resources.hitPoints.max',
name: "hp.min", name: 'hp.min',
label: "Hit Points (Min)", label: 'DAGGERHEART.UI.ItemBrowser.hitPointsMin',
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max', field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max',
operator: "gte" operator: 'gte'
}, },
{ {
key: "system.resources.hitPoints.max", key: 'system.resources.hitPoints.max',
name: "hp.max", name: 'hp.max',
label: "Hit Points (Max)", label: 'DAGGERHEART.UI.ItemBrowser.hitPointsMax',
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max', field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max',
operator: "lte" operator: 'lte'
}, },
{ {
key: "system.resources.stress.max", key: 'system.resources.stress.max',
name: "stress.min", name: 'stress.min',
label: "Stress (Min)", label: 'DAGGERHEART.UI.ItemBrowser.stressMin',
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max', field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max',
operator: "gte" operator: 'gte'
}, },
{ {
key: "system.resources.stress.max", key: 'system.resources.stress.max',
name: "stress.max", name: 'stress.max',
label: "Stress (Max)", label: 'DAGGERHEART.UI.ItemBrowser.stressMax',
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max', field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max',
operator: "lte" operator: 'lte'
}, }
] ]
}, },
items: { items: {
columns: [ columns: [
{ {
key: "type", key: 'type',
label: "Type" label: 'DAGGERHEART.GENERAL.type'
}, },
{ {
key: "system.secondary", key: 'system.secondary',
label: "Subtype", label: 'DAGGERHEART.UI.ItemBrowser.subtype',
format: (isSecondary) => isSecondary ? "secondary" : (isSecondary === false ? "primary" : '-') format: isSecondary => (isSecondary ? 'secondary' : isSecondary === false ? 'primary' : '-')
}, },
{ {
key: "system.tier", key: 'system.tier',
label: "Tier" label: 'DAGGERHEART.GENERAL.Tiers.singular'
} }
], ],
filters: [ filters: [
{ {
key: "type", key: 'type',
label: "Type", label: 'DAGGERHEART.GENERAL.type',
choices: () => CONFIG.Item.documentClass.TYPES.filter(t => ["armor", "weapon", "consumable", "loot"].includes(t)).map(t => ({ value: t, label: t })) choices: () =>
CONFIG.Item.documentClass.TYPES.filter(t =>
['armor', 'weapon', 'consumable', 'loot'].includes(t)
).map(t => ({ value: t, label: t }))
}, },
{ {
key: "system.secondary", key: 'system.secondary',
label: "Subtype", label: 'DAGGERHEART.UI.ItemBrowser.subtype',
choices: [ choices: [
{ value: false, label: "Primary Weapon"}, { value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon' },
{ value: true, label: "Secondary Weapon"} { value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon' }
] ]
}, },
{ {
key: "system.tier", key: 'system.tier',
label: "Tier", label: 'DAGGERHEART.GENERAL.Tiers.singular',
choices: [{ value: "1", label: "1"}, { value: "2", label: "2"}, { value: "3", label: "3"}, { value: "4", label: "4"}] choices: [
{ value: '1', label: '1' },
{ value: '2', label: '2' },
{ value: '3', label: '3' },
{ value: '4', label: '4' }
]
}, },
{ {
key: "system.burden", key: 'system.burden',
label: "Burden", label: 'DAGGERHEART.GENERAL.burden',
field: 'system.api.models.items.DHWeapon.schema.fields.burden' field: 'system.api.models.items.DHWeapon.schema.fields.burden'
}, },
{ {
key: "system.attack.roll.trait", key: 'system.attack.roll.trait',
label: "Trait", label: 'DAGGERHEART.GENERAL.Trait.single',
field: 'system.api.models.actions.actionsTypes.attack.schema.fields.roll.fields.trait' field: 'system.api.models.actions.actionsTypes.attack.schema.fields.roll.fields.trait'
}, },
{ {
key: "system.attack.range", key: 'system.attack.range',
label: "Range", label: 'DAGGERHEART.GENERAL.range',
field: 'system.api.models.actions.actionsTypes.attack.schema.fields.range' field: 'system.api.models.actions.actionsTypes.attack.schema.fields.range'
}, },
{ {
key: "system.baseScore", key: 'system.baseScore',
name: "armor.min", name: 'armor.min',
label: "Armor Score (Min)", label: 'DAGGERHEART.UI.ItemBrowser.armorScoreMin',
field: 'system.api.models.items.DHArmor.schema.fields.baseScore', field: 'system.api.models.items.DHArmor.schema.fields.baseScore',
operator: "gte" operator: 'gte'
}, },
{ {
key: "system.baseScore", key: 'system.baseScore',
name: "armor.max", name: 'armor.max',
label: "Armor Score (Max)", label: 'DAGGERHEART.UI.ItemBrowser.armorScoreMax',
field: 'system.api.models.items.DHArmor.schema.fields.baseScore', field: 'system.api.models.items.DHArmor.schema.fields.baseScore',
operator: "lte" operator: 'lte'
}, },
{ {
key: "system.itemFeatures", key: 'system.itemFeatures',
label: "Features", label: 'DAGGERHEART.GENERAL.features',
choices: () => [...Object.entries(CONFIG.DH.ITEM.weaponFeatures), ...Object.entries(CONFIG.DH.ITEM.armorFeatures)].map(([k,v]) => ({ value: k, label: v.label})), choices: () =>
operator: "contains3" [
...Object.entries(CONFIG.DH.ITEM.weaponFeatures),
...Object.entries(CONFIG.DH.ITEM.armorFeatures)
].map(([k, v]) => ({ value: k, label: v.label })),
operator: 'contains3'
} }
] ]
}, },
features: { features: {
columns: [ columns: [],
filters: []
],
filters: [
]
}, },
cards: { cards: {
columns: [ columns: [
{ {
key: "system.type", key: 'system.type',
label: "Type" label: 'DAGGERHEART.GENERAL.type'
}, },
{ {
key: "system.domain", key: 'system.domain',
label: "Domain" label: 'DAGGERHEART.GENERAL.Domain.single'
}, },
{ {
key: "system.level", key: 'system.level',
label: "Level" label: 'DAGGERHEART.GENERAL.level'
} }
], ],
filters: [ filters: [
{ {
key: "system.type", key: 'system.type',
label: "Type", label: 'DAGGERHEART.GENERAL.type',
field: 'system.api.models.items.DHDomainCard.schema.fields.type' field: 'system.api.models.items.DHDomainCard.schema.fields.type'
}, },
{ {
key: "system.domain", key: 'system.domain',
label: "Domain", label: 'DAGGERHEART.GENERAL.Domain.single',
field: 'system.api.models.items.DHDomainCard.schema.fields.domain', field: 'system.api.models.items.DHDomainCard.schema.fields.domain',
operator: "contains2" operator: 'contains2'
}, },
{ {
key: "system.level", key: 'system.level',
name: "level.min", name: 'level.min',
label: "Level (Min)", label: 'DAGGERHEART.UI.ItemBrowser.levelMin',
field: 'system.api.models.items.DHDomainCard.schema.fields.level', field: 'system.api.models.items.DHDomainCard.schema.fields.level',
operator: "gte" operator: 'gte'
}, },
{ {
key: "system.level", key: 'system.level',
name: "level.max", name: 'level.max',
label: "Level (Max)", label: 'DAGGERHEART.UI.ItemBrowser.levelMax',
field: 'system.api.models.items.DHDomainCard.schema.fields.level', field: 'system.api.models.items.DHDomainCard.schema.fields.level',
operator: "lte" operator: 'lte'
}, },
{ {
key: "system.recallCost", key: 'system.recallCost',
name: "recall.min", name: 'recall.min',
label: "Recall Cost (Min)", label: 'DAGGERHEART.UI.ItemBrowser.recallCostMin',
field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost', field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost',
operator: "gte" operator: 'gte'
}, },
{ {
key: "system.recallCost", key: 'system.recallCost',
name: "recall.max", name: 'recall.max',
label: "Recall Cost (Max)", label: 'DAGGERHEART.UI.ItemBrowser.recallCostMax',
field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost', field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost',
operator: "lte" operator: 'lte'
} }
] ]
}, },
classes: { classes: {
columns: [ columns: [
{ {
key: "system.evasion", key: 'system.evasion',
label: "Evasion" label: 'DAGGERHEART.GENERAL.evasion'
}, },
{ {
key: "system.hitPoints", key: 'system.hitPoints',
label: "Hit Points" label: 'DAGGERHEART.GENERAL.HitPoints.plural'
}, },
{ {
key: "system.domains", key: 'system.domains',
label: "Domains" label: 'DAGGERHEART.GENERAL.Domain.plural'
} }
], ],
filters: [ filters: [
{ {
key: "system.evasion", key: 'system.evasion',
name: "evasion.min", name: 'evasion.min',
label: "Evasion (Min)", label: 'DAGGERHEART.UI.ItemBrowser.evasionMin',
field: 'system.api.models.items.DHClass.schema.fields.evasion', field: 'system.api.models.items.DHClass.schema.fields.evasion',
operator: "gte" operator: 'gte'
}, },
{ {
key: "system.evasion", key: 'system.evasion',
name: "evasion.max", name: 'evasion.max',
label: "Evasion (Max)", label: 'DAGGERHEART.UI.ItemBrowser.evasionMax',
field: 'system.api.models.items.DHClass.schema.fields.evasion', field: 'system.api.models.items.DHClass.schema.fields.evasion',
operator: "lte" operator: 'lte'
}, },
{ {
key: "system.hitPoints", key: 'system.hitPoints',
name: "hp.min", name: 'hp.min',
label: "Hit Points (Min)", label: 'DAGGERHEART.UI.ItemBrowser.hitPointsMin',
field: 'system.api.models.items.DHClass.schema.fields.hitPoints', field: 'system.api.models.items.DHClass.schema.fields.hitPoints',
operator: "gte" operator: 'gte'
}, },
{ {
key: "system.hitPoints", key: 'system.hitPoints',
name: "hp.max", name: 'hp.max',
label: "Hit Points (Max)", label: 'DAGGERHEART.UI.ItemBrowser.hitPointsMax',
field: 'system.api.models.items.DHClass.schema.fields.hitPoints', field: 'system.api.models.items.DHClass.schema.fields.hitPoints',
operator: "lte" operator: 'lte'
}, },
{ {
key: "system.domains", key: 'system.domains',
label: "Domains", label: 'DAGGERHEART.GENERAL.Domain.plural',
choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label})), choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label })),
operator: "contains2" operator: 'contains2'
} }
] ]
}, },
subclasses: { subclasses: {
columns: [ columns: [
{ {
key: "id", key: 'id',
label: "Class", label: 'TYPES.Item.class',
format: (id) => { format: id => {
return ""; return '';
} }
}, },
{ {
key: "system.spellcastingTrait", key: 'system.spellcastingTrait',
label: "Spellcasting Trait" label: 'DAGGERHEART.ITEMS.Subclass.spellcastingTrait'
} }
], ],
filters: [] filters: []
@ -273,133 +281,133 @@ export const typeConfig = {
beastforms: { beastforms: {
columns: [ columns: [
{ {
key: "system.tier", key: 'system.tier',
label: "Tier" label: 'DAGGERHEART.GENERAL.Tiers.singular'
}, },
{ {
key: "system.mainTrait", key: 'system.mainTrait',
label: "Main Trait" label: 'DAGGERHEART.GENERAL.Trait.single'
} }
], ],
filters: [ filters: [
{ {
key: "system.tier", key: 'system.tier',
label: "Tier", label: 'DAGGERHEART.GENERAL.Tiers.singular',
field: 'system.api.models.items.DHBeastform.schema.fields.tier' field: 'system.api.models.items.DHBeastform.schema.fields.tier'
}, },
{ {
key: "system.mainTrait", key: 'system.mainTrait',
label: "Main Trait", label: 'DAGGERHEART.GENERAL.Trait.single',
field: 'system.api.models.items.DHBeastform.schema.fields.mainTrait' field: 'system.api.models.items.DHBeastform.schema.fields.mainTrait'
} }
] ]
} }
} };
export const compendiumConfig = { export const compendiumConfig = {
"daggerheart": { daggerheart: {
id: "daggerheart", id: 'daggerheart',
label: "DAGGERHEART", label: 'DAGGERHEART',
folders: { folders: {
"adversaries": { adversaries: {
id: "adversaries", id: 'adversaries',
keys: ["adversaries"], keys: ['adversaries'],
label: "Adversaries", label: 'DAGGERHEART.UI.ItemBrowser.folders.adversaries',
type: ["adversary"], type: ['adversary'],
listType: "adversaries" listType: 'adversaries'
}, },
"ancestries": { ancestries: {
id: "ancestries", id: 'ancestries',
keys: ["ancestries"], keys: ['ancestries'],
label: "Ancestries", label: 'DAGGERHEART.UI.ItemBrowser.folders.ancestries',
type: ["ancestry"], type: ['ancestry'],
folders: { folders: {
"features": { features: {
id: "features", id: 'features',
keys: ["ancestries"], keys: ['ancestries'],
label: "Features", label: 'DAGGERHEART.UI.ItemBrowser.folders.features',
type: ["feature"] type: ['feature']
} }
} }
}, },
"equipments": { equipments: {
id: "equipments", id: 'equipments',
keys: ["armors", "weapons", "consumables", "loot"], keys: ['armors', 'weapons', 'consumables', 'loot'],
label: "Equipment", label: 'DAGGERHEART.UI.ItemBrowser.folders.equipment',
type: ["armor", "weapon", "consumable", "loot"], type: ['armor', 'weapon', 'consumable', 'loot'],
listType: "items" listType: 'items'
}, },
"classes": { classes: {
id: "classes", id: 'classes',
keys: ["classes"], keys: ['classes'],
label: "Classes", label: 'DAGGERHEART.UI.ItemBrowser.folders.classes',
type: ["class"], type: ['class'],
folders: { folders: {
"features": { features: {
id: "features", id: 'features',
keys: ["classes"], keys: ['classes'],
label: "Features", label: 'DAGGERHEART.UI.ItemBrowser.folders.features',
type: ["feature"] type: ['feature']
}, },
"items": { items: {
id: "items", id: 'items',
keys: ["classes"], keys: ['classes'],
label: "Items", label: 'DAGGERHEART.UI.ItemBrowser.folders.items',
type: ["armor", "weapon", "consumable", "loot"], type: ['armor', 'weapon', 'consumable', 'loot'],
listType: "items" listType: 'items'
} }
}, },
listType: "classes" listType: 'classes'
}, },
"subclasses": { subclasses: {
id: "subclasses", id: 'subclasses',
keys: ["subclasses"], keys: ['subclasses'],
label: "Subclasses", label: 'DAGGERHEART.UI.ItemBrowser.folders.subclasses',
type: ["subclass"], type: ['subclass'],
listType: "subclasses" listType: 'subclasses'
}, },
"domains": { domains: {
id: "domains", id: 'domains',
keys: ["domains"], keys: ['domains'],
label: "Domain Cards", label: 'DAGGERHEART.UI.ItemBrowser.folders.domainCards',
type: ["domainCard"], type: ['domainCard'],
listType: "cards" listType: 'cards'
}, },
"communities": { communities: {
id: "communities", id: 'communities',
keys: ["communities"], keys: ['communities'],
label: "Communities", label: 'DAGGERHEART.UI.ItemBrowser.folders.communities',
type: ["community"], type: ['community'],
folders: { folders: {
"features": { features: {
id: "features", id: 'features',
keys: ["communities"], keys: ['communities'],
label: "Features", label: 'DAGGERHEART.UI.ItemBrowser.folders.features',
type: ["feature"] type: ['feature']
} }
} }
}, },
"environments": { environments: {
id: "environments", id: 'environments',
keys: ["environments"], keys: ['environments'],
label: "Environments", label: 'DAGGERHEART.UI.ItemBrowser.folders.environments',
type: ["environment"] type: ['environment']
}, },
"beastforms": { beastforms: {
id: "beastforms", id: 'beastforms',
keys: ["beastforms"], keys: ['beastforms'],
label: "Beastforms", label: 'DAGGERHEART.UI.ItemBrowser.folders.beastforms',
type: ["beastform"], type: ['beastform'],
listType: "beastforms", listType: 'beastforms',
folders: { folders: {
"features": { features: {
id: "features", id: 'features',
keys: ["beastforms"], keys: ['beastforms'],
label: "Features", label: 'DAGGERHEART.UI.ItemBrowser.folders.features',
type: ["feature"] type: ['feature']
} }
} }
} }
} }
} }
} };

View file

@ -26,5 +26,6 @@ export const gameSettings = {
Fear: 'ResourcesFear' Fear: 'ResourcesFear'
}, },
LevelTiers: 'LevelTiers', LevelTiers: 'LevelTiers',
Countdowns: 'Countdowns' Countdowns: 'Countdowns',
LastMigrationVersion: 'LastMigrationVersion'
}; };

View file

@ -163,7 +163,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
const hasRoll = this.getUseHasRoll(byPass); const hasRoll = this.getUseHasRoll(byPass);
return { return {
event, event,
title: `${this.item.name}: ${this.name}`, title: `${this.item.name}: ${game.i18n.localize(this.name)}`,
source: { source: {
item: this.item._id, item: this.item._id,
action: this._id, action: this._id,
@ -209,15 +209,15 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
} }
async consume(config, successCost = false) { async consume(config, successCost = false) {
const actor= this.actor.system.partner ?? this.actor, const actor = this.actor.system.partner ?? this.actor,
usefulResources = { usefulResources = {
...foundry.utils.deepClone(actor.system.resources), ...foundry.utils.deepClone(actor.system.resources),
fear: { fear: {
value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear),
max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear,
reversed: false reversed: false
} }
}; };
for (var cost of config.costs) { for (var cost of config.costs) {
if (cost.keyIsID) { if (cost.keyIsID) {

View file

@ -10,7 +10,8 @@ export default class DhpAdversary extends BaseDataActor {
return foundry.utils.mergeObject(super.metadata, { return foundry.utils.mergeObject(super.metadata, {
label: 'TYPES.Actor.adversary', label: 'TYPES.Actor.adversary',
type: 'adversary', type: 'adversary',
settingSheet: DHAdversarySettings settingSheet: DHAdversarySettings,
hasAttribution: true
}); });
} }
@ -26,7 +27,7 @@ export default class DhpAdversary extends BaseDataActor {
}), }),
type: new fields.StringField({ type: new fields.StringField({
required: true, required: true,
choices: CONFIG.DH.ACTOR.adversaryTypes, choices: CONFIG.DH.ACTOR.allAdversaryTypes,
initial: CONFIG.DH.ACTOR.adversaryTypes.standard.id initial: CONFIG.DH.ACTOR.adversaryTypes.standard.id
}), }),
motivesAndTactics: new fields.StringField(), motivesAndTactics: new fields.StringField(),

View file

@ -1,5 +1,5 @@
import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs'; import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs';
import { createScrollText, getScrollTextData } from '../../helpers/utils.mjs'; import { getScrollTextData } from '../../helpers/utils.mjs';
const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) => const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) =>
new foundry.data.fields.SchemaField({ new foundry.data.fields.SchemaField({
@ -39,7 +39,8 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
type: 'base', type: 'base',
isNPC: true, isNPC: true,
settingSheet: null, settingSheet: null,
hasResistances: true hasResistances: true,
hasAttribution: false
}; };
} }
@ -53,6 +54,13 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
const fields = foundry.data.fields; const fields = foundry.data.fields;
const schema = {}; const schema = {};
if (this.metadata.hasAttribution) {
schema.attribution = new fields.SchemaField({
source: new fields.StringField(),
page: new fields.NumberField(),
artist: new fields.StringField()
});
}
if (this.metadata.isNPC) schema.description = new fields.HTMLField({ required: true, nullable: true }); if (this.metadata.isNPC) schema.description = new fields.HTMLField({ required: true, nullable: true });
if (this.metadata.hasResistances) if (this.metadata.hasResistances)
schema.resistance = new fields.SchemaField({ schema.resistance = new fields.SchemaField({
@ -78,6 +86,13 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
*/ */
static DEFAULT_ICON = null; static DEFAULT_ICON = null;
get attributionLabel() {
if (!this.attribution) return;
const { source, page } = this.attribution;
return [source, page ? `pg ${page}.` : null].filter(x => x).join('. ');
}
/* -------------------------------------------- */ /* -------------------------------------------- */
/** /**
@ -133,6 +148,6 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
_onUpdate(changes, options, userId) { _onUpdate(changes, options, userId) {
super._onUpdate(changes, options, userId); super._onUpdate(changes, options, userId);
createScrollText(this.parent, options.scrollingTextData); if (options.scrollingTextData) this.parent.queueScrollText(options.scrollingTextData);
} }
} }

View file

@ -95,7 +95,7 @@ export default class DhCharacter extends BaseDataActor {
}), }),
attack: new ActionField({ attack: new ActionField({
initial: { initial: {
name: 'Unarmed Attack', name: 'DAGGERHEART.GENERAL.unarmedAttack',
img: 'icons/skills/melee/unarmed-punch-fist-yellow-red.webp', img: 'icons/skills/melee/unarmed-punch-fist-yellow-red.webp',
_id: foundry.utils.randomID(), _id: foundry.utils.randomID(),
systemPath: 'attack', systemPath: 'attack',
@ -444,16 +444,12 @@ export default class DhCharacter extends BaseDataActor {
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) { } else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) {
if (this.class.subclass) { if (this.class.subclass) {
const subclassState = this.class.subclass.system.featureState; const subclassState = this.class.subclass.system.featureState;
const subclass =
item.system.identifier === 'multiclass' ? this.multiclass.subclass : this.class.subclass;
const featureType = subclass
? (subclass.system.features.find(x => x.item?.uuid === item.uuid)?.type ?? null)
: null;
if ( if (
featureType === CONFIG.DH.ITEM.featureSubTypes.foundation || item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.foundation ||
(featureType === CONFIG.DH.ITEM.featureSubTypes.specialization && subclassState >= 2) || (item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.specialization &&
(featureType === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3) subclassState >= 2) ||
(item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3)
) { ) {
subclassFeatures.push(item); subclassFeatures.push(item);
} }

View file

@ -12,7 +12,8 @@ export default class DhEnvironment extends BaseDataActor {
label: 'TYPES.Actor.environment', label: 'TYPES.Actor.environment',
type: 'environment', type: 'environment',
settingSheet: DHEnvironmentSettings, settingSheet: DHEnvironmentSettings,
hasResistances: false hasResistances: false,
hasAttribution: true
}); });
} }

View file

@ -25,7 +25,7 @@ export default class CostField extends fields.ArrayField {
config.costs = CostField.calcCosts.call(this, costs); config.costs = CostField.calcCosts.call(this, costs);
const hasCost = CostField.hasCost.call(this, config.costs); const hasCost = CostField.hasCost.call(this, config.costs);
if (config.isFastForward && !hasCost) if (config.isFastForward && !hasCost)
return ui.notifications.warn("You don't have the resources to use that action."); return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.insufficientResources'));
return hasCost; return hasCost;
} }

View file

@ -23,14 +23,22 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
multiplier: new fields.StringField({ multiplier: new fields.StringField({
choices: CONFIG.DH.GENERAL.multiplierTypes, choices: CONFIG.DH.GENERAL.multiplierTypes,
initial: 'prof', initial: 'prof',
label: 'Multiplier' label: 'DAGGERHEART.ACTIONS.Config.damage.multiplier'
}), }),
flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }), flatMultiplier: new fields.NumberField({
dice: new fields.StringField({ choices: CONFIG.DH.GENERAL.diceTypes, initial: 'd6', label: 'Dice' }), nullable: true,
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }), initial: 1,
label: 'DAGGERHEART.ACTIONS.Config.damage.flatMultiplier'
}),
dice: new fields.StringField({
choices: CONFIG.DH.GENERAL.diceTypes,
initial: 'd6',
label: 'DAGGERHEART.GENERAL.Dice.single'
}),
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'DAGGERHEART.GENERAL.bonus' }),
custom: new fields.SchemaField({ custom: new fields.SchemaField({
enabled: new fields.BooleanField({ label: 'Custom Formula' }), enabled: new fields.BooleanField({ label: 'DAGGERHEART.ACTIONS.Config.general.customFormula' }),
formula: new FormulaField({ label: 'Formula', initial: '' }) formula: new FormulaField({ label: 'DAGGERHEART.ACTIONS.Config.general.formula', initial: '' })
}) })
}; };
} }

View file

@ -25,7 +25,8 @@ export default class UsesField extends fields.SchemaField {
if (uses && !uses.value) uses.value = 0; if (uses && !uses.value) uses.value = 0;
config.uses = uses; config.uses = uses;
const hasUses = UsesField.hasUses.call(this, config.uses); const hasUses = UsesField.hasUses.call(this, config.uses);
if (config.isFastForward && !hasUses) return ui.notifications.warn("That action doesn't have remaining uses."); if (config.isFastForward && !hasUses)
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.actionNoUsesRemaining'));
return hasUses; return hasUses;
} }

View file

@ -26,7 +26,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
hasResource: false, hasResource: false,
isQuantifiable: false, isQuantifiable: false,
isInventoryItem: false, isInventoryItem: false,
hasActions: false hasActions: false,
hasAttribution: true
}; };
} }
@ -37,7 +38,13 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
/** @inheritDoc */ /** @inheritDoc */
static defineSchema() { static defineSchema() {
const schema = {}; const schema = {
attribution: new fields.SchemaField({
source: new fields.StringField(),
page: new fields.NumberField(),
artist: new fields.StringField()
})
};
if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true }); if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true });
@ -110,6 +117,13 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
return []; return [];
} }
get attributionLabel() {
if (!this.attribution) return;
const { source, page } = this.attribution;
return [source, page ? `pg ${page}.` : null].filter(x => x).join('. ');
}
/** /**
* Obtain a data object used to evaluate any dice rolls associated with this Item Type * Obtain a data object used to evaluate any dice rolls associated with this Item Type
* @param {object} [options] - Options which modify the getRollData method. * @param {object} [options] - Options which modify the getRollData method.
@ -144,50 +158,30 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
} }
if (this.actor && this.actor.type === 'character' && this.features) { if (this.actor && this.actor.type === 'character' && this.features) {
const featureUpdates = {}; const features = [];
for (let f of this.features) { for (let f of this.features) {
const fBase = f.item ?? f; const fBase = f.item ?? f;
const feature = fBase.system ? fBase : await foundry.utils.fromUuid(fBase.uuid); const feature = fBase.system ? fBase : await foundry.utils.fromUuid(fBase.uuid);
const createData = foundry.utils.mergeObject( const multiclass = this.isMulticlass ? 'multiclass' : null;
feature.toObject(), features.push(
{ foundry.utils.mergeObject(
system: { feature.toObject(),
originItemType: this.parent.type, {
originId: data._id, _stats: { compendiumSource: fBase.uuid },
identifier: this.isMulticlass ? 'multiclass' : null system: {
} originItemType: this.parent.type,
}, identifier: multiclass ?? (f.item ? f.type : null)
{ inplace: false } }
},
{ inplace: false }
)
); );
const [doc] = await this.actor.createEmbeddedDocuments('Item', [createData]);
if (!featureUpdates.features)
featureUpdates.features = this.features.map(x => (x.item ? { ...x, item: x.item.uuid } : x.uuid));
if (f.item) {
const existingFeature = featureUpdates.features.find(x => x.item === f.item.uuid);
existingFeature.item = doc.uuid;
} else {
const replaceIndex = featureUpdates.features.findIndex(x => x === f.uuid);
featureUpdates.features.splice(replaceIndex, 1, doc.uuid);
}
} }
await this.updateSource(featureUpdates); await this.actor.createEmbeddedDocuments('Item', features);
} }
} }
async _preDelete() {
if (!this.actor || this.actor.type !== 'character') return;
const items = this.actor.items.filter(item => item.system.originId === this.parent.id);
if (items.length > 0)
await this.actor.deleteEmbeddedDocuments(
'Item',
items.map(x => x.id)
);
}
async _preUpdate(changed, options, userId) { async _preUpdate(changed, options, userId) {
const allowed = await super._preUpdate(changed, options, userId); const allowed = await super._preUpdate(changed, options, userId);
if (allowed === false) return false; if (allowed === false) return false;
@ -207,6 +201,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
super._onUpdate(changed, options, userId); super._onUpdate(changed, options, userId);
updateLinkedItemApps(options, this.parent.sheet); updateLinkedItemApps(options, this.parent.sheet);
createScrollText(this.parent?.parent, options.scrollingTextData);
if (this.parent?.parent && options.scrollingTextData)
this.parent.parent.queueScrollText(options.scrollingTextData);
} }
} }

View file

@ -1,5 +1,4 @@
import BaseDataItem from './base.mjs'; import BaseDataItem from './base.mjs';
import { ActionField, ActionsField } from '../fields/actionField.mjs';
export default class DHFeature extends BaseDataItem { export default class DHFeature extends BaseDataItem {
/** @inheritDoc */ /** @inheritDoc */
@ -30,24 +29,7 @@ export default class DHFeature extends BaseDataItem {
nullable: true, nullable: true,
initial: null initial: null
}), }),
originId: new fields.StringField({ nullable: true, initial: null }),
identifier: new fields.StringField() identifier: new fields.StringField()
}; };
} }
get spellcastingModifier() {
let traitValue = 0;
if (this.actor && this.originId && ['class', 'subclass'].includes(this.originItemType)) {
if (this.originItemType === 'subclass') {
traitValue =
this.actor.system.traits[this.actor.items.get(this.originId).system.spellcastingTrait]?.value ?? 0;
} else {
const { value: multiclass, subclass } = this.actor.system.multiclass;
const selectedSubclass = multiclass?.id === this.originId ? subclass : this.actor.system.class.subclass;
traitValue = this.actor.system.traits[selectedSubclass.system.spellcastingTrait]?.value ?? 0;
}
}
return traitValue;
}
} }

View file

@ -199,8 +199,8 @@ export default class DHWeapon extends AttachableItem {
]; ];
for (const { value, type } of attack.damage.parts) { for (const { value, type } of attack.damage.parts) {
const parts = [value.dice]; const parts = value.custom.enabled ? [game.i18n.localize('DAGGERHEART.GENERAL.custom')] : [value.dice];
if (value.bonus) parts.push(value.bonus.signedString()); if (!value.custom.enabled && value.bonus) parts.push(value.bonus.signedString());
if (type.size > 0) { if (type.size > 0) {
const typeTags = Array.from(type) const typeTags = Array.from(type)

View file

@ -89,6 +89,11 @@ export default class DhAppearance extends foundry.abstract.DataModel {
initial: false, initial: false,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessageTarget.label' label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessageTarget.label'
}) })
}),
hideAttribution: new fields.BooleanField({
required: true,
initial: false,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.hideAttribution.label'
}) })
}; };
} }

View file

@ -108,6 +108,13 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
}), }),
description: new fields.HTMLField() description: new fields.HTMLField()
}) })
),
adversaryTypes: new fields.TypedObjectField(
new fields.SchemaField({
id: new fields.StringField({ required: true }),
label: new fields.StringField({ required: true, label: 'DAGGERHEART.GENERAL.label' }),
description: new fields.StringField()
})
) )
}; };
} }

View file

@ -1,3 +1,4 @@
export { default as BaseRoll } from './baseRoll.mjs';
export { default as D20Roll } from './d20Roll.mjs'; export { default as D20Roll } from './d20Roll.mjs';
export { default as DamageRoll } from './damageRoll.mjs'; export { default as DamageRoll } from './damageRoll.mjs';
export { default as DHRoll } from './dhRoll.mjs'; export { default as DHRoll } from './dhRoll.mjs';

7
module/dice/baseRoll.mjs Normal file
View file

@ -0,0 +1,7 @@
export default class BaseRoll extends Roll {
/** @inheritdoc */
static CHAT_TEMPLATE = 'systems/daggerheart/templates/ui/chat/foundryRoll.hbs';
/** @inheritdoc */
static TOOLTIP_TEMPLATE = 'systems/daggerheart/templates/ui/chat/foundryRollTooltip.hbs';
}

View file

@ -37,7 +37,13 @@ export default class DamageRoll extends DHRoll {
Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll)) Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll))
), ),
diceRoll = Roll.fromTerms([pool]); diceRoll = Roll.fromTerms([pool]);
await game.dice3d.showForRoll(diceRoll, game.user, true, chatMessage.whisper, chatMessage.blind); await game.dice3d.showForRoll(
diceRoll,
game.user,
true,
chatMessage.whisper?.length > 0 ? chatMessage.whisper : null,
chatMessage.blind
);
} }
await super.buildPost(roll, config, message); await super.buildPost(roll, config, message);
if (config.source?.message) { if (config.source?.message) {

View file

@ -84,7 +84,7 @@ export default class DHRoll extends Roll {
static async toMessage(roll, config) { static async toMessage(roll, config) {
const cls = getDocumentClass('ChatMessage'), const cls = getDocumentClass('ChatMessage'),
msg = { msgData = {
type: this.messageType, type: this.messageType,
user: game.user.id, user: game.user.id,
title: roll.title, title: roll.title,
@ -94,8 +94,16 @@ export default class DHRoll extends Roll {
rolls: [roll] rolls: [roll]
}; };
config.selectedRollMode ??= game.settings.get('core', 'rollMode'); config.selectedRollMode ??= game.settings.get('core', 'rollMode');
if (roll._evaluated) return await cls.create(msg, { rollMode: config.selectedRollMode });
return msg; if (roll._evaluated) {
const message = await cls.create(msgData, { rollMode: config.selectedRollMode });
if (game.modules.get('dice-so-nice')?.active) {
await game.dice3d.waitFor3DAnimationByMessageID(message.id);
}
return message;
} else return msgData;
} }
/** @inheritDoc */ /** @inheritDoc */

View file

@ -1,10 +1,13 @@
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs'; import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
import { LevelOptionType } from '../data/levelTier.mjs'; import { LevelOptionType } from '../data/levelTier.mjs';
import DHFeature from '../data/item/feature.mjs'; import DHFeature from '../data/item/feature.mjs';
import { damageKeyToNumber } from '../helpers/utils.mjs'; import { createScrollText, damageKeyToNumber, versionCompare } from '../helpers/utils.mjs';
import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs'; import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs';
export default class DhpActor extends Actor { export default class DhpActor extends Actor {
#scrollTextQueue = [];
#scrollTextInterval;
/** /**
* Return the first Actor active owner. * Return the first Actor active owner.
*/ */
@ -27,7 +30,7 @@ export default class DhpActor extends Actor {
/** @inheritDoc */ /** @inheritDoc */
static migrateData(source) { static migrateData(source) {
if(source.system?.attack && !source.system.attack.type) source.system.attack.type = "attack"; if (source.system?.attack && !source.system.attack.type) source.system.attack.type = 'attack';
return super.migrateData(source); return super.migrateData(source);
} }
@ -572,19 +575,15 @@ export default class DhpActor extends Actor {
if (armorSlotResult) { if (armorSlotResult) {
const { modifiedDamage, armorSpent, stressSpent } = armorSlotResult; const { modifiedDamage, armorSpent, stressSpent } = armorSlotResult;
updates.find(u => u.key === 'hitPoints').value = modifiedDamage; updates.find(u => u.key === 'hitPoints').value = modifiedDamage;
if(armorSpent) { if (armorSpent) {
const armorUpdate = updates.find(u => u.key === 'armor'); const armorUpdate = updates.find(u => u.key === 'armor');
if(armorUpdate) if (armorUpdate) armorUpdate.value += armorSpent;
armorUpdate.value += armorSpent; else updates.push({ value: armorSpent, key: 'armor' });
else
updates.push({ value: armorSpent, key: 'armor' });
} }
if(stressSpent) { if (stressSpent) {
const stressUpdate = updates.find(u => u.key === 'stress'); const stressUpdate = updates.find(u => u.key === 'stress');
if(stressUpdate) if (stressUpdate) stressUpdate.value += stressSpent;
stressUpdate.value += stressSpent; else updates.push({ value: stressSpent, key: 'stress' });
else
updates.push({ value: stressSpent, key: 'stress' });
} }
} }
} }
@ -754,4 +753,45 @@ export default class DhpActor extends Actor {
} }
} }
} }
queueScrollText(scrollingTextData) {
this.#scrollTextQueue.push(...scrollingTextData.map(data => () => createScrollText(this, data)));
if (!this.#scrollTextInterval) {
const scrollFunc = this.#scrollTextQueue.pop();
scrollFunc?.();
const intervalFunc = () => {
const scrollFunc = this.#scrollTextQueue.pop();
scrollFunc?.();
if (this.#scrollTextQueue.length === 0) {
clearInterval(this.#scrollTextInterval);
this.#scrollTextInterval = null;
}
};
this.#scrollTextInterval = setInterval(intervalFunc.bind(this), 600);
}
}
/** @inheritdoc */
async importFromJSON(json) {
if (!this.type === 'character') return await super.importFromJSON(json);
if (!CONST.WORLD_DOCUMENT_TYPES.includes(this.documentName)) {
throw new Error('Only world Documents may be imported');
}
const parsedJSON = JSON.parse(json);
if (versionCompare(parsedJSON._stats.systemVersion, '1.1.0')) {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.localize('DAGGERHEART.ACTORS.Character.InvalidOldCharacterImportTitle')
},
content: game.i18n.localize('DAGGERHEART.ACTORS.Character.InvalidOldCharacterImportText')
});
if (!confirmed) return;
}
return await super.importFromJSON(json);
}
} }

View file

@ -13,6 +13,10 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */ /* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
const html = await super.renderHTML({ actor: actorData, author: this.author }); const html = await super.renderHTML({ actor: actorData, author: this.author });
if (this.flags.core?.RollTable) {
html.querySelector('.roll-buttons.apply-buttons').remove();
}
this.enrichChatMessage(html); this.enrichChatMessage(html);
this.addChatListeners(html); this.addChatListeners(html);
@ -43,6 +47,18 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
return super._preDelete(options, user); return super._preDelete(options, user);
} }
/** @inheritDoc */
_onUpdate(changes, options, userId) {
super._onUpdate(changes, options, userId);
const lastMessage = Array.from(game.messages).sort((a, b) => b.timestamp - a.timestamp)[0];
if (lastMessage.id === this.id && ui.chat.isAtBottom) {
setTimeout(() => {
ui.chat.scrollBottom();
}, 5);
}
}
enrichChatMessage(html) { enrichChatMessage(html) {
const elements = html.querySelectorAll('[data-perm-id]'); const elements = html.querySelectorAll('[data-perm-id]');
elements.forEach(e => { elements.forEach(e => {
@ -55,7 +71,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
}); });
if (this.isContentVisible) { if (this.isContentVisible) {
if(this.type === 'dualityRoll') { if (this.type === 'dualityRoll') {
html.classList.add('duality'); html.classList.add('duality');
switch (this.system.roll?.result?.duality) { switch (this.system.roll?.result?.duality) {
case 1: case 1:
@ -70,27 +86,28 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
} }
} }
const autoExpandRoll = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).expandRollMessage, const autoExpandRoll = game.settings.get(
rollSections = html.querySelectorAll(".roll-part"), CONFIG.DH.id,
itemDesc = html.querySelector(".domain-card-move"); CONFIG.DH.SETTINGS.gameSettings.appearance
).expandRollMessage,
rollSections = html.querySelectorAll('.roll-part'),
itemDesc = html.querySelector('.domain-card-move');
rollSections.forEach(s => { rollSections.forEach(s => {
if(s.classList.contains("roll-section")) { if (s.classList.contains('roll-section')) {
const toExpand = s.querySelector('[data-action="expandRoll"]'); const toExpand = s.querySelector('[data-action="expandRoll"]');
toExpand.classList.toggle("expanded", autoExpandRoll.roll); toExpand.classList.toggle('expanded', autoExpandRoll.roll);
} else if(s.classList.contains("damage-section")) } else if (s.classList.contains('damage-section'))
s.classList.toggle("expanded", autoExpandRoll.damage); s.classList.toggle('expanded', autoExpandRoll.damage);
else if(s.classList.contains("target-section")) else if (s.classList.contains('target-section')) s.classList.toggle('expanded', autoExpandRoll.target);
s.classList.toggle("expanded", autoExpandRoll.target);
}); });
if(itemDesc && autoExpandRoll.desc) if (itemDesc && autoExpandRoll.desc) itemDesc.setAttribute('open', '');
itemDesc.setAttribute("open", "");
} }
if(!game.user.isGM) { if (!game.user.isGM) {
const applyButtons = html.querySelector(".apply-buttons"); const applyButtons = html.querySelector('.apply-buttons');
applyButtons?.remove(); applyButtons?.remove();
if(!this.isAuthor && !this.speakerActor?.isOwner) { if (!this.isAuthor && !this.speakerActor?.isOwner) {
const buttons = html.querySelectorAll(".ability-card-footer > .ability-use-button"); const buttons = html.querySelectorAll('.ability-card-footer > .ability-use-button');
buttons.forEach(b => b.remove()); buttons.forEach(b => b.remove());
} }
} }

View file

@ -28,6 +28,14 @@ export default class DHItem extends foundry.documents.Item {
return doc; return doc;
} }
/* -------------------------------------------- */
/** @inheritDoc */
static migrateData(source) {
if (source.system?.attack && !source.system.attack.type) source.system.attack.type = 'attack';
return super.migrateData(source);
}
/** /**
* @inheritdoc * @inheritdoc
* @param {object} options - Options which modify the getRollData method. * @param {object} options - Options which modify the getRollData method.

View file

@ -13,7 +13,8 @@ export default class RegisterHandlebarsHelpers {
hasProperty: foundry.utils.hasProperty, hasProperty: foundry.utils.hasProperty,
getProperty: foundry.utils.getProperty, getProperty: foundry.utils.getProperty,
setVar: this.setVar, setVar: this.setVar,
empty: this.empty empty: this.empty,
pluralize: this.pluralize
}); });
} }
static add(a, b) { static add(a, b) {
@ -64,7 +65,7 @@ export default class RegisterHandlebarsHelpers {
return isNumerical ? (!result ? 0 : Number(result)) : result; return isNumerical ? (!result ? 0 : Number(result)) : result;
} }
static setVar(name, value, context) { static setVar(name, value) {
this[name] = value; this[name] = value;
} }
@ -72,4 +73,20 @@ export default class RegisterHandlebarsHelpers {
if (!(typeof object === 'object')) return true; if (!(typeof object === 'object')) return true;
return Object.keys(object).length === 0; return Object.keys(object).length === 0;
} }
/**
* Pluralize helper that returns the appropriate localized string based on count
* @param {number} count - The number to check for plurality
* @param {string} baseKey - The base localization key (e.g., "DAGGERHEART.GENERAL.Target")
* @returns {string} The localized singular or plural string
*
* Usage: {{pluralize currentTargets.length "DAGGERHEART.GENERAL.Target"}}
* Returns: "Target" if count is exactly 1, "Targets" if count is 0, 2+, or invalid
*/
static pluralize(count, baseKey) {
const numericCount = Number(count);
const isSingular = !isNaN(numericCount) && numericCount === 1;
const key = isSingular ? `${baseKey}.single` : `${baseKey}.plural`;
return game.i18n.localize(key);
}
} }

View file

@ -371,17 +371,15 @@ export function getScrollTextData(resources, resource, key) {
return { text, stroke, fill, direction }; return { text, stroke, fill, direction };
} }
export function createScrollText(actor, optionsData) { export function createScrollText(actor, data) {
if (actor && optionsData?.length) { if (actor) {
actor.getActiveTokens().forEach(token => { actor.getActiveTokens().forEach(token => {
optionsData.forEach(data => { const { text, ...options } = data;
const { text, ...options } = data; canvas.interface.createScrollingText(token.getCenterPoint(), data.text, {
canvas.interface.createScrollingText(token.getCenterPoint(), data.text, { duration: 2000,
duration: 2000, distance: token.h,
distance: token.h, jitter: 0,
jitter: 0, ...options
...options
});
}); });
}); });
} }
@ -420,3 +418,14 @@ export async function createEmbeddedItemsWithEffects(actor, baseData) {
export const slugify = name => { export const slugify = name => {
return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', ''); return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', '');
}; };
export const versionCompare = (current, target) => {
const currentSplit = current.split('.').map(x => Number.parseInt(x));
const targetSplit = target.split('.').map(x => Number.parseInt(x));
for (var i = 0; i < currentSplit.length; i++) {
if (currentSplit[i] < targetSplit[i]) return true;
if (currentSplit[i] > targetSplit[i]) return false;
}
return false;
};

View file

@ -1,3 +1,4 @@
export { preloadHandlebarsTemplates as handlebarsRegistration } from './handlebars.mjs'; export { preloadHandlebarsTemplates as handlebarsRegistration } from './handlebars.mjs';
export * as settingsRegistration from './settings.mjs'; export * as settingsRegistration from './settings.mjs';
export * as socketRegistration from './socket.mjs'; export * as socketRegistration from './socket.mjs';
export { runMigrations } from './migrations.mjs';

View file

@ -30,10 +30,11 @@ export const preloadHandlebarsTemplates = async function () {
'systems/daggerheart/templates/dialogs/downtime/activities.hbs', 'systems/daggerheart/templates/dialogs/downtime/activities.hbs',
'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs', 'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs',
'systems/daggerheart/templates/ui/chat/parts/roll-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/roll-part.hbs',
'systems/daggerheart/templates/ui/chat/parts/damage-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/damage-part.hbs',
'systems/daggerheart/templates/ui/chat/parts/target-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/target-part.hbs',
'systems/daggerheart/templates/ui/chat/parts/button-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/button-part.hbs',
'systems/daggerheart/templates/scene/dh-config.hbs'
]); ]);
}; };

View file

@ -0,0 +1,41 @@
import { versionCompare } from '../helpers/utils.mjs';
export async function runMigrations() {
let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion);
if (!lastMigrationVersion) lastMigrationVersion = '1.0.6';
if (versionCompare(lastMigrationVersion, '1.1.0')) {
const compendiumActors = [];
for (let pack of game.packs) {
const documents = await pack.getDocuments();
compendiumActors.push(...documents.filter(x => x.type === 'character'));
}
[...compendiumActors, ...game.actors].forEach(actor => {
const items = actor.items.reduce((acc, item) => {
if (item.type === 'feature') {
const { originItemType, isMulticlass, identifier } = item.system;
const base = originItemType
? actor.items.find(
x => x.type === originItemType && Boolean(isMulticlass) === Boolean(x.system.isMulticlass)
)
: null;
if (base) {
const feature = base.system.features.find(x => x.item && x.item.uuid === item.uuid);
if (feature && identifier !== 'multiclass') {
acc.push({ _id: item.id, system: { identifier: feature.type } });
}
}
}
return acc;
}, []);
actor.updateEmbeddedDocuments('Item', items);
});
lastMigrationVersion = '1.1.0';
}
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion);
}

View file

@ -91,6 +91,12 @@ const registerMenus = () => {
}; };
const registerNonConfigSettings = () => { const registerNonConfigSettings = () => {
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, {
scope: 'world',
config: false,
type: String
});
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers, { game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers, {
scope: 'world', scope: 'world',
config: false, config: false,

View file

@ -7,7 +7,7 @@
"rollup": "^4.40.0" "rollup": "^4.40.0"
}, },
"scripts": { "scripts": {
"start": "concurrently \"rollup -c --watch\" \"node ../../../../FoundryDev/main.js --dataPath=../../../ --noupnp\" \"gulp\"", "start": "node ./tools/run-start.mjs",
"start-test": "node ./resources/app/main.js --dataPath=./ && rollup -c --watch && gulp", "start-test": "node ./resources/app/main.js --dataPath=./ && rollup -c --watch && gulp",
"build": "npm run rollup && npm run gulp", "build": "npm run rollup && npm run gulp",
"rollup": "rollup -c", "rollup": "rollup -c",
@ -16,7 +16,8 @@
"pushLDBtoYML": "node ./tools/pushLDBtoYML.mjs", "pushLDBtoYML": "node ./tools/pushLDBtoYML.mjs",
"pullYMLtoLDB": "node ./tools/pullYMLtoLDB.mjs", "pullYMLtoLDB": "node ./tools/pullYMLtoLDB.mjs",
"pullYMLtoLDBBuild": "node ./tools/pullYMLtoLDBBuild.mjs", "pullYMLtoLDBBuild": "node ./tools/pullYMLtoLDBBuild.mjs",
"createSymlink": "node ./tools/create-symlink.mjs" "createSymlink": "node ./tools/create-symlink.mjs",
"setup:dev": "node ./tools/dev-setup.mjs"
}, },
"devDependencies": { "devDependencies": {
"@foundryvtt/foundryvtt-cli": "^1.0.2", "@foundryvtt/foundryvtt-cli": "^1.0.2",

View file

@ -1,3 +1,13 @@
---
name: Pull Request
about: Create a new pull request
title: '[PR] <Insert Title here>'
labels: pr
assignees: ''
---
Is this a community PR? Please go to preview tab and click [here](?expand=1&template=community_pull_request_template.md). If not, delete this line.
## Description ## Description
Please include a summary of the change and which issue is fixed (if applicable). Also include relevant context or motivation for the change. Please include a summary of the change and which issue is fixed (if applicable). Also include relevant context or motivation for the change.

View file

@ -143,6 +143,11 @@
"difficulty": null, "difficulty": null,
"damageMod": "none" "damageMod": "none"
} }
},
"attribution": {
"source": "Daggerheart SRD",
"page": 75,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -152,9 +157,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1754010222829, "createdTime": 1754010222829,
"modifiedTime": 1755259462470, "modifiedTime": 1755384241210,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"ownership": { "ownership": {

View file

@ -106,6 +106,11 @@
}, },
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 91,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -115,9 +120,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784219, "createdTime": 1753922784219,
"modifiedTime": 1755259462665, "modifiedTime": 1755385356620,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "G7jiltRjgvVhZewm", "_id": "G7jiltRjgvVhZewm",

View file

@ -105,6 +105,11 @@
"img": "icons/weapons/daggers/dagger-bone-black.webp", "img": "icons/weapons/daggers/dagger-bone-black.webp",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 84,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -114,9 +119,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784220, "createdTime": 1753922784220,
"modifiedTime": 1755259462932, "modifiedTime": 1755384980487,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "vNIbYQ4YSzNf0WPE", "_id": "vNIbYQ4YSzNf0WPE",

View file

@ -117,6 +117,11 @@
"img": "icons/magic/unholy/beam-ringed-impact-purple.webp", "img": "icons/magic/unholy/beam-ringed-impact-purple.webp",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 97,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -126,9 +131,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784221, "createdTime": 1753922784221,
"modifiedTime": 1755259462752, "modifiedTime": 1755385620034,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "WPEOIGfclNJxWb87", "_id": "WPEOIGfclNJxWb87",

View file

@ -111,6 +111,11 @@
"img": "icons/weapons/bows/longbow-recurve-leather-brown.webp", "img": "icons/weapons/bows/longbow-recurve-leather-brown.webp",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 77,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -120,9 +125,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784222, "createdTime": 1753922784222,
"modifiedTime": 1755259462476, "modifiedTime": 1755384306205,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "JRhrrEg5UroURiAD", "_id": "JRhrrEg5UroURiAD",

View file

@ -106,6 +106,11 @@
}, },
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 84,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -115,9 +120,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784223, "createdTime": 1753922784223,
"modifiedTime": 1755259462516, "modifiedTime": 1755384973132,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "0ts6CGd93lLqGZI5", "_id": "0ts6CGd93lLqGZI5",

View file

@ -112,6 +112,11 @@
}, },
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 84,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784224, "createdTime": 1753922784224,
"modifiedTime": 1755259462844, "modifiedTime": 1755384989183,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "h5RuhzGL17dW5FBT", "_id": "h5RuhzGL17dW5FBT",

View file

@ -112,6 +112,11 @@
"range": "melee", "range": "melee",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 85,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784224, "createdTime": 1753922784224,
"modifiedTime": 1755264708230, "modifiedTime": 1755385012352,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "dgH3fW9FTYLaIDvS", "_id": "dgH3fW9FTYLaIDvS",

View file

@ -117,6 +117,11 @@
"type": "attack", "type": "attack",
"range": "melee", "range": "melee",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 75,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -126,9 +131,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784226, "createdTime": 1753922784226,
"modifiedTime": 1755259462479, "modifiedTime": 1755384265295,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "71qKDLKO3CsrNkdy", "_id": "71qKDLKO3CsrNkdy",

View file

@ -112,6 +112,11 @@
"type": "attack", "type": "attack",
"range": "melee", "range": "melee",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 77,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784226, "createdTime": 1753922784226,
"modifiedTime": 1755259462481, "modifiedTime": 1755384320981,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "B4LZcGuBAHzyVdzy", "_id": "B4LZcGuBAHzyVdzy",

View file

@ -115,6 +115,11 @@
"img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp", "img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 83,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -124,9 +129,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784231, "createdTime": 1753922784231,
"modifiedTime": 1755259462487, "modifiedTime": 1755384340788,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "2UeZ0tEe7AzgSJNd", "_id": "2UeZ0tEe7AzgSJNd",

View file

@ -111,6 +111,11 @@
"img": "icons/weapons/clubs/club-banded-barbed-black.webp", "img": "icons/weapons/clubs/club-banded-barbed-black.webp",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 75,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -120,9 +125,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784233, "createdTime": 1753922784233,
"modifiedTime": 1755259462491, "modifiedTime": 1755384280132,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "8Zkqk1jU09nKL2fy", "_id": "8Zkqk1jU09nKL2fy",

View file

@ -106,6 +106,11 @@
"img": "icons/magic/light/beam-rays-magenta.webp", "img": "icons/magic/light/beam-rays-magenta.webp",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 85,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -115,9 +120,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784233, "createdTime": 1753922784233,
"modifiedTime": 1755259462855, "modifiedTime": 1755385025439,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "jDmHqGvzg5wjgmxE", "_id": "jDmHqGvzg5wjgmxE",

View file

@ -99,6 +99,11 @@
}, },
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 85,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -108,9 +113,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784234, "createdTime": 1753922784234,
"modifiedTime": 1755259462618, "modifiedTime": 1755385032835,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "99TqczuQipBmaB8i", "_id": "99TqczuQipBmaB8i",

View file

@ -106,6 +106,11 @@
"type": "attack", "type": "attack",
"range": "melee", "range": "melee",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 75,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -115,9 +120,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784236, "createdTime": 1753922784236,
"modifiedTime": 1755259462495, "modifiedTime": 1755384289735,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "uOP5oT9QzXPlnf3p", "_id": "uOP5oT9QzXPlnf3p",

View file

@ -117,6 +117,11 @@
"type": "attack", "type": "attack",
"range": "melee", "range": "melee",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 85,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -126,9 +131,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784237, "createdTime": 1753922784237,
"modifiedTime": 1755264799637, "modifiedTime": 1755385040425,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "ZxWaWPdzFIUPNC62", "_id": "ZxWaWPdzFIUPNC62",

View file

@ -112,6 +112,11 @@
"type": "attack", "type": "attack",
"range": "melee", "range": "melee",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 76,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784237, "createdTime": 1753922784237,
"modifiedTime": 1755259462499, "modifiedTime": 1755384362436,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "CBBuEXAlLKFMJdjg", "_id": "CBBuEXAlLKFMJdjg",

View file

@ -117,6 +117,11 @@
"img": "icons/weapons/staves/staff-ornate-purple.webp", "img": "icons/weapons/staves/staff-ornate-purple.webp",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 85,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -126,9 +131,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784239, "createdTime": 1753922784239,
"modifiedTime": 1755259462512, "modifiedTime": 1755385049086,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "0NxCSugvKQ4W8OYZ", "_id": "0NxCSugvKQ4W8OYZ",

View file

@ -106,6 +106,11 @@
"range": "melee", "range": "melee",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 86,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -115,9 +120,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784239, "createdTime": 1753922784239,
"modifiedTime": 1755264898243, "modifiedTime": 1755385067530,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "tyBOpLfigAhI9bU3", "_id": "tyBOpLfigAhI9bU3",

View file

@ -99,6 +99,11 @@
"type": "attack", "type": "attack",
"range": "melee", "range": "melee",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 86,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -108,9 +113,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784240, "createdTime": 1753922784240,
"modifiedTime": 1755264925295, "modifiedTime": 1755385079522,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "zx99sOGTXicP4SSD", "_id": "zx99sOGTXicP4SSD",

View file

@ -111,6 +111,11 @@
"img": "icons/magic/nature/root-vines-grow-brown.webp", "img": "icons/magic/nature/root-vines-grow-brown.webp",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 76,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -120,9 +125,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784246, "createdTime": 1753922784246,
"modifiedTime": 1755259462506, "modifiedTime": 1755384371297,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "9x2xY9zwc3xzbXo5", "_id": "9x2xY9zwc3xzbXo5",

View file

@ -112,6 +112,11 @@
"type": "attack", "type": "attack",
"range": "melee", "range": "melee",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 91,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784247, "createdTime": 1753922784247,
"modifiedTime": 1755265775161, "modifiedTime": 1755385363507,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "pnyjIGxxvurcWmTv", "_id": "pnyjIGxxvurcWmTv",

View file

@ -112,6 +112,11 @@
"type": "attack", "type": "attack",
"range": "far", "range": "far",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 92,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784248, "createdTime": 1753922784248,
"modifiedTime": 1755266281854, "modifiedTime": 1755385375748,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "kE4dfhqmIQpNd44e", "_id": "kE4dfhqmIQpNd44e",

View file

@ -112,6 +112,11 @@
}, },
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 92,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784249, "createdTime": 1753922784249,
"modifiedTime": 1755259462532, "modifiedTime": 1755385382792,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "2VN3BftageoTTIzu", "_id": "2VN3BftageoTTIzu",

View file

@ -112,6 +112,11 @@
"img": "icons/magic/symbols/rune-sigil-rough-white-teal.webp", "img": "icons/magic/symbols/rune-sigil-rough-white-teal.webp",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 92,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784249, "createdTime": 1753922784249,
"modifiedTime": 1755259462726, "modifiedTime": 1755385392005,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "SxSOkM4bcVOFyjbo", "_id": "SxSOkM4bcVOFyjbo",

View file

@ -112,6 +112,11 @@
}, },
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 92,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784252, "createdTime": 1753922784252,
"modifiedTime": 1755259462568, "modifiedTime": 1755385398938,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "5lphJAgzoqZI3VoG", "_id": "5lphJAgzoqZI3VoG",

View file

@ -112,6 +112,11 @@
"range": "melee", "range": "melee",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 86,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784253, "createdTime": 1753922784253,
"modifiedTime": 1755264935543, "modifiedTime": 1755385087255,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "NoRZ1PqB8N5wcIw0", "_id": "NoRZ1PqB8N5wcIw0",

View file

@ -111,6 +111,11 @@
"type": "attack", "type": "attack",
"range": "melee", "range": "melee",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 93,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -120,9 +125,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784253, "createdTime": 1753922784253,
"modifiedTime": 1755266383523, "modifiedTime": 1755385409189,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "tBWHW00epmMnkawe", "_id": "tBWHW00epmMnkawe",

View file

@ -112,6 +112,11 @@
"type": "attack", "type": "attack",
"range": "melee", "range": "melee",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 76,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784257, "createdTime": 1753922784257,
"modifiedTime": 1755259591554, "modifiedTime": 1755384380804,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "wNzeuQLfLUMvgHlQ", "_id": "wNzeuQLfLUMvgHlQ",

View file

@ -112,6 +112,11 @@
}, },
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 93,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784258, "createdTime": 1753922784258,
"modifiedTime": 1755259462937, "modifiedTime": 1755385415645,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "wR7cFKrHvRzbzhBT", "_id": "wR7cFKrHvRzbzhBT",

View file

@ -106,6 +106,11 @@
"range": "melee", "range": "melee",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 86,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -115,9 +120,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784258, "createdTime": 1753922784258,
"modifiedTime": 1755264962798, "modifiedTime": 1755385098856,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "TLzY1nDw0Bu9Ud40", "_id": "TLzY1nDw0Bu9Ud40",

View file

@ -99,6 +99,11 @@
}, },
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 93,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -108,9 +113,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784259, "createdTime": 1753922784259,
"modifiedTime": 1755259462705, "modifiedTime": 1755385425940,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "P7h54ZePFPHpYwvB", "_id": "P7h54ZePFPHpYwvB",

View file

@ -138,6 +138,11 @@
"difficulty": null, "difficulty": null,
"damageMod": "none" "damageMod": "none"
} }
},
"attribution": {
"source": "Daggerheart SRD",
"page": 86,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -147,9 +152,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1754090776362, "createdTime": 1754090776362,
"modifiedTime": 1755259462811, "modifiedTime": 1755385106827,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"ownership": { "ownership": {

View file

@ -112,6 +112,11 @@
"range": "melee", "range": "melee",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 86,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "1.0.4", "systemVersion": "1.0.5",
"createdTime": 1753922784262, "createdTime": 1753922784262,
"modifiedTime": 1755265009751, "modifiedTime": 1755385114729,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "ChwwVqowFw8hJQwT", "_id": "ChwwVqowFw8hJQwT",

View file

@ -99,6 +99,11 @@
}, },
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 97,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -108,9 +113,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784264, "createdTime": 1753922784264,
"modifiedTime": 1755259462703, "modifiedTime": 1755385629418,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "OsLG2BjaEdTZUJU9", "_id": "OsLG2BjaEdTZUJU9",

View file

@ -112,6 +112,11 @@
"img": "icons/weapons/staves/staff-animal-skull-bull.webp", "img": "icons/weapons/staves/staff-animal-skull-bull.webp",
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
},
"attribution": {
"source": "Daggerheart SRD",
"page": 97,
"artist": ""
} }
}, },
"flags": {}, "flags": {},
@ -121,9 +126,9 @@
"exportSource": null, "exportSource": null,
"coreVersion": "13.347", "coreVersion": "13.347",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "1.0.5",
"createdTime": 1753922784265, "createdTime": 1753922784265,
"modifiedTime": 1755259462708, "modifiedTime": 1755385635754,
"lastModifiedBy": "VZIeX2YDvX338Zvr" "lastModifiedBy": "VZIeX2YDvX338Zvr"
}, },
"_id": "PELRry1vqjBzSAlr", "_id": "PELRry1vqjBzSAlr",

Some files were not shown because too many files have changed in this diff Show more