mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-04-21 23:13:39 +02:00
Compare commits
44 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3eda3c4c05 | ||
|
|
646e0debbd | ||
|
|
3cbc18f42b | ||
|
|
f850cbda76 | ||
|
|
f2ec5ef458 | ||
|
|
c683bc4352 | ||
|
|
fa04c9920f | ||
|
|
03110377e1 | ||
|
|
1fea8438ba | ||
|
|
4944722139 | ||
|
|
4b92001f97 | ||
|
|
2fde61a1d5 | ||
|
|
d9b322406d | ||
|
|
16c07d23bb | ||
|
|
91aff8b10d | ||
|
|
7e9385bc39 | ||
|
|
aa8771bf0d | ||
|
|
7d5cdeb09d | ||
|
|
8808e4646d | ||
|
|
a77d2088a0 | ||
|
|
c6335980ba | ||
|
|
1176328f62 | ||
| a62d28cd96 | |||
|
|
8d8dea81fe | ||
|
|
fb07938e54 | ||
|
|
c337338c8b | ||
|
|
f900011510 | ||
|
|
56a6613a73 | ||
|
|
e003db3ec1 | ||
|
|
66c90d69e3 | ||
|
|
a839ca0066 | ||
|
|
6ed975f5b7 | ||
|
|
e2c97a7b61 | ||
|
|
3ec013ff50 | ||
|
|
94f1fbdd9b | ||
|
|
f22b67367b | ||
|
|
8a0b1b8e22 | ||
|
|
1a57b55723 | ||
|
|
f910cf9795 | ||
|
|
6804bfe047 | ||
|
|
28d9254883 | ||
|
|
b076c2481b | ||
|
|
bdfc97bb3b | ||
|
|
e111f7c2ae |
212 changed files with 4007 additions and 1655 deletions
|
|
@ -1,3 +1,5 @@
|
||||||
[*]
|
[*]
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
indent_style = spaces
|
indent_style = spaces
|
||||||
|
[*.yml]
|
||||||
|
indent_size = 2
|
||||||
|
|
|
||||||
42
.github/workflows/ci.yml
vendored
Normal file
42
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
name: Project CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [24.x]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 10
|
||||||
|
|
||||||
|
- name: Cache NPM Deps
|
||||||
|
id: cache-npm
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules/
|
||||||
|
key: npm-${{ hashFiles('package-lock.json') }}
|
||||||
|
|
||||||
|
- name: Install NPM Deps
|
||||||
|
if: ${{ steps.cache-npm.outputs.cache-hit != 'true' }}
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: npm run lint
|
||||||
2
.github/workflows/deploy.yml
vendored
2
.github/workflows/deploy.yml
vendored
|
|
@ -35,7 +35,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
version: ${{steps.get_version.outputs.version-without-v}}
|
version: ${{steps.get_version.outputs.version-without-v}}
|
||||||
url: https://github.com/${{github.repository}}
|
url: https://github.com/${{github.repository}}
|
||||||
manifest: https://raw.githubusercontent.com/${{github.repository}}/main/system.json
|
manifest: https://raw.githubusercontent.com/${{github.repository}}/v14/system.json
|
||||||
download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/system.zip
|
download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/system.zip
|
||||||
|
|
||||||
# Create a zip file with all files required by the module to add to the release
|
# Create a zip file with all files required by the module to add to the release
|
||||||
|
|
|
||||||
|
|
@ -1,78 +1,9 @@
|
||||||
# Contributing to Foundryborne
|
# Contributing to Daggerheart
|
||||||
|
|
||||||
Welcome! This is a community-driven project to bring [Daggerheart](https://www.daggerheart.com/) to [FoundryVTT](https://foundryvtt.com/) as a full system. We're excited to have you here and appreciate your interest in contributing.
|
Thank you for your interest in contributing to the Foundryborne project!
|
||||||
|
|
||||||
---
|
To ensure that all contributions align with our project goals and architectural standards, we ask that you **do not submit outside contributions without first receiving feedback from the development team.**
|
||||||
|
|
||||||
## 🤝 How to Contribute
|
If you have an idea or a fix you'd like to contribute, please start a discussion or open an issue first. We'd love to hear from you and collaborate on the best way to move forward!
|
||||||
|
|
||||||
We welcome contributions of all kinds:
|
Thank you for your understanding and support.
|
||||||
|
|
||||||
- Bug reports
|
|
||||||
- Feature suggestions
|
|
||||||
- Code contributions
|
|
||||||
- UI/UX mockups
|
|
||||||
- Documentation improvements
|
|
||||||
- Questions and discussions
|
|
||||||
|
|
||||||
Please be respectful and collaborative — we’re all here to build something great together.
|
|
||||||
|
|
||||||
### Community Translations
|
|
||||||
|
|
||||||
Please note that we are not accepting community translations in the main project. Instead, community translations should be published as a module.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧭 General Guidelines
|
|
||||||
|
|
||||||
- **Use GitHub Issues** to report bugs or propose features
|
|
||||||
- **Start a Discussion** for larger ideas or questions
|
|
||||||
- **Open a Pull Request** once you've confirmed your work aligns with project direction
|
|
||||||
- **Keep things modular and maintainable** — if you're not sure how to structure something, ask!
|
|
||||||
- **Orient your code on existing examples**, and feel free to suggest a standard if it makes things clearer
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🗂️ Project Structure
|
|
||||||
|
|
||||||
Please try to follow the general logic of the existing code when submitting PRs.
|
|
||||||
|
|
||||||
We encourage contributors to leave comments or open Discussions when proposing structural or organizational changes.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧾 Issue & PR Best Practices
|
|
||||||
|
|
||||||
**For Issues:**
|
|
||||||
|
|
||||||
- Use clear, descriptive titles
|
|
||||||
- Provide a concise explanation of the problem or idea
|
|
||||||
- Include reproduction steps or example scenarios if it's a bug
|
|
||||||
- Add screenshots or logs if helpful
|
|
||||||
|
|
||||||
**For Pull Requests:**
|
|
||||||
|
|
||||||
- Use a clear title summarizing the change
|
|
||||||
- Provide a brief description of what your code does and why
|
|
||||||
- Link to any related Issues
|
|
||||||
- Keep PRs focused — smaller is better
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔖 Labels and Boards
|
|
||||||
|
|
||||||
We use GitHub labels to help organize contributions. If your issue or PR relates to a specific category, feel free to tag it. There is also a GitHub Project Board to help track active work and priorities.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📣 Communication
|
|
||||||
|
|
||||||
Discussions are currently happening on GitHub — in Issues, PRs, and [GitHub Discussions](https://github.com/Foundryborne/daggerheart/discussions). You're welcome to use any of these, though we may consolidate to one in the future.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🤗 Thank You!
|
|
||||||
|
|
||||||
Whether you're fixing a typo or designing entire mechanics — every contribution matters. Thank you for helping bring _Daggerheart_ to life in FoundryVTT through **Foundryborne**!
|
|
||||||
|
|
||||||
🐸🛠️
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,11 @@ CONFIG.Dice.daggerheart = {
|
||||||
FateRoll: FateRoll
|
FateRoll: FateRoll
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CONFIG.RegionBehavior.dataModels = {
|
||||||
|
...CONFIG.RegionBehavior.dataModels,
|
||||||
|
...data.regionBehaviors
|
||||||
|
};
|
||||||
|
|
||||||
Object.assign(CONFIG.Dice.termTypes, dice.diceTypes);
|
Object.assign(CONFIG.Dice.termTypes, dice.diceTypes);
|
||||||
|
|
||||||
CONFIG.Actor.documentClass = documents.DhpActor;
|
CONFIG.Actor.documentClass = documents.DhpActor;
|
||||||
|
|
@ -355,6 +360,8 @@ Hooks.on(CONFIG.DH.HOOKS.hooksConfig.groupRollStart, async data => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateActorsRangeDependentEffects = async token => {
|
const updateActorsRangeDependentEffects = async token => {
|
||||||
|
if (!token) return;
|
||||||
|
|
||||||
const rangeMeasurement = game.settings.get(
|
const rangeMeasurement = game.settings.get(
|
||||||
CONFIG.DH.id,
|
CONFIG.DH.id,
|
||||||
CONFIG.DH.SETTINGS.gameSettings.variantRules
|
CONFIG.DH.SETTINGS.gameSettings.variantRules
|
||||||
|
|
|
||||||
14
eslint.config.mjs
Normal file
14
eslint.config.mjs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import globals from 'globals';
|
||||||
|
import { defineConfig } from 'eslint/config';
|
||||||
|
import prettier from 'eslint-plugin-prettier';
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
{ files: ['**/*.{js,mjs,cjs}'], languageOptions: { globals: globals.browser } },
|
||||||
|
{ plugins: { prettier } },
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,mjs,cjs}'],
|
||||||
|
rules: {
|
||||||
|
'prettier/prettier': 'error'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
89
lang/en.json
89
lang/en.json
|
|
@ -74,9 +74,7 @@
|
||||||
"name": "Summon",
|
"name": "Summon",
|
||||||
"tooltip": "Create tokens in the scene.",
|
"tooltip": "Create tokens in the scene.",
|
||||||
"error": "You do not have permission to summon tokens or there is no active scene.",
|
"error": "You do not have permission to summon tokens or there is no active scene.",
|
||||||
"invalidDrop": "You can only drop Actor entities to summon.",
|
"invalidDrop": "You can only drop Actor entities to summon."
|
||||||
"chatMessageTitle": "Test2",
|
|
||||||
"chatMessageHeaderTitle": "Summoning"
|
|
||||||
},
|
},
|
||||||
"transform": {
|
"transform": {
|
||||||
"name": "Transform",
|
"name": "Transform",
|
||||||
|
|
@ -111,11 +109,18 @@
|
||||||
"customFormula": "Custom Formula",
|
"customFormula": "Custom Formula",
|
||||||
"formula": "Formula"
|
"formula": "Formula"
|
||||||
},
|
},
|
||||||
|
"area": {
|
||||||
|
"sectionTitle": "Areas",
|
||||||
|
"shape": "Shape",
|
||||||
|
"size": "Size"
|
||||||
|
},
|
||||||
"displayInChat": "Display in chat",
|
"displayInChat": "Display in chat",
|
||||||
"deleteTriggerTitle": "Delete Trigger",
|
"deleteTriggerTitle": "Delete Trigger",
|
||||||
"deleteTriggerContent": "Are you sure you want to delete the {trigger} trigger?",
|
"deleteTriggerContent": "Are you sure you want to delete the {trigger} trigger?",
|
||||||
"advantageState": "Advantage State",
|
"advantageState": "Advantage State",
|
||||||
"damageOnSave": "Damage on Save"
|
"damageOnSave": "Damage on Save",
|
||||||
|
"useDefaultItemValues": "Use default Item values",
|
||||||
|
"itemDamageIsUsed": "Item Damage Is Used"
|
||||||
},
|
},
|
||||||
"RollField": {
|
"RollField": {
|
||||||
"diceRolling": {
|
"diceRolling": {
|
||||||
|
|
@ -130,7 +135,7 @@
|
||||||
"attackModifier": "Attack Modifier",
|
"attackModifier": "Attack Modifier",
|
||||||
"attackName": "Attack Name",
|
"attackName": "Attack Name",
|
||||||
"criticalThreshold": "Critical Threshold",
|
"criticalThreshold": "Critical Threshold",
|
||||||
"includeBase": { "label": "Include Item Damage" },
|
"includeBase": { "label": "Use Item Damage" },
|
||||||
"groupAttack": { "label": "Group Attack" },
|
"groupAttack": { "label": "Group Attack" },
|
||||||
"multiplier": "Multiplier",
|
"multiplier": "Multiplier",
|
||||||
"saveHint": "Set a default Trait to enable Reaction Roll. It can be changed later in Reaction Roll Dialog.",
|
"saveHint": "Set a default Trait to enable Reaction Roll. It can be changed later in Reaction Roll Dialog.",
|
||||||
|
|
@ -163,7 +168,8 @@
|
||||||
"rangeDependence": {
|
"rangeDependence": {
|
||||||
"title": "Range Dependence"
|
"title": "Range Dependence"
|
||||||
},
|
},
|
||||||
"stacking": { "title": "Stacking" }
|
"stacking": { "title": "Stacking" },
|
||||||
|
"targetDispositions": "Affected Dispositions"
|
||||||
},
|
},
|
||||||
"RangeDependance": {
|
"RangeDependance": {
|
||||||
"hint": "Settings for an optional distance at which this effect should activate",
|
"hint": "Settings for an optional distance at which this effect should activate",
|
||||||
|
|
@ -210,7 +216,13 @@
|
||||||
"type": { "label": "Type" }
|
"type": { "label": "Type" }
|
||||||
},
|
},
|
||||||
"hordeDamage": "Horde Damage",
|
"hordeDamage": "Horde Damage",
|
||||||
"horderHp": "Horde/HP"
|
"horderHp": "Horde/HP",
|
||||||
|
"adversaryReactionRoll": {
|
||||||
|
"headerTitle": "Adversary Reaction Roll"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Base": {
|
||||||
|
"CannotAddType": "Cannot add {itemType} items to {actorType} actors."
|
||||||
},
|
},
|
||||||
"Character": {
|
"Character": {
|
||||||
"advantageSources": {
|
"advantageSources": {
|
||||||
|
|
@ -235,6 +247,8 @@
|
||||||
},
|
},
|
||||||
"defaultHopeDice": "Default Hope Dice",
|
"defaultHopeDice": "Default Hope Dice",
|
||||||
"defaultFearDice": "Default Fear Dice",
|
"defaultFearDice": "Default Fear Dice",
|
||||||
|
"defaultAdvantageDice": "Default Advantage Dice",
|
||||||
|
"defaultDisadvantageDice": "Default Disadvantage Dice",
|
||||||
"disadvantageSources": {
|
"disadvantageSources": {
|
||||||
"label": "Disadvantage Sources",
|
"label": "Disadvantage Sources",
|
||||||
"hint": "Add single words or short text as reminders and hints of what a character has disadvantage on."
|
"hint": "Add single words or short text as reminders and hints of what a character has disadvantage on."
|
||||||
|
|
@ -333,7 +347,8 @@
|
||||||
"minor": "MIN",
|
"minor": "MIN",
|
||||||
"major": "MAJ",
|
"major": "MAJ",
|
||||||
"severe": "SEV"
|
"severe": "SEV"
|
||||||
}
|
},
|
||||||
|
"triggerRestContent": "This will trigger a dialog to players make their downtime moves. Are you sure?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"APPLICATIONS": {
|
"APPLICATIONS": {
|
||||||
|
|
@ -467,6 +482,10 @@
|
||||||
"defaultOwnershipTooltip": "The default player ownership of countdowns",
|
"defaultOwnershipTooltip": "The default player ownership of countdowns",
|
||||||
"hideNewCountdowns": "Hide New Countdowns"
|
"hideNewCountdowns": "Hide New Countdowns"
|
||||||
},
|
},
|
||||||
|
"CreateItemDialog": {
|
||||||
|
"createItem": "Create Item",
|
||||||
|
"browseCompendium": "Browse Compendium"
|
||||||
|
},
|
||||||
"DaggerheartMenu": {
|
"DaggerheartMenu": {
|
||||||
"title": "GM Tools",
|
"title": "GM Tools",
|
||||||
"refreshFeatures": "Refresh Features",
|
"refreshFeatures": "Refresh Features",
|
||||||
|
|
@ -681,6 +700,12 @@
|
||||||
"noPlayers": "No players to assign ownership to",
|
"noPlayers": "No players to assign ownership to",
|
||||||
"default": "Default Ownership"
|
"default": "Default Ownership"
|
||||||
},
|
},
|
||||||
|
"PendingReactionsDialog": {
|
||||||
|
"title": "Pending Reaction Rolls Found",
|
||||||
|
"unfinishedRolls": "Some Tokens still need to roll their Reaction Roll.",
|
||||||
|
"confirmation": "Are you sure you want to continue ?",
|
||||||
|
"warning": "Undone reaction rolls will be considered as failed"
|
||||||
|
},
|
||||||
"ReactionRoll": {
|
"ReactionRoll": {
|
||||||
"title": "Reaction Roll: {trait}"
|
"title": "Reaction Roll: {trait}"
|
||||||
},
|
},
|
||||||
|
|
@ -706,7 +731,7 @@
|
||||||
"FIELDS": {
|
"FIELDS": {
|
||||||
"initiator": {
|
"initiator": {
|
||||||
"memberId": { "label": "Initiating Character" },
|
"memberId": { "label": "Initiating Character" },
|
||||||
"cost": { "label": "Initiation Cost" }
|
"cost": { "label": "Hope Cost" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"leaderTitle": "Initiating Character",
|
"leaderTitle": "Initiating Character",
|
||||||
|
|
@ -735,11 +760,11 @@
|
||||||
},
|
},
|
||||||
"GroupRollSelect": {
|
"GroupRollSelect": {
|
||||||
"title": "Group Roll",
|
"title": "Group Roll",
|
||||||
|
"aidingCharacters": "Aiding Characters",
|
||||||
"leader": "Leader",
|
"leader": "Leader",
|
||||||
"leaderRoll": "Leader Roll",
|
"leaderRoll": "Leader Roll",
|
||||||
"openDialogForAll": "Open Dialog For All",
|
"openDialogForAll": "Open Dialog For All",
|
||||||
"startGroupRoll": "Start Group Roll",
|
"startGroupRoll": "Start Group Roll",
|
||||||
"cancelGroupRoll": "Cancel",
|
|
||||||
"finishGroupRoll": "Finish Group Roll",
|
"finishGroupRoll": "Finish Group Roll",
|
||||||
"cancelConfirmTitle": "Cancel Group Roll",
|
"cancelConfirmTitle": "Cancel Group Roll",
|
||||||
"cancelConfirmText": "Are you sure you want to cancel the Group Roll? This will close it for all other players too."
|
"cancelConfirmText": "Are you sure you want to cancel the Group Roll? This will close it for all other players too."
|
||||||
|
|
@ -764,6 +789,11 @@
|
||||||
"session": "Next Session",
|
"session": "Next Session",
|
||||||
"custom": "Custom"
|
"custom": "Custom"
|
||||||
},
|
},
|
||||||
|
"ActionAutomationChoices": {
|
||||||
|
"never": "Never",
|
||||||
|
"showDialog": "Show Dialog Only",
|
||||||
|
"always": "Always"
|
||||||
|
},
|
||||||
"AdversaryTrait": {
|
"AdversaryTrait": {
|
||||||
"relentless": {
|
"relentless": {
|
||||||
"name": "Relentless",
|
"name": "Relentless",
|
||||||
|
|
@ -1293,6 +1323,11 @@
|
||||||
"on": "On",
|
"on": "On",
|
||||||
"onWithToggle": "On With Toggle"
|
"onWithToggle": "On With Toggle"
|
||||||
},
|
},
|
||||||
|
"SceneRangeMeasurementTypes": {
|
||||||
|
"disable": "Disable Daggerheart Range Measurement",
|
||||||
|
"default": "Default",
|
||||||
|
"custom": "Custom"
|
||||||
|
},
|
||||||
"SelectAction": {
|
"SelectAction": {
|
||||||
"selectType": "Select Action Type",
|
"selectType": "Select Action Type",
|
||||||
"selectAction": "Action Selection"
|
"selectAction": "Action Selection"
|
||||||
|
|
@ -1992,6 +2027,10 @@
|
||||||
"hint": "Multiply any damage dealt to you by this number"
|
"hint": "Multiply any damage dealt to you by this number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Battlepoints": {
|
||||||
|
"full": "Battlepoints",
|
||||||
|
"short": "BP"
|
||||||
|
},
|
||||||
"Bonuses": {
|
"Bonuses": {
|
||||||
"rest": {
|
"rest": {
|
||||||
"downtimeAction": "Downtime Action",
|
"downtimeAction": "Downtime Action",
|
||||||
|
|
@ -2402,10 +2441,13 @@
|
||||||
"maxWithThing": "Max {thing}",
|
"maxWithThing": "Max {thing}",
|
||||||
"missingDragDropThing": "Drop {thing} here",
|
"missingDragDropThing": "Drop {thing} here",
|
||||||
"multiclass": "Multiclass",
|
"multiclass": "Multiclass",
|
||||||
|
"name": "Name",
|
||||||
"newCategory": "New Category",
|
"newCategory": "New Category",
|
||||||
"newThing": "New {thing}",
|
"newThing": "New {thing}",
|
||||||
|
"next": "Next",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"noTarget": "No current target",
|
"noTarget": "No current target",
|
||||||
|
"optionalThing": "Optional {thing}",
|
||||||
"partner": "Partner",
|
"partner": "Partner",
|
||||||
"player": {
|
"player": {
|
||||||
"single": "Player",
|
"single": "Player",
|
||||||
|
|
@ -2432,9 +2474,11 @@
|
||||||
"rollDamage": "Roll Damage",
|
"rollDamage": "Roll Damage",
|
||||||
"rollWith": "{roll} Roll",
|
"rollWith": "{roll} Roll",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
|
"saveSettings": "Save Settings",
|
||||||
"scalable": "Scalable",
|
"scalable": "Scalable",
|
||||||
"scars": "Scars",
|
"scars": "Scars",
|
||||||
"situationalBonus": "Situational Bonus",
|
"situationalBonus": "Situational Bonus",
|
||||||
|
"searchPlaceholder": "Search...",
|
||||||
"spent": "Spent",
|
"spent": "Spent",
|
||||||
"step": "Step",
|
"step": "Step",
|
||||||
"stress": "Stress",
|
"stress": "Stress",
|
||||||
|
|
@ -2585,8 +2629,14 @@
|
||||||
},
|
},
|
||||||
"Weapon": {
|
"Weapon": {
|
||||||
"weaponType": "Weapon Type",
|
"weaponType": "Weapon Type",
|
||||||
"primaryWeapon": "Primary Weapon",
|
"primaryWeapon": {
|
||||||
"secondaryWeapon": "Secondary Weapon"
|
"full": "Primary Weapon",
|
||||||
|
"short": "Primary"
|
||||||
|
},
|
||||||
|
"secondaryWeapon": {
|
||||||
|
"full": "Secondary Weapon",
|
||||||
|
"short": "Secondary"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"MACROS": {
|
"MACROS": {
|
||||||
|
|
@ -2840,6 +2890,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Keybindings": {
|
"Keybindings": {
|
||||||
|
"partySheet": {
|
||||||
|
"name": "Toggle Party Sheet",
|
||||||
|
"hint": "Open or close the active party's sheet"
|
||||||
|
},
|
||||||
"spotlight": {
|
"spotlight": {
|
||||||
"name": "Spotlight Combatant",
|
"name": "Spotlight Combatant",
|
||||||
"hint": "Move the spotlight to a hovered or selected token that's present in an active encounter"
|
"hint": "Move the spotlight to a hovered or selected token that's present in an active encounter"
|
||||||
|
|
@ -3010,6 +3064,9 @@
|
||||||
"resourceRoll": {
|
"resourceRoll": {
|
||||||
"playerMessage": "{user} rerolled their {name}"
|
"playerMessage": "{user} rerolled their {name}"
|
||||||
},
|
},
|
||||||
|
"saveRoll": {
|
||||||
|
"reactionRollAllTargets": "Reaction Roll All Targets"
|
||||||
|
},
|
||||||
"tagTeam": {
|
"tagTeam": {
|
||||||
"title": "Tag Team",
|
"title": "Tag Team",
|
||||||
"membersTitle": "Members"
|
"membersTitle": "Members"
|
||||||
|
|
@ -3038,9 +3095,9 @@
|
||||||
},
|
},
|
||||||
"ItemBrowser": {
|
"ItemBrowser": {
|
||||||
"title": "Daggerheart Compendium Browser",
|
"title": "Daggerheart Compendium Browser",
|
||||||
|
"windowTitle": "Compendium Browser",
|
||||||
"hint": "Select a Folder in sidebar to start browsing through the compendium",
|
"hint": "Select a Folder in sidebar to start browsing through the compendium",
|
||||||
"browserSettings": "Browser Settings",
|
"browserSettings": "Browser Settings",
|
||||||
"searchPlaceholder": "Search...",
|
|
||||||
"columnName": "Name",
|
"columnName": "Name",
|
||||||
"tooltipFilters": "Filters",
|
"tooltipFilters": "Filters",
|
||||||
"tooltipErase": "Erase",
|
"tooltipErase": "Erase",
|
||||||
|
|
@ -3076,7 +3133,7 @@
|
||||||
"weapons": "Weapons",
|
"weapons": "Weapons",
|
||||||
"armors": "Armors",
|
"armors": "Armors",
|
||||||
"consumables": "Consumables",
|
"consumables": "Consumables",
|
||||||
"loots": "Loots"
|
"loots": "Loot"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Notifications": {
|
"Notifications": {
|
||||||
|
|
@ -3171,6 +3228,9 @@
|
||||||
"companion": "Level {level} - {partner}",
|
"companion": "Level {level} - {partner}",
|
||||||
"companionNoPartner": "No Partner",
|
"companionNoPartner": "No Partner",
|
||||||
"duplicateToNewTier": "Duplicate to New Tier",
|
"duplicateToNewTier": "Duplicate to New Tier",
|
||||||
|
"activateParty": "Make Active Party",
|
||||||
|
"partyIsActive": "Active",
|
||||||
|
"createAdversary": "Create Adversary",
|
||||||
"pickTierTitle": "Pick a new tier for this adversary"
|
"pickTierTitle": "Pick a new tier for this adversary"
|
||||||
},
|
},
|
||||||
"daggerheartMenu": {
|
"daggerheartMenu": {
|
||||||
|
|
@ -3182,6 +3242,7 @@
|
||||||
"Tooltip": {
|
"Tooltip": {
|
||||||
"disableEffect": "Disable Effect",
|
"disableEffect": "Disable Effect",
|
||||||
"enableEffect": "Enable Effect",
|
"enableEffect": "Enable Effect",
|
||||||
|
"edit": "Edit",
|
||||||
"openItemWorld": "Open Item World",
|
"openItemWorld": "Open Item World",
|
||||||
"openActorWorld": "Open Actor World",
|
"openActorWorld": "Open Actor World",
|
||||||
"sendToChat": "Send to Chat",
|
"sendToChat": "Send to Chat",
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
||||||
this.character = character;
|
this.character = character;
|
||||||
|
|
||||||
this.setup = {
|
this.setup = {
|
||||||
traits: this.character.system.traits,
|
traits: Object.keys(this.character.system.traits).reduce((acc, key) => {
|
||||||
|
acc[key] = { value: null };
|
||||||
|
return acc;
|
||||||
|
}, {}),
|
||||||
ancestryName: {
|
ancestryName: {
|
||||||
primary: '',
|
primary: '',
|
||||||
secondary: ''
|
secondary: ''
|
||||||
|
|
@ -377,8 +380,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
||||||
];
|
];
|
||||||
return Object.values(this.setup.traits).reduce((acc, x) => {
|
return Object.values(this.setup.traits).reduce((acc, x) => {
|
||||||
const index = traitCompareArray.indexOf(x.value);
|
const index = traitCompareArray.indexOf(x.value);
|
||||||
|
if (index === -1) return acc;
|
||||||
|
|
||||||
traitCompareArray.splice(index, 1);
|
traitCompareArray.splice(index, 1);
|
||||||
acc += index !== -1;
|
acc += 1;
|
||||||
return acc;
|
return acc;
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,8 @@ export default class ActionSelectionDialog extends HandlebarsApplicationMixin(Ap
|
||||||
|
|
||||||
static async #onChooseAction(event, button) {
|
static async #onChooseAction(event, button) {
|
||||||
const { actionId } = button.dataset;
|
const { actionId } = button.dataset;
|
||||||
this.#action = this.#item.system.actionsList.find(a => a._id === actionId);
|
this.action = this.item.system.actionsList.find(a => a._id === actionId);
|
||||||
Object.defineProperty(this.#event, 'shiftKey', {
|
Object.defineProperty(this.event, 'shiftKey', {
|
||||||
get() {
|
get() {
|
||||||
return event.shiftKey;
|
return event.shiftKey;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
context.advantage = this.config.roll?.advantage;
|
context.advantage = this.config.roll?.advantage;
|
||||||
context.disadvantage = this.config.roll?.disadvantage;
|
context.disadvantage = this.config.roll?.disadvantage;
|
||||||
context.diceOptions = CONFIG.DH.GENERAL.diceTypes;
|
context.diceOptions = CONFIG.DH.GENERAL.diceTypes;
|
||||||
|
context.dieFaces = CONFIG.DH.GENERAL.dieFaces.reduce((acc, face) => {
|
||||||
|
acc[face] = `d${face}`;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
context.isLite = this.config.roll?.lite;
|
context.isLite = this.config.roll?.lite;
|
||||||
context.extraFormula = this.config.extraFormula;
|
context.extraFormula = this.config.extraFormula;
|
||||||
context.formula = this.roll.constructFormula(this.config);
|
context.formula = this.roll.constructFormula(this.config);
|
||||||
|
|
@ -152,9 +156,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
}
|
}
|
||||||
if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses);
|
if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses);
|
||||||
if (rest.roll?.dice) {
|
if (rest.roll?.dice) {
|
||||||
Object.entries(rest.roll.dice).forEach(([key, value]) => {
|
this.roll = foundry.utils.mergeObject(this.roll, rest.roll.dice);
|
||||||
this.roll[key] = value;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (rest.hasOwnProperty('trait')) {
|
if (rest.hasOwnProperty('trait')) {
|
||||||
this.config.roll.trait = rest.trait;
|
this.config.roll.trait = rest.trait;
|
||||||
|
|
@ -173,6 +175,15 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
this.disadvantage = advantage === -1;
|
this.disadvantage = advantage === -1;
|
||||||
|
|
||||||
this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage;
|
this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage;
|
||||||
|
|
||||||
|
if (this.config.roll.advantage === 1 && this.config.data.rules.roll.defaultAdvantageDice) {
|
||||||
|
const faces = Number.parseInt(this.config.data.rules.roll.defaultAdvantageDice);
|
||||||
|
this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;
|
||||||
|
} else if (this.config.roll.advantage === -1 && this.config.data.rules.roll.defaultDisadvantageDice) {
|
||||||
|
const faces = Number.parseInt(this.config.data.rules.roll.defaultDisadvantageDice);
|
||||||
|
this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;
|
||||||
|
}
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -259,8 +259,9 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
||||||
const resetValue = increasing
|
const resetValue = increasing
|
||||||
? 0
|
? 0
|
||||||
: feature.system.resource.max
|
: feature.system.resource.max
|
||||||
? Roll.replaceFormulaData(feature.system.resource.max, this.actor)
|
? new Roll(Roll.replaceFormulaData(feature.system.resource.max, this.actor)).evaluateSync().total
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
await feature.update({ 'system.resource.value': resetValue });
|
await feature.update({ 'system.resource.value': resetValue });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
||||||
initializationPart.insertAdjacentHTML('afterend', '<div class="team-container"></div>');
|
initializationPart.insertAdjacentHTML('afterend', '<div class="team-container"></div>');
|
||||||
initializationPart.insertAdjacentHTML(
|
initializationPart.insertAdjacentHTML(
|
||||||
'afterend',
|
'afterend',
|
||||||
`<div class="section-title">${game.i18n.localize('Aiding Characters')}</div>`
|
`<div class="section-title">${game.i18n.localize('DAGGERHEART.APPLICATIONS.GroupRollSelect.aidingCharacters')}</div>`
|
||||||
);
|
);
|
||||||
|
|
||||||
const teamContainer = this.element.querySelector('.team-container');
|
const teamContainer = this.element.querySelector('.team-container');
|
||||||
|
|
|
||||||
|
|
@ -38,13 +38,15 @@ export default class ItemTransferDialog extends HandlebarsApplicationMixin(Appli
|
||||||
originActor ??= item?.actor;
|
originActor ??= item?.actor;
|
||||||
const homebrewKey = CONFIG.DH.SETTINGS.gameSettings.Homebrew;
|
const homebrewKey = CONFIG.DH.SETTINGS.gameSettings.Homebrew;
|
||||||
const currencySetting = game.settings.get(CONFIG.DH.id, homebrewKey).currency?.[currency] ?? null;
|
const currencySetting = game.settings.get(CONFIG.DH.id, homebrewKey).currency?.[currency] ?? null;
|
||||||
|
const max = item?.system.quantity ?? originActor.system.gold[currency] ?? 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
originActor,
|
originActor,
|
||||||
targetActor,
|
targetActor,
|
||||||
itemImage: item?.img,
|
itemImage: item?.img,
|
||||||
currencyIcon: currencySetting?.icon,
|
currencyIcon: currencySetting?.icon,
|
||||||
max: item?.system.quantity ?? originActor.system.gold[currency] ?? 0,
|
max,
|
||||||
|
initial: targetActor.system.metadata.quantifiable?.includes(item.type) ? max : 1,
|
||||||
title: item?.name ?? currencySetting?.label
|
title: item?.name ?? currencySetting?.label
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
owned: member.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)
|
owned: member.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.initiator = null;
|
this.initiator = { cost: 3 };
|
||||||
this.openForAllPlayers = true;
|
this.openForAllPlayers = true;
|
||||||
|
|
||||||
this.tabGroups.application = Object.keys(party.system.tagTeam.members).length
|
this.tabGroups.application = Object.keys(party.system.tagTeam.members).length
|
||||||
|
|
@ -115,6 +115,12 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
async _onRender(context, options) {
|
async _onRender(context, options) {
|
||||||
await super._onRender(context, options);
|
await super._onRender(context, options);
|
||||||
|
|
||||||
|
// if (this.element.querySelector('.roll-selection')) {
|
||||||
|
// for (const element of this.element.querySelectorAll('.team-member-container')) {
|
||||||
|
// element.classList.add('select-padding');
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (this.element.querySelector('.team-container')) return;
|
if (this.element.querySelector('.team-container')) return;
|
||||||
const initializationPart = this.element.querySelector('.initialization-container');
|
const initializationPart = this.element.querySelector('.initialization-container');
|
||||||
initializationPart.insertAdjacentHTML('afterend', '<div class="team-container"></div>');
|
initializationPart.insertAdjacentHTML('afterend', '<div class="team-container"></div>');
|
||||||
|
|
@ -133,7 +139,10 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
context.members = {};
|
context.members = {};
|
||||||
context.allHaveRolled = Object.keys(this.party.system.tagTeam.members).every(key => {
|
context.allHaveRolled = Object.keys(this.party.system.tagTeam.members).every(key => {
|
||||||
const data = this.party.system.tagTeam.members[key];
|
const data = this.party.system.tagTeam.members[key];
|
||||||
return Boolean(data.rollData);
|
const hasRolled = Boolean(data.rollData);
|
||||||
|
if (!hasRolled) return false;
|
||||||
|
|
||||||
|
return !data.rollData.options.hasDamage || Boolean(data.rollData.options.damage);
|
||||||
});
|
});
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
|
|
|
||||||
|
|
@ -122,15 +122,14 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
|
||||||
|
|
||||||
async toggleClowncar(actors) {
|
async toggleClowncar(actors) {
|
||||||
const animationDuration = 500;
|
const animationDuration = 500;
|
||||||
const activeTokens = actors.flatMap(member => member.getActiveTokens());
|
const scene = game.scenes.get(game.user.viewedScene);
|
||||||
|
/* getDependentTokens returns already removed tokens with id = null. Need to filter that until it's potentially fixed from Foundry */
|
||||||
|
const activeTokens = actors.flatMap(member => member.getDependentTokens({ scenes: scene }).filter(x => x._id));
|
||||||
const { x: actorX, y: actorY } = this.document;
|
const { x: actorX, y: actorY } = this.document;
|
||||||
if (activeTokens.length > 0) {
|
if (activeTokens.length > 0) {
|
||||||
for (let token of activeTokens) {
|
for (let token of activeTokens) {
|
||||||
await token.document.update(
|
await token.update({ x: actorX, y: actorY, alpha: 0 }, { animation: { duration: animationDuration } });
|
||||||
{ x: actorX, y: actorY, alpha: 0 },
|
setTimeout(() => token.delete(), animationDuration);
|
||||||
{ animation: { duration: animationDuration } }
|
|
||||||
);
|
|
||||||
setTimeout(() => token.document.delete(), animationDuration);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const activeScene = game.scenes.find(x => x.id === game.user.viewedScene);
|
const activeScene = game.scenes.find(x => x.id === game.user.viewedScene);
|
||||||
|
|
@ -140,11 +139,16 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
|
||||||
tokenData.push(data.toObject());
|
tokenData.push(data.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const viewedLevel = game.scenes.get(game.user.viewedScene).levels.get(game.user.viewedLevel);
|
||||||
|
const elevation = this.actor.token?.elevation ?? viewedLevel.elevation.bottom;
|
||||||
|
|
||||||
const newTokens = await activeScene.createEmbeddedDocuments(
|
const newTokens = await activeScene.createEmbeddedDocuments(
|
||||||
'Token',
|
'Token',
|
||||||
tokenData.map(tokenData => ({
|
tokenData.map(tokenData => ({
|
||||||
...tokenData,
|
...tokenData,
|
||||||
alpha: 0,
|
alpha: 0,
|
||||||
|
level: viewedLevel,
|
||||||
|
elevation: elevation,
|
||||||
x: actorX,
|
x: actorX,
|
||||||
y: actorY
|
y: actorY
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -118,8 +118,13 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
break;
|
break;
|
||||||
case 'footer':
|
case 'footer':
|
||||||
partContext.buttons = [
|
partContext.buttons = [
|
||||||
{ type: 'button', action: 'reset', icon: 'fa-solid fa-arrow-rotate-left', label: 'Reset' },
|
{
|
||||||
{ type: 'submit', icon: 'fa-solid fa-floppy-disk', label: 'Save Changes' }
|
type: 'button',
|
||||||
|
action: 'reset',
|
||||||
|
icon: 'fa-solid fa-arrow-rotate-left',
|
||||||
|
label: game.i18n.localize('SETTINGS.UI.ACTIONS.Reset')
|
||||||
|
},
|
||||||
|
{ type: 'submit', icon: 'fa-solid fa-floppy-disk', label: game.i18n.localize('EDITOR.Save') }
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,9 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
key = event.target.closest('[data-key]').dataset.key;
|
key = event.target.closest('[data-key]').dataset.key;
|
||||||
if (!this.action[key]) return;
|
if (!this.action[key]) return;
|
||||||
|
|
||||||
data[key].push(this.action.defaultValues[key] ?? {});
|
const value = key === 'areas' ? { name: this.action.item.name } : {};
|
||||||
|
|
||||||
|
data[key].push(this.action.defaultValues[key] ?? value);
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,17 @@ export default class DHActionConfig extends DHActionBaseConfig {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async addEffect(_event) {
|
static async addEffect(event) {
|
||||||
|
const { areaIndex } = event.target.dataset;
|
||||||
if (!this.action.effects) return;
|
if (!this.action.effects) return;
|
||||||
const data = this.action.toObject();
|
const data = this.action.toObject();
|
||||||
|
|
||||||
const created = await this.action.item.createEmbeddedDocuments('ActiveEffect', [
|
const created = await this.action.item.createEmbeddedDocuments('ActiveEffect', [
|
||||||
game.system.api.data.activeEffects.BaseEffect.getDefaultObject()
|
game.system.api.data.activeEffects.BaseEffect.getDefaultObject({ transfer: false })
|
||||||
]);
|
]);
|
||||||
|
|
||||||
data.effects.push({ _id: created[0]._id });
|
if (areaIndex !== undefined) data.areas[areaIndex].effects.push(created[0]._id);
|
||||||
|
else data.effects.push({ _id: created[0]._id });
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
this.action.item.effects.get(created[0]._id).sheet.render(true);
|
this.action.item.effects.get(created[0]._id).sheet.render(true);
|
||||||
}
|
}
|
||||||
|
|
@ -52,9 +54,19 @@ export default class DHActionConfig extends DHActionBaseConfig {
|
||||||
|
|
||||||
static removeEffect(event, button) {
|
static removeEffect(event, button) {
|
||||||
if (!this.action.effects) return;
|
if (!this.action.effects) return;
|
||||||
const index = button.dataset.index,
|
|
||||||
|
const { areaIndex, index } = button.dataset;
|
||||||
|
let effectId = null;
|
||||||
|
if (areaIndex !== undefined) {
|
||||||
|
effectId = this.action.areas[areaIndex].effects[index];
|
||||||
|
const data = this.action.toObject();
|
||||||
|
data.areas[areaIndex].effects.splice(index, 1);
|
||||||
|
this.constructor.updateForm.call(this, null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
} else {
|
||||||
effectId = this.action.effects[index]._id;
|
effectId = this.action.effects[index]._id;
|
||||||
this.constructor.removeElement.bind(this)(event, button);
|
this.constructor.removeElement.call(this, event, button);
|
||||||
|
}
|
||||||
|
|
||||||
this.action.item.deleteEmbeddedDocuments('ActiveEffect', [effectId]);
|
this.action.item.deleteEmbeddedDocuments('ActiveEffect', [effectId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,21 +31,35 @@ export default class DHActionSettingsConfig extends DHActionBaseConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async addEffect(_event) {
|
static async addEffect(_event) {
|
||||||
|
const { areaIndex } = event.target.dataset;
|
||||||
if (!this.action.effects) return;
|
if (!this.action.effects) return;
|
||||||
const effectData = game.system.api.data.activeEffects.BaseEffect.getDefaultObject();
|
|
||||||
|
const effectData = game.system.api.data.activeEffects.BaseEffect.getDefaultObject({ transfer: false });
|
||||||
const data = this.action.toObject();
|
const data = this.action.toObject();
|
||||||
|
|
||||||
this.sheetUpdate(data, effectData);
|
this.sheetUpdate(data, effectData);
|
||||||
this.effects = [...this.effects, effectData];
|
this.effects = [...this.effects, effectData];
|
||||||
data.effects.push({ _id: effectData.id });
|
|
||||||
|
if (areaIndex !== undefined) data.areas[areaIndex].effects.push(effectData.id);
|
||||||
|
else data.effects.push({ _id: effectData.id });
|
||||||
|
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
}
|
}
|
||||||
|
|
||||||
static removeEffect(event, button) {
|
static removeEffect(event, button) {
|
||||||
if (!this.action.effects) return;
|
if (!this.action.effects) return;
|
||||||
const index = button.dataset.index,
|
const { areaIndex, index } = button.dataset;
|
||||||
|
let effectId = null;
|
||||||
|
if (areaIndex !== undefined) {
|
||||||
|
effectId = this.action.areas[areaIndex].effects[index];
|
||||||
|
const data = this.action.toObject();
|
||||||
|
data.areas[areaIndex].effects.splice(index, 1);
|
||||||
|
this.constructor.updateForm.call(this, null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
} else {
|
||||||
effectId = this.action.effects[index]._id;
|
effectId = this.action.effects[index]._id;
|
||||||
this.constructor.removeElement.bind(this)(event, button);
|
this.constructor.removeElement.call(this, event, button);
|
||||||
|
}
|
||||||
|
|
||||||
this.sheetUpdate(
|
this.sheetUpdate(
|
||||||
this.action.toObject(),
|
this.action.toObject(),
|
||||||
this.effects.find(x => x.id === effectId),
|
this.effects.find(x => x.id === effectId),
|
||||||
|
|
|
||||||
|
|
@ -217,8 +217,8 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
||||||
static #reactionRoll(event) {
|
static #reactionRoll(event) {
|
||||||
const config = {
|
const config = {
|
||||||
event,
|
event,
|
||||||
title: `Reaction Roll: ${this.actor.name}`,
|
title: game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll'),
|
||||||
headerTitle: 'Adversary Reaction Roll',
|
headerTitle: game.i18n.localize('DAGGERHEART.ACTORS.Adversary.adversaryReactionRoll.headerTitle'),
|
||||||
roll: {
|
roll: {
|
||||||
type: 'trait'
|
type: 'trait'
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
classes: ['character'],
|
classes: ['character'],
|
||||||
position: { width: 850, height: 800 },
|
position: { width: 850, height: 800 },
|
||||||
/* Foundry adds disabled to all buttons and inputs if editPermission is missing. This is not desired. */
|
|
||||||
editPermission: CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER,
|
|
||||||
actions: {
|
actions: {
|
||||||
toggleVault: CharacterSheet.#toggleVault,
|
toggleVault: CharacterSheet.#toggleVault,
|
||||||
rollAttribute: CharacterSheet.#rollAttribute,
|
rollAttribute: CharacterSheet.#rollAttribute,
|
||||||
|
|
@ -68,7 +66,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
handler: CharacterSheet.#getEquipamentContextOptions,
|
handler: CharacterSheet.#getEquipmentContextOptions,
|
||||||
selector: '[data-item-uuid][data-type="armor"], [data-item-uuid][data-type="weapon"]',
|
selector: '[data-item-uuid][data-type="armor"], [data-item-uuid][data-type="weapon"]',
|
||||||
options: {
|
options: {
|
||||||
parentClassHooks: false,
|
parentClassHooks: false,
|
||||||
|
|
@ -170,6 +168,16 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
return applicationOptions;
|
return applicationOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
_toggleDisabled(disabled) {
|
||||||
|
// Overriden to only disable text inputs by default.
|
||||||
|
// Everything else is done by checking @root.editable in the sheet
|
||||||
|
const form = this.form;
|
||||||
|
for (const input of form.querySelectorAll('input:not([type=search]), .editor.prosemirror')) {
|
||||||
|
input.disabled = disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
async _onRender(context, options) {
|
async _onRender(context, options) {
|
||||||
await super._onRender(context, options);
|
await super._onRender(context, options);
|
||||||
|
|
@ -315,11 +323,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
/**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */
|
/**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */
|
||||||
const options = [
|
const options = [
|
||||||
{
|
{
|
||||||
name: 'toLoadout',
|
label: 'toLoadout',
|
||||||
icon: 'fa-solid fa-arrow-up',
|
icon: 'fa-solid fa-arrow-up',
|
||||||
condition: target => {
|
visible: target => {
|
||||||
const doc = getDocFromElementSync(target);
|
const doc = getDocFromElementSync(target);
|
||||||
return doc && doc.system.inVault;
|
return doc?.isOwner && doc.system.inVault;
|
||||||
},
|
},
|
||||||
callback: async target => {
|
callback: async target => {
|
||||||
const doc = await getDocFromElement(target);
|
const doc = await getDocFromElement(target);
|
||||||
|
|
@ -329,11 +337,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'recall',
|
label: 'recall',
|
||||||
icon: 'fa-solid fa-bolt-lightning',
|
icon: 'fa-solid fa-bolt-lightning',
|
||||||
condition: target => {
|
visible: target => {
|
||||||
const doc = getDocFromElementSync(target);
|
const doc = getDocFromElementSync(target);
|
||||||
return doc && doc.system.inVault;
|
return doc?.isOwner && doc.system.inVault;
|
||||||
},
|
},
|
||||||
callback: async (target, event) => {
|
callback: async (target, event) => {
|
||||||
const doc = await getDocFromElement(target);
|
const doc = await getDocFromElement(target);
|
||||||
|
|
@ -368,17 +376,17 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'toVault',
|
label: 'toVault',
|
||||||
icon: 'fa-solid fa-arrow-down',
|
icon: 'fa-solid fa-arrow-down',
|
||||||
condition: target => {
|
visible: target => {
|
||||||
const doc = getDocFromElementSync(target);
|
const doc = getDocFromElementSync(target);
|
||||||
return doc && !doc.system.inVault;
|
return doc?.isOwner && !doc.system.inVault;
|
||||||
},
|
},
|
||||||
callback: async target => (await getDocFromElement(target)).update({ 'system.inVault': true })
|
callback: async target => (await getDocFromElement(target)).update({ 'system.inVault': true })
|
||||||
}
|
}
|
||||||
].map(option => ({
|
].map(option => ({
|
||||||
...option,
|
...option,
|
||||||
name: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.name}`,
|
label: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.label}`,
|
||||||
icon: `<i class="${option.icon}"></i>`
|
icon: `<i class="${option.icon}"></i>`
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -391,29 +399,29 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
* @this {CharacterSheet}
|
* @this {CharacterSheet}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
static #getEquipamentContextOptions() {
|
static #getEquipmentContextOptions() {
|
||||||
const options = [
|
const options = [
|
||||||
{
|
{
|
||||||
name: 'equip',
|
label: 'equip',
|
||||||
icon: 'fa-solid fa-hands',
|
icon: 'fa-solid fa-hands',
|
||||||
condition: target => {
|
visible: target => {
|
||||||
const doc = getDocFromElementSync(target);
|
const doc = getDocFromElementSync(target);
|
||||||
return doc && !doc.system.equipped;
|
return doc.isOwner && doc && !doc.system.equipped;
|
||||||
},
|
},
|
||||||
callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target)
|
callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'unequip',
|
label: 'unequip',
|
||||||
icon: 'fa-solid fa-hands',
|
icon: 'fa-solid fa-hands',
|
||||||
condition: target => {
|
visible: target => {
|
||||||
const doc = getDocFromElementSync(target);
|
const doc = getDocFromElementSync(target);
|
||||||
return doc && doc.system.equipped;
|
return doc.isOwner && doc && doc.system.equipped;
|
||||||
},
|
},
|
||||||
callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target)
|
callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target)
|
||||||
}
|
}
|
||||||
].map(option => ({
|
].map(option => ({
|
||||||
...option,
|
...option,
|
||||||
name: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.name}`,
|
label: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.label}`,
|
||||||
icon: `<i class="${option.icon}"></i>`
|
icon: `<i class="${option.icon}"></i>`
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,7 @@ export default class Party extends DHBaseActorSheet {
|
||||||
title: game.i18n.localize(`DAGGERHEART.APPLICATIONS.Downtime.${button.dataset.type}.title`),
|
title: game.i18n.localize(`DAGGERHEART.APPLICATIONS.Downtime.${button.dataset.type}.title`),
|
||||||
icon: button.dataset.type === 'shortRest' ? 'fa-solid fa-utensils' : 'fa-solid fa-bed'
|
icon: button.dataset.type === 'shortRest' ? 'fa-solid fa-utensils' : 'fa-solid fa-bed'
|
||||||
},
|
},
|
||||||
content: 'This will trigger a dialog to players make their downtime moves, are you sure?',
|
content: game.i18n.localize('DAGGERHEART.ACTORS.Party.triggerRestContent'),
|
||||||
classes: ['daggerheart', 'dialog', 'dh-style']
|
classes: ['daggerheart', 'dialog', 'dh-style']
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -418,18 +418,18 @@ export default function DHApplicationMixin(Base) {
|
||||||
/**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */
|
/**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */
|
||||||
const options = [
|
const options = [
|
||||||
{
|
{
|
||||||
name: 'disableEffect',
|
label: 'disableEffect',
|
||||||
icon: 'fa-solid fa-lightbulb',
|
icon: 'fa-solid fa-lightbulb',
|
||||||
condition: element => {
|
visible: element => {
|
||||||
const target = element.closest('[data-item-uuid]');
|
const target = element.closest('[data-item-uuid]');
|
||||||
return !target.dataset.disabled && target.dataset.itemType !== 'beastform';
|
return !target.dataset.disabled && target.dataset.itemType !== 'beastform';
|
||||||
},
|
},
|
||||||
callback: async target => (await getDocFromElement(target)).update({ disabled: true })
|
callback: async target => (await getDocFromElement(target)).update({ disabled: true })
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'enableEffect',
|
label: 'enableEffect',
|
||||||
icon: 'fa-regular fa-lightbulb',
|
icon: 'fa-regular fa-lightbulb',
|
||||||
condition: element => {
|
visible: element => {
|
||||||
const target = element.closest('[data-item-uuid]');
|
const target = element.closest('[data-item-uuid]');
|
||||||
return target.dataset.disabled && target.dataset.itemType !== 'beastform';
|
return target.dataset.disabled && target.dataset.itemType !== 'beastform';
|
||||||
},
|
},
|
||||||
|
|
@ -437,7 +437,7 @@ export default function DHApplicationMixin(Base) {
|
||||||
}
|
}
|
||||||
].map(option => ({
|
].map(option => ({
|
||||||
...option,
|
...option,
|
||||||
name: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.name}`,
|
label: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.label}`,
|
||||||
icon: `<i class="${option.icon}"></i>`
|
icon: `<i class="${option.icon}"></i>`
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -468,14 +468,14 @@ export default function DHApplicationMixin(Base) {
|
||||||
_getContextMenuCommonOptions({ usable = false, toChat = false, deletable = true }) {
|
_getContextMenuCommonOptions({ usable = false, toChat = false, deletable = true }) {
|
||||||
const options = [
|
const options = [
|
||||||
{
|
{
|
||||||
name: 'CONTROLS.CommonEdit',
|
label: 'CONTROLS.CommonEdit',
|
||||||
icon: 'fa-solid fa-pen-to-square',
|
icon: 'fa-solid fa-pen-to-square',
|
||||||
condition: target => {
|
visible: target => {
|
||||||
const { dataset } = target.closest('[data-item-uuid]');
|
const { dataset } = target.closest('[data-item-uuid]');
|
||||||
const doc = getDocFromElementSync(target);
|
const doc = getDocFromElementSync(target);
|
||||||
return (
|
return (
|
||||||
(!dataset.noCompendiumEdit && !doc) ||
|
(!dataset.noCompendiumEdit && !doc) ||
|
||||||
(doc && (!doc?.hasOwnProperty('systemPath') || doc?.inCollection))
|
(doc?.isOwner && (!doc?.hasOwnProperty('systemPath') || doc?.inCollection))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
callback: async target => (await getDocFromElement(target)).sheet.render({ force: true })
|
callback: async target => (await getDocFromElement(target)).sheet.render({ force: true })
|
||||||
|
|
@ -484,14 +484,14 @@ export default function DHApplicationMixin(Base) {
|
||||||
|
|
||||||
if (usable) {
|
if (usable) {
|
||||||
options.unshift({
|
options.unshift({
|
||||||
name: 'DAGGERHEART.GENERAL.damage',
|
label: 'DAGGERHEART.GENERAL.damage',
|
||||||
icon: 'fa-solid fa-explosion',
|
icon: 'fa-solid fa-explosion',
|
||||||
condition: target => {
|
visible: target => {
|
||||||
const doc = getDocFromElementSync(target);
|
const doc = getDocFromElementSync(target);
|
||||||
return (
|
const hasDamage =
|
||||||
!foundry.utils.isEmpty(doc?.system?.attack?.damage.parts) ||
|
!foundry.utils.isEmpty(doc?.system?.attack?.damage.parts) ||
|
||||||
!foundry.utils.isEmpty(doc?.damage?.parts)
|
!foundry.utils.isEmpty(doc?.damage?.parts);
|
||||||
);
|
return doc?.isOwner && hasDamage;
|
||||||
},
|
},
|
||||||
callback: async (target, event) => {
|
callback: async (target, event) => {
|
||||||
const doc = await getDocFromElement(target),
|
const doc = await getDocFromElement(target),
|
||||||
|
|
@ -507,11 +507,11 @@ export default function DHApplicationMixin(Base) {
|
||||||
});
|
});
|
||||||
|
|
||||||
options.unshift({
|
options.unshift({
|
||||||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
|
label: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
|
||||||
icon: 'fa-solid fa-burst',
|
icon: 'fa-solid fa-burst',
|
||||||
condition: target => {
|
visible: target => {
|
||||||
const doc = getDocFromElementSync(target);
|
const doc = getDocFromElementSync(target);
|
||||||
return doc && !(doc.type === 'domainCard' && doc.system.inVault);
|
return doc?.isOwner && !(doc.type === 'domainCard' && doc.system.inVault);
|
||||||
},
|
},
|
||||||
callback: async (target, event) => (await getDocFromElement(target)).use(event)
|
callback: async (target, event) => (await getDocFromElement(target)).use(event)
|
||||||
});
|
});
|
||||||
|
|
@ -519,18 +519,19 @@ export default function DHApplicationMixin(Base) {
|
||||||
|
|
||||||
if (toChat)
|
if (toChat)
|
||||||
options.push({
|
options.push({
|
||||||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
|
label: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
|
||||||
icon: 'fa-solid fa-message',
|
icon: 'fa-solid fa-message',
|
||||||
callback: async target => (await getDocFromElement(target)).toChat(this.document.uuid)
|
callback: async target => (await getDocFromElement(target)).toChat(this.document.uuid)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (deletable)
|
if (deletable)
|
||||||
options.push({
|
options.push({
|
||||||
name: 'CONTROLS.CommonDelete',
|
label: 'CONTROLS.CommonDelete',
|
||||||
icon: 'fa-solid fa-trash',
|
icon: 'fa-solid fa-trash',
|
||||||
condition: element => {
|
visible: element => {
|
||||||
const target = element.closest('[data-item-uuid]');
|
const target = element.closest('[data-item-uuid]');
|
||||||
return target.dataset.itemType !== 'beastform';
|
const doc = getDocFromElementSync(target);
|
||||||
|
return doc?.isOwner && target.dataset.itemType !== 'beastform';
|
||||||
},
|
},
|
||||||
callback: async (target, event) => {
|
callback: async (target, event) => {
|
||||||
const doc = await getDocFromElement(target);
|
const doc = await getDocFromElement(target);
|
||||||
|
|
@ -644,12 +645,12 @@ export default function DHApplicationMixin(Base) {
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
action: 'create',
|
action: 'create',
|
||||||
label: 'Create Item',
|
label: game.i18n.localize('DAGGERHEART.APPLICATIONS.CreateItemDialog.createItem'),
|
||||||
icon: 'fa-solid fa-plus'
|
icon: 'fa-solid fa-plus'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
action: 'browse',
|
action: 'browse',
|
||||||
label: 'Browse Compendium',
|
label: game.i18n.localize('DAGGERHEART.APPLICATIONS.CreateItemDialog.browseCompendium'),
|
||||||
icon: 'fa-solid fa-book'
|
icon: 'fa-solid fa-book'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
.hideAttribution;
|
.hideAttribution;
|
||||||
|
|
||||||
// Prepare inventory data
|
// Prepare inventory data
|
||||||
if (['party', 'character'].includes(this.document.type)) {
|
if (this.document.system.metadata.hasInventory) {
|
||||||
context.inventory = {
|
context.inventory = {
|
||||||
currencies: {},
|
currencies: {},
|
||||||
weapons: this.document.itemTypes.weapon.sort((a, b) => a.sort - b.sort),
|
weapons: this.document.itemTypes.weapon.sort((a, b) => a.sort - b.sort),
|
||||||
|
|
@ -283,11 +283,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
async _onDropItem(event, item) {
|
async _onDropItem(event, item) {
|
||||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||||
const originActor = item.actor;
|
const originActor = item.actor;
|
||||||
if (
|
if (!originActor || originActor.uuid === this.document.uuid || !this.document.system.metadata.hasInventory) {
|
||||||
item.actor?.uuid === this.document.uuid ||
|
|
||||||
!originActor ||
|
|
||||||
!['character', 'party'].includes(this.document.type)
|
|
||||||
) {
|
|
||||||
return super._onDropItem(event, item);
|
return super._onDropItem(event, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -302,47 +298,79 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.system.metadata.isQuantifiable) {
|
// Perform the actual transfer, showing a dialog when doing it
|
||||||
const actorItem = originActor.items.get(data.originId);
|
const availableQuantity = Math.max(1, item.system.quantity);
|
||||||
const quantityTransfered = await game.system.api.applications.dialogs.ItemTransferDialog.configure({
|
const actorItem = originActor.items.get(data.originId) ?? item;
|
||||||
|
if (availableQuantity > 1) {
|
||||||
|
const quantityTransferred = await game.system.api.applications.dialogs.ItemTransferDialog.configure({
|
||||||
item,
|
item,
|
||||||
targetActor: this.document
|
targetActor: this.document
|
||||||
});
|
});
|
||||||
|
return this.#transferItem(actorItem, quantityTransferred);
|
||||||
if (quantityTransfered) {
|
|
||||||
const existingItem = this.document.items.find(x => itemIsIdentical(x, item));
|
|
||||||
if (existingItem) {
|
|
||||||
await existingItem.update({
|
|
||||||
'system.quantity': existingItem.system.quantity + quantityTransfered
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const createData = item.toObject();
|
|
||||||
await this.document.createEmbeddedDocuments('Item', [
|
|
||||||
{
|
|
||||||
...createData,
|
|
||||||
system: {
|
|
||||||
...createData.system,
|
|
||||||
quantity: quantityTransfered
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quantityTransfered === actorItem.system.quantity) {
|
|
||||||
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
|
||||||
} else {
|
|
||||||
await actorItem.update({
|
|
||||||
'system.quantity': actorItem.system.quantity - quantityTransfered
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
await this.document.createEmbeddedDocuments('Item', [item.toObject()]);
|
return this.#transferItem(actorItem, availableQuantity);
|
||||||
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to perform the actual transfer of an item to this actor, including stack/unstack logic based on target quantifiability.
|
||||||
|
* Make sure item is the actor item before calling this method or there will be issues
|
||||||
|
*/
|
||||||
|
async #transferItem(item, quantity) {
|
||||||
|
const originActor = item.actor;
|
||||||
|
const targetActor = this.document;
|
||||||
|
const allowStacking = targetActor.system.metadata.quantifiable?.includes(item.type);
|
||||||
|
|
||||||
|
const batch = [];
|
||||||
|
|
||||||
|
// First add/update the item to the target actor
|
||||||
|
const existing = allowStacking ? targetActor.items.find(x => itemIsIdentical(x, item)) : null;
|
||||||
|
if (existing) {
|
||||||
|
batch.push({
|
||||||
|
action: 'update',
|
||||||
|
documentName: 'Item',
|
||||||
|
parent: targetActor,
|
||||||
|
updates: [{ '_id': existing.id, 'system.quantity': existing.system.quantity + quantity }]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const itemsToCreate = [];
|
||||||
|
if (allowStacking) {
|
||||||
|
itemsToCreate.push(foundry.utils.mergeObject(item.toObject(true), { system: { quantity } }));
|
||||||
|
} else {
|
||||||
|
const createData = new Array(Math.max(1, quantity))
|
||||||
|
.fill(0)
|
||||||
|
.map(() => foundry.utils.mergeObject(item.toObject(), { system: { quantity: 1 } }));
|
||||||
|
itemsToCreate.push(...createData);
|
||||||
|
}
|
||||||
|
batch.push({
|
||||||
|
action: 'create',
|
||||||
|
documentName: 'Item',
|
||||||
|
parent: targetActor,
|
||||||
|
data: itemsToCreate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the item from the original actor (by either deleting it, or updating its quantity)
|
||||||
|
if (quantity >= item.system.quantity) {
|
||||||
|
batch.push({
|
||||||
|
action: 'delete',
|
||||||
|
documentName: 'Item',
|
||||||
|
parent: originActor,
|
||||||
|
ids: [item.id]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
batch.push({
|
||||||
|
action: 'update',
|
||||||
|
documentName: 'Item',
|
||||||
|
parent: originActor,
|
||||||
|
updates: [{ '_id': item.id, 'system.quantity': item.system.quantity - quantity }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundry.documents.modifyBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On dragStart on the item.
|
* On dragStart on the item.
|
||||||
* @param {DragEvent} event - The drag event
|
* @param {DragEvent} event - The drag event
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,12 @@ export default class AncestrySheet extends DHHeritageSheet {
|
||||||
if (data.type === 'ActiveEffect') return super._onDrop(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 =
|
if (target) {
|
||||||
this.document.system[target.dataset.type === 'primary' ? 'primaryFeature' : 'secondaryFeature'];
|
const typeField =
|
||||||
|
this.document.system[target.dataset.type === 'primary' ? 'primaryFeature' : 'secondaryFeature'];
|
||||||
if (!typeField) {
|
if (!typeField) {
|
||||||
super._onDrop(event);
|
super._onDrop(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
||||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//Might be wrong location but testing out if here is okay.
|
//Might be wrong location but testing out if here is okay.
|
||||||
/**@override */
|
/**@override */
|
||||||
async _prepareContext(options) {
|
async _prepareContext(options) {
|
||||||
const context = await super._prepareContext(options);
|
const context = await super._prepareContext(options);
|
||||||
|
|
|
||||||
|
|
@ -46,50 +46,67 @@ export default class DhActorDirectory extends foundry.applications.sidebar.tabs.
|
||||||
|
|
||||||
_getEntryContextOptions() {
|
_getEntryContextOptions() {
|
||||||
const options = super._getEntryContextOptions();
|
const options = super._getEntryContextOptions();
|
||||||
options.push({
|
options.push(
|
||||||
name: 'DAGGERHEART.UI.Sidebar.actorDirectory.duplicateToNewTier',
|
{
|
||||||
icon: `<i class="fa-solid fa-arrow-trend-up" inert></i>`,
|
label: 'DAGGERHEART.UI.Sidebar.actorDirectory.duplicateToNewTier',
|
||||||
condition: li => {
|
icon: `<i class="fa-solid fa-arrow-trend-up" inert></i>`,
|
||||||
const actor = game.actors.get(li.dataset.entryId);
|
visible: li => {
|
||||||
return actor?.type === 'adversary' && actor.system.type !== 'social';
|
const actor = game.actors.get(li.dataset.entryId);
|
||||||
},
|
return actor?.type === 'adversary' && actor.system.type !== 'social';
|
||||||
callback: async li => {
|
},
|
||||||
const actor = game.actors.get(li.dataset.entryId);
|
callback: async li => {
|
||||||
if (!actor) throw new Error('Unexpected missing actor');
|
const actor = game.actors.get(li.dataset.entryId);
|
||||||
|
if (!actor) throw new Error('Unexpected missing actor');
|
||||||
|
|
||||||
const tiers = [1, 2, 3, 4].filter(t => t !== actor.system.tier);
|
const tiers = [1, 2, 3, 4].filter(t => t !== actor.system.tier);
|
||||||
const content = document.createElement('div');
|
const content = document.createElement('div');
|
||||||
const select = document.createElement('select');
|
const select = document.createElement('select');
|
||||||
select.name = 'tier';
|
select.name = 'tier';
|
||||||
select.append(
|
select.append(
|
||||||
...tiers.map(t => {
|
...tiers.map(t => {
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
option.value = t;
|
option.value = t;
|
||||||
option.textContent = game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${t}`);
|
option.textContent = game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${t}`);
|
||||||
return option;
|
return option;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
content.append(select);
|
content.append(select);
|
||||||
|
|
||||||
const tier = await foundry.applications.api.Dialog.input({
|
const tier = await foundry.applications.api.Dialog.input({
|
||||||
classes: ['dh-style', 'dialog'],
|
classes: ['dh-style', 'dialog'],
|
||||||
window: { title: 'DAGGERHEART.UI.Sidebar.actorDirectory.pickTierTitle' },
|
window: { title: 'DAGGERHEART.UI.Sidebar.actorDirectory.pickTierTitle' },
|
||||||
content,
|
content,
|
||||||
ok: {
|
ok: {
|
||||||
label: 'Create Adversary',
|
label: 'DAGGERHEART.UI.Sidebar.actorDirectory.createAdversary',
|
||||||
callback: (event, button, dialog) => Number(button.form.elements.tier.value)
|
callback: (event, button, dialog) => Number(button.form.elements.tier.value)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tier === actor.system.tier) {
|
||||||
|
ui.notifications.warn('This actor is already at this tier');
|
||||||
|
} else if (tier) {
|
||||||
|
const source = actor.system.adjustForTier(tier);
|
||||||
|
await Actor.create(source);
|
||||||
|
ui.notifications.info(`Tier ${tier} ${actor.name} created`);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'DAGGERHEART.UI.Sidebar.actorDirectory.activateParty',
|
||||||
|
icon: `<i class="fa-regular fa-square"></i>`,
|
||||||
|
visible: li => {
|
||||||
|
const actor = game.actors.get(li.dataset.entryId);
|
||||||
|
return actor && actor.type === 'party' && !actor.system.active;
|
||||||
|
},
|
||||||
|
callback: async li => {
|
||||||
|
const actor = game.actors.get(li.dataset.entryId);
|
||||||
|
if (!actor) throw new Error('Unexpected missing actor');
|
||||||
|
|
||||||
if (tier === actor.system.tier) {
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.ActiveParty, actor.id);
|
||||||
ui.notifications.warn('This actor is already at this tier');
|
ui.actors.render();
|
||||||
} else if (tier) {
|
|
||||||
const source = actor.system.adjustForTier(tier);
|
|
||||||
await Actor.create(source);
|
|
||||||
ui.notifications.info(`Tier ${tier} ${actor.name} created`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,23 +103,10 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
_getEntryContextOptions() {
|
_getEntryContextOptions() {
|
||||||
return [
|
return [
|
||||||
...super._getEntryContextOptions(),
|
...super._getEntryContextOptions(),
|
||||||
// {
|
|
||||||
// name: 'Reroll',
|
|
||||||
// icon: '<i class="fa-solid fa-dice"></i>',
|
|
||||||
// condition: li => {
|
|
||||||
// const message = game.messages.get(li.dataset.messageId);
|
|
||||||
|
|
||||||
// return (game.user.isGM || message.isAuthor) && message.rolls.length > 0;
|
|
||||||
// },
|
|
||||||
// callback: li => {
|
|
||||||
// const message = game.messages.get(li.dataset.messageId);
|
|
||||||
// new game.system.api.applications.dialogs.RerollDialog(message).render({ force: true });
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
name: game.i18n.localize('DAGGERHEART.UI.ChatLog.rerollDamage'),
|
label: 'DAGGERHEART.UI.ChatLog.rerollDamage',
|
||||||
icon: '<i class="fa-solid fa-dice"></i>',
|
icon: '<i class="fa-solid fa-dice"></i>',
|
||||||
condition: li => {
|
visible: li => {
|
||||||
const message = game.messages.get(li.dataset.messageId);
|
const message = game.messages.get(li.dataset.messageId);
|
||||||
const hasRolledDamage = message.system.hasDamage
|
const hasRolledDamage = message.system.hasDamage
|
||||||
? Object.keys(message.system.damage).length > 0
|
? Object.keys(message.system.damage).length > 0
|
||||||
|
|
|
||||||
|
|
@ -84,15 +84,15 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
||||||
_getCombatContextOptions() {
|
_getCombatContextOptions() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: 'COMBAT.ClearMovementHistories',
|
label: 'COMBAT.ClearMovementHistories',
|
||||||
icon: '<i class="fa-solid fa-shoe-prints"></i>',
|
icon: '<i class="fa-solid fa-shoe-prints"></i>',
|
||||||
condition: () => game.user.isGM && this.viewed?.combatants.size > 0,
|
visible: () => game.user.isGM && this.viewed?.combatants.size > 0,
|
||||||
callback: () => this.viewed.clearMovementHistories()
|
callback: () => this.viewed.clearMovementHistories()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'COMBAT.Delete',
|
label: 'COMBAT.Delete',
|
||||||
icon: '<i class="fa-solid fa-trash"></i>',
|
icon: '<i class="fa-solid fa-trash"></i>',
|
||||||
condition: () => game.user.isGM && !!this.viewed,
|
visible: () => game.user.isGM && !!this.viewed,
|
||||||
callback: () => this.viewed.endCombat()
|
callback: () => this.viewed.endCombat()
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV
|
||||||
tag: 'div',
|
tag: 'div',
|
||||||
window: {
|
window: {
|
||||||
frame: true,
|
frame: true,
|
||||||
title: 'Fear',
|
title: 'DAGGERHEART.GENERAL.fear',
|
||||||
positioned: true,
|
positioned: true,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
minimizable: false
|
minimizable: false
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
tag: 'div',
|
tag: 'div',
|
||||||
window: {
|
window: {
|
||||||
frame: true,
|
frame: true,
|
||||||
title: 'Compendium Browser',
|
title: 'DAGGERHEART.UI.ItemBrowser.windowTitle',
|
||||||
icon: 'fa-solid fa-book-atlas',
|
icon: 'fa-solid fa-book-atlas',
|
||||||
positioned: true,
|
positioned: true,
|
||||||
resizable: true
|
resizable: true
|
||||||
|
|
@ -207,8 +207,23 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
label: game.i18n.localize(col.label)
|
label: game.i18n.localize(col.label)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const splitPath = folderId?.split('.') ?? [];
|
||||||
|
const { pathLabels } = splitPath.reduce(
|
||||||
|
(acc, curr) => {
|
||||||
|
acc.currentPath = !acc.currentPath ? curr : [acc.currentPath, curr].join('.');
|
||||||
|
if (curr === 'folder') return acc;
|
||||||
|
|
||||||
|
const label = foundry.utils.getProperty(this.config, acc.currentPath)?.label;
|
||||||
|
if (label) acc.pathLabels.push(game.i18n.localize(label));
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ pathLabels: [], currentPath: '' }
|
||||||
|
);
|
||||||
|
|
||||||
this.selectedMenu = {
|
this.selectedMenu = {
|
||||||
path: folderId?.split('.') ?? [],
|
path: splitPath,
|
||||||
|
pathLabels: pathLabels,
|
||||||
data: {
|
data: {
|
||||||
...folderData,
|
...folderData,
|
||||||
columns: columns
|
columns: columns
|
||||||
|
|
@ -568,7 +583,9 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
const { itemUuid } = event.target.closest('[data-item-uuid]').dataset,
|
const { itemUuid } = event.target.closest('[data-item-uuid]').dataset,
|
||||||
item = await foundry.utils.fromUuid(itemUuid),
|
item = await foundry.utils.fromUuid(itemUuid),
|
||||||
dragData = item.toDragData();
|
dragData = item.toDragData();
|
||||||
|
|
||||||
event.dataTransfer.setData('text/plain', JSON.stringify(dragData));
|
event.dataTransfer.setData('text/plain', JSON.stringify(dragData));
|
||||||
|
event.dataTransfer.setDragImage(event.target.querySelector('img'), 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_canDragStart() {
|
_canDragStart() {
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,11 @@ export default class DHContextMenu extends foundry.applications.ux.ContextMenu {
|
||||||
static triggerContextMenu(event, altSelector) {
|
static triggerContextMenu(event, altSelector) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const { clientX, clientY } = event;
|
|
||||||
const selector = altSelector ?? '[data-item-uuid]';
|
const selector = altSelector ?? '[data-item-uuid]';
|
||||||
|
if (ui.context?.selector === selector) return;
|
||||||
|
|
||||||
|
const { clientX, clientY } = event;
|
||||||
const target = event.target.closest(selector) ?? event.currentTarget.closest(selector);
|
const target = event.target.closest(selector) ?? event.currentTarget.closest(selector);
|
||||||
target?.dispatchEvent(
|
target?.dispatchEvent(
|
||||||
new PointerEvent('contextmenu', {
|
new PointerEvent('contextmenu', {
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ export default class FilterMenu extends foundry.applications.ux.ContextMenu {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const damageTypeFilter = Object.values(CONFIG.DH.GENERAL.damageTypes).map(({ id, abbreviation }) => ({
|
const damageTypeFilter = Object.values(CONFIG.DH.GENERAL.damageTypes).map(({ id, abbreviation }) => ({
|
||||||
group: 'Damage Type', //TODO localize
|
group: game.i18n.localize('DAGGERHEART.GENERAL.damageType'),
|
||||||
name: game.i18n.localize(abbreviation),
|
name: game.i18n.localize(abbreviation),
|
||||||
filter: {
|
filter: {
|
||||||
field: 'system.damage.type',
|
field: 'system.damage.type',
|
||||||
|
|
|
||||||
|
|
@ -95,4 +95,61 @@ export default class DhRegionLayer extends foundry.canvas.layers.RegionLayer {
|
||||||
});
|
});
|
||||||
return inBounds.length === 1 ? inBounds[0] : null;
|
return inBounds.length === 1 ? inBounds[0] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getTemplateShape({ type, angle, range, direction } = {}) {
|
||||||
|
const { line, rectangle, inFront, cone, circle, emanation } = CONFIG.DH.GENERAL.templateTypes;
|
||||||
|
|
||||||
|
/* Length calculation */
|
||||||
|
const { grid, distance } = CONFIG.Scene.documentClass.schema.fields.grid.fields;
|
||||||
|
const sceneGridSize = canvas.scene?.grid.size ?? grid.size.initial;
|
||||||
|
const sceneGridDistance = canvas.scene?.grid.distance ?? distance.getInitialValue();
|
||||||
|
const dimensionConstant = sceneGridSize / sceneGridDistance;
|
||||||
|
|
||||||
|
const settings = canvas.scene?.rangeSettings;
|
||||||
|
const rangeNumber = Number(range);
|
||||||
|
const length = (!Number.isNaN(rangeNumber) ? rangeNumber : settings ? settings[range] : 0) * dimensionConstant;
|
||||||
|
/*----*/
|
||||||
|
|
||||||
|
const shapeData = {
|
||||||
|
...canvas.mousePosition,
|
||||||
|
type: type,
|
||||||
|
direction: direction ?? 0
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case rectangle.id:
|
||||||
|
shapeData.width = length;
|
||||||
|
shapeData.height = length;
|
||||||
|
break;
|
||||||
|
case line.id:
|
||||||
|
shapeData.length = length;
|
||||||
|
shapeData.width = 5 * dimensionConstant;
|
||||||
|
break;
|
||||||
|
case cone.id:
|
||||||
|
shapeData.angle = angle ?? CONFIG.MeasuredTemplate.defaults.angle;
|
||||||
|
shapeData.radius = length;
|
||||||
|
break;
|
||||||
|
case inFront.id:
|
||||||
|
shapeData.angle = '180';
|
||||||
|
shapeData.radius = length;
|
||||||
|
shapeData.type = cone.id;
|
||||||
|
break;
|
||||||
|
case circle.id:
|
||||||
|
shapeData.radius = length;
|
||||||
|
break;
|
||||||
|
case emanation.id:
|
||||||
|
shapeData.radius = length;
|
||||||
|
shapeData.base = {
|
||||||
|
type: 'token',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
shape: game.canvas.grid.isHexagonal ? CONST.TOKEN_SHAPES.ELLIPSE_1 : CONST.TOKEN_SHAPES.RECTANGLE_1
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shapeData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1 @@
|
||||||
export default class DhTokenLayer extends foundry.canvas.layers.TokenLayer {
|
export default class DhTokenLayer extends foundry.canvas.layers.TokenLayer {}
|
||||||
async _createPreview(createData, options) {
|
|
||||||
if (options.actor) {
|
|
||||||
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
|
||||||
if (options.actor?.system.metadata.usesSize) {
|
|
||||||
const tokenSize = tokenSizes[options.actor.system.size];
|
|
||||||
if (tokenSize && options.actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) {
|
|
||||||
createData.width = tokenSize;
|
|
||||||
createData.height = tokenSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return super._createPreview(createData, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -115,3 +115,10 @@ export const advantageState = {
|
||||||
value: 1
|
value: 1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const areaTypes = {
|
||||||
|
placed: {
|
||||||
|
id: 'placed',
|
||||||
|
label: 'Placed Area'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -80,12 +80,30 @@ export const groupAttackRange = {
|
||||||
|
|
||||||
/* circle|cone|rect|ray used to be CONST.MEASURED_TEMPLATE_TYPES. Hardcoded for now */
|
/* circle|cone|rect|ray used to be CONST.MEASURED_TEMPLATE_TYPES. Hardcoded for now */
|
||||||
export const templateTypes = {
|
export const templateTypes = {
|
||||||
CIRCLE: 'circle',
|
circle: {
|
||||||
CONE: 'cone',
|
id: 'circle',
|
||||||
RECTANGLE: 'rectangle',
|
label: 'Circle'
|
||||||
LINE: 'line',
|
},
|
||||||
EMANATION: 'emanation',
|
cone: {
|
||||||
INFRONT: 'inFront'
|
id: 'cone',
|
||||||
|
label: 'Cone'
|
||||||
|
},
|
||||||
|
rectangle: {
|
||||||
|
id: 'rectangle',
|
||||||
|
label: 'Rectangle'
|
||||||
|
},
|
||||||
|
line: {
|
||||||
|
id: 'line',
|
||||||
|
label: 'Line'
|
||||||
|
},
|
||||||
|
emanation: {
|
||||||
|
id: 'emanation',
|
||||||
|
label: 'Emanation'
|
||||||
|
},
|
||||||
|
inFront: {
|
||||||
|
id: 'inFront',
|
||||||
|
label: 'In Front'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const rangeInclusion = {
|
export const rangeInclusion = {
|
||||||
|
|
@ -955,15 +973,15 @@ export const countdownAppMode = {
|
||||||
export const sceneRangeMeasurementSetting = {
|
export const sceneRangeMeasurementSetting = {
|
||||||
disable: {
|
disable: {
|
||||||
id: 'disable',
|
id: 'disable',
|
||||||
label: 'Disable Daggerheart Range Measurement'
|
label: 'DAGGERHEART.CONFIG.SceneRangeMeasurementTypes.disable'
|
||||||
},
|
},
|
||||||
default: {
|
default: {
|
||||||
id: 'default',
|
id: 'default',
|
||||||
label: 'Default'
|
label: 'DAGGERHEART.CONFIG.SceneRangeMeasurementTypes.default'
|
||||||
},
|
},
|
||||||
custom: {
|
custom: {
|
||||||
id: 'custom',
|
id: 'custom',
|
||||||
label: 'Custom'
|
label: 'DAGGERHEART.CONFIG.SceneRangeMeasurementTypes.custom'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1092,3 +1110,18 @@ export const fallAndCollisionDamage = {
|
||||||
damageFormula: '1d20 + 5'
|
damageFormula: '1d20 + 5'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const simpleDispositions = {
|
||||||
|
[-1]: {
|
||||||
|
id: -1,
|
||||||
|
label: 'TOKEN.DISPOSITION.HOSTILE'
|
||||||
|
},
|
||||||
|
[0]: {
|
||||||
|
id: 0,
|
||||||
|
label: 'TOKEN.DISPOSITION.NEUTRAL'
|
||||||
|
},
|
||||||
|
[1]: {
|
||||||
|
id: 1,
|
||||||
|
label: 'TOKEN.DISPOSITION.FRIENDLY'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,12 @@ export const typeConfig = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'system.type',
|
key: 'system.type',
|
||||||
label: 'DAGGERHEART.GENERAL.type'
|
label: 'DAGGERHEART.GENERAL.type',
|
||||||
|
format: type => {
|
||||||
|
if (!type) return '-';
|
||||||
|
|
||||||
|
return CONFIG.DH.ACTOR.allAdversaryTypes()[type].label;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
filters: [
|
filters: [
|
||||||
|
|
@ -69,12 +74,18 @@ export const typeConfig = {
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
key: 'type',
|
key: 'type',
|
||||||
label: 'DAGGERHEART.GENERAL.type'
|
label: 'DAGGERHEART.GENERAL.type',
|
||||||
|
format: type => (type ? `TYPES.Item.${type}` : '-')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'system.secondary',
|
key: 'system.secondary',
|
||||||
label: 'DAGGERHEART.UI.ItemBrowser.subtype',
|
label: 'DAGGERHEART.UI.ItemBrowser.subtype',
|
||||||
format: isSecondary => (isSecondary ? 'secondary' : isSecondary === false ? 'primary' : '-')
|
format: isSecondary =>
|
||||||
|
isSecondary
|
||||||
|
? 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon.short'
|
||||||
|
: isSecondary === false
|
||||||
|
? 'DAGGERHEART.ITEMS.Weapon.primaryWeapon.short'
|
||||||
|
: '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'system.tier',
|
key: 'system.tier',
|
||||||
|
|
@ -94,8 +105,8 @@ export const typeConfig = {
|
||||||
key: 'system.secondary',
|
key: 'system.secondary',
|
||||||
label: 'DAGGERHEART.UI.ItemBrowser.subtype',
|
label: 'DAGGERHEART.UI.ItemBrowser.subtype',
|
||||||
choices: [
|
choices: [
|
||||||
{ value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon' },
|
{ value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon.full' },
|
||||||
{ value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon' }
|
{ value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -253,11 +264,13 @@ export const typeConfig = {
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
key: 'system.type',
|
key: 'system.type',
|
||||||
label: 'DAGGERHEART.GENERAL.type'
|
label: 'DAGGERHEART.GENERAL.type',
|
||||||
|
format: type => (type ? `DAGGERHEART.CONFIG.DomainCardTypes.${type}` : '-')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'system.domain',
|
key: 'system.domain',
|
||||||
label: 'DAGGERHEART.GENERAL.Domain.single'
|
label: 'DAGGERHEART.GENERAL.Domain.single',
|
||||||
|
format: domain => (domain ? CONFIG.DH.DOMAIN.allDomains()[domain].label : '-')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'system.level',
|
key: 'system.level',
|
||||||
|
|
@ -318,7 +331,14 @@ export const typeConfig = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'system.domains',
|
key: 'system.domains',
|
||||||
label: 'DAGGERHEART.GENERAL.Domain.plural'
|
label: 'DAGGERHEART.GENERAL.Domain.plural',
|
||||||
|
format: domains => {
|
||||||
|
const config = CONFIG.DH.DOMAIN.allDomains();
|
||||||
|
return domains
|
||||||
|
.map(x => (x ? game.i18n.localize(config[x].label) : null))
|
||||||
|
.filter(x => x)
|
||||||
|
.join(', ');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
filters: [
|
filters: [
|
||||||
|
|
@ -362,18 +382,19 @@ export const typeConfig = {
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
key: 'system.linkedClass',
|
key: 'system.linkedClass',
|
||||||
label: 'Class',
|
label: 'TYPES.Item.class',
|
||||||
format: linkedClass => linkedClass?.name ?? 'DAGGERHEART.UI.ItemBrowser.missing'
|
format: linkedClass => linkedClass?.name ?? 'DAGGERHEART.UI.ItemBrowser.missing'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'system.spellcastingTrait',
|
key: 'system.spellcastingTrait',
|
||||||
label: 'DAGGERHEART.ITEMS.Subclass.spellcastingTrait'
|
label: 'DAGGERHEART.ITEMS.Subclass.spellcastingTrait',
|
||||||
|
format: trait => (trait ? `DAGGERHEART.CONFIG.Traits.${trait}.name` : '-')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
key: 'system.linkedClass.uuid',
|
key: 'system.linkedClass.uuid',
|
||||||
label: 'Class',
|
label: 'TYPES.Item.class',
|
||||||
choices: items => {
|
choices: items => {
|
||||||
const list = items
|
const list = items
|
||||||
.filter(item => item.system.linkedClass)
|
.filter(item => item.system.linkedClass)
|
||||||
|
|
@ -397,7 +418,8 @@ export const typeConfig = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'system.mainTrait',
|
key: 'system.mainTrait',
|
||||||
label: 'DAGGERHEART.GENERAL.Trait.single'
|
label: 'DAGGERHEART.GENERAL.Trait.single',
|
||||||
|
format: trait => (trait ? `DAGGERHEART.CONFIG.Traits.${trait}.name` : '-')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
filters: [
|
filters: [
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
export const keybindings = {
|
export const keybindings = {
|
||||||
spotlight: 'DHSpotlight'
|
spotlight: 'DHSpotlight',
|
||||||
|
partySheet: 'DHPartySheet'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const menu = {
|
export const menu = {
|
||||||
|
|
@ -40,24 +41,21 @@ export const gameSettings = {
|
||||||
LastMigrationVersion: 'LastMigrationVersion',
|
LastMigrationVersion: 'LastMigrationVersion',
|
||||||
SpotlightRequestQueue: 'SpotlightRequestQueue',
|
SpotlightRequestQueue: 'SpotlightRequestQueue',
|
||||||
CompendiumBrowserSettings: 'CompendiumBrowserSettings',
|
CompendiumBrowserSettings: 'CompendiumBrowserSettings',
|
||||||
SpotlightTracker: 'SpotlightTracker'
|
SpotlightTracker: 'SpotlightTracker',
|
||||||
|
ActiveParty: 'ActiveParty'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const actionAutomationChoices = {
|
export const actionAutomationChoices = {
|
||||||
never: {
|
never: {
|
||||||
id: 'never',
|
id: 'never',
|
||||||
label: 'Never'
|
label: 'DAGGERHEART.CONFIG.ActionAutomationChoices.never'
|
||||||
},
|
},
|
||||||
showDialog: {
|
showDialog: {
|
||||||
id: 'showDialog',
|
id: 'showDialog',
|
||||||
label: 'Show Dialog only'
|
label: 'DAGGERHEART.CONFIG.ActionAutomationChoices.showDialog'
|
||||||
},
|
},
|
||||||
// npcOnly: {
|
|
||||||
// id: "npcOnly",
|
|
||||||
// label: "Always for non-characters"
|
|
||||||
// },
|
|
||||||
always: {
|
always: {
|
||||||
id: 'always',
|
id: 'always',
|
||||||
label: 'Always'
|
label: 'DAGGERHEART.CONFIG.ActionAutomationChoices.always'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,3 +15,4 @@ export * as chatMessages from './chat-message/_modules.mjs';
|
||||||
export * as fields from './fields/_module.mjs';
|
export * as fields from './fields/_module.mjs';
|
||||||
export * as items from './item/_module.mjs';
|
export * as items from './item/_module.mjs';
|
||||||
export * as scenes from './scene/_module.mjs';
|
export * as scenes from './scene/_module.mjs';
|
||||||
|
export * as regionBehaviors from './regionBehavior/_module.mjs';
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export default class DHAttackAction extends DHDamageAction {
|
||||||
if (!!this.item?.system?.attack) {
|
if (!!this.item?.system?.attack) {
|
||||||
if (this.damage.includeBase) {
|
if (this.damage.includeBase) {
|
||||||
const baseDamage = this.getParentDamage();
|
const baseDamage = this.getParentDamage();
|
||||||
this.damage.parts.unshift(new DHDamageData(baseDamage));
|
this.damage.parts.hitPoints = new DHDamageData(baseDamage);
|
||||||
}
|
}
|
||||||
if (this.roll.useDefault) {
|
if (this.roll.useDefault) {
|
||||||
this.roll.trait = this.item.system.attack.roll.trait;
|
this.roll.trait = this.item.system.attack.roll.trait;
|
||||||
|
|
@ -51,7 +51,7 @@ export default class DHAttackAction extends DHDamageAction {
|
||||||
async use(event, options) {
|
async use(event, options) {
|
||||||
const result = await super.use(event, options);
|
const result = await super.use(event, options);
|
||||||
|
|
||||||
if (result.message?.system.action.roll?.type === 'attack') {
|
if (result?.message?.system.action?.roll?.type === 'attack') {
|
||||||
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
|
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
|
||||||
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.characterAttack.id);
|
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.characterAttack.id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ const fields = foundry.data.fields;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel) {
|
export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel) {
|
||||||
static extraSchemas = ['cost', 'uses', 'range'];
|
static extraSchemas = ['areas', 'cost', 'uses', 'range'];
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
|
|
@ -110,6 +110,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
return this._id;
|
return this._id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns true if the current user is the owner of the containing item */
|
||||||
|
get isOwner() {
|
||||||
|
return this.item?.isOwner ?? true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return Item the action is attached too.
|
* Return Item the action is attached too.
|
||||||
*/
|
*/
|
||||||
|
|
@ -143,6 +148,12 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns true if the action is usable */
|
||||||
|
get usable() {
|
||||||
|
const actor = this.actor;
|
||||||
|
return this.isOwner && actor?.type === 'character';
|
||||||
|
}
|
||||||
|
|
||||||
static getRollType(parent) {
|
static getRollType(parent) {
|
||||||
return 'trait';
|
return 'trait';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,12 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
max: new fields.NumberField({ integer: true, label: 'DAGGERHEART.GENERAL.max' })
|
max: new fields.NumberField({ integer: true, label: 'DAGGERHEART.GENERAL.max' })
|
||||||
},
|
},
|
||||||
{ nullable: true, initial: null }
|
{ nullable: true, initial: null }
|
||||||
|
),
|
||||||
|
targetDispositions: new fields.SetField(
|
||||||
|
new fields.NumberField({
|
||||||
|
choices: CONFIG.DH.GENERAL.simpleDispositions
|
||||||
|
}),
|
||||||
|
{ label: 'DAGGERHEART.ACTIVEEFFECT.Config.targetDispositions' }
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -131,13 +137,14 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
return armorChange.getArmorData();
|
return armorChange.getArmorData();
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDefaultObject() {
|
static getDefaultObject(options = { transfer: true }) {
|
||||||
return {
|
return {
|
||||||
name: 'New Effect',
|
name: 'New Effect',
|
||||||
id: foundry.utils.randomID(),
|
id: foundry.utils.randomID(),
|
||||||
disabled: false,
|
disabled: false,
|
||||||
img: 'icons/magic/life/heart-cross-blue.webp',
|
img: 'icons/magic/life/heart-cross-blue.webp',
|
||||||
description: '',
|
description: '',
|
||||||
|
transfer: options.transfer,
|
||||||
statuses: [],
|
statuses: [],
|
||||||
changes: [],
|
changes: [],
|
||||||
system: {
|
system: {
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ export default class ArmorChange extends foundry.abstract.DataModel {
|
||||||
{
|
{
|
||||||
...change,
|
...change,
|
||||||
key: 'system.damageThresholds.major',
|
key: 'system.damageThresholds.major',
|
||||||
type: CONFIG.DH.GENERAL.activeEffectModes.override.id,
|
type: CONFIG.DH.GENERAL.activeEffectModes.add.id,
|
||||||
priority: 50,
|
priority: 50,
|
||||||
value: major
|
value: major
|
||||||
},
|
},
|
||||||
|
|
@ -96,7 +96,7 @@ export default class ArmorChange extends foundry.abstract.DataModel {
|
||||||
{
|
{
|
||||||
...change,
|
...change,
|
||||||
key: 'system.damageThresholds.severe',
|
key: 'system.damageThresholds.severe',
|
||||||
type: CONFIG.DH.GENERAL.activeEffectModes.override.id,
|
type: CONFIG.DH.GENERAL.activeEffectModes.add.id,
|
||||||
priority: 50,
|
priority: 50,
|
||||||
value: severe
|
value: severe
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ export default class DhpAdversary extends DhCreature {
|
||||||
}
|
}
|
||||||
|
|
||||||
isItemValid(source) {
|
isItemValid(source) {
|
||||||
return source.type === 'feature';
|
return super.isItemValid(source) || source.type === 'feature';
|
||||||
}
|
}
|
||||||
|
|
||||||
async _preUpdate(changes, options, user) {
|
async _preUpdate(changes, options, user) {
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,8 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
||||||
hasResistances: true,
|
hasResistances: true,
|
||||||
hasAttribution: false,
|
hasAttribution: false,
|
||||||
hasLimitedView: true,
|
hasLimitedView: true,
|
||||||
usesSize: false
|
usesSize: false,
|
||||||
|
hasInventory: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,6 +169,11 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
isItemValid(source) {
|
||||||
|
const inventoryTypes = ['weapon', 'armor', 'consumable', 'loot'];
|
||||||
|
return this.metadata.hasInventory && inventoryTypes.includes(source.type);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||||
import DhLevelData from '../levelData.mjs';
|
import DhLevelData from '../levelData.mjs';
|
||||||
import { commonActorRules } from './base.mjs';
|
import { commonActorRules } from './base.mjs';
|
||||||
import DhCreature from './creature.mjs';
|
import DhCreature from './creature.mjs';
|
||||||
import { attributeField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
import { attributeField, stressDamageReductionRule, bonusField, GoldField } from '../fields/actorField.mjs';
|
||||||
import { ActionField } from '../fields/actionField.mjs';
|
import { ActionField } from '../fields/actionField.mjs';
|
||||||
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
|
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
|
||||||
import { getArmorSources } from '../../helpers/utils.mjs';
|
import { getArmorSources } from '../../helpers/utils.mjs';
|
||||||
|
|
@ -18,7 +18,9 @@ export default class DhCharacter extends DhCreature {
|
||||||
label: 'TYPES.Actor.character',
|
label: 'TYPES.Actor.character',
|
||||||
type: 'character',
|
type: 'character',
|
||||||
settingSheet: DHCharacterSettings,
|
settingSheet: DHCharacterSettings,
|
||||||
isNPC: false
|
isNPC: false,
|
||||||
|
hasInventory: true,
|
||||||
|
quantifiable: ['loot', 'consumable']
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,12 +64,7 @@ export default class DhCharacter extends DhCreature {
|
||||||
core: new fields.BooleanField({ initial: false })
|
core: new fields.BooleanField({ initial: false })
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
gold: new fields.SchemaField({
|
gold: new GoldField(),
|
||||||
coins: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
handfuls: new fields.NumberField({ initial: 1, integer: true }),
|
|
||||||
bags: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
chests: new fields.NumberField({ initial: 0, integer: true })
|
|
||||||
}),
|
|
||||||
scars: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.scars' }),
|
scars: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.scars' }),
|
||||||
biography: new fields.SchemaField({
|
biography: new fields.SchemaField({
|
||||||
background: new fields.HTMLField(),
|
background: new fields.HTMLField(),
|
||||||
|
|
@ -153,7 +150,6 @@ export default class DhCharacter extends DhCreature {
|
||||||
shortMoves: new fields.NumberField({
|
shortMoves: new fields.NumberField({
|
||||||
required: true,
|
required: true,
|
||||||
integer: true,
|
integer: true,
|
||||||
min: 0,
|
|
||||||
initial: 0,
|
initial: 0,
|
||||||
label: 'DAGGERHEART.GENERAL.Bonuses.rest.shortRest.shortRestMoves.label',
|
label: 'DAGGERHEART.GENERAL.Bonuses.rest.shortRest.shortRestMoves.label',
|
||||||
hint: 'DAGGERHEART.GENERAL.Bonuses.rest.shortRest.shortRestMoves.hint'
|
hint: 'DAGGERHEART.GENERAL.Bonuses.rest.shortRest.shortRestMoves.hint'
|
||||||
|
|
@ -161,7 +157,6 @@ export default class DhCharacter extends DhCreature {
|
||||||
longMoves: new fields.NumberField({
|
longMoves: new fields.NumberField({
|
||||||
required: true,
|
required: true,
|
||||||
integer: true,
|
integer: true,
|
||||||
min: 0,
|
|
||||||
initial: 0,
|
initial: 0,
|
||||||
label: 'DAGGERHEART.GENERAL.Bonuses.rest.shortRest.longRestMoves.label',
|
label: 'DAGGERHEART.GENERAL.Bonuses.rest.shortRest.longRestMoves.label',
|
||||||
hint: 'DAGGERHEART.GENERAL.Bonuses.rest.shortRest.longRestMoves.hint'
|
hint: 'DAGGERHEART.GENERAL.Bonuses.rest.shortRest.longRestMoves.hint'
|
||||||
|
|
@ -171,7 +166,6 @@ export default class DhCharacter extends DhCreature {
|
||||||
shortMoves: new fields.NumberField({
|
shortMoves: new fields.NumberField({
|
||||||
required: true,
|
required: true,
|
||||||
integer: true,
|
integer: true,
|
||||||
min: 0,
|
|
||||||
initial: 0,
|
initial: 0,
|
||||||
label: 'DAGGERHEART.GENERAL.Bonuses.rest.longRest.shortRestMoves.label',
|
label: 'DAGGERHEART.GENERAL.Bonuses.rest.longRest.shortRestMoves.label',
|
||||||
hint: 'DAGGERHEART.GENERAL.Bonuses.rest.longRest.shortRestMoves.hint'
|
hint: 'DAGGERHEART.GENERAL.Bonuses.rest.longRest.shortRestMoves.hint'
|
||||||
|
|
@ -179,7 +173,6 @@ export default class DhCharacter extends DhCreature {
|
||||||
longMoves: new fields.NumberField({
|
longMoves: new fields.NumberField({
|
||||||
required: true,
|
required: true,
|
||||||
integer: true,
|
integer: true,
|
||||||
min: 0,
|
|
||||||
initial: 0,
|
initial: 0,
|
||||||
label: 'DAGGERHEART.GENERAL.Bonuses.rest.longRest.longRestMoves.label',
|
label: 'DAGGERHEART.GENERAL.Bonuses.rest.longRest.longRestMoves.label',
|
||||||
hint: 'DAGGERHEART.GENERAL.Bonuses.rest.longRest.longRestMoves.hint'
|
hint: 'DAGGERHEART.GENERAL.Bonuses.rest.longRest.longRestMoves.hint'
|
||||||
|
|
@ -293,6 +286,22 @@ export default class DhCharacter extends DhCreature {
|
||||||
guaranteedCritical: new fields.BooleanField({
|
guaranteedCritical: new fields.BooleanField({
|
||||||
label: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.label',
|
label: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.label',
|
||||||
hint: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.hint'
|
hint: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.hint'
|
||||||
|
}),
|
||||||
|
defaultAdvantageDice: new fields.NumberField({
|
||||||
|
nullable: true,
|
||||||
|
required: true,
|
||||||
|
integer: true,
|
||||||
|
choices: CONFIG.DH.GENERAL.dieFaces,
|
||||||
|
initial: null,
|
||||||
|
label: 'DAGGERHEART.ACTORS.Character.defaultAdvantageDice'
|
||||||
|
}),
|
||||||
|
defaultDisadvantageDice: new fields.NumberField({
|
||||||
|
nullable: true,
|
||||||
|
required: true,
|
||||||
|
integer: true,
|
||||||
|
choices: CONFIG.DH.GENERAL.dieFaces,
|
||||||
|
initial: null,
|
||||||
|
label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -438,6 +447,11 @@ export default class DhCharacter extends DhCreature {
|
||||||
return attack;
|
return attack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* All items are valid on characters */
|
||||||
|
isItemValid() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
isItemAvailable(item) {
|
isItemAvailable(item) {
|
||||||
if (!super.isItemAvailable(this)) return false;
|
if (!super.isItemAvailable(this)) return false;
|
||||||
|
|
@ -736,13 +750,22 @@ export default class DhCharacter extends DhCreature {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Armor and ArmorEffects can set a Base Damage Threshold. Characters only gain level*2 bonus to severe if this is not present */
|
||||||
|
const severeThresholdMulitplier =
|
||||||
|
this.armor ||
|
||||||
|
this.parent.appliedEffects.some(x =>
|
||||||
|
x.system.changes.some(x => x.type === 'armor' && x.value.damageThresholds)
|
||||||
|
)
|
||||||
|
? 1
|
||||||
|
: 2;
|
||||||
|
|
||||||
this.damageThresholds = {
|
this.damageThresholds = {
|
||||||
major: this.armor
|
major: this.armor
|
||||||
? this.armor.system.baseThresholds.major + this.levelData.level.current
|
? this.armor.system.baseThresholds.major + this.levelData.level.current
|
||||||
: this.levelData.level.current,
|
: this.levelData.level.current,
|
||||||
severe: this.armor
|
severe: this.armor
|
||||||
? this.armor.system.baseThresholds.severe + this.levelData.level.current
|
? this.armor.system.baseThresholds.severe + this.levelData.level.current
|
||||||
: this.levelData.level.current * 2
|
: this.levelData.level.current * severeThresholdMulitplier
|
||||||
};
|
};
|
||||||
|
|
||||||
const globalHopeMax = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxHope;
|
const globalHopeMax = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxHope;
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,24 @@ export default class DhCompanion extends DhCreature {
|
||||||
initial: false,
|
initial: false,
|
||||||
label: 'DAGGERHEART.GENERAL.Rules.conditionImmunities.vulnerable'
|
label: 'DAGGERHEART.GENERAL.Rules.conditionImmunities.vulnerable'
|
||||||
})
|
})
|
||||||
|
}),
|
||||||
|
roll: new fields.SchemaField({
|
||||||
|
defaultAdvantageDice: new fields.NumberField({
|
||||||
|
nullable: true,
|
||||||
|
required: true,
|
||||||
|
integer: true,
|
||||||
|
choices: CONFIG.DH.GENERAL.dieFaces,
|
||||||
|
initial: null,
|
||||||
|
label: 'DAGGERHEART.ACTORS.Character.defaultAdvantageDice'
|
||||||
|
}),
|
||||||
|
defaultDisadvantageDice: new fields.NumberField({
|
||||||
|
nullable: true,
|
||||||
|
required: true,
|
||||||
|
integer: true,
|
||||||
|
choices: CONFIG.DH.GENERAL.dieFaces,
|
||||||
|
initial: null,
|
||||||
|
label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice'
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
attack: new ActionField({
|
attack: new ActionField({
|
||||||
|
|
@ -118,10 +136,6 @@ export default class DhCompanion extends DhCreature {
|
||||||
return this.levelupChoicesLeft > 0;
|
return this.levelupChoicesLeft > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
isItemValid() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareBaseData() {
|
prepareBaseData() {
|
||||||
super.prepareBaseData();
|
super.prepareBaseData();
|
||||||
this.attack.roll.bonus = this.partner?.system?.spellcastModifier ?? 0;
|
this.attack.roll.bonus = this.partner?.system?.spellcastModifier ?? 0;
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ export default class DhEnvironment extends BaseDataActor {
|
||||||
}
|
}
|
||||||
|
|
||||||
isItemValid(source) {
|
isItemValid(source) {
|
||||||
return source.type === 'feature';
|
return super.isItemValid(source) || source.type === 'feature';
|
||||||
}
|
}
|
||||||
|
|
||||||
_onUpdate(changes, options, userId) {
|
_onUpdate(changes, options, userId) {
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,17 @@ import BaseDataActor from './base.mjs';
|
||||||
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
||||||
import TagTeamData from '../tagTeamData.mjs';
|
import TagTeamData from '../tagTeamData.mjs';
|
||||||
import GroupRollData from '../groupRollData.mjs';
|
import GroupRollData from '../groupRollData.mjs';
|
||||||
|
import { GoldField } from '../fields/actorField.mjs';
|
||||||
|
|
||||||
export default class DhParty extends BaseDataActor {
|
export default class DhParty extends BaseDataActor {
|
||||||
|
/** @inheritdoc */
|
||||||
|
static get metadata() {
|
||||||
|
return foundry.utils.mergeObject(super.metadata, {
|
||||||
|
hasInventory: true,
|
||||||
|
quantifiable: ['weapon', 'armor', 'loot', 'consumable']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
@ -11,17 +20,16 @@ export default class DhParty extends BaseDataActor {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
partyMembers: new ForeignDocumentUUIDArrayField({ type: 'Actor' }, { prune: true }),
|
partyMembers: new ForeignDocumentUUIDArrayField({ type: 'Actor' }, { prune: true }),
|
||||||
notes: new fields.HTMLField(),
|
notes: new fields.HTMLField(),
|
||||||
gold: new fields.SchemaField({
|
gold: new GoldField(),
|
||||||
coins: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
handfuls: new fields.NumberField({ initial: 1, integer: true }),
|
|
||||||
bags: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
chests: new fields.NumberField({ initial: 0, integer: true })
|
|
||||||
}),
|
|
||||||
tagTeam: new fields.EmbeddedDataField(TagTeamData),
|
tagTeam: new fields.EmbeddedDataField(TagTeamData),
|
||||||
groupRoll: new fields.EmbeddedDataField(GroupRollData)
|
groupRoll: new fields.EmbeddedDataField(GroupRollData)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get active() {
|
||||||
|
return game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.ActiveParty) === this.parent.id;
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
|
|
@ -29,10 +37,6 @@ export default class DhParty extends BaseDataActor {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
isItemValid(source) {
|
|
||||||
return ['weapon', 'armor', 'consumable', 'loot'].includes(source.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareBaseData() {
|
prepareBaseData() {
|
||||||
super.prepareBaseData();
|
super.prepareBaseData();
|
||||||
|
|
||||||
|
|
@ -44,6 +48,16 @@ export default class DhParty extends BaseDataActor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onCreate(data, options, userId) {
|
||||||
|
super._onCreate(data, options, userId);
|
||||||
|
|
||||||
|
if (game.user.isActiveGM && !game.actors.party) {
|
||||||
|
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.ActiveParty, this.parent.id).then(_ => {
|
||||||
|
ui.actors.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_onDelete(options, userId) {
|
_onDelete(options, userId) {
|
||||||
super._onDelete(options, userId);
|
super._onDelete(options, userId);
|
||||||
|
|
||||||
|
|
@ -51,5 +65,11 @@ export default class DhParty extends BaseDataActor {
|
||||||
for (const member of this.partyMembers) {
|
for (const member of this.partyMembers) {
|
||||||
member?.parties?.delete(this.parent);
|
member?.parties?.delete(this.parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this *was* the active party, delete it. We can't use game.actors.party as this actor was already deleted
|
||||||
|
const isWorldActor = !this.parent?.parent && !this.parent.compendium;
|
||||||
|
const activePartyId = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.ActiveParty);
|
||||||
|
if (isWorldActor && this.id === activePartyId)
|
||||||
|
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.ActiveParty, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,26 +7,31 @@ export default class DHAbilityUse extends foundry.abstract.TypeDataModel {
|
||||||
img: new fields.StringField({}),
|
img: new fields.StringField({}),
|
||||||
name: new fields.StringField({}),
|
name: new fields.StringField({}),
|
||||||
description: new fields.StringField({}),
|
description: new fields.StringField({}),
|
||||||
actions: new fields.ArrayField(
|
source: new fields.SchemaField({
|
||||||
new fields.ObjectField({
|
actor: new fields.StringField(),
|
||||||
name: new fields.StringField({}),
|
item: new fields.StringField(),
|
||||||
damage: new fields.SchemaField({
|
action: new fields.StringField()
|
||||||
type: new fields.StringField({}),
|
})
|
||||||
value: new fields.StringField({})
|
|
||||||
}),
|
|
||||||
healing: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({}),
|
|
||||||
value: new fields.StringField({})
|
|
||||||
}),
|
|
||||||
cost: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({}),
|
|
||||||
value: new fields.NumberField({})
|
|
||||||
}),
|
|
||||||
target: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({ nullable: true })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get actionActor() {
|
||||||
|
if (!this.source.actor) return null;
|
||||||
|
return fromUuidSync(this.source.actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
get actionItem() {
|
||||||
|
const actionActor = this.actionActor;
|
||||||
|
if (!actionActor || !this.source.item) return null;
|
||||||
|
|
||||||
|
const item = actionActor.items.get(this.source.item);
|
||||||
|
return item ? item.system.actions?.find(a => a.id === this.source.action) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get action() {
|
||||||
|
const { actionItem: itemAction } = this;
|
||||||
|
if (!this.source.action) return null;
|
||||||
|
if (itemAction) return itemAction;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
export { default as AreasField } from './areasField.mjs';
|
||||||
export { default as CostField } from './costField.mjs';
|
export { default as CostField } from './costField.mjs';
|
||||||
export { default as CountdownField } from './countdownField.mjs';
|
export { default as CountdownField } from './countdownField.mjs';
|
||||||
export { default as UsesField } from './usesField.mjs';
|
export { default as UsesField } from './usesField.mjs';
|
||||||
|
|
|
||||||
40
module/data/fields/action/areasField.mjs
Normal file
40
module/data/fields/action/areasField.mjs
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
export default class AreasField extends fields.ArrayField {
|
||||||
|
/**
|
||||||
|
* Action Workflow order
|
||||||
|
*/
|
||||||
|
static order = 150;
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
constructor(options = {}, context = {}) {
|
||||||
|
const element = new fields.SchemaField({
|
||||||
|
name: new fields.StringField({
|
||||||
|
nullable: false,
|
||||||
|
initial: 'Area',
|
||||||
|
label: 'DAGGERHEART.GENERAL.name'
|
||||||
|
}),
|
||||||
|
type: new fields.StringField({
|
||||||
|
nullable: false,
|
||||||
|
choices: CONFIG.DH.ACTIONS.areaTypes,
|
||||||
|
initial: CONFIG.DH.ACTIONS.areaTypes.placed.id,
|
||||||
|
label: 'DAGGERHEART.GENERAL.type'
|
||||||
|
}),
|
||||||
|
shape: new fields.StringField({
|
||||||
|
nullable: false,
|
||||||
|
choices: CONFIG.DH.GENERAL.templateTypes,
|
||||||
|
initial: CONFIG.DH.GENERAL.templateTypes.circle.id,
|
||||||
|
label: 'DAGGERHEART.ACTIONS.Config.area.shape'
|
||||||
|
}),
|
||||||
|
/* Could be opened up to allow numbers to be input aswell. Probably best handled via an autocomplete in that case to allow the select options but also free text */
|
||||||
|
size: new fields.StringField({
|
||||||
|
nullable: false,
|
||||||
|
choices: CONFIG.DH.GENERAL.range,
|
||||||
|
initial: CONFIG.DH.GENERAL.range.veryClose.id,
|
||||||
|
label: 'DAGGERHEART.ACTIONS.Config.area.size'
|
||||||
|
}),
|
||||||
|
effects: new fields.ArrayField(new fields.DocumentIdField())
|
||||||
|
});
|
||||||
|
super(element, options, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -320,7 +320,7 @@ export class DHDamageData extends DHResourceData {
|
||||||
required: true
|
required: true
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
label: 'Type'
|
label: game.i18n.localize('DAGGERHEART.GENERAL.type')
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { itemAbleRollParse } from '../../../helpers/utils.mjs';
|
||||||
import FormulaField from '../formulaField.mjs';
|
import FormulaField from '../formulaField.mjs';
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
@ -36,20 +37,23 @@ export default class DHSummonField extends fields.ArrayField {
|
||||||
const rolls = [];
|
const rolls = [];
|
||||||
const summonData = [];
|
const summonData = [];
|
||||||
for (const summon of this.summon) {
|
for (const summon of this.summon) {
|
||||||
let count = summon.count;
|
const roll = new Roll(itemAbleRollParse(summon.count, this.actor, this.item));
|
||||||
const roll = new Roll(summon.count);
|
await roll.evaluate();
|
||||||
if (!roll.isDeterministic) {
|
const count = roll.total;
|
||||||
await roll.evaluate();
|
if (!roll.isDeterministic && game.modules.get('dice-so-nice')?.active) rolls.push(roll);
|
||||||
if (game.modules.get('dice-so-nice')?.active) rolls.push(roll);
|
|
||||||
count = roll.total;
|
|
||||||
}
|
|
||||||
|
|
||||||
const actor = DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID));
|
const actor = await DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID));
|
||||||
/* Extending summon data in memory so it's available in actionField.toChat. Think it's harmless, but ugly. Could maybe find a better way. */
|
/* Extending summon data in memory so it's available in actionField.toChat. Think it's harmless, but ugly. Could maybe find a better way. */
|
||||||
summon.rolledCount = count;
|
|
||||||
summon.actor = actor.toObject();
|
summon.actor = actor.toObject();
|
||||||
|
|
||||||
summonData.push({ actor, count: count });
|
const countNumber = Number.parseInt(count);
|
||||||
|
for (let i = 0; i < countNumber; i++) {
|
||||||
|
const remaining = countNumber - i;
|
||||||
|
summonData.push({
|
||||||
|
actor,
|
||||||
|
tokenPreviewName: `${actor.prototypeToken.name}${remaining > 1 ? ` (${remaining}x)` : ''}`
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rolls.length) await Promise.all(rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true)));
|
if (rolls.length) await Promise.all(rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true)));
|
||||||
|
|
@ -58,32 +62,22 @@ export default class DHSummonField extends fields.ArrayField {
|
||||||
DHSummonField.handleSummon(summonData, this.actor);
|
DHSummonField.handleSummon(summonData, this.actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for any available instances of the actor present in the world if we're missing artwork in the compendium */
|
/* Check for any available instances of the actor present in the world if we're missing artwork in the compendium. If none exists, create one. */
|
||||||
static getWorldActor(baseActor) {
|
static async getWorldActor(baseActor) {
|
||||||
const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`];
|
const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`];
|
||||||
if (baseActor.inCompendium && dataType && baseActor.img === dataType.DEFAULT_ICON) {
|
if (baseActor.inCompendium && dataType && baseActor.img === dataType.DEFAULT_ICON) {
|
||||||
const worldActorCopy = game.actors.find(x => x.name === baseActor.name);
|
const worldActorCopy = game.actors.find(x => x.name === baseActor.name);
|
||||||
return worldActorCopy ?? baseActor;
|
if (worldActorCopy) return worldActorCopy;
|
||||||
|
|
||||||
|
return await game.system.api.documents.DhpActor.create(baseActor.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseActor;
|
return baseActor;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async handleSummon(summonData, actionActor, summonIndex = 0) {
|
static async handleSummon(summonData, actionActor) {
|
||||||
const summon = summonData[summonIndex];
|
await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: actionActor.token?.elevation });
|
||||||
const result = await CONFIG.ux.TokenManager.createPreviewAsync(summon.actor, {
|
|
||||||
name: `${summon.actor.prototypeToken.name}${summon.count > 1 ? ` (${summon.count}x)` : ''}`
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result) return actionActor.sheet?.maximize();
|
return actionActor.sheet?.maximize();
|
||||||
summon.actor = result.actor;
|
|
||||||
|
|
||||||
summon.count--;
|
|
||||||
if (summon.count <= 0) {
|
|
||||||
summonIndex++;
|
|
||||||
if (summonIndex === summonData.length) return actionActor.sheet?.maximize();
|
|
||||||
}
|
|
||||||
|
|
||||||
DHSummonField.handleSummon(summonData, actionActor, summonIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -281,8 +281,14 @@ export function ActionMixin(Base) {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
img: this.baseAction ? this.parent.parent.img : this.img,
|
img: this.baseAction ? this.parent.parent.img : this.img,
|
||||||
tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'],
|
tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'],
|
||||||
|
areas: this.areas,
|
||||||
summon: this.summon
|
summon: this.summon
|
||||||
},
|
},
|
||||||
|
source: {
|
||||||
|
actor: this.actor.uuid,
|
||||||
|
item: this.item.id,
|
||||||
|
action: this.id
|
||||||
|
},
|
||||||
itemOrigin: this.item,
|
itemOrigin: this.item,
|
||||||
description: this.description || (this.item instanceof Item ? this.item.system.description : '')
|
description: this.description || (this.item instanceof Item ? this.item.system.description : '')
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -103,4 +103,15 @@ class ResourcesField extends fields.TypedObjectField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { attributeField, ResourcesField, stressDamageReductionRule, bonusField };
|
class GoldField extends fields.SchemaField {
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
coins: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
handfuls: new fields.NumberField({ initial: 1, integer: true }),
|
||||||
|
bags: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
chests: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { attributeField, ResourcesField, GoldField, stressDamageReductionRule, bonusField };
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
* @property {string} label - A localizable label used on application.
|
* @property {string} label - A localizable label used on application.
|
||||||
* @property {string} type - The system type that this data model represents.
|
* @property {string} type - The system type that this data model represents.
|
||||||
* @property {boolean} hasDescription - Indicates whether items of this type have description field
|
* @property {boolean} hasDescription - Indicates whether items of this type have description field
|
||||||
* @property {boolean} isQuantifiable - Indicates whether items of this type have quantity field
|
|
||||||
* @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item
|
* @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -24,7 +23,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
type: 'base',
|
type: 'base',
|
||||||
hasDescription: false,
|
hasDescription: false,
|
||||||
hasResource: false,
|
hasResource: false,
|
||||||
isQuantifiable: false,
|
|
||||||
isInventoryItem: false,
|
isInventoryItem: false,
|
||||||
hasActions: false,
|
hasActions: false,
|
||||||
hasAttribution: true
|
hasAttribution: true
|
||||||
|
|
@ -83,7 +81,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.metadata.isQuantifiable)
|
if (this.metadata.isInventoryItem)
|
||||||
schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true });
|
schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true });
|
||||||
|
|
||||||
if (this.metadata.hasActions) schema.actions = new ActionsField();
|
if (this.metadata.hasActions) schema.actions = new ActionsField();
|
||||||
|
|
@ -110,6 +108,10 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
get actionsList() {
|
get actionsList() {
|
||||||
|
// No actions on non-characters
|
||||||
|
if (this.metadata.isInventoryItem && this.actor && this.actor.type !== 'character') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
return this.actions;
|
return this.actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ export default class DHConsumable extends BaseDataItem {
|
||||||
label: 'TYPES.Item.consumable',
|
label: 'TYPES.Item.consumable',
|
||||||
type: 'consumable',
|
type: 'consumable',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true,
|
|
||||||
isInventoryItem: true,
|
isInventoryItem: true,
|
||||||
hasActions: true
|
hasActions: true
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ export default class DHLoot extends BaseDataItem {
|
||||||
label: 'TYPES.Item.loot',
|
label: 'TYPES.Item.loot',
|
||||||
type: 'loot',
|
type: 'loot',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true,
|
|
||||||
isInventoryItem: true,
|
isInventoryItem: true,
|
||||||
hasActions: true
|
hasActions: true
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,9 @@ export default class DHWeapon extends AttachableItem {
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
get actionsList() {
|
get actionsList() {
|
||||||
return [this.attack, ...this.actions];
|
// No actions on non-characters
|
||||||
|
if (this.actor && this.actor.type !== 'character') return [];
|
||||||
|
return [this.attack, ...super.actionsList];
|
||||||
}
|
}
|
||||||
|
|
||||||
get customActions() {
|
get customActions() {
|
||||||
|
|
|
||||||
1
module/data/regionBehavior/_module.mjs
Normal file
1
module/data/regionBehavior/_module.mjs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as applyActiveEffect } from './applyActiveEffect.mjs';
|
||||||
40
module/data/regionBehavior/applyActiveEffect.mjs
Normal file
40
module/data/regionBehavior/applyActiveEffect.mjs
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
export default class DhApplyActiveEffect extends CONFIG.RegionBehavior.dataModels.applyActiveEffect {
|
||||||
|
static async #getApplicableEffects(token) {
|
||||||
|
const effects = await Promise.all(this.effects.map(foundry.utils.fromUuid));
|
||||||
|
return effects.filter(
|
||||||
|
effect => !effect.system.targetDispositions.size || effect.system.targetDispositions.has(token.disposition)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onTokenEnter(event) {
|
||||||
|
if (!event.user.isSelf) return;
|
||||||
|
const { token, movement } = event.data;
|
||||||
|
const actor = token.actor;
|
||||||
|
if (!actor) return;
|
||||||
|
const resumeMovement = movement ? token.pauseMovement() : undefined;
|
||||||
|
const effects = await DhApplyActiveEffect.#getApplicableEffects.bind(this)(event.data.token);
|
||||||
|
const toCreate = [];
|
||||||
|
for (const effect of effects) {
|
||||||
|
const data = effect.toObject();
|
||||||
|
delete data._id;
|
||||||
|
if (effect.compendium) {
|
||||||
|
data._stats.duplicateSource = null;
|
||||||
|
data._stats.compendiumSource = effect.uuid;
|
||||||
|
} else {
|
||||||
|
data._stats.duplicateSource = effect.uuid;
|
||||||
|
data._stats.compendiumSource = null;
|
||||||
|
}
|
||||||
|
data._stats.exportSource = null;
|
||||||
|
data.origin = this.parent.uuid;
|
||||||
|
toCreate.push(data);
|
||||||
|
}
|
||||||
|
if (toCreate.length) await actor.createEmbeddedDocuments('ActiveEffect', toCreate);
|
||||||
|
await resumeMovement?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static events = {
|
||||||
|
...CONFIG.RegionBehavior.dataModels.applyActiveEffect.events,
|
||||||
|
[CONST.REGION_EVENTS.TOKEN_ENTER]: this.#onTokenEnter
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ export class MemberData extends foundry.abstract.DataModel {
|
||||||
required: true,
|
required: true,
|
||||||
choices: CONFIG.DH.GENERAL.tagTeamRollTypes,
|
choices: CONFIG.DH.GENERAL.tagTeamRollTypes,
|
||||||
initial: CONFIG.DH.GENERAL.tagTeamRollTypes.trait.id,
|
initial: CONFIG.DH.GENERAL.tagTeamRollTypes.trait.id,
|
||||||
label: 'Roll Type'
|
label: game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.rollType')
|
||||||
}),
|
}),
|
||||||
rollChoice: new fields.StringField({ nullable: true, initial: null }),
|
rollChoice: new fields.StringField({ nullable: true, initial: null }),
|
||||||
rollData: new fields.JSONField({ nullable: true, initial: null }),
|
rollData: new fields.JSONField({ nullable: true, initial: null }),
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,7 @@ export default class DHRoll extends Roll {
|
||||||
roll: this,
|
roll: this,
|
||||||
parent: chatData.parent,
|
parent: chatData.parent,
|
||||||
targetMode: chatData.targetMode,
|
targetMode: chatData.targetMode,
|
||||||
|
areas: chatData.action?.areas,
|
||||||
metagamingSettings
|
metagamingSettings
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import D20Roll from './d20Roll.mjs';
|
||||||
import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||||
|
|
||||||
export default class DualityRoll extends D20Roll {
|
export default class DualityRoll extends D20Roll {
|
||||||
_advantageFaces = 6;
|
|
||||||
_advantageNumber = 1;
|
_advantageNumber = 1;
|
||||||
_rallyIndex;
|
_rallyIndex;
|
||||||
|
|
||||||
|
|
@ -11,6 +10,11 @@ export default class DualityRoll extends D20Roll {
|
||||||
super(formula, data, options);
|
super(formula, data, options);
|
||||||
this.rallyChoices = this.setRallyChoices();
|
this.rallyChoices = this.setRallyChoices();
|
||||||
this.guaranteedCritical = options.guaranteedCritical;
|
this.guaranteedCritical = options.guaranteedCritical;
|
||||||
|
|
||||||
|
const advantageFaces = data.rules?.roll?.defaultAdvantageDice
|
||||||
|
? Number.parseInt(data.rules.roll.defaultAdvantageDice)
|
||||||
|
: 6;
|
||||||
|
this.advantageFaces = Number.isNaN(advantageFaces) ? 6 : advantageFaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messageType = 'dualityRoll';
|
static messageType = 'dualityRoll';
|
||||||
|
|
@ -51,14 +55,6 @@ export default class DualityRoll extends D20Roll {
|
||||||
return this.dice[2] instanceof game.system.api.dice.diceTypes.DisadvantageDie ? this.dice[2] : null;
|
return this.dice[2] instanceof game.system.api.dice.diceTypes.DisadvantageDie ? this.dice[2] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get advantageFaces() {
|
|
||||||
return this._advantageFaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
set advantageFaces(faces) {
|
|
||||||
this._advantageFaces = this.getFaces(faces);
|
|
||||||
}
|
|
||||||
|
|
||||||
get advantageNumber() {
|
get advantageNumber() {
|
||||||
return this._advantageNumber;
|
return this._advantageNumber;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,6 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
static effectSafeEval(expression) {
|
static effectSafeEval(expression) {
|
||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line no-new-func
|
|
||||||
const evl = new Function('sandbox', `with (sandbox) { return ${expression}}`);
|
const evl = new Function('sandbox', `with (sandbox) { return ${expression}}`);
|
||||||
result = evl(Roll.MATH_PROXY);
|
result = evl(Roll.MATH_PROXY);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
||||||
|
|
@ -113,11 +113,13 @@ export default class DhpActor extends Actor {
|
||||||
_onUpdate(changes, options, userId) {
|
_onUpdate(changes, options, userId) {
|
||||||
super._onUpdate(changes, options, userId);
|
super._onUpdate(changes, options, userId);
|
||||||
for (const party of this.parties) {
|
for (const party of this.parties) {
|
||||||
party.render();
|
party.render({ parts: ['partyMembers'] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _preDelete() {
|
async _preDelete(options, user) {
|
||||||
|
if ((await super._preDelete(options, user)) === false) return false;
|
||||||
|
|
||||||
if (this.prototypeToken.actorLink) {
|
if (this.prototypeToken.actorLink) {
|
||||||
game.system.registeredTriggers.unregisterItemTriggers(this.items);
|
game.system.registeredTriggers.unregisterItemTriggers(this.items);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -130,7 +132,7 @@ export default class DhpActor extends Actor {
|
||||||
_onDelete(options, userId) {
|
_onDelete(options, userId) {
|
||||||
super._onDelete(options, userId);
|
super._onDelete(options, userId);
|
||||||
for (const party of this.parties) {
|
for (const party of this.parties) {
|
||||||
party.render();
|
party.render({ parts: ['partyMembers'] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -600,6 +602,7 @@ export default class DhpActor extends Actor {
|
||||||
rollData.system = this.system.getRollData();
|
rollData.system = this.system.getRollData();
|
||||||
rollData.prof = this.system.proficiency ?? 1;
|
rollData.prof = this.system.proficiency ?? 1;
|
||||||
rollData.cast = this.system.spellcastModifier ?? 1;
|
rollData.cast = this.system.spellcastModifier ?? 1;
|
||||||
|
|
||||||
return rollData;
|
return rollData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,10 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
element.addEventListener('click', this.onApplyEffect.bind(this))
|
element.addEventListener('click', this.onApplyEffect.bind(this))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
for (const element of html.querySelectorAll('.action-areas')) {
|
||||||
|
element.addEventListener('click', this.onCreateAreas.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
html.querySelectorAll('.roll-target').forEach(element => {
|
html.querySelectorAll('.roll-target').forEach(element => {
|
||||||
element.addEventListener('mouseenter', this.hoverTarget);
|
element.addEventListener('mouseenter', this.hoverTarget);
|
||||||
element.addEventListener('mouseleave', this.unhoverTarget);
|
element.addEventListener('mouseleave', this.unhoverTarget);
|
||||||
|
|
@ -178,8 +182,8 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
const pendingingSaves = targets.filter(t => t.saved.success === null);
|
const pendingingSaves = targets.filter(t => t.saved.success === null);
|
||||||
if (pendingingSaves.length) {
|
if (pendingingSaves.length) {
|
||||||
const confirm = await foundry.applications.api.DialogV2.confirm({
|
const confirm = await foundry.applications.api.DialogV2.confirm({
|
||||||
window: { title: 'Pending Reaction Rolls found' },
|
window: { title: game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.title') },
|
||||||
content: `<p>Some Tokens still need to roll their Reaction Roll.</p><p>Are you sure you want to continue ?</p><p><i>Undone reaction rolls will be considered as failed</i></p>`
|
content: `<p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}</p><p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}</p><p><i>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}</i></p>`
|
||||||
});
|
});
|
||||||
if (!confirm) return;
|
if (!confirm) return;
|
||||||
}
|
}
|
||||||
|
|
@ -249,6 +253,54 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
this.system.action?.workflow.get('effects')?.execute(config, targets, true);
|
this.system.action?.workflow.get('effects')?.execute(config, targets, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async onCreateAreas(event) {
|
||||||
|
const createArea = async selectedArea => {
|
||||||
|
const effects = selectedArea.effects.map(effect => this.system.action.item.effects.get(effect).uuid);
|
||||||
|
const { shape: type, size: range } = selectedArea;
|
||||||
|
const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({ type, range });
|
||||||
|
|
||||||
|
await canvas.regions.placeRegion(
|
||||||
|
{
|
||||||
|
name: selectedArea.name,
|
||||||
|
shapes: [shapeData],
|
||||||
|
restriction: { enabled: false, type: 'move', priority: 0 },
|
||||||
|
behaviors: [
|
||||||
|
{
|
||||||
|
name: game.i18n.localize('TYPES.RegionBehavior.applyActiveEffect'),
|
||||||
|
type: 'applyActiveEffect',
|
||||||
|
system: {
|
||||||
|
effects: effects
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
displayMeasurements: true,
|
||||||
|
locked: false,
|
||||||
|
ownership: { default: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE },
|
||||||
|
visibility: CONST.REGION_VISIBILITY.ALWAYS
|
||||||
|
},
|
||||||
|
{ create: true }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.system.action.areas.length === 1) createArea(this.system.action.areas[0]);
|
||||||
|
else if (this.system.action.areas.length > 1) {
|
||||||
|
new foundry.applications.ux.ContextMenu.implementation(
|
||||||
|
event.target,
|
||||||
|
'.action-areas',
|
||||||
|
this.system.action.areas.map(area => ({
|
||||||
|
label: area.name,
|
||||||
|
onClick: () => createArea(area)
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
jQuery: false,
|
||||||
|
fixed: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
CONFIG.ux.ContextMenu.triggerContextMenu(event, '.action-areas');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
filterPermTargets(targets) {
|
filterPermTargets(targets) {
|
||||||
return targets.filter(t => fromUuidSync(t.actorId)?.canUserModify(game.user, 'update'));
|
return targets.filter(t => fromUuidSync(t.actorId)?.canUserModify(game.user, 'update'));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,11 @@
|
||||||
export default class DhActorCollection extends foundry.documents.collections.Actors {
|
export default class DhActorCollection extends foundry.documents.collections.Actors {
|
||||||
|
/** @returns the active party */
|
||||||
|
get party() {
|
||||||
|
const id = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.ActiveParty);
|
||||||
|
const actor = game.actors.get(id);
|
||||||
|
return actor?.type === 'party' ? actor : null;
|
||||||
|
}
|
||||||
|
|
||||||
/** Ensure companions are initialized after all other subtypes. */
|
/** Ensure companions are initialized after all other subtypes. */
|
||||||
_initialize() {
|
_initialize() {
|
||||||
super._initialize();
|
super._initialize();
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,13 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
static async createDocuments(sources, operation) {
|
static async createDocuments(sources, operation) {
|
||||||
// Ensure that items being created are valid to the actor its being added to
|
// Ensure that items being created are valid to the actor its being added to
|
||||||
const actor = operation.parent;
|
const actor = operation.parent;
|
||||||
sources = actor?.system?.isItemValid ? sources.filter(s => actor.system.isItemValid(s)) : sources;
|
const filtered = actor ? sources.filter(s => actor.system.isItemValid(s)) : sources;
|
||||||
return super.createDocuments(sources, operation);
|
if (actor && filtered.length === 0 && sources.length > 0) {
|
||||||
|
const itemType = _loc(`TYPES.Item.${sources[0].type}`);
|
||||||
|
const actorType = _loc(`TYPES.Actor.${actor.type}`);
|
||||||
|
ui.notifications.error('DAGGERHEART.ACTORS.Base.CannotAddType', { format: { itemType, actorType } });
|
||||||
|
}
|
||||||
|
return super.createDocuments(filtered, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
@ -71,6 +76,13 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
return this.system.metadata.isInventoryItem ?? false;
|
return this.system.metadata.isInventoryItem ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns true if the item can be used */
|
||||||
|
get usable() {
|
||||||
|
const actor = this.actor;
|
||||||
|
const actionsList = this.system.actionsList;
|
||||||
|
return this.isOwner && actor?.type === 'character' && (actionsList?.size || actionsList?.length);
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static async createDialog(data = {}, createOptions = {}, options = {}) {
|
static async createDialog(data = {}, createOptions = {}, options = {}) {
|
||||||
const { folders, types, template, context = {}, ...dialogOptions } = options;
|
const { folders, types, template, context = {}, ...dialogOptions } = options;
|
||||||
|
|
|
||||||
|
|
@ -1,104 +1,68 @@
|
||||||
/**
|
/**
|
||||||
* A singleton class that handles preview tokens.
|
* A singleton class that handles creating tokens.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class DhTokenManager {
|
export default class DhTokenManager {
|
||||||
#activePreview;
|
|
||||||
#actor;
|
|
||||||
#resolve;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a template preview, deactivating any existing ones.
|
* Create a token previer
|
||||||
* @param {object} data
|
* @param {Actor} actor
|
||||||
|
* @param {object} tokenData
|
||||||
*/
|
*/
|
||||||
async createPreview(actor, tokenData) {
|
async createPreview(actor, tokenData) {
|
||||||
this.#actor = actor;
|
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
||||||
const token = await canvas.tokens._createPreview(
|
if (actor?.system.metadata.usesSize) {
|
||||||
{
|
const tokenSize = tokenSizes[actor.system.size];
|
||||||
...actor.prototypeToken,
|
if (tokenSize && actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) {
|
||||||
displayName: 50,
|
tokenData.width = tokenSize;
|
||||||
...tokenData
|
tokenData.height = tokenSize;
|
||||||
},
|
}
|
||||||
{ renderSheet: false, actor }
|
}
|
||||||
|
|
||||||
|
return await canvas.tokens.placeTokens(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
...actor.prototypeToken.toObject(),
|
||||||
|
actorId: actor.id,
|
||||||
|
displayName: 50,
|
||||||
|
...tokenData
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{ create: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
this.#activePreview = {
|
|
||||||
document: token.document,
|
|
||||||
object: token,
|
|
||||||
origin: { x: token.document.x, y: token.document.y }
|
|
||||||
};
|
|
||||||
|
|
||||||
this.#activePreview.events = {
|
|
||||||
contextmenu: this.#cancelTemplate.bind(this),
|
|
||||||
mousedown: this.#confirmTemplate.bind(this),
|
|
||||||
mousemove: this.#onDragMouseMove.bind(this)
|
|
||||||
};
|
|
||||||
|
|
||||||
canvas.stage.on('mousemove', this.#activePreview.events.mousemove);
|
|
||||||
canvas.stage.on('mousedown', this.#activePreview.events.mousedown);
|
|
||||||
canvas.app.view.addEventListener('contextmenu', this.#activePreview.events.contextmenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Currently intended for using as a preview of where to create a token. (note the flag) */
|
|
||||||
async createPreviewAsync(actor, tokenData = {}) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
this.#resolve = resolve;
|
|
||||||
this.createPreview(actor, { ...tokenData, flags: { daggerheart: { createPlacement: true } } });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the movement of the token preview on mousedrag.
|
* Creates new tokens on the canvas by placing previews.
|
||||||
* @param {mousemove Event} event
|
* @param {object} tokenData
|
||||||
|
* @param {object} options
|
||||||
*/
|
*/
|
||||||
#onDragMouseMove(event) {
|
async createTokensWithPreview(tokensData, { elevation } = {}) {
|
||||||
event.stopPropagation();
|
const scene = game.scenes.get(game.user.viewedScene);
|
||||||
const { moveTime, object } = this.#activePreview;
|
if (!scene) return;
|
||||||
const update = {};
|
|
||||||
|
|
||||||
const now = Date.now();
|
const level = scene.levels.get(game.user.viewedLevel);
|
||||||
if (now - (moveTime || 0) <= 16) return;
|
if (!level) return;
|
||||||
this.#activePreview.moveTime = now;
|
|
||||||
|
|
||||||
let cursor = event.getLocalPosition(canvas.templates);
|
const createElevation = elevation ?? level.elevation.bottom;
|
||||||
|
for (const tokenData of tokensData) {
|
||||||
|
const previewTokens = await this.createPreview(tokenData.actor, {
|
||||||
|
name: tokenData.tokenPreviewName,
|
||||||
|
level: game.user.viewedLevel,
|
||||||
|
elevation: createElevation,
|
||||||
|
flags: { daggerheart: { createPlacement: true } }
|
||||||
|
});
|
||||||
|
if (!previewTokens?.length) return null;
|
||||||
|
|
||||||
Object.assign(update, canvas.grid.getTopLeftPoint(cursor));
|
await canvas.scene.createEmbeddedDocuments(
|
||||||
|
'Token',
|
||||||
object.document.updateSource(update);
|
previewTokens.map(x => ({
|
||||||
object.renderFlags.set({ refresh: true });
|
...x.toObject(),
|
||||||
}
|
name: tokenData.actor.prototypeToken.name,
|
||||||
|
displayName: tokenData.actor.prototypeToken.displayName,
|
||||||
/**
|
flags: tokenData.actor.prototypeToken.flags
|
||||||
* Cancels the preview token on right-click.
|
})),
|
||||||
* @param {contextmenu Event} event
|
{ controlObject: true, parent: canvas.scene }
|
||||||
*/
|
);
|
||||||
#cancelTemplate(_event, resolved) {
|
}
|
||||||
const { mousemove, mousedown, contextmenu } = this.#activePreview.events;
|
|
||||||
this.#activePreview.object.destroy();
|
|
||||||
|
|
||||||
canvas.stage.off('mousemove', mousemove);
|
|
||||||
canvas.stage.off('mousedown', mousedown);
|
|
||||||
canvas.app.view.removeEventListener('contextmenu', contextmenu);
|
|
||||||
if (this.#resolve && !resolved) this.#resolve(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a real Actor and token at the preview location and cancels the preview.
|
|
||||||
* @param {click Event} event
|
|
||||||
*/
|
|
||||||
async #confirmTemplate(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
this.#cancelTemplate(event, true);
|
|
||||||
|
|
||||||
const actor = this.#actor.inCompendium
|
|
||||||
? await game.system.api.documents.DhpActor.create(this.#actor.toObject())
|
|
||||||
: this.#actor;
|
|
||||||
const tokenData = await actor.getTokenDocument();
|
|
||||||
const result = await canvas.scene.createEmbeddedDocuments('Token', [
|
|
||||||
{ ...tokenData.toObject(), x: this.#activePreview.document.x, y: this.#activePreview.document.y }
|
|
||||||
]);
|
|
||||||
|
|
||||||
this.#activePreview = undefined;
|
|
||||||
if (this.#resolve && result.length) this.#resolve(result[0]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export default function DhTemplateEnricher(match, _options) {
|
||||||
)?.id
|
)?.id
|
||||||
: params.range;
|
: params.range;
|
||||||
|
|
||||||
if (!Object.values(CONFIG.DH.GENERAL.templateTypes).find(x => x === type) || !range) return match[0];
|
if (!CONFIG.DH.GENERAL.templateTypes[type] || !range) return match[0];
|
||||||
|
|
||||||
const label = game.i18n.localize(`DAGGERHEART.CONFIG.TemplateTypes.${type}`);
|
const label = game.i18n.localize(`DAGGERHEART.CONFIG.TemplateTypes.${type}`);
|
||||||
const rangeDisplay = Number.isNaN(Number(range))
|
const rangeDisplay = Number.isNaN(Number(range))
|
||||||
|
|
@ -49,8 +49,6 @@ export default function DhTemplateEnricher(match, _options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const renderMeasuredTemplate = async event => {
|
export const renderMeasuredTemplate = async event => {
|
||||||
const { LINE, RECTANGLE, INFRONT, CONE } = CONFIG.DH.GENERAL.templateTypes;
|
|
||||||
|
|
||||||
const button = event.currentTarget,
|
const button = event.currentTarget,
|
||||||
type = button.dataset.type,
|
type = button.dataset.type,
|
||||||
range = button.dataset.range,
|
range = button.dataset.range,
|
||||||
|
|
@ -59,49 +57,16 @@ export const renderMeasuredTemplate = async event => {
|
||||||
|
|
||||||
if (!type || !range || !game.canvas.scene) return;
|
if (!type || !range || !game.canvas.scene) return;
|
||||||
|
|
||||||
const usedType = type === 'inFront' ? 'cone' : type;
|
const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({
|
||||||
const usedAngle =
|
type,
|
||||||
type === CONE ? (angle ?? CONFIG.MeasuredTemplate.defaults.angle) : type === INFRONT ? '180' : undefined;
|
angle,
|
||||||
|
range,
|
||||||
let baseDistance = getTemplateDistance(range);
|
direction
|
||||||
|
});
|
||||||
const { grid, distance } = CONFIG.Scene.documentClass.schema.fields.grid.fields;
|
|
||||||
const sceneGridSize = canvas.scene?.grid.size ?? grid.size.initial;
|
|
||||||
const sceneGridDistance = canvas.scene?.grid.distance ?? distance.getInitialValue();
|
|
||||||
const dimensionConstant = sceneGridSize / sceneGridDistance;
|
|
||||||
|
|
||||||
baseDistance *= dimensionConstant;
|
|
||||||
|
|
||||||
const length = baseDistance;
|
|
||||||
const radius = length;
|
|
||||||
|
|
||||||
const shapeWidth = type === LINE ? 5 * dimensionConstant : type === RECTANGLE ? length : undefined;
|
|
||||||
|
|
||||||
const { width, height } = game.canvas.scene.dimensions;
|
|
||||||
const shapeData = {
|
|
||||||
x: width / 2,
|
|
||||||
y: height / 2,
|
|
||||||
base: {
|
|
||||||
type: 'token',
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: 1,
|
|
||||||
height: 1,
|
|
||||||
shape: game.canvas.grid.isHexagonal ? CONST.TOKEN_SHAPES.ELLIPSE_1 : CONST.TOKEN_SHAPES.RECTANGLE_1
|
|
||||||
},
|
|
||||||
t: usedType,
|
|
||||||
length: length,
|
|
||||||
width: shapeWidth,
|
|
||||||
height: length,
|
|
||||||
angle: usedAngle,
|
|
||||||
radius: radius,
|
|
||||||
direction: direction,
|
|
||||||
type: usedType
|
|
||||||
};
|
|
||||||
|
|
||||||
await canvas.regions.placeRegion(
|
await canvas.regions.placeRegion(
|
||||||
{
|
{
|
||||||
name: usedType.capitalize(),
|
name: type.capitalize(),
|
||||||
shapes: [shapeData],
|
shapes: [shapeData],
|
||||||
restriction: { enabled: false, type: 'move', priority: 0 },
|
restriction: { enabled: false, type: 'move', priority: 0 },
|
||||||
behaviors: [],
|
behaviors: [],
|
||||||
|
|
@ -113,11 +78,3 @@ export const renderMeasuredTemplate = async event => {
|
||||||
{ create: true }
|
{ create: true }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTemplateDistance = range => {
|
|
||||||
const rangeNumber = Number(range);
|
|
||||||
if (!Number.isNaN(rangeNumber)) return rangeNumber;
|
|
||||||
|
|
||||||
const settings = canvas.scene?.rangeSettings;
|
|
||||||
return settings ? settings[range] : 0;
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,13 @@ export const getDeleteKeys = (property, innerProperty, innerPropertyDefaultValue
|
||||||
|
|
||||||
// Fix on Foundry native formula replacement for DH
|
// Fix on Foundry native formula replacement for DH
|
||||||
const nativeReplaceFormulaData = Roll.replaceFormulaData;
|
const nativeReplaceFormulaData = Roll.replaceFormulaData;
|
||||||
Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false } = {}) {
|
Roll.replaceFormulaData = function (formula, baseData = {}, { missing, warn = false } = {}) {
|
||||||
|
/* Inserting global data */
|
||||||
|
const data = {
|
||||||
|
...baseData,
|
||||||
|
partySize: game.actors?.party?.system.partyMembers.length ?? 0
|
||||||
|
};
|
||||||
|
|
||||||
const terms = Object.keys(CONFIG.DH.GENERAL.multiplierTypes).map(type => {
|
const terms = Object.keys(CONFIG.DH.GENERAL.multiplierTypes).map(type => {
|
||||||
return { term: type, default: 1 };
|
return { term: type, default: 1 };
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ export const preloadHandlebarsTemplates = async function () {
|
||||||
'templates/generic/tab-navigation.hbs',
|
'templates/generic/tab-navigation.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs',
|
'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/action-item.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/action-item.hbs',
|
||||||
|
'systems/daggerheart/templates/sheets/global/partials/gold.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/domain-card-item.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/domain-card-item.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/item-resource.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/item-resource.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/resource-section/resource-section.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/resource-section/resource-section.hbs',
|
||||||
|
|
@ -28,6 +29,7 @@ export const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/actionTypes/uses.hbs',
|
'systems/daggerheart/templates/actionTypes/uses.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/roll.hbs',
|
'systems/daggerheart/templates/actionTypes/roll.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/save.hbs',
|
'systems/daggerheart/templates/actionTypes/save.hbs',
|
||||||
|
'systems/daggerheart/templates/actionTypes/areas.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/cost.hbs',
|
'systems/daggerheart/templates/actionTypes/cost.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/range-target.hbs',
|
'systems/daggerheart/templates/actionTypes/range-target.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/effect.hbs',
|
'systems/daggerheart/templates/actionTypes/effect.hbs',
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,27 @@ export const registerKeyBindings = () => {
|
||||||
reservedModifiers: [],
|
reservedModifiers: [],
|
||||||
precedence: CONST.KEYBINDING_PRECEDENCE.NORMAL
|
precedence: CONST.KEYBINDING_PRECEDENCE.NORMAL
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.keybindings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.keybindings.partySheet, {
|
||||||
|
name: _loc('DAGGERHEART.SETTINGS.Keybindings.partySheet.name'),
|
||||||
|
hint: _loc('DAGGERHEART.SETTINGS.Keybindings.partySheet.hint'),
|
||||||
|
editable: [{ key: 'KeyP' }],
|
||||||
|
onDown: () => {
|
||||||
|
const controlled = canvas.ready ? canvas.tokens.controlled : [];
|
||||||
|
const selectedParty = controlled.find(c => c.actor?.type === 'party')?.actor;
|
||||||
|
const party = selectedParty ?? game.actors.party;
|
||||||
|
if (!party) return;
|
||||||
|
|
||||||
|
const sheet = party.sheet;
|
||||||
|
if (!sheet.rendered) {
|
||||||
|
sheet.render(true);
|
||||||
|
} else if (sheet.minimized) {
|
||||||
|
sheet.maximize();
|
||||||
|
} else {
|
||||||
|
sheet.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerMenuSettings = () => {
|
const registerMenuSettings = () => {
|
||||||
|
|
@ -189,4 +210,11 @@ const registerNonConfigSettings = () => {
|
||||||
config: false,
|
config: false,
|
||||||
type: SpotlightTracker
|
type: SpotlightTracker
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.ActiveParty, {
|
||||||
|
scope: 'world',
|
||||||
|
config: false,
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
872
package-lock.json
generated
872
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -17,13 +17,18 @@
|
||||||
"pullYMLtoLDB": "node ./tools/pullYMLtoLDB.mjs",
|
"pullYMLtoLDB": "node ./tools/pullYMLtoLDB.mjs",
|
||||||
"pullYMLtoLDBBuild": "node ./tools/pullYMLtoLDB.mjs --build",
|
"pullYMLtoLDBBuild": "node ./tools/pullYMLtoLDB.mjs --build",
|
||||||
"createSymlink": "node ./tools/create-symlink.mjs",
|
"createSymlink": "node ./tools/create-symlink.mjs",
|
||||||
"setup:dev": "node ./tools/dev-setup.mjs"
|
"setup:dev": "node ./tools/dev-setup.mjs",
|
||||||
|
"lint": "eslint",
|
||||||
|
"lint:fix": "eslint --fix"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@foundryvtt/foundryvtt-cli": "^1.0.2",
|
"@foundryvtt/foundryvtt-cli": "^1.0.2",
|
||||||
"@rollup/plugin-commonjs": "^25.0.7",
|
"@rollup/plugin-commonjs": "^25.0.7",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
|
"eslint": "^10.2.1",
|
||||||
|
"eslint-plugin-prettier": "^5.5.5",
|
||||||
|
"globals": "^17.5.0",
|
||||||
"husky": "^9.1.5",
|
"husky": "^9.1.5",
|
||||||
"lint-staged": "^15.2.10",
|
"lint-staged": "^15.2.10",
|
||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.32",
|
||||||
|
|
|
||||||
|
|
@ -169,12 +169,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -225,7 +222,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -251,7 +248,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -319,7 +317,7 @@
|
||||||
"_id": "ctXYwil2D1zfsekT",
|
"_id": "ctXYwil2D1zfsekT",
|
||||||
"img": "icons/magic/earth/barrier-stone-explosion-red.webp",
|
"img": "icons/magic/earth/barrier-stone-explosion-red.webp",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Mark a Stress</strong> to have the @Lookup[@name] burst out of the ground. All creatures within Very Close range must succeed on an Agility Reaction Roll or be knocked over, making them Vulnerable until they next act.</p><p>@Template[type:emanation|range:vc]</p>",
|
"description": "<p><strong>Mark a Stress</strong> to have the @Lookup[@name] burst out of the ground. All creatures within Very Close range must succeed on an Agility Reaction Roll or be knocked over, making them Vulnerable until they next act.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"4ppSeiTdbqnMzWAs": {
|
"4ppSeiTdbqnMzWAs": {
|
||||||
|
|
@ -344,7 +342,8 @@
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": {},
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -378,7 +377,16 @@
|
||||||
},
|
},
|
||||||
"name": "Roll Save",
|
"name": "Roll Save",
|
||||||
"img": "icons/magic/earth/barrier-stone-explosion-red.webp",
|
"img": "icons/magic/earth/barrier-stone-explosion-red.webp",
|
||||||
"range": "veryClose"
|
"range": "veryClose",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Earth Eruption",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "veryClose",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -581,7 +589,7 @@
|
||||||
"_id": "aNIVT5LKhwLyjKpI",
|
"_id": "aNIVT5LKhwLyjKpI",
|
||||||
"img": "icons/magic/acid/dissolve-drip-droplet-smoke.webp",
|
"img": "icons/magic/acid/dissolve-drip-droplet-smoke.webp",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When the @Lookup[@name] takes Severe damage, all creatures within Close range are bathed in their acidic blood, taking <strong>1d10</strong> physical damage. This splash covers the ground within Very Close range with blood, and all creatures other than the @Lookup[@name] who move through it take <strong>1d6</strong> physical damage.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p>When the @Lookup[@name] takes Severe damage, all creatures within Close range are bathed in their acidic blood, taking <strong>1d10</strong> physical damage. This splash covers the ground within Very Close range with blood, and all creatures other than the @Lookup[@name] who move through it take <strong>1d6</strong> physical damage.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"XbtTzOBvlTaxOKTy": {
|
"XbtTzOBvlTaxOKTy": {
|
||||||
|
|
@ -627,7 +635,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -636,7 +645,23 @@
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"name": "Splash",
|
"name": "Splash",
|
||||||
"img": "icons/magic/acid/dissolve-drip-droplet-smoke.webp",
|
"img": "icons/magic/acid/dissolve-drip-droplet-smoke.webp",
|
||||||
"range": "close"
|
"range": "close",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Acid Bath: Damage Area",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Acid Ground",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "veryClose",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"xpcp1ECTWF20kxve": {
|
"xpcp1ECTWF20kxve": {
|
||||||
"type": "damage",
|
"type": "damage",
|
||||||
|
|
@ -690,7 +715,8 @@
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"name": "Acid Ground",
|
"name": "Acid Ground",
|
||||||
"img": "icons/magic/acid/dissolve-pool-bubbles.webp",
|
"img": "icons/magic/acid/dissolve-pool-bubbles.webp",
|
||||||
"range": ""
|
"range": "",
|
||||||
|
"areas": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -132,12 +132,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -188,7 +185,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -214,7 +211,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -376,7 +374,7 @@
|
||||||
"name": "Whirlwind",
|
"name": "Whirlwind",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Spend a Fear</strong> to whirl, making an attack against all targets within Very Close range. Targets the @Lookup[@name] succeeds against take <strong>3d8</strong> direct physical damage.</p><p>@Template[type:emanation|range:vc]</p>",
|
"description": "<p><strong>Spend a Fear</strong> to whirl, making an attack against all targets within Very Close range. Targets the @Lookup[@name] succeeds against take <strong>3d8</strong> direct physical damage.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"RV1wKufKrMPN6MOo": {
|
"RV1wKufKrMPN6MOo": {
|
||||||
|
|
@ -429,7 +427,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false,
|
"includeBase": false,
|
||||||
"direct": true
|
"direct": true,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -458,7 +457,16 @@
|
||||||
},
|
},
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"img": "icons/skills/melee/strike-slashes-orange.webp",
|
"img": "icons/skills/melee/strike-slashes-orange.webp",
|
||||||
"range": "veryClose"
|
"range": "veryClose",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Whirlwind",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "veryClose",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -484,7 +492,7 @@
|
||||||
"name": "Mind Dance",
|
"name": "Mind Dance",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Mark a Stress</strong> to create a magically dazzling display that grapples the minds of nearby foes. All targets within Close range must make an Instinct Reaction Roll. For each target who failed, you gain a Fear and the @Lookup[@name] learns one of the target’s fears.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p><strong>Mark a Stress</strong> to create a magically dazzling display that grapples the minds of nearby foes. All targets within Close range must make an Instinct Reaction Roll. For each target who failed, you gain a Fear and the @Lookup[@name] learns one of the target’s fears.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"GNwsDlCabx3fiG4g": {
|
"GNwsDlCabx3fiG4g": {
|
||||||
|
|
@ -509,7 +517,8 @@
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": {},
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -538,7 +547,16 @@
|
||||||
},
|
},
|
||||||
"name": "Roll Save",
|
"name": "Roll Save",
|
||||||
"img": "icons/magic/light/explosion-glow-spiral-yellow.webp",
|
"img": "icons/magic/light/explosion-glow-spiral-yellow.webp",
|
||||||
"range": "close"
|
"range": "close",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Mind Dance",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -564,14 +582,14 @@
|
||||||
"name": "Hallucinatory Breath",
|
"name": "Hallucinatory Breath",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><em>Countdown (Loop 1d6)</em>. When the @Lookup[@name] takes damage for the first time, activate the countdown. When it triggers, the @Lookup[@name] breathes hallucinatory gas on all targets in front of them up to Far range. Targets must make an Instinct Reaction Roll or be tormented by fearful hallucinations. Targets whose fears are known to the @Lookup[@name] have disadvantage on this roll. Targets who fail lose 2 Hope and take <strong>3d8+3</strong> direct magic damage.</p><p>@Template[type:inFront|range:f]</p>",
|
"description": "<p><em>Countdown (Loop 1d6)</em>. When the @Lookup[@name] takes damage for the first time, activate the countdown. When it triggers, the @Lookup[@name] breathes hallucinatory gas on all targets in front of them up to Far range. Targets must make an Instinct Reaction Roll or be tormented by fearful hallucinations. Targets whose fears are known to the @Lookup[@name] have disadvantage on this roll. Targets who fail lose 2 Hope and take <strong>3d8+3</strong> direct magic damage.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"YOyKyKGTUEWkMmJe": {
|
"YOyKyKGTUEWkMmJe": {
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
"_id": "YOyKyKGTUEWkMmJe",
|
"_id": "YOyKyKGTUEWkMmJe",
|
||||||
"systemPath": "actions",
|
"systemPath": "actions",
|
||||||
"description": "<p>The @Lookup[@name] breathes hallucinatory gas on all targets in front of them up to Far range. Targets must make an Instinct Reaction Roll or be tormented by fearful hallucinations. Targets whose fears are known to the @Lookup[@name] have disadvantage on this roll. Targets who fail lose 2 Hope and take <strong>3d8+3</strong> direct magic damage.</p><p>@Template[type:inFront|range:f]</p>",
|
"description": "",
|
||||||
"chatDisplay": true,
|
"chatDisplay": true,
|
||||||
"actionType": "action",
|
"actionType": "action",
|
||||||
"cost": [],
|
"cost": [],
|
||||||
|
|
@ -634,7 +652,8 @@
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -663,7 +682,16 @@
|
||||||
},
|
},
|
||||||
"name": "Roll Save",
|
"name": "Roll Save",
|
||||||
"img": "icons/magic/air/fog-gas-smoke-purple.webp",
|
"img": "icons/magic/air/fog-gas-smoke-purple.webp",
|
||||||
"range": "far"
|
"range": "far",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Hallucinatory Breath",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "inFront",
|
||||||
|
"size": "far",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"lBhmLc33pcXzJHT3": {
|
"lBhmLc33pcXzJHT3": {
|
||||||
"type": "countdown",
|
"type": "countdown",
|
||||||
|
|
|
||||||
|
|
@ -143,12 +143,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -199,7 +196,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -225,7 +222,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -312,7 +310,7 @@
|
||||||
"name": "Beam Of Decay",
|
"name": "Beam Of Decay",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Mark 2 Stress</strong> to cause all targets within Far range to make a Strength Reaction Roll. Targets who fail take <strong>2d20+12</strong> magic damage and you gain a Fear. Targets who succeed take half damage. A target who marks 2 or more HP must also <strong>mark 2 Stress</strong> and becomes <em>Vulnerable</em> until they roll with Hope.</p><p>@Template[type:emanation|range:f]</p>",
|
"description": "<p><strong>Mark 2 Stress</strong> to cause all targets within Far range to make a Strength Reaction Roll. Targets who fail take <strong>2d20+12</strong> magic damage and you gain a Fear. Targets who succeed take half damage. A target who marks 2 or more HP must also <strong>mark 2 Stress</strong> and becomes <em>Vulnerable</em> until they roll with Hope.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"vaXLESD4sRkQ3Ahn": {
|
"vaXLESD4sRkQ3Ahn": {
|
||||||
|
|
@ -364,7 +362,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -398,7 +397,16 @@
|
||||||
},
|
},
|
||||||
"name": "Roll Save",
|
"name": "Roll Save",
|
||||||
"img": "icons/magic/unholy/projectile-missile-green.webp",
|
"img": "icons/magic/unholy/projectile-missile-green.webp",
|
||||||
"range": "far"
|
"range": "far",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Beam Of Decay",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "far",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"ME8AMAjgTAChHa3C": {
|
"ME8AMAjgTAChHa3C": {
|
||||||
"type": "healing",
|
"type": "healing",
|
||||||
|
|
|
||||||
|
|
@ -132,12 +132,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -188,7 +185,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -214,7 +211,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -246,7 +244,7 @@
|
||||||
"name": "Focused Volley",
|
"name": "Focused Volley",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Spend a Fear</strong> to target a point within Far range. Make an attack with advantage against all targets within Close range of that point. Targets the @Lookup[@name] succeeds against take <strong>1d10+4</strong> physical damage.</p><p>@Template[type:circle|range:c]</p>",
|
"description": "<p><strong>Spend a Fear</strong> to target a point within Far range. Make an attack with advantage against all targets within Close range of that point. Targets the @Lookup[@name] succeeds against take <strong>1d10+4</strong> physical damage.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"uG7Hl2DqaT69aNs1": {
|
"uG7Hl2DqaT69aNs1": {
|
||||||
|
|
@ -296,7 +294,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -325,7 +324,16 @@
|
||||||
},
|
},
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"img": "icons/skills/ranged/arrows-flying-triple-brown.webp",
|
"img": "icons/skills/ranged/arrows-flying-triple-brown.webp",
|
||||||
"range": "far"
|
"range": "far",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Focused Volley",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "circle",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -351,7 +359,7 @@
|
||||||
"name": "Supressing Fire",
|
"name": "Supressing Fire",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Mark a Stress</strong> to target a point within Far range. Until the next roll with Fear, a creature who moves within Close range of that point must make an Agility Reaction Roll. On a failure, they take <strong>2d6+3</strong> physical damage. On a success, they take half damage.</p><p>@Template[type:circle|range:c]</p>",
|
"description": "<p><strong>Mark a Stress</strong> to target a point within Far range. Until the next roll with Fear, a creature who moves within Close range of that point must make an Agility Reaction Roll. On a failure, they take <strong>2d6+3</strong> physical damage. On a success, they take half damage.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"mH6mmJIMM1fwzePt": {
|
"mH6mmJIMM1fwzePt": {
|
||||||
|
|
@ -394,7 +402,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -423,7 +432,16 @@
|
||||||
},
|
},
|
||||||
"name": "Agility Roll",
|
"name": "Agility Roll",
|
||||||
"img": "icons/skills/ranged/arrows-flying-salvo-blue.webp",
|
"img": "icons/skills/ranged/arrows-flying-salvo-blue.webp",
|
||||||
"range": ""
|
"range": "",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Supressing Fire",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "circle",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -138,12 +138,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -194,7 +191,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -220,7 +217,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -399,7 +397,7 @@
|
||||||
"name": "Fumigation",
|
"name": "Fumigation",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>Drop a smoke bomb that fills the air within Close range with smoke, Dizzying all targets in this area. Dizzied targets have disadvantage on their next action roll, then clear the condition.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p>Drop a smoke bomb that fills the air within Close range with smoke, Dizzying all targets in this area. Dizzied targets have disadvantage on their next action roll, then clear the condition.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"sp7RfJRQJsEUm09m": {
|
"sp7RfJRQJsEUm09m": {
|
||||||
|
|
@ -427,7 +425,16 @@
|
||||||
},
|
},
|
||||||
"name": "Drop Bomb",
|
"name": "Drop Bomb",
|
||||||
"img": "icons/magic/air/fog-gas-smoke-green.webp",
|
"img": "icons/magic/air/fog-gas-smoke-green.webp",
|
||||||
"range": "close"
|
"range": "close",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Fumigation",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -450,18 +457,15 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
},
|
||||||
|
"changes": []
|
||||||
},
|
},
|
||||||
"changes": [],
|
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "<p>Dizzied targets have disadvantage on their next action roll, then clear the condition.</p>",
|
"description": "<p>Dizzied targets have disadvantage on their next action roll, then clear the condition.</p>",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -471,6 +475,9 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": null,
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!h5RuhzGL17dW5FBT.lAmiK8wVxjyHwKlp.yP4ot8VqS56RnxnE"
|
"_key": "!actors.items.effects!h5RuhzGL17dW5FBT.lAmiK8wVxjyHwKlp.yP4ot8VqS56RnxnE"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -138,12 +138,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -194,7 +191,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -220,7 +217,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -465,7 +463,7 @@
|
||||||
"name": "Fire Jets",
|
"name": "Fire Jets",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>The @Lookup[@name] shoots into the air, spinning and releasing jets of flame. Make an attack against all targets within Close range. Targets the @Lookup[@name] succeeds against take <strong>2d8</strong> physical damage.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p>The @Lookup[@name] shoots into the air, spinning and releasing jets of flame. Make an attack against all targets within Close range. Targets the @Lookup[@name] succeeds against take <strong>2d8</strong> physical damage.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"hRAKaOdzQXLYBNVV": {
|
"hRAKaOdzQXLYBNVV": {
|
||||||
|
|
@ -510,7 +508,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -539,7 +538,16 @@
|
||||||
},
|
},
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"img": "icons/magic/fire/explosion-embers-orange.webp",
|
"img": "icons/magic/fire/explosion-embers-orange.webp",
|
||||||
"range": "close"
|
"range": "close",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Fire Jets",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -565,7 +573,7 @@
|
||||||
"name": "Trample",
|
"name": "Trample",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>The @Lookup[@name] rockets around erratically. Make an attack against all PCs within Close range. Targets the @Lookup[@name] succeeds against take <strong>1d6+5</strong> physical damage and are Vulnerable until their next roll with Hope.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p>The @Lookup[@name] rockets around erratically. Make an attack against all PCs within Close range. Targets the @Lookup[@name] succeeds against take <strong>1d6+5</strong> physical damage and are Vulnerable until their next roll with Hope.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"IOgPMu12Xnn33TfG": {
|
"IOgPMu12Xnn33TfG": {
|
||||||
|
|
@ -610,7 +618,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -644,7 +653,16 @@
|
||||||
},
|
},
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"img": "icons/skills/movement/arrow-upward-yellow.webp",
|
"img": "icons/skills/movement/arrow-upward-yellow.webp",
|
||||||
"range": "close"
|
"range": "close",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Trample",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -720,7 +738,7 @@
|
||||||
"name": "Shocking Gas",
|
"name": "Shocking Gas",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>The @Lookup[@name] sprays out a silver gas sparking with lightning. All targets within Close range must succeed on a Finesse Reaction Roll or mark 3 Stress.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p>The @Lookup[@name] sprays out a silver gas sparking with lightning. All targets within Close range must succeed on a Finesse Reaction Roll or mark 3 Stress.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"ky4OMl558J5wCbDp": {
|
"ky4OMl558J5wCbDp": {
|
||||||
|
|
@ -764,7 +782,8 @@
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -793,7 +812,16 @@
|
||||||
},
|
},
|
||||||
"name": "Roll Save",
|
"name": "Roll Save",
|
||||||
"img": "icons/magic/lightning/bolt-strike-embers-teal.webp",
|
"img": "icons/magic/lightning/bolt-strike-embers-teal.webp",
|
||||||
"range": "close"
|
"range": "close",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Shocking Gas",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -819,7 +847,7 @@
|
||||||
"name": "Stunning Clap",
|
"name": "Stunning Clap",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>The @Lookup[@name] leaps and their sides clap, creating a small sonic boom. All targets within Very Close range must succeed on a Strength Reaction Roll or become <em>Vulnerable</em> until the cube is defeated.</p><p>@Template[type:emanation|range:vc]</p>",
|
"description": "<p>The @Lookup[@name] leaps and their sides clap, creating a small sonic boom. All targets within Very Close range must succeed on a Strength Reaction Roll or become <em>Vulnerable</em> until the cube is defeated.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"LQtopkrtSlCQ5MAr": {
|
"LQtopkrtSlCQ5MAr": {
|
||||||
|
|
@ -837,7 +865,8 @@
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": {},
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -871,7 +900,16 @@
|
||||||
},
|
},
|
||||||
"name": "Roll Save",
|
"name": "Roll Save",
|
||||||
"img": "icons/magic/sonic/explosion-impact-shock-wave.webp",
|
"img": "icons/magic/sonic/explosion-impact-shock-wave.webp",
|
||||||
"range": "veryClose"
|
"range": "veryClose",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Stunning Clap",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "veryClose",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -947,7 +985,7 @@
|
||||||
"name": "Psionic Whine",
|
"name": "Psionic Whine",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>The @Lookup[@name] releases a cluster of mechanical bees whose buzz rattles mortal minds. All targets within Close range must succeed on a Presence Reaction Roll or take <strong>2d4+9</strong> direct magic damage.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p>The @Lookup[@name] releases a cluster of mechanical bees whose buzz rattles mortal minds. All targets within Close range must succeed on a Presence Reaction Roll or take <strong>2d4+9</strong> direct magic damage.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"3R3pGOUj4rHaUzPK": {
|
"3R3pGOUj4rHaUzPK": {
|
||||||
|
|
@ -992,7 +1030,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -1021,7 +1060,16 @@
|
||||||
},
|
},
|
||||||
"name": "Bees!",
|
"name": "Bees!",
|
||||||
"img": "icons/creatures/invertebrates/wasp-swarm-tan.webp",
|
"img": "icons/creatures/invertebrates/wasp-swarm-tan.webp",
|
||||||
"range": "close"
|
"range": "close",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Psionic Whine",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -1148,7 +1196,7 @@
|
||||||
"name": "Death Quake",
|
"name": "Death Quake",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When the @Lookup[@name] marks their last HP, the magic powering them ruptures in an explosion of force. All targets within Close range must succeed on an Instinct Reaction Roll or take <strong>2d8+1</strong> magic damage</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p>When the @Lookup[@name] marks their last HP, the magic powering them ruptures in an explosion of force. All targets within Close range must succeed on an Instinct Reaction Roll or take <strong>2d8+1</strong> magic damage</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"oCpv4zi9jtEpo0K1": {
|
"oCpv4zi9jtEpo0K1": {
|
||||||
|
|
@ -1193,7 +1241,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -1222,7 +1271,16 @@
|
||||||
},
|
},
|
||||||
"name": "Roll Save",
|
"name": "Roll Save",
|
||||||
"img": "icons/magic/sonic/explosion-shock-wave-teal.webp",
|
"img": "icons/magic/sonic/explosion-shock-wave-teal.webp",
|
||||||
"range": "close"
|
"range": "close",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Death Quake",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -139,12 +139,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -195,7 +192,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -221,7 +218,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -312,7 +310,7 @@
|
||||||
"_id": "zGvaBYJPOOnQVQEn",
|
"_id": "zGvaBYJPOOnQVQEn",
|
||||||
"img": "icons/magic/earth/projectile-stone-boulder-orange.webp",
|
"img": "icons/magic/earth/projectile-stone-boulder-orange.webp",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Mark a Stress</strong> to pick up heavy objects and throw them at all targets in front of the @Lookup[@name] within Far range. Make an attack against these targets. Targets the @Lookup[@name] succeeds against take <strong>1d10+2</strong> physical damage. If they succeed against more than one target, you gain a Fear.</p><p>@Template[type:inFront|range:f]</p>",
|
"description": "<p><strong>Mark a Stress</strong> to pick up heavy objects and throw them at all targets in front of the @Lookup[@name] within Far range. Make an attack against these targets. Targets the @Lookup[@name] succeeds against take <strong>1d10+2</strong> physical damage. If they succeed against more than one target, you gain a Fear.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"3p1qfHy5uHe4H2hB": {
|
"3p1qfHy5uHe4H2hB": {
|
||||||
|
|
@ -367,7 +365,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false,
|
"includeBase": false,
|
||||||
"direct": true
|
"direct": true,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -396,7 +395,16 @@
|
||||||
},
|
},
|
||||||
"name": "Throw",
|
"name": "Throw",
|
||||||
"img": "icons/magic/earth/projectile-stone-boulder-orange.webp",
|
"img": "icons/magic/earth/projectile-stone-boulder-orange.webp",
|
||||||
"range": "far"
|
"range": "far",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Hail of Boulders",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "inFront",
|
||||||
|
"size": "far",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"pmeromzI4eQOilbp": {
|
"pmeromzI4eQOilbp": {
|
||||||
"type": "healing",
|
"type": "healing",
|
||||||
|
|
|
||||||
|
|
@ -132,12 +132,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -188,7 +185,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -214,7 +211,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -359,7 +357,7 @@
|
||||||
"name": "Magic Burst",
|
"name": "Magic Burst",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Mark a Stress</strong> to make an attack against all targets within Close range. Targets the @Lookup[@name] succeeds against take <strong>2d6+4</strong> magic damage.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p><strong>Mark a Stress</strong> to make an attack against all targets within Close range. Targets the @Lookup[@name] succeeds against take <strong>2d6+4</strong> magic damage.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"iF0PD1t3yovKMTfy": {
|
"iF0PD1t3yovKMTfy": {
|
||||||
|
|
@ -411,7 +409,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -440,7 +439,16 @@
|
||||||
},
|
},
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"img": "icons/magic/lightning/bolt-strike-purple.webp",
|
"img": "icons/magic/lightning/bolt-strike-purple.webp",
|
||||||
"range": "close"
|
"range": "close",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Magic Burst",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -132,12 +132,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -188,7 +185,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -214,7 +211,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -562,7 +560,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -591,7 +590,16 @@
|
||||||
},
|
},
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"img": "icons/magic/sonic/explosion-shock-wave-teal.webp",
|
"img": "icons/magic/sonic/explosion-shock-wave-teal.webp",
|
||||||
"range": "veryClose"
|
"range": "veryClose",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Death Quake",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "veryClose",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -143,12 +143,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -199,7 +196,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -225,7 +222,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -486,7 +484,7 @@
|
||||||
"name": "Shadow Shackles",
|
"name": "Shadow Shackles",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Spend a Fear</strong> and choose a point within Far range. All targets within Close range of that point are <em>Restrained</em> in smoky chains until they break free with a successful Strength or Instinct Roll. A target Restrained by this feature must spend a Hope to make an action roll.</p><p>@Template[type:circle|range:c]</p>",
|
"description": "<p><strong>Spend a Fear</strong> and choose a point within Far range. All targets within Close range of that point are <em>Restrained</em> in smoky chains until they break free with a successful Strength or Instinct Roll. A target Restrained by this feature must spend a Hope to make an action roll.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"g4RDHrY0AEYXjH52": {
|
"g4RDHrY0AEYXjH52": {
|
||||||
|
|
@ -521,7 +519,16 @@
|
||||||
},
|
},
|
||||||
"name": "Spend Fear",
|
"name": "Spend Fear",
|
||||||
"img": "icons/magic/air/fog-gas-smoke-dense-pink.webp",
|
"img": "icons/magic/air/fog-gas-smoke-dense-pink.webp",
|
||||||
"range": "far"
|
"range": "far",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Shadow Shackles",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "circle",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -137,12 +137,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -193,7 +190,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -219,7 +216,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -228,7 +226,7 @@
|
||||||
"_id": "0DSCzAFXy0hV4afJ",
|
"_id": "0DSCzAFXy0hV4afJ",
|
||||||
"img": "icons/magic/earth/barrier-stone-brown-green.webp",
|
"img": "icons/magic/earth/barrier-stone-brown-green.webp",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>Slam the ground, knocking all targets within Very Close range back to Far range. Each target knocked back this way must mark a Stress.</p><p>@Template[type:emanation|range:vc]</p>",
|
"description": "<p>Slam the ground, knocking all targets within Very Close range back to Far range. Each target knocked back this way must mark a Stress.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"55hCZsJQhJNcZ0lX": {
|
"55hCZsJQhJNcZ0lX": {
|
||||||
|
|
@ -272,7 +270,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -281,7 +280,16 @@
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"name": "Stress Damage",
|
"name": "Stress Damage",
|
||||||
"img": "icons/magic/earth/barrier-stone-brown-green.webp",
|
"img": "icons/magic/earth/barrier-stone-brown-green.webp",
|
||||||
"range": "veryClose"
|
"range": "veryClose",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Ground Slam",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "veryClose",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -138,12 +138,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -194,7 +191,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -220,7 +217,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -296,7 +294,7 @@
|
||||||
"name": "Your Struggle is Pointless",
|
"name": "Your Struggle is Pointless",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Spend a Fear</strong> to weigh down the spirits of all PCs within Far range. All targets affected replace their Hope Die with a <strong>d8</strong> until they roll a success with Hope or their next rest.</p><p>@Template[type:emanation|range:f]</p>",
|
"description": "<p><strong>Spend a Fear</strong> to weigh down the spirits of all PCs within Far range. All targets affected replace their Hope Die with a <strong>d8</strong> until they roll a success with Hope or their next rest.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"n0RYO05pROFU6ov3": {
|
"n0RYO05pROFU6ov3": {
|
||||||
|
|
@ -331,7 +329,16 @@
|
||||||
},
|
},
|
||||||
"name": "Spend Fear",
|
"name": "Spend Fear",
|
||||||
"img": "icons/magic/death/skull-flames-white-blue.webp",
|
"img": "icons/magic/death/skull-flames-white-blue.webp",
|
||||||
"range": "far"
|
"range": "far",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Your Struggle is Pointless",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "far",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -411,7 +418,7 @@
|
||||||
"name": "Your Friends Will Fail You",
|
"name": "Your Friends Will Fail You",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When a PC fails with Fear, you can <strong>mark a Stress</strong> to cause all other PCs within Close range to lose a Hope.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p>When a PC fails with Fear, you can <strong>mark a Stress</strong> to cause all other PCs within Close range to lose a Hope.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"urrp8SCFgqbmSTvm": {
|
"urrp8SCFgqbmSTvm": {
|
||||||
|
|
@ -462,7 +469,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -471,7 +479,16 @@
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"name": "Lose Hope",
|
"name": "Lose Hope",
|
||||||
"img": "icons/creatures/unholy/demon-fire-horned-mask.webp",
|
"img": "icons/creatures/unholy/demon-fire-horned-mask.webp",
|
||||||
"range": ""
|
"range": "",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Your Friends Will Fail You",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -138,12 +138,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -194,7 +191,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -220,14 +217,15 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"name": "Terrifying",
|
"name": "Terrifying",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When the @Lookup[@name] makes a successful attack, all PCs within Far range must lose a Hope and you gain a Fear.</p><p>@Template[type:emanation|range:f]</p>",
|
"description": "<p>When the @Lookup[@name] makes a successful attack, all PCs within Far range must lose a Hope and you gain a Fear.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"v3XbljQeHEyfuSXz": {
|
"v3XbljQeHEyfuSXz": {
|
||||||
|
|
@ -278,7 +276,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -287,7 +286,16 @@
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"name": "Lose Hope",
|
"name": "Lose Hope",
|
||||||
"img": "icons/magic/death/skull-energy-light-purple.webp",
|
"img": "icons/magic/death/skull-energy-light-purple.webp",
|
||||||
"range": ""
|
"range": "",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Terrifying",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "far",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -533,7 +541,7 @@
|
||||||
"name": "You Pale in Comparison",
|
"name": "You Pale in Comparison",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When a PC fails a roll within Close range of the @Lookup[@name], they must mark a Stress.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p>When a PC fails a roll within Close range of the @Lookup[@name], they must mark a Stress.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"kuCPWb9cu3pZdAhh": {
|
"kuCPWb9cu3pZdAhh": {
|
||||||
|
|
@ -577,7 +585,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -586,7 +595,16 @@
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"name": "Mark Stress",
|
"name": "Mark Stress",
|
||||||
"img": "icons/magic/control/fear-fright-shadow-monster-red.webp",
|
"img": "icons/magic/control/fear-fright-shadow-monster-red.webp",
|
||||||
"range": "close"
|
"range": "close",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "You Pale in Comparison",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -139,12 +139,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -195,7 +192,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -221,7 +218,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -279,7 +277,7 @@
|
||||||
"name": "Rivalry",
|
"name": "Rivalry",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When a creature within Close range takes damage from a different adversary, you can <strong>mark a Stress</strong> to add a <strong>d4</strong> to the damage roll.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p>When a creature within Close range takes damage from a different adversary, you can <strong>mark a Stress</strong> to add a <strong>d4</strong> to the damage roll.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"UU3H5aPQejOSoFZw": {
|
"UU3H5aPQejOSoFZw": {
|
||||||
|
|
@ -309,7 +307,16 @@
|
||||||
},
|
},
|
||||||
"name": "Mark Stress",
|
"name": "Mark Stress",
|
||||||
"img": "icons/magic/perception/eye-ringed-glow-angry-small-teal.webp",
|
"img": "icons/magic/perception/eye-ringed-glow-angry-small-teal.webp",
|
||||||
"range": ""
|
"range": "",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Rivalry",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -335,7 +342,7 @@
|
||||||
"name": "What's Yours Is Mine",
|
"name": "What's Yours Is Mine",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When a PC takes Severe damage within Very Close range of the @Lookup[@name], you can <strong>spend a Fear</strong> to cause the target to make a Finesse Reaction Roll. On a failure, the @Lookup[@name] seizes one item or consumable of their choice from the target’s inventory.</p><p>@Template[type:emanation|range:vc]</p>",
|
"description": "<p>When a PC takes Severe damage within Very Close range of the @Lookup[@name], you can <strong>spend a Fear</strong> to cause the target to make a Finesse Reaction Roll. On a failure, the @Lookup[@name] seizes one item or consumable of their choice from the target’s inventory.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"3cGZ2CofM9HUlELH": {
|
"3cGZ2CofM9HUlELH": {
|
||||||
|
|
@ -353,7 +360,8 @@
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": {},
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -382,7 +390,16 @@
|
||||||
},
|
},
|
||||||
"name": "Roll Save",
|
"name": "Roll Save",
|
||||||
"img": "icons/magic/perception/hand-eye-black.webp",
|
"img": "icons/magic/perception/hand-eye-black.webp",
|
||||||
"range": "veryClose"
|
"range": "veryClose",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "What's Yours Is Mine",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "veryClose",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -139,12 +139,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -195,7 +192,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -221,7 +218,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -253,7 +251,7 @@
|
||||||
"name": "Battle Lust",
|
"name": "Battle Lust",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Spend a Fear</strong> to boil the blood of all PCs within Far range. They use a d20 as their Fear Die until the end of the scene.</p><p>@Template[type:emanation|range:f]</p>",
|
"description": "<p><strong>Spend a Fear</strong> to boil the blood of all PCs within Far range. They use a d20 as their Fear Die until the end of the scene.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"jKvzbQT0vp66DDOH": {
|
"jKvzbQT0vp66DDOH": {
|
||||||
|
|
@ -294,7 +292,16 @@
|
||||||
"amount": null
|
"amount": null
|
||||||
},
|
},
|
||||||
"name": "Spend Fear",
|
"name": "Spend Fear",
|
||||||
"range": "far"
|
"range": "far",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Battle Lust",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "far",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -374,7 +381,7 @@
|
||||||
"name": "Retalliation",
|
"name": "Retalliation",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When the @Lookup[@name] takes damage from an attack within Close range, you can <strong>mark a Stress</strong> to make a standard attack against the attacker.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p>When the @Lookup[@name] takes damage from an attack within Close range, you can <strong>mark a Stress</strong> to make a standard attack against the attacker.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"hxrdtBm4dYN7KGZm": {
|
"hxrdtBm4dYN7KGZm": {
|
||||||
|
|
@ -427,7 +434,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false,
|
"includeBase": false,
|
||||||
"direct": true
|
"direct": true,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -456,7 +464,16 @@
|
||||||
},
|
},
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"img": "icons/skills/melee/blood-slash-foam-red.webp",
|
"img": "icons/skills/melee/blood-slash-foam-red.webp",
|
||||||
"range": "veryClose"
|
"range": "veryClose",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Retalliation",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -138,12 +138,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -194,7 +191,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -220,7 +217,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -252,7 +250,7 @@
|
||||||
"name": "Dreadhowl",
|
"name": "Dreadhowl",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Mark a Stress</strong> to make all targets within Very Close range lose a Hope. If a target is not able to lose a Hope, they must instead mark 2 Stress.</p><p>@Template[type:emanation|range:vc]</p>",
|
"description": "<p><strong>Mark a Stress</strong> to make all targets within Very Close range lose a Hope. If a target is not able to lose a Hope, they must instead mark 2 Stress.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"XyLlX9RWSxciZ7oV": {
|
"XyLlX9RWSxciZ7oV": {
|
||||||
|
|
@ -296,7 +294,8 @@
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -305,7 +304,16 @@
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"name": "Lose Hope",
|
"name": "Lose Hope",
|
||||||
"img": "icons/creatures/unholy/demons-horned-glowing-pink.webp",
|
"img": "icons/creatures/unholy/demons-horned-glowing-pink.webp",
|
||||||
"range": "veryClose"
|
"range": "veryClose",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Dreadhowl",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "veryClose",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -137,12 +137,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -193,7 +190,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -219,7 +216,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -295,7 +293,7 @@
|
||||||
"name": "Screech",
|
"name": "Screech",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Mark a Stress</strong> to send a high-pitch screech out toward all targets in front of the @Lookup[@name] within Far range. Those targets must mark <strong>1d4</strong> Stress.</p><p>@Template[type:inFront|range:f]</p>",
|
"description": "<p><strong>Mark a Stress</strong> to send a high-pitch screech out toward all targets in front of the @Lookup[@name] within Far range. Those targets must mark <strong>1d4</strong> Stress.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"2ILfoiBoMyBCtBsL": {
|
"2ILfoiBoMyBCtBsL": {
|
||||||
|
|
@ -338,7 +336,8 @@
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -347,7 +346,16 @@
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"name": "Mark Stress",
|
"name": "Mark Stress",
|
||||||
"img": "icons/magic/sonic/projectile-sound-rings-wave.webp",
|
"img": "icons/magic/sonic/projectile-sound-rings-wave.webp",
|
||||||
"range": "far"
|
"range": "far",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Screech",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "inFront",
|
||||||
|
"size": "far",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -138,12 +138,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -194,7 +191,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -220,14 +217,15 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"name": "Bramble Patch",
|
"name": "Bramble Patch",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Mark a Stress</strong> to target a point within Far range. Create a patch of thorns that covers an area within Close range of that point. All targets within that area take <strong>2d6+2</strong> physical damage when they act. A target must succeed on a Finesse Roll or deal more than 20 damage to the @Lookup[@name] with an attack to leave the area.</p><p>@Template[type:circle|range:c]</p>",
|
"description": "<p><strong>Mark a Stress</strong> to target a point within Far range. Create a patch of thorns that covers an area within Close range of that point. All targets within that area take <strong>2d6+2</strong> physical damage when they act. A target must succeed on a Finesse Roll or deal more than 20 damage to the @Lookup[@name] with an attack to leave the area.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"iCJdIs57hfh5Cb0u": {
|
"iCJdIs57hfh5Cb0u": {
|
||||||
|
|
@ -252,7 +250,8 @@
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": {},
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -281,7 +280,16 @@
|
||||||
},
|
},
|
||||||
"name": "Mark Stress",
|
"name": "Mark Stress",
|
||||||
"img": "icons/magic/nature/root-vines-grow-brown.webp",
|
"img": "icons/magic/nature/root-vines-grow-brown.webp",
|
||||||
"range": ""
|
"range": "",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Bramble Patch",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "circle",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"cpZ5c9d3opSA4BN9": {
|
"cpZ5c9d3opSA4BN9": {
|
||||||
"type": "damage",
|
"type": "damage",
|
||||||
|
|
@ -414,7 +422,7 @@
|
||||||
"name": "We Are All One",
|
"name": "We Are All One",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When an ally dies within Close range, you can spend a Fear to clear 2 HP and 2 Stress as the fallen ally’s life force is returned to the forest.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p>When an ally dies within Close range, you can spend a Fear to clear 2 HP and 2 Stress as the fallen ally’s life force is returned to the forest.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"SrhuW3mfOuqg1ys6": {
|
"SrhuW3mfOuqg1ys6": {
|
||||||
|
|
@ -490,7 +498,8 @@
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "self",
|
"type": "self",
|
||||||
|
|
@ -514,7 +523,16 @@
|
||||||
},
|
},
|
||||||
"name": "Spend Fear",
|
"name": "Spend Fear",
|
||||||
"img": "icons/magic/unholy/orb-hands-pink.webp",
|
"img": "icons/magic/unholy/orb-hands-pink.webp",
|
||||||
"range": "self"
|
"range": "self",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "We Are All One",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -138,12 +138,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -194,7 +191,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -220,14 +217,15 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"name": "Conflagration",
|
"name": "Conflagration",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Spend a Fear</strong> to unleash an all-consuming firestorm and make an attack against all targets within Close range. Targets the @Lookup[@name] succeeds against take <strong>2d10+6</strong> direct magic damage.</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p><strong>Spend a Fear</strong> to unleash an all-consuming firestorm and make an attack against all targets within Close range. Targets the @Lookup[@name] succeeds against take <strong>2d10+6</strong> direct magic damage.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"v7zZo52Dnj1e1i2G": {
|
"v7zZo52Dnj1e1i2G": {
|
||||||
|
|
@ -279,7 +277,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -308,7 +307,16 @@
|
||||||
},
|
},
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"img": "icons/magic/fire/projectile-beams-salvo-red.webp",
|
"img": "icons/magic/fire/projectile-beams-salvo-red.webp",
|
||||||
"range": "close"
|
"range": "close",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Conflagration",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "close",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -465,14 +473,14 @@
|
||||||
"name": "Shackles of Guilt",
|
"name": "Shackles of Guilt",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><em>Countdown (Loop 2d6)</em>. When the @Lookup[@name] is in the spotlight for the first time, activate the countdown. When it triggers, all targets within Far range become Vulnerable and must mark a Stress as they relive their greatest regrets. A target can break free from their regret with a successful Presence or Strength Roll. When a PC fails to break free, they lose a Hope.</p><p>@Template[type:emanation|range:f]</p>",
|
"description": "<p><em>Countdown (Loop 2d6)</em>. When the @Lookup[@name] is in the spotlight for the first time, activate the countdown. When it triggers, all targets within Far range become Vulnerable and must mark a Stress as they relive their greatest regrets. A target can break free from their regret with a successful Presence or Strength Roll. When a PC fails to break free, they lose a Hope.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"7b0FkpAnWz9a5EWx": {
|
"7b0FkpAnWz9a5EWx": {
|
||||||
"type": "damage",
|
"type": "damage",
|
||||||
"_id": "7b0FkpAnWz9a5EWx",
|
"_id": "7b0FkpAnWz9a5EWx",
|
||||||
"systemPath": "actions",
|
"systemPath": "actions",
|
||||||
"description": "<p>When the countdown triggers, all targets within Far range become Vulnerable and must mark a Stress as they relive their greatest regrets. A target can break free from their regret with a successful Presence or Strength Roll. When a PC fails to break free, they lose a Hope.</p><p>@Template[type:emanation|range:f]</p>",
|
"description": "<p>When it triggers, all targets within Far range become Vulnerable and must mark a Stress as they relive their greatest regrets. A target can break free from their regret with a successful Presence or Strength Roll. When a PC fails to break free, they lose a Hope.</p>",
|
||||||
"chatDisplay": true,
|
"chatDisplay": true,
|
||||||
"actionType": "action",
|
"actionType": "action",
|
||||||
"cost": [],
|
"cost": [],
|
||||||
|
|
@ -509,7 +517,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -523,7 +532,16 @@
|
||||||
],
|
],
|
||||||
"name": "Mark Stress",
|
"name": "Mark Stress",
|
||||||
"img": "icons/magic/unholy/strike-hand-glow-pink.webp",
|
"img": "icons/magic/unholy/strike-hand-glow-pink.webp",
|
||||||
"range": ""
|
"range": "",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Shackles of Guilt",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "far",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"11PtfoxbgOXxNlkG": {
|
"11PtfoxbgOXxNlkG": {
|
||||||
"type": "countdown",
|
"type": "countdown",
|
||||||
|
|
|
||||||
|
|
@ -173,12 +173,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -229,7 +226,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -255,7 +252,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -392,7 +390,7 @@
|
||||||
"name": "Tormenting Lash",
|
"name": "Tormenting Lash",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Mark a Stress</strong> to make a standard attack against all targets within Very Close range. When a target uses armor to reduce damage from this attack, they must mark 2 Armor Slots.</p><p>@Template[type:emanation|range:vc]</p>",
|
"description": "<p><strong>Mark a Stress</strong> to make a standard attack against all targets within Very Close range. When a target uses armor to reduce damage from this attack, they must mark 2 Armor Slots.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"zMVhUekP8pcyQGFR": {
|
"zMVhUekP8pcyQGFR": {
|
||||||
|
|
@ -444,7 +442,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -473,7 +472,16 @@
|
||||||
},
|
},
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"img": "icons/skills/melee/blood-slash-foam-red.webp",
|
"img": "icons/skills/melee/blood-slash-foam-red.webp",
|
||||||
"range": "veryClose"
|
"range": "veryClose",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Tormenting Lash",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "veryClose",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -499,14 +507,14 @@
|
||||||
"name": "All-Consuming Rage",
|
"name": "All-Consuming Rage",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><em>Countdown (Decreasing 8)</em>. When the @Lookup[@name] is in the spotlight for the first time, activate the countdown. When it triggers, create a torrent of incarnate rage that rends flesh from bone. All targets within Far range must make a Presence Reaction Roll. Targets who fail take <strong>2d6+10</strong> direct magic damage. Targets who succeed take half damage. For each HP marked from this damage, summon a @UUID[Compendium.daggerheart.adversaries.Actor.OsLG2BjaEdTZUJU9]{Fallen Shock Troop} within Very Close range of the target who marked that HP. If the countdown ever decreases its maximum value to 0, the @Lookup[@name] marks their remaining HP and all targets within Far range must mark all remaining HP and make a death move.</p><p>@Template[type:emanation|range:f]</p>",
|
"description": "<p><em>Countdown (Decreasing 8)</em>. When the @Lookup[@name] is in the spotlight for the first time, activate the countdown. When it triggers, create a torrent of incarnate rage that rends flesh from bone. All targets within Far range must make a Presence Reaction Roll. Targets who fail take <strong>2d6+10</strong> direct magic damage. Targets who succeed take half damage. For each HP marked from this damage, summon a @UUID[Compendium.daggerheart.adversaries.Actor.OsLG2BjaEdTZUJU9]{Fallen Shock Troop} within Very Close range of the target who marked that HP. If the countdown ever decreases its maximum value to 0, the @Lookup[@name] marks their remaining HP and all targets within Far range must mark all remaining HP and make a death move.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"rgy5wXyXJWh6uWxC": {
|
"rgy5wXyXJWh6uWxC": {
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
"_id": "rgy5wXyXJWh6uWxC",
|
"_id": "rgy5wXyXJWh6uWxC",
|
||||||
"systemPath": "actions",
|
"systemPath": "actions",
|
||||||
"description": "<p>When it triggers, create a torrent of incarnate rage that rends flesh from bone. All targets within Far range must make a Presence Reaction Roll. Targets who fail take <strong>2d6+10</strong> direct magic damage. Targets who succeed take half damage. For each HP marked from this damage, summon a @UUID[Compendium.daggerheart.adversaries.Actor.OsLG2BjaEdTZUJU9]{Fallen Shock Troop} within Very Close range of the target who marked that HP. If the countdown ever decreases its maximum value to 0, the @Lookup[@name] marks their remaining HP and all targets within Far range must mark all remaining HP and make a death move.</p><p>@Template[type:emanation|range:f]</p>",
|
"description": "<p>When it triggers, create a torrent of incarnate rage that rends flesh from bone. All targets within Far range must make a Presence Reaction Roll. Targets who fail take <strong>2d6+10</strong> direct magic damage. Targets who succeed take half damage. For each HP marked from this damage, summon a @UUID[Compendium.daggerheart.adversaries.Actor.OsLG2BjaEdTZUJU9]{Fallen Shock Troop} within Very Close range of the target who marked that HP. If the countdown ever decreases its maximum value to 0, the @Lookup[@name] marks their remaining HP and all targets within Far range must mark all remaining HP and make a death move.</p>",
|
||||||
"chatDisplay": true,
|
"chatDisplay": true,
|
||||||
"actionType": "action",
|
"actionType": "action",
|
||||||
"cost": [],
|
"cost": [],
|
||||||
|
|
@ -544,7 +552,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -573,7 +582,16 @@
|
||||||
},
|
},
|
||||||
"name": "Roll Save",
|
"name": "Roll Save",
|
||||||
"img": "icons/magic/control/fear-fright-monster-grin-red-orange.webp",
|
"img": "icons/magic/control/fear-fright-monster-grin-red-orange.webp",
|
||||||
"range": "far"
|
"range": "far",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "All-Consuming Rage",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "far",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"8e3BHmOFLvRwPbTW": {
|
"8e3BHmOFLvRwPbTW": {
|
||||||
"type": "countdown",
|
"type": "countdown",
|
||||||
|
|
@ -636,7 +654,7 @@
|
||||||
"name": "Doombringer",
|
"name": "Doombringer",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When a target marks HP from an attack by the @Lookup[@name], all PCs within Far range of the target must lose a Hope.</p><p>@Template[type:emanation|range:f]</p>",
|
"description": "<p>When a target marks HP from an attack by the @Lookup[@name], all PCs within Far range of the target must lose a Hope.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"WEBPJCbXfBeyHFJ4": {
|
"WEBPJCbXfBeyHFJ4": {
|
||||||
|
|
@ -680,7 +698,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -689,7 +708,16 @@
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"name": "Lose Hope",
|
"name": "Lose Hope",
|
||||||
"img": "icons/magic/death/skull-energy-light-purple.webp",
|
"img": "icons/magic/death/skull-energy-light-purple.webp",
|
||||||
"range": ""
|
"range": "",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Doombringer",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "far",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -174,12 +174,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -230,7 +227,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -256,7 +253,8 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
@ -393,7 +391,7 @@
|
||||||
"name": "Shattering Strike",
|
"name": "Shattering Strike",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Mark a Stress</strong> to make a standard attack against all targets within Very Close range. PCs the @Lookup[@name] succeeds against lose a number of Hope equal to the HP they marked from this attack.</p><p>@Template[type:emanation|range:vc]</p>",
|
"description": "<p><strong>Mark a Stress</strong> to make a standard attack against all targets within Very Close range. PCs the @Lookup[@name] succeeds against lose a number of Hope equal to the HP they marked from this attack.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"t1GhGnEhNYyJ7p2U": {
|
"t1GhGnEhNYyJ7p2U": {
|
||||||
|
|
@ -438,7 +436,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -467,7 +466,16 @@
|
||||||
},
|
},
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"img": "icons/skills/melee/sword-stuck-glowing-pink.webp",
|
"img": "icons/skills/melee/sword-stuck-glowing-pink.webp",
|
||||||
"range": "veryClose"
|
"range": "veryClose",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Shattering Strike",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "veryClose",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -496,34 +504,42 @@
|
||||||
"description": "<p><strong>Spend a Fear</strong> to summon a number of @UUID[Compendium.daggerheart.adversaries.Actor.OsLG2BjaEdTZUJU9]{Fallen Shock Troops} equal to twice the number of PCs. The Shock Troops appear at Far range.</p>",
|
"description": "<p><strong>Spend a Fear</strong> to summon a number of @UUID[Compendium.daggerheart.adversaries.Actor.OsLG2BjaEdTZUJU9]{Fallen Shock Troops} equal to twice the number of PCs. The Shock Troops appear at Far range.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"hGMzqw00JTlYfHYy": {
|
"SrU7qbh8LcOgfozT": {
|
||||||
"type": "effect",
|
"type": "summon",
|
||||||
"_id": "hGMzqw00JTlYfHYy",
|
"_id": "SrU7qbh8LcOgfozT",
|
||||||
"systemPath": "actions",
|
"systemPath": "actions",
|
||||||
|
"baseAction": false,
|
||||||
"description": "",
|
"description": "",
|
||||||
"chatDisplay": true,
|
"chatDisplay": true,
|
||||||
|
"originItem": {
|
||||||
|
"type": "itemCollection"
|
||||||
|
},
|
||||||
"actionType": "action",
|
"actionType": "action",
|
||||||
|
"triggers": [],
|
||||||
"cost": [
|
"cost": [
|
||||||
{
|
{
|
||||||
"scalable": false,
|
"scalable": false,
|
||||||
"key": "fear",
|
"key": "fear",
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"step": null
|
"itemId": null,
|
||||||
|
"step": null,
|
||||||
|
"consumeOnSuccess": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"uses": {
|
"uses": {
|
||||||
"value": null,
|
"value": null,
|
||||||
"max": "",
|
"max": "",
|
||||||
"recovery": null
|
"recovery": null,
|
||||||
},
|
"consumeOnSuccess": false
|
||||||
"effects": [],
|
|
||||||
"target": {
|
|
||||||
"type": "self",
|
|
||||||
"amount": null
|
|
||||||
},
|
},
|
||||||
|
"summon": [
|
||||||
|
{
|
||||||
|
"actorUUID": "Compendium.daggerheart.adversaries.Actor.OsLG2BjaEdTZUJU9",
|
||||||
|
"count": "@partySize*2"
|
||||||
|
}
|
||||||
|
],
|
||||||
"name": "Spend Fear",
|
"name": "Spend Fear",
|
||||||
"img": "icons/magic/death/undead-skeleton-worn-blue.webp",
|
"range": "far"
|
||||||
"range": ""
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
@ -565,19 +581,25 @@
|
||||||
"max": "",
|
"max": "",
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"effects": [
|
"effects": [],
|
||||||
{
|
|
||||||
"_id": "eZp1PSCHZzdb47oM",
|
|
||||||
"onSave": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
"amount": null
|
"amount": null
|
||||||
},
|
},
|
||||||
"name": "Circle",
|
"name": "Circle",
|
||||||
"img": "icons/magic/unholy/barrier-fire-pink.webp",
|
"img": "icons/magic/unholy/barrier-fire-pink.webp",
|
||||||
"range": ""
|
"range": "",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Circle of Defilement",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "far",
|
||||||
|
"effects": [
|
||||||
|
"KlTKlZXjdXQ9v6qB"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"4x13WyksHU0u0j20": {
|
"4x13WyksHU0u0j20": {
|
||||||
"type": "countdown",
|
"type": "countdown",
|
||||||
|
|
@ -625,42 +647,58 @@
|
||||||
"img": "icons/magic/unholy/barrier-fire-pink.webp",
|
"img": "icons/magic/unholy/barrier-fire-pink.webp",
|
||||||
"effects": [
|
"effects": [
|
||||||
{
|
{
|
||||||
"name": "Circle of Defilement",
|
"name": "Circle Of Defilement",
|
||||||
|
"disabled": false,
|
||||||
"img": "icons/magic/unholy/barrier-fire-pink.webp",
|
"img": "icons/magic/unholy/barrier-fire-pink.webp",
|
||||||
"origin": "Compendium.daggerheart.adversaries.Actor.RXkZTwBRi4dJ3JE5.Item.55P7ZijSbQeVHCw4",
|
"description": "<p>You are <em>Vulnerable</em> until you leave the circle.</p>",
|
||||||
"transfer": false,
|
"transfer": false,
|
||||||
"_id": "eZp1PSCHZzdb47oM",
|
"statuses": [
|
||||||
"type": "base",
|
"vulnerable"
|
||||||
|
],
|
||||||
"system": {
|
"system": {
|
||||||
"rangeDependence": {
|
"rangeDependence": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
},
|
||||||
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"description": "",
|
||||||
|
"type": ""
|
||||||
|
},
|
||||||
|
"stacking": null,
|
||||||
|
"targetDispositions": [
|
||||||
|
1,
|
||||||
|
0
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"changes": [],
|
"_id": "KlTKlZXjdXQ9v6qB",
|
||||||
"disabled": false,
|
"type": "base",
|
||||||
"duration": {
|
"start": {
|
||||||
"startTime": null,
|
"time": 0,
|
||||||
"combat": null,
|
"combat": null,
|
||||||
"seconds": null,
|
"combatant": null,
|
||||||
"rounds": null,
|
"initiative": null,
|
||||||
"turns": null,
|
"round": null,
|
||||||
"startRound": null,
|
"turn": null
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "<p><em>Vulnerable</em> until you leave the circle. The circle can be removed by dealing Severe damage to the Undefeated Champion.</p>",
|
"duration": {
|
||||||
|
"value": null,
|
||||||
|
"units": "seconds",
|
||||||
|
"expiry": null,
|
||||||
|
"expired": false
|
||||||
|
},
|
||||||
|
"origin": null,
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"statuses": [
|
"showIcon": 1,
|
||||||
"vulnerable"
|
"folder": null,
|
||||||
],
|
|
||||||
"sort": 0,
|
"sort": 0,
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
"_key": "!actors.items.effects!RXkZTwBRi4dJ3JE5.55P7ZijSbQeVHCw4.eZp1PSCHZzdb47oM"
|
"_key": "!actors.items.effects!RXkZTwBRi4dJ3JE5.55P7ZijSbQeVHCw4.KlTKlZXjdXQ9v6qB"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"folder": null,
|
"folder": null,
|
||||||
|
|
@ -775,7 +813,7 @@
|
||||||
"name": "Doombringer",
|
"name": "Doombringer",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When a target marks HP from an attack by the @Lookup[@name], all PCs within Far range of the target lose a Hope.</p><p>@Template[type:emanation|range:f]</p>",
|
"description": "<p>When a target marks HP from an attack by the @Lookup[@name], all PCs within Far range of the target lose a Hope.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"liwKSCTmQqZasAf6": {
|
"liwKSCTmQqZasAf6": {
|
||||||
|
|
@ -819,7 +857,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"includeBase": false
|
"includeBase": false,
|
||||||
|
"groupAttack": ""
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"type": "any",
|
"type": "any",
|
||||||
|
|
@ -828,7 +867,16 @@
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"name": "Lose Hope",
|
"name": "Lose Hope",
|
||||||
"img": "icons/magic/death/skull-energy-light-purple.webp",
|
"img": "icons/magic/death/skull-energy-light-purple.webp",
|
||||||
"range": "far"
|
"range": "far",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Doombringer",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "emanation",
|
||||||
|
"size": "far",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
|
|
@ -138,12 +138,9 @@
|
||||||
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
|
||||||
"anchorX": 0.5,
|
"anchorX": 0.5,
|
||||||
"anchorY": 0.5,
|
"anchorY": 0.5,
|
||||||
"offsetX": 0,
|
|
||||||
"offsetY": 0,
|
|
||||||
"fit": "contain",
|
"fit": "contain",
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"rotation": 0,
|
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"alphaThreshold": 0.75
|
"alphaThreshold": 0.75
|
||||||
},
|
},
|
||||||
|
|
@ -194,7 +191,7 @@
|
||||||
"saturation": 0,
|
"saturation": 0,
|
||||||
"contrast": 0
|
"contrast": 0
|
||||||
},
|
},
|
||||||
"detectionModes": [],
|
"detectionModes": {},
|
||||||
"occludable": {
|
"occludable": {
|
||||||
"radius": 0
|
"radius": 0
|
||||||
},
|
},
|
||||||
|
|
@ -220,14 +217,15 @@
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"randomImg": false,
|
"randomImg": false,
|
||||||
"appendNumber": false,
|
"appendNumber": false,
|
||||||
"prependAdjective": false
|
"prependAdjective": false,
|
||||||
|
"depth": 1
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"name": "Battering Ram",
|
"name": "Battering Ram",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Mark a Stress</strong> to have the @Lookup[@name] charge at an inanimate object within Close range they could feasibly smash (such as a wall, cart, or market stand) and destroy it. All targets within Very Close range of the object must succeed on an Agility Reaction Roll or take <strong>2d4+3</strong> physical damage from the shrapnel.</p><p>@Template[type:circle|range:vc]</p>",
|
"description": "<p><strong>Mark a Stress</strong> to have the @Lookup[@name] charge at an inanimate object within Close range they could feasibly smash (such as a wall, cart, or market stand) and destroy it. All targets within Very Close range of the object must succeed on an Agility Reaction Roll or take <strong>2d4+3</strong> physical damage from the shrapnel.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"zns57MqnZ6M1d4r0": {
|
"zns57MqnZ6M1d4r0": {
|
||||||
|
|
@ -308,7 +306,16 @@
|
||||||
},
|
},
|
||||||
"name": "Roll Save",
|
"name": "Roll Save",
|
||||||
"img": "icons/skills/melee/shield-damaged-broken-orange.webp",
|
"img": "icons/skills/melee/shield-damaged-broken-orange.webp",
|
||||||
"range": "close"
|
"range": "close",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"name": "Battering Ram",
|
||||||
|
"type": "placed",
|
||||||
|
"shape": "circle",
|
||||||
|
"size": "veryClose",
|
||||||
|
"effects": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue