From 85111648aa1396b4f81a953dcc15c08d2088b9c3 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Wed, 20 Aug 2025 02:42:26 +0200 Subject: [PATCH 01/66] Fixed Spear/HallowedAxe and type in SRD (#1023) --- ...eapon_Advanced_Spear_pK6dsNABKKp1CIGN.json | 41 +------------------ .../weapon_Hallowed_Axe_Vayg7CnRTFBrunjM.json | 6 +-- ...eapon_Improved_Spear_j5Pt1thLfcvopBij.json | 41 +------------------ ...apon_Legendary_Spear_4e5pWxi2qohuGsWh.json | 41 +------------------ .../weapon_Spear_TF85tKJetUjLwh54.json | 41 +------------------ ...rnal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json | 2 +- 6 files changed, 8 insertions(+), 164 deletions(-) diff --git a/src/packs/items/weapons/weapon_Advanced_Spear_pK6dsNABKKp1CIGN.json b/src/packs/items/weapons/weapon_Advanced_Spear_pK6dsNABKKp1CIGN.json index a1a7ad44..af308099 100644 --- a/src/packs/items/weapons/weapon_Advanced_Spear_pK6dsNABKKp1CIGN.json +++ b/src/packs/items/weapons/weapon_Advanced_Spear_pK6dsNABKKp1CIGN.json @@ -99,46 +99,7 @@ "artist": "" } }, - "effects": [ - { - "name": "Cumbersome", - "description": "-1 to Finesse", - "img": "icons/commodities/metal/mail-plate-steel.webp", - "changes": [ - { - "key": "system.traits.finesse.value", - "mode": 2, - "value": "-1" - } - ], - "_id": "hl0S2LrBY5Mg69q6", - "type": "base", - "system": {}, - "disabled": false, - "duration": { - "startTime": null, - "combat": null - }, - "origin": null, - "tint": "#ffffff", - "transfer": true, - "statuses": [], - "sort": 0, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.346", - "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753831987001, - "modifiedTime": 1753831987001, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" - }, - "_key": "!items.effects!pK6dsNABKKp1CIGN.hl0S2LrBY5Mg69q6" - } - ], + "effects": [], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/items/weapons/weapon_Hallowed_Axe_Vayg7CnRTFBrunjM.json b/src/packs/items/weapons/weapon_Hallowed_Axe_Vayg7CnRTFBrunjM.json index 11e0cc87..1a6d341f 100644 --- a/src/packs/items/weapons/weapon_Hallowed_Axe_Vayg7CnRTFBrunjM.json +++ b/src/packs/items/weapons/weapon_Hallowed_Axe_Vayg7CnRTFBrunjM.json @@ -25,7 +25,7 @@ "amount": 1 }, "roll": { - "trait": "agility", + "trait": "strength", "type": "attack", "difficulty": null, "bonus": null, @@ -112,9 +112,9 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.0.6", "createdTime": 1753828229603, - "modifiedTime": 1755430661659, + "modifiedTime": 1755633052433, "lastModifiedBy": "VZIeX2YDvX338Zvr" }, "_key": "!items!Vayg7CnRTFBrunjM" diff --git a/src/packs/items/weapons/weapon_Improved_Spear_j5Pt1thLfcvopBij.json b/src/packs/items/weapons/weapon_Improved_Spear_j5Pt1thLfcvopBij.json index a398b785..32c080ff 100644 --- a/src/packs/items/weapons/weapon_Improved_Spear_j5Pt1thLfcvopBij.json +++ b/src/packs/items/weapons/weapon_Improved_Spear_j5Pt1thLfcvopBij.json @@ -99,46 +99,7 @@ "artist": "" } }, - "effects": [ - { - "name": "Cumbersome", - "description": "-1 to Finesse", - "img": "icons/commodities/metal/mail-plate-steel.webp", - "changes": [ - { - "key": "system.traits.finesse.value", - "mode": 2, - "value": "-1" - } - ], - "_id": "8twXPJELZpvFWA5K", - "type": "base", - "system": {}, - "disabled": false, - "duration": { - "startTime": null, - "combat": null - }, - "origin": null, - "tint": "#ffffff", - "transfer": true, - "statuses": [], - "sort": 0, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.346", - "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753829466016, - "modifiedTime": 1753829466016, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" - }, - "_key": "!items.effects!j5Pt1thLfcvopBij.8twXPJELZpvFWA5K" - } - ], + "effects": [], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/items/weapons/weapon_Legendary_Spear_4e5pWxi2qohuGsWh.json b/src/packs/items/weapons/weapon_Legendary_Spear_4e5pWxi2qohuGsWh.json index c7fa407a..1f76d2aa 100644 --- a/src/packs/items/weapons/weapon_Legendary_Spear_4e5pWxi2qohuGsWh.json +++ b/src/packs/items/weapons/weapon_Legendary_Spear_4e5pWxi2qohuGsWh.json @@ -99,46 +99,7 @@ "artist": "" } }, - "effects": [ - { - "name": "Cumbersome", - "description": "-1 to Finesse", - "img": "icons/commodities/metal/mail-plate-steel.webp", - "changes": [ - { - "key": "system.traits.finesse.value", - "mode": 2, - "value": "-1" - } - ], - "_id": "f44KWDgCQeKYfccr", - "type": "base", - "system": {}, - "disabled": false, - "duration": { - "startTime": null, - "combat": null - }, - "origin": null, - "tint": "#ffffff", - "transfer": true, - "statuses": [], - "sort": 0, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.346", - "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753834816288, - "modifiedTime": 1753834816288, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" - }, - "_key": "!items.effects!4e5pWxi2qohuGsWh.f44KWDgCQeKYfccr" - } - ], + "effects": [], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/items/weapons/weapon_Spear_TF85tKJetUjLwh54.json b/src/packs/items/weapons/weapon_Spear_TF85tKJetUjLwh54.json index 77bd4f42..2432a75b 100644 --- a/src/packs/items/weapons/weapon_Spear_TF85tKJetUjLwh54.json +++ b/src/packs/items/weapons/weapon_Spear_TF85tKJetUjLwh54.json @@ -99,46 +99,7 @@ "artist": "" } }, - "effects": [ - { - "name": "Cumbersome", - "description": "-1 to Finesse", - "img": "icons/commodities/metal/mail-plate-steel.webp", - "changes": [ - { - "key": "system.traits.finesse.value", - "mode": 2, - "value": "-1" - } - ], - "_id": "Z5MnVI8EOOgzRdXC", - "type": "base", - "system": {}, - "disabled": false, - "duration": { - "startTime": null, - "combat": null - }, - "origin": null, - "tint": "#ffffff", - "transfer": true, - "statuses": [], - "sort": 0, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.346", - "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753828072355, - "modifiedTime": 1753828072355, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" - }, - "_key": "!items.effects!TF85tKJetUjLwh54.Z5MnVI8EOOgzRdXC" - } - ], + "effects": [], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/journals/journal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json b/src/packs/journals/journal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json index ece610c0..a1bc9f84 100644 --- a/src/packs/journals/journal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json +++ b/src/packs/journals/journal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json @@ -125,7 +125,7 @@ "image": {}, "text": { "format": 1, - "content": "

FLOW OF THE GAME

Daggerheart is a conversation. The GM describes fictional scenarios involving the PCs, and the players take turns describing how their characters react. The goal of every person at the table is to build upon everyone else’s ideas and collaboratively tell a satisfying story. The system facilitates this collaborative process by providing structure to the conversation and mechanics for resolving moments of tension where fate or fortune determine the outcome of events.

PLAYER PRINCIPLES & BEST PRACTICES

To get the most out of Daggerheart, we recommend players keep the following principles and practices in mind throughout each session:

PRINCIPLES

BEST PRACTICES

For more information, see the Daggerheart Core Rulebook, pages 9 and 108.

CORE GAMEPLAY LOOP

The core gameplay loop is the procedure that drives every scene, both in and out of combat:

STEP 1: SET THE SCENE

The GM describes a scenario, establishing the PCs’ surroundings and any dangers, NPCs, or other important details the characters would notice.

STEP 2: ASK AND ANSWER QUESTIONS

The players ask clarifying questions to explore the scene more deeply and gather information that could inform their characters’ actions. The GM responds to these questions by giving the players information their characters could easily obtain, or by asking questions of their own to the players. The players also respond to any questions the GM poses to them.

In this way, the table builds out the fiction collaboratively.

STEP 3: BUILD ON THE FICTION

As the scene develops, the players find opportunities to take action—problems to solve, obstacles to overcome, mysteries to investigate, and so on. The players describe how their characters proceed; if their proposed actions carry no chance of failure (or if failure would be boring), they automatically succeed. But if the outcome of their action is unknown, the GM calls for an action roll. Either way, the table works the outcome into the story and moves the fiction forward, narrating how the PC’s actions have changed things.

STEP 4: GO BACK TO STEP 1

The process repeats from the beginning, with the GM relaying any updated details or material changes to the players. This process continues until the end of the scene is triggered by a mechanic or arrives organically.

THE SPORLIGHT

The spotlight is a symbol that represents the table’s attention—and therefore the immediate focus of both the narrative and the game mechanics. Any time a character or player becomes the focus of a scene, they “are in the spotlight” or “have the spotlight.”

The spotlight moves around the table organically as scenes unfold unless a mechanical trigger determines where the spotlight goes next. For example, when a player fails an action roll, the mechanics prompt the GM to seize the spotlight and make a GM move.

TURN ORDER & ACTION ECONOMY

Daggerheart’s turns don’t follow a traditional, rigid format:
there is no explicit initiative mechanic and characters don’t have a set number of actions they can take or things they can do before the spotlight passes to someone else. A player with the spotlight describes what their character does and the spotlight simply swings to whoever:

  1. the fiction would naturally turn it toward

  2. hasn’t had the focus in a while, or

  3. a triggered mechanic puts it on


Optional: Spotlight Tracker Tool

If your group prefers a more traditional action economy, you can use tokens to track how many times a player has had the spotlight: At the start of a session or scene, each player adds a certain number of tokens (we recommend 3) to their character sheet and removes a token each time they take an action. If the spotlight would swing to someone without any tokens, it swings to someone else instead. Once every player has used all their available tokens, players refill their character sheet with the same number of tokens as before, then continue playing.


MAKING MOVES & TAKING ACTION

Any time a character does something to advance the story, such as speaking with another character, interacting with the environment, making an attack, casting a spell, or using a class feature, they are making a move.

ACTION ROLLS

Any move where success would be trivial or failure would be boring automatically succeeds, but any move that’s difficult to accomplish or risky to attempt triggers an action roll.

OVERVIEW

All action rolls require a pair of d12s called Duality Dice.

These are two visually distinct twelve-sided dice, with one die representing Hope and the other representing Fear.

To make an action roll, you roll the Duality Dice, sum the results, apply any relevant modifiers, and compare the total to a Difficulty number to determine the outcome:

Note: A Critical Success counts as a roll “with Hope.”

After resolving the action roll, the table works together to weave the outcome into the narrative and play continues.

FAILING FORWARD

In Daggerheart, every time you roll the dice, the scene changes in some way. There is no such thing as a roll where “nothing happens,” because the fiction constantly evolves based on the successes and failures of the characters.

PROCEDURE

The following steps describe in more detail the procedure that all action rolls utilize:

STEP 1: PICK AN APPROPRIATE TRAIT

Some actions and effects specify in their description which trait applies to the roll; otherwise, the GM tells the acting player which character trait best applies to the action being attempted. If more than one trait could apply to the roll, the GM chooses or lets the acting player decide.

STEP 2: DETERMINE THE DIFFICULTY

Some actions and features say in their description what the Difficulty is. Otherwise, the GM determines the Difficulty based on the scenario. The GM can choose whether to share the Difficulty with the table. In either case, the GM should communicate the potential consequences of failure to the acting player.

STEP 3: APPLY EXTRA DICE AND MODIFIERS

The acting player decides whether to Utilize an Experience or activate other effects, then, if applicable, adds the appropriate tokens and dice (such as advantage or Rally dice) to their dice pool.


Note: Unless an action, ability, or feature specifically allows for it, a player must declare the use of any Experiences, extra dice, or other modifiers before they roll.


STEP 4: ROLL THE DICE

The acting player rolls their entire dice pool and announces the results in the format of “[total result] with [Hope/Fear]”— or “Critical Success!” in the case of matching Duality Dice.


Example: A player is making an action roll with a +1 in the relevant trait and no other modifiers; they roll the Duality Dice and get a result of 5 on their Hope Die and 7 on their Fear Die, then announce “I rolled a 13 with Fear!”


STEP 5: RESOLVE THE OUTCOME

The active player and the GM work together, along with the suggestions and support of the rest of the table, to resolve the outcome of the action.

GM MOVES AND ADVERSARY ACTIONS

GMs also make moves. They should consider making a move when a player does one of the following things:

After the GM turn is done, the spotlight goes back to the PCs.

Many adversaries and environments have Fear Features, especially powerful or consequential moves that the GM must spend Fear to activate.


Note: This Fear is in addition to any Fear the GM has previously spent to seize the spotlight or activate another action or ability.


ADVERSARY ACTIONS

When play passes to the GM, the GM can make a GM move to spotlight an adversary. A spotlighted adversary can:

The GM can spend additional Fear to spotlight additional adversaries. Once the GM has finished, the spotlight swings back to the PCs.

SPECIAL ROLLS

Some rolls have unique specifications or otherwise modify the action roll procedure: trait rolls, Spellcast Rolls, attack rolls, and damage rolls. Unless otherwise noted, you can apply any bonus, modifier, or effect to a special roll as if it were a standard action roll.

TRAIT ROLLS

An action roll that specifies which character trait applies to it is called a trait roll. In the text of a feature or effect, a trait roll is referenced with the format “[Trait] Roll (Difficulty)” (e.g., “Agility Roll (12)”). If the text of an effect doesn’t specify a trait roll’s Difficulty, the GM sets the Difficulty based on the circumstances.

Features and effects that affect a trait roll also affect any action roll that uses the same trait, including attack rolls, Spellcast rolls, and standard action rolls.


Example: The katari’s ancestry feature “Feline Instincts,” which allows the katari to reroll an Agility Roll, can also be used on a standard action roll using Agility to traverse dangerous terrain or on an attack roll made with a weapon that uses Agility.


SPELLCAST ROLLS

Spellcast Rolls are trait rolls that require you to use your Spellcast trait. Your Spellcast trait, if you have one, is determined by your subclass.

Spellcast Rolls are only made when a character uses a feature that requires one. A successful Spellcast Roll activates the effect as described by the feature.


Notes: A Spellcast Roll that can damage a target is also considered an attack roll.

When you cast a spell, the text tells you when the effect ends. The GM can spend a Fear to end a temporary effect. If your spell doesn’t specify when it ends, it ends when you choose or at a natural moment of the story. You can choose to end your spell early.

You can cast and maintain the effects of more than one spell at the same time.


REACTION ROLLS

A reaction roll is made in response to an attack or a hazard, representing a character’s attempt to avoid or withstand an imminent effect.

Reaction rolls work like action rolls, except they don’t generate Hope or Fear, don’t trigger additional GM moves, and other characters can’t aid you with Help an Ally.

If you critically succeed on a reaction roll, you don’t clear a Stress or gain a Hope, but you do ignore any effects that would have impacted you on a success, such as taking damage or marking Stress.

GROUP ACTION ROLLS

When multiple PCs take action together, the party chooses one PC to lead the action. Each other player then describes how their character collaborates on the task. The leader makes an action roll as usual, while the other players make reaction rolls using whichever traits they and the GM decide fit best.

The lead character gains a +1 bonus to their lead action roll for each of these reaction rolls that succeeded and a −1 penalty for each these reaction rolls that failed.

TAG TEAM ROLLS

Each player can, once per session, initiate a Tag Team Roll between their character and another PC by spending 3 Hope. The players work with one another to describe how they combine their actions in a unique and exciting way. Both players make separate action rolls; before resolving the roll’s outcome, choose one of the rolls to apply to both actions. On a roll with Hope, all PCs involved gain a Hope. On a roll with Fear, the GM gains a Fear token for each PC involved.

On a successful Tag Team attack roll, both players roll damage and add the totals together to determine the damage dealt, which is then treated as if it came from a single source. If the attacks deal different types of damage, the players choose which type to deal.


Notes:

A Tag Team Roll counts as a single action roll for the purposes of any countdowns or features that track action rolls.

Though each player may only initiate one Tag Team Roll per session, one PC can be involved in multiple Tag Team Rolls.


ADVANTAGE & DISADVANTAGE

Some features and effects let you roll with advantage or disadvantage on an action or reaction roll:

Advantage or disadvantage can be granted or imposed by mechanical triggers or at the GM’s discretion. When a PC aids you with Help an Ally, they roll their own advantage die and you add it to your total.

Advantage and disadvantage dice cancel each out, one-for-one, when they would be added to the same dice pool, so you’ll never roll both at the same time. If you have advantage or disadvantage from other sources that don’t affect your own dice pool, such as another player’s Help an Ally move, their effects stack with your rolled results.

HOPE & FEAR

Hope and Fear are metacurrencies representing the cosmic forces that shape the events of your table’s story. Hope powers PC abilities and features, while Fear powers the abilities of the GM and the adversaries and environments they control.

HOPE

Every PC starts with 2 Hope at character creation and gains more throughout play. A PC can have a maximum of 6 Hope at one time, and Hope carries over between sessions.

Players can spend Hope to:


Note: When using a Hope Feature, if you rolled with Hope for that action, the Hope you gain from that roll can be spent on that feature (or toward it, if it requires spending multiple Hope).


FEAR

The GM gains Fear whenever a player rolls with Fear and can spend Fear at any time to make or enhance a GM move or to use a Fear Feature. The GM can have up to 12 Fear at one time. Fear carries over between sessions.

COMBAT

Though Daggerheart relies on the same flow of collaborative storytelling in and out of combat, physical conflicts rely more heavily on several key mechanics related to attacking, maneuvering, and taking damage.

EVASION

Evasion represents a character’s ability to avoid attacks and other unwanted effects. Any roll made against a PC has a Difficulty equal to the target’s Evasion. A PC’s base Evasion is determined by their class, but can be modified by domain cards, equipment, conditions, and other effects.


Note: attacks rolled against adversaries use the target’s Difficulty instead of Evasion.


HIT POINTS & DAMAGE THRESHOLDS

Hit Points (HP) represent a character’s ability to withstand physical injury. When a character takes damage, they mark 1 to 3 HP, based on their damage thresholds:

A PC’s damage thresholds are calculated by adding their level to the listed damage thresholds of their equipped armor. A PC’s starting HP is based on their class, but they can gain additional Hit Points through advancements, features, and other effects.

An adversary’s Damage Thresholds and HP are listed in their stat blocks.

When a character marks their last Hit Point, they fall. If a PC falls, they make a death move.

Characters can clear Hit Points by taking downtime moves (see: Downtime) or by activating relevant special abilities or effects.


Optional Rule: Massive Damage

If a character ever takes damage equal to twice their Severe threshold, they mark 4 HP instead of 3.


STRESS

Stress represents how much mental, physical, and emotional strain a character can endure. Some special abilities or effects require the character activating them to mark Stress, and the GM can require a PC to mark Stress as a GM move or to represent the cost, complication, or consequence of an action roll.

When a character marks their last Stress, they become Vulnerable (see: Conditions) until they clear at least 1 Stress.

When a character must mark 1 or more Stress but can’t, they mark 1 HP instead. A character can’t use a move that requires them to mark Stress if all of their Stress is marked.

PCs can clear Stress by making downtime moves (see: Downtime). A PC’s maximum Stress is determined by their class, but they can increase it through advancements, abilities, and other effects.

ATTACKING

ATTACK ROLLS

An attack roll is an action roll intended to inflict harm. The trait that applies to an attack roll is specified by the weapon or spell being used. Unarmed attack rolls use either Strength or Finesse (GM’s choice). An attack roll’s Difficulty, unless otherwise noted, is equal to the Difficulty score of its target.

DAMAGE ROLLS

On a successful attack, roll damage. Damage is calculated from the damage roll listed in the attack’s description with the format “xdy+[modifier]” (e.g., for a spell that inflicts “1d8+2” damage, you roll an eight-sided and add 2 to the result; the damage dealt is equal to the total).

Any time an effect says to deal damage using your Spellcast trait, you roll a number of dice equal to your Spellcast trait.


Note: If your Spellcast trait is +0 or lower, you don’t roll anything.


For weapons, the number of damage dice you roll is equal to your Proficiency. Note that your Proficiency multiplies the number of dice you roll, but doesn’t affect the modifier. For example, a PC with Proficiency 2 and wielding a weapon with adamage rating of “d8+2” deals damage equal to “2d8+2” on a successful attack.

Successful unarmed attacks inflict [Proficiency]d4 damage.

CRITICAL DAMAGE

When you get a critical success (i.e., you roll matching values on your Duality Dice) on an attack roll, you deal extra damage.

Make the damage roll as usual, but add the maximum possible result of the damage dice to the final total. For instance, if an attack would normally deal 2d8+1 damage, a critical success would deal 2d8+1+16.

DAMAGE TYPES

There are two damage types: physical damage (phy) and magic damage (mag). Unless stated otherwise, mundane weapons and unarmed attacks deal physical damage, and spells deal magic damage.

RESISTANCE, IMMUNITY, AND DIRECT DAMAGE

If a target has resistance to a damage type, then they reduce incoming damage of that type by half before comparing it to their Hit Point Thresholds. If the target has additional ways of reducing incoming damage, such as marking Armor Slots, they apply the resistance effect first. The effects of multiple resistances to the same damage type do not stack.

If a target has immunity to a damage type, they ignore incoming damage of that type.

If an attack deals both physical and magic damage, a character can only benefit from resistance or immunity if they are resistant or immune to both damage types.

Direct damage is damage that can’t be reduced by marking Armor Slots.

MULTI-TARGET ATTACK ROLLS

If a spell or ability allows you to target multiple adversaries, make one attack roll and one damage roll, then apply the results to each target individually.

MULTIPLE DAMAGE SOURCES

Damage dealt simultaneously from multiple sources is always totaled before it’s compared to its target’s damage thresholds.


For example, if a PC with orc ancestry makes a successful attack against a target in Melee range and decides to spend a Hope to use their “Tusks” feature (which gives them an extra 1d6 damage on a damage roll), they would roll their normal weapon damage and add a d6 to the result, then deal that total damage to the adversary.


MAPS, RANGE, AND MOVEMENT

You can play Daggerheart using “theater of the mind” or maps and miniatures. The conversions below from abstract ranges to physical measurements assume 1 inch of map represents about 5 feet of fictional space.

Daggerheart uses the following ranges to translate fictional positioning into relative distance for the purposes of targeting, movement, and other game mechanics:

Range is measured from the source of an effect, such as the attacker or spellcaster, to the target or object of an effect.

A weapon, spell, ability, item, or other effect’s stated range is a maximum range; unless otherwise noted, it can be used at closer distances.


Optional Rule: Defined Ranges

If your table would rather operate with more precise range rules, you can use a 1-inch grid battle map during combat.

If you do, use the following guidelines for play:


MOVEMENT UNDER PRESSURE

When you’re under pressure or in danger and make an action roll, you can move to a location within Close range as part of that action. If you’re not already making an action roll, or if you want to move farther than your Close range, you need to succeed on an Agility Roll to safely reposition yourself.

An adversary can move within Close range for free as part of an action, or within Very Far range as a separate action.

AREA OF EFFECT

Unless stated otherwise, all the targets of a group effect must be within Very Close range of a single origin point within your effect’s range.

LINE OF SIGHT & COVER

Unless stated otherwise, a ranged attacker must have line of sight to their intended target to make an attack roll. If a partial obstruction lies between the attacker and target, the target has cover. Attacks made through cover are rolled with disadvantage. If the obstruction is total, there is no line of sight.

CONDITIONS

Conditions are effects that grant specific benefits or drawbacks to the target they are attached to.

STANDARD CONDITIONS

Daggerheart has three standard conditions:

HIDDEN

While you’re out of sight from all enemies and they don’t otherwise know your location, you gain the Hidden condition. Any rolls against a Hidden creature have disadvantage. After an adversary moves to where they would see you, you move into their line of sight, or you make an attack, you are no longer Hidden.

RESTRAINED

Restrained characters can’t move, but you can still take actions from their current position.

VULNERABLE

When a creature is Vulnerable, all rolls targeting them have advantage.

Some features can apply special or unique conditions, which work as described in the feature text.

Unless otherwise noted, the same condition can’t be applied more than once to the same target.

TEMPORARY TAGS & SPECIAL CONDITIONS

The temporary tag denotes a condition or effect that the affected creature can clear by making a move against it. When an affected PC makes a move to clear a temporary condition or effect, it normally requires a successful action roll using an appropriate trait. When an affected adversary makes a move to clear a temporary condition or effect, the GM puts the spotlight on the adversary and describes how they do it; this doesn’t require a roll but it does use up that adversary’s spotlight.

Special conditions are only cleared when specific requirements are met, such as completing a certain action or using a particular item. The requirements for clearing these conditions are stated in the text of the effect that applies the condition.

DOWNTIME

Between conflicts, the party can take a rest to recover expended resources and deepen their bonds. During a rest, each PC can make up to two downtime moves.

When the party rests, they must choose between a short rest and a long rest. If a party takes three short rests in a row, their next rest must be a long rest.

If a short rest is interrupted, such as by an adversary's attack, the characters don’t gain its benefits. If a long rest is interrupted, the characters only gain the benefits of a short rest.

A short rest lasts enough time for the party to catch its breath, about an hour in-world. Each player can move domain cards between their loadout and vault for free, then choose twice from the following list of downtime moves (players can choose the same move twice):

At the end of a short rest, any features or effects with a limited number of uses per rest refresh and any features or effects that last until your next rest expire.

A long rest is when the characters make camp and relax or sleep for several in-game hours. Each player can move domain cards between their loadout and vault for free, then choose twice from the following list of downtime moves (players can choose the same move twice):

At the end of a long rest, any features or effects with a limited number of uses per rest or per long rest refresh and any features or effects that last until your next rest or until your next long rest expire.

DOWNTIME CONSEQUENCES

On a short rest, the GM gains 1d4 Fear. On a long rest, they gain Fear equal to 1d4 + the number of PCs, and they can advance a long-term countdown of their choice.

DEATH

When a PC marks their last Hit Point, they must make a death move by choosing one of the following options:

If your character dies, work with the GM before the next session to create a new character at the current level of the rest of the party.

ADDITIONAL RULES

The following rules apply to many aspects of the game.

ROUNDING UP

This game doesn’t use fractions; if you need to round to a whole number, round up unless otherwise specified. When in doubt, resolve any ambiguity in favor of the PCs.

REROLLING DICE

When a feature allows you to reroll a die, you always take the new result unless the feature specifically says otherwise.

INCOMING DAMAGE

Incoming damage means the total damage from a single attack or source, before Armor Slots are marked.

SIMULTANEOUS EFFECTS

If the resolution order of multiple effects is unclear, the person in control of the effects (player or GM) decides what order to resolve them in.

STACKING EFFECTS

Unless stated otherwise, all effects beside conditions and advantage/disadvantage can stack.

ONGOING SPELL EFFECTS

If an effect doesn’t have a listed mechanical expiration, it only ends when decided by the controlling player, the GM, or the demands of the fiction.

SPENDING RESOURCES

Unless an effect states otherwise, you can’t spend Hope or mark Stress multiple times on the same feature to increase or repeat its effects on the same roll.

USING FEATURES AFTER A ROLL

If a feature allows you to affect a roll after the result has been totaled, you can use it after the GM declares whether the roll succeeds or fails, but not after the consequences unfold or another roll is made.

LEVELING UP

Your party levels up whenever the GM decides you’ve reached a narrative milestone (usually about every 3 sessions). All party members level up at the same time.

Daggerheart has 10 PC levels divided into 4 tiers:


→ Tier 1 encompasses level 1 only.

→ Tier 2 encompasses levels 2–4.

→ Tier 3 encompasses levels 5–7.

→ Tier 4 encompasses levels 8–10.


Your tier affects your damage thresholds, tier achievements, and access to advancements.

STEP ONE: TIER ACHIEVEMENTS

Take any applicable tier achievements

STEP TWO: ADVANCEMENTS

Choose any two advancements with at least one unmarked slot from your tier or below. Options with multiple slots can be chosen more than once. When you choose an advancement, mark one of its slots.

STEP THREE: DAMAGE THRESHOLDS

Increase all damage thresholds by 1.

STEP FOUR: DOMAIN CARDS

Acquire a new domain card at your level or lower from one of your class’s domains and add it to your loadout or vault. If your loadout is already full, you can’t add the new card to it until you move another into your vault. You can also exchange one domain card you’ve previously acquired for a different domain card of the same level or lower.

MULTICLASSING

Starting at level 5, you can choose multiclassing as an option when leveling up. When you multiclass, you choose an additional class, gain access to one of its domains, and acquire its class feature. Take the appropriate multiclass module and add it to the right side of your character sheet, then choose a foundation card from one of its subclasses. If your foundation cards specify different Spellcast traits, you can choose which one to apply when making a Spellcast roll.

Whenever you have the option to acquire a new domain card, you can choose from cards at or below half your current level (rounded up) from the domain you chose when you selected the multiclass advancement.

EQUIPMENT

Your equipped weapons and armor are the ones listed in the “Active Weapons” and “Active Armor” sections of your character sheet. Your character can only attack with weapons, benefit from armor, and gain features from items they have equipped. You can’t equip weapons or armor with a higher tier than you.

PCs can carry up to two additional weapons in the “Inventory Weapon” areas of the character sheet.

You can swap an Inventory Weapon with an Active Weapon at no cost during a rest or moment of calm; otherwise, you must mark a Stress to do so.

Your character can only have one Active Armor at a time.

They can’t equip armor while in danger or under pressure; otherwise, they can equip or unequip armor without cost.

Each armor has its own Armor Slots; if your character unequips their armor, track how many of its Armor Slots are marked. You can't carry armor in your inventory. When your character equips or unequips armor, recalculate your damage thresholds.

WEAPONS

All weapons have a tier, trait, range, damage die, damage type, and burden. Some weapons also have a feature.

CATEGORY

A weapon’s category specifies whether it is a Primary or Secondary weapon. Your character can only equip up to one weapon of each category at a time.

TRAIT

A weapon’s trait specifies which trait to use when making an attack roll with it.

RANGE

A weapon’s range specifies the maximum distance between the attacker and their target when attacking with it.

DAMAGE

A weapon’s damage indicates the size of the damage dice you roll on a successful attack with it; you roll a number of dice equal to your Proficiency. If the damage includes a flat modifier, this number is added to the total damage rolled, but is not altered or affected by Proficiency.

DAMAGE TYPE

A weapon’s damage type indicates whether it deals physical or magic damage. Weapons that deal magic damage can only be wielded by characters with a Spellcast trait.

BURDEN

A weapon’s burden indicates how many hands it occupies when equipped. Your character’s maximum burden is 2 hands.

FEATURE

A weapon’s feature is a special rule that stays in effect while the weapon is equipped.

You can throw an equipped weapon at a target within Very Close range, making the attack roll with Finesse. On a success, deal damage as usual for that weapon. Once thrown, the weapon is no longer considered equipped. Until you retrieve and re-equip it, you can’t attack with it or benefit from its features.

Combat Wheelchair

By Mark Thompson

The combat wheelchair is a ruleset designed to help you play a wheelchair user in Daggerheart. This section provides mechanics and narrative guidance for you to work from, but feel free to adapt the flavor text to best suit your character. Have fun with your character’s wheelchair design, and make it as unique or tailored to them as you please.

ACTION AND MOVEMENT

When describing how your character moves, you can use descriptions such as the following:

CONSEQUENCES

Here are some ways you might describe complications you encounter when your character uses their wheelchair:

GMs should avoid breaking a character's wheelchair or otherwise removing it from play as a consequence, unless everyone at the table, especially the wheelchair user’s player, gives their approval.

EVASION

Your character is assumed to be skilled in moving their wheelchair and navigating numerous situations in it. As a result, the only wheelchair that gives a penalty to a PC's Evasion is the Heavy Frame model.

BURDEN

All wheelchairs can be maneuvered using one or two hands outside of combat. However, when being used as a weapon, the chair is restricted to requiring one or two hands to perform attacks, depending on the model you’ve chosen. If you’re playing a character who has limited to no mobility in their arms, their wheelchair can be attuned to them by magical means. For example, your character might use a psychic link to guide the chair around like a pseudo-electric wheelchair. All the rules presented here can be tailored and adapted to any character's needs.

CHOOSING YOUR MODEL

All combat wheelchairs are equipped as Primary Weapons.

There are three models of wheelchair available: light, heavy, and arcane. You’re encouraged to consider the type of character you’re playing and the class they belong to, then choose the model that best matches that character concept.

ARMOR

Every armor has a name, base damage thresholds, and a base Armor Score. Some armor also has a feature.

While unarmored, your character’s base Armor Score is 0, their Major threshold is equal to their level, and their Severe threshold is equal to twice their level.

REDUCING INCOMING DAMAGE

When you take damage, you can mark one Armor Slot to reduce the number of Hit Points you would mark by one. If your character has an Armor Score of 0, you can’t mark Armor Slots. If an effect temporarily increases your Armor Score,

it increases your available Armor Slots by the same amount; when the effect ends, so does the availability of these Armor Slots.

LOOT

Loot comprises any consumables or reusable items the party acquires.

Items can be used until sold, discarded, or lost.

To generate a random item, choose a rarity, roll the designated dice, and match the total to the item in the table:

@UUID[RollTable.KKqUrMMXPpm7uhYT]{Loot}

Consumables

Consumables are loot that can only be used once. You can hold up to five of each consumable at a time. Using a consumable doesn’t require a roll unless required by the GM or the demands of the fiction.

To generate a random consumable, choose a rarity, roll the designated dice, and match the total to the item in the table:

@UUID[RollTable.wZXyi343PSVVwWB3]{Consumables}

GOLD

Gold is an abstract measurement of how much wealth a character has, and is measured in handfuls, bags, and chests, with 10 handfuls to 1 bag, and 10 bags to 1 chest. When you have marked all of the slots in a category and you gain another gold reward in that category, mark a slot in the following category and clear all the slots in the current one.

For example, if you have 9 handfuls and gain another, you instead mark 1 bag and erase all handfuls. If you have 9 bags and gain another, you mark 1 chest and erase all bags.

You can’t have more than 1 chest, so if all your Gold slots are marked, you’ll need to spend some of your gold or store it somewhere else before you can acquire more.


Optional Rule: Gold Coins

If your group wants to track gold with more granularity, you can add coins as your lowest denomination. Following the established pattern, 10 coins equal 1 handful.


" + "content": "

FLOW OF THE GAME

Daggerheart is a conversation. The GM describes fictional scenarios involving the PCs, and the players take turns describing how their characters react. The goal of every person at the table is to build upon everyone else’s ideas and collaboratively tell a satisfying story. The system facilitates this collaborative process by providing structure to the conversation and mechanics for resolving moments of tension where fate or fortune determine the outcome of events.

PLAYER PRINCIPLES & BEST PRACTICES

To get the most out of Daggerheart, we recommend players keep the following principles and practices in mind throughout each session:

PRINCIPLES

BEST PRACTICES

For more information, see the Daggerheart Core Rulebook, pages 9 and 108.

CORE GAMEPLAY LOOP

The core gameplay loop is the procedure that drives every scene, both in and out of combat:

STEP 1: SET THE SCENE

The GM describes a scenario, establishing the PCs’ surroundings and any dangers, NPCs, or other important details the characters would notice.

STEP 2: ASK AND ANSWER QUESTIONS

The players ask clarifying questions to explore the scene more deeply and gather information that could inform their characters’ actions. The GM responds to these questions by giving the players information their characters could easily obtain, or by asking questions of their own to the players. The players also respond to any questions the GM poses to them.

In this way, the table builds out the fiction collaboratively.

STEP 3: BUILD ON THE FICTION

As the scene develops, the players find opportunities to take action—problems to solve, obstacles to overcome, mysteries to investigate, and so on. The players describe how their characters proceed; if their proposed actions carry no chance of failure (or if failure would be boring), they automatically succeed. But if the outcome of their action is unknown, the GM calls for an action roll. Either way, the table works the outcome into the story and moves the fiction forward, narrating how the PC’s actions have changed things.

STEP 4: GO BACK TO STEP 1

The process repeats from the beginning, with the GM relaying any updated details or material changes to the players. This process continues until the end of the scene is triggered by a mechanic or arrives organically.

THE SPOTLIGHT

The spotlight is a symbol that represents the table’s attention—and therefore the immediate focus of both the narrative and the game mechanics. Any time a character or player becomes the focus of a scene, they “are in the spotlight” or “have the spotlight.”

The spotlight moves around the table organically as scenes unfold unless a mechanical trigger determines where the spotlight goes next. For example, when a player fails an action roll, the mechanics prompt the GM to seize the spotlight and make a GM move.

TURN ORDER & ACTION ECONOMY

Daggerheart’s turns don’t follow a traditional, rigid format:
there is no explicit initiative mechanic and characters don’t have a set number of actions they can take or things they can do before the spotlight passes to someone else. A player with the spotlight describes what their character does and the spotlight simply swings to whoever:

  1. the fiction would naturally turn it toward

  2. hasn’t had the focus in a while, or

  3. a triggered mechanic puts it on


Optional: Spotlight Tracker Tool

If your group prefers a more traditional action economy, you can use tokens to track how many times a player has had the spotlight: At the start of a session or scene, each player adds a certain number of tokens (we recommend 3) to their character sheet and removes a token each time they take an action. If the spotlight would swing to someone without any tokens, it swings to someone else instead. Once every player has used all their available tokens, players refill their character sheet with the same number of tokens as before, then continue playing.


MAKING MOVES & TAKING ACTION

Any time a character does something to advance the story, such as speaking with another character, interacting with the environment, making an attack, casting a spell, or using a class feature, they are making a move.

ACTION ROLLS

Any move where success would be trivial or failure would be boring automatically succeeds, but any move that’s difficult to accomplish or risky to attempt triggers an action roll.

OVERVIEW

All action rolls require a pair of d12s called Duality Dice.

These are two visually distinct twelve-sided dice, with one die representing Hope and the other representing Fear.

To make an action roll, you roll the Duality Dice, sum the results, apply any relevant modifiers, and compare the total to a Difficulty number to determine the outcome:

Note: A Critical Success counts as a roll “with Hope.”

After resolving the action roll, the table works together to weave the outcome into the narrative and play continues.

FAILING FORWARD

In Daggerheart, every time you roll the dice, the scene changes in some way. There is no such thing as a roll where “nothing happens,” because the fiction constantly evolves based on the successes and failures of the characters.

PROCEDURE

The following steps describe in more detail the procedure that all action rolls utilize:

STEP 1: PICK AN APPROPRIATE TRAIT

Some actions and effects specify in their description which trait applies to the roll; otherwise, the GM tells the acting player which character trait best applies to the action being attempted. If more than one trait could apply to the roll, the GM chooses or lets the acting player decide.

STEP 2: DETERMINE THE DIFFICULTY

Some actions and features say in their description what the Difficulty is. Otherwise, the GM determines the Difficulty based on the scenario. The GM can choose whether to share the Difficulty with the table. In either case, the GM should communicate the potential consequences of failure to the acting player.

STEP 3: APPLY EXTRA DICE AND MODIFIERS

The acting player decides whether to Utilize an Experience or activate other effects, then, if applicable, adds the appropriate tokens and dice (such as advantage or Rally dice) to their dice pool.


Note: Unless an action, ability, or feature specifically allows for it, a player must declare the use of any Experiences, extra dice, or other modifiers before they roll.


STEP 4: ROLL THE DICE

The acting player rolls their entire dice pool and announces the results in the format of “[total result] with [Hope/Fear]”— or “Critical Success!” in the case of matching Duality Dice.


Example: A player is making an action roll with a +1 in the relevant trait and no other modifiers; they roll the Duality Dice and get a result of 5 on their Hope Die and 7 on their Fear Die, then announce “I rolled a 13 with Fear!”


STEP 5: RESOLVE THE OUTCOME

The active player and the GM work together, along with the suggestions and support of the rest of the table, to resolve the outcome of the action.

GM MOVES AND ADVERSARY ACTIONS

GMs also make moves. They should consider making a move when a player does one of the following things:

After the GM turn is done, the spotlight goes back to the PCs.

Many adversaries and environments have Fear Features, especially powerful or consequential moves that the GM must spend Fear to activate.


Note: This Fear is in addition to any Fear the GM has previously spent to seize the spotlight or activate another action or ability.


ADVERSARY ACTIONS

When play passes to the GM, the GM can make a GM move to spotlight an adversary. A spotlighted adversary can:

The GM can spend additional Fear to spotlight additional adversaries. Once the GM has finished, the spotlight swings back to the PCs.

SPECIAL ROLLS

Some rolls have unique specifications or otherwise modify the action roll procedure: trait rolls, Spellcast Rolls, attack rolls, and damage rolls. Unless otherwise noted, you can apply any bonus, modifier, or effect to a special roll as if it were a standard action roll.

TRAIT ROLLS

An action roll that specifies which character trait applies to it is called a trait roll. In the text of a feature or effect, a trait roll is referenced with the format “[Trait] Roll (Difficulty)” (e.g., “Agility Roll (12)”). If the text of an effect doesn’t specify a trait roll’s Difficulty, the GM sets the Difficulty based on the circumstances.

Features and effects that affect a trait roll also affect any action roll that uses the same trait, including attack rolls, Spellcast rolls, and standard action rolls.


Example: The katari’s ancestry feature “Feline Instincts,” which allows the katari to reroll an Agility Roll, can also be used on a standard action roll using Agility to traverse dangerous terrain or on an attack roll made with a weapon that uses Agility.


SPELLCAST ROLLS

Spellcast Rolls are trait rolls that require you to use your Spellcast trait. Your Spellcast trait, if you have one, is determined by your subclass.

Spellcast Rolls are only made when a character uses a feature that requires one. A successful Spellcast Roll activates the effect as described by the feature.


Notes: A Spellcast Roll that can damage a target is also considered an attack roll.

When you cast a spell, the text tells you when the effect ends. The GM can spend a Fear to end a temporary effect. If your spell doesn’t specify when it ends, it ends when you choose or at a natural moment of the story. You can choose to end your spell early.

You can cast and maintain the effects of more than one spell at the same time.


REACTION ROLLS

A reaction roll is made in response to an attack or a hazard, representing a character’s attempt to avoid or withstand an imminent effect.

Reaction rolls work like action rolls, except they don’t generate Hope or Fear, don’t trigger additional GM moves, and other characters can’t aid you with Help an Ally.

If you critically succeed on a reaction roll, you don’t clear a Stress or gain a Hope, but you do ignore any effects that would have impacted you on a success, such as taking damage or marking Stress.

GROUP ACTION ROLLS

When multiple PCs take action together, the party chooses one PC to lead the action. Each other player then describes how their character collaborates on the task. The leader makes an action roll as usual, while the other players make reaction rolls using whichever traits they and the GM decide fit best.

The lead character gains a +1 bonus to their lead action roll for each of these reaction rolls that succeeded and a −1 penalty for each these reaction rolls that failed.

TAG TEAM ROLLS

Each player can, once per session, initiate a Tag Team Roll between their character and another PC by spending 3 Hope. The players work with one another to describe how they combine their actions in a unique and exciting way. Both players make separate action rolls; before resolving the roll’s outcome, choose one of the rolls to apply to both actions. On a roll with Hope, all PCs involved gain a Hope. On a roll with Fear, the GM gains a Fear token for each PC involved.

On a successful Tag Team attack roll, both players roll damage and add the totals together to determine the damage dealt, which is then treated as if it came from a single source. If the attacks deal different types of damage, the players choose which type to deal.


Notes:

A Tag Team Roll counts as a single action roll for the purposes of any countdowns or features that track action rolls.

Though each player may only initiate one Tag Team Roll per session, one PC can be involved in multiple Tag Team Rolls.


ADVANTAGE & DISADVANTAGE

Some features and effects let you roll with advantage or disadvantage on an action or reaction roll:

Advantage or disadvantage can be granted or imposed by mechanical triggers or at the GM’s discretion. When a PC aids you with Help an Ally, they roll their own advantage die and you add it to your total.

Advantage and disadvantage dice cancel each out, one-for-one, when they would be added to the same dice pool, so you’ll never roll both at the same time. If you have advantage or disadvantage from other sources that don’t affect your own dice pool, such as another player’s Help an Ally move, their effects stack with your rolled results.

HOPE & FEAR

Hope and Fear are metacurrencies representing the cosmic forces that shape the events of your table’s story. Hope powers PC abilities and features, while Fear powers the abilities of the GM and the adversaries and environments they control.

HOPE

Every PC starts with 2 Hope at character creation and gains more throughout play. A PC can have a maximum of 6 Hope at one time, and Hope carries over between sessions.

Players can spend Hope to:


Note: When using a Hope Feature, if you rolled with Hope for that action, the Hope you gain from that roll can be spent on that feature (or toward it, if it requires spending multiple Hope).


FEAR

The GM gains Fear whenever a player rolls with Fear and can spend Fear at any time to make or enhance a GM move or to use a Fear Feature. The GM can have up to 12 Fear at one time. Fear carries over between sessions.

COMBAT

Though Daggerheart relies on the same flow of collaborative storytelling in and out of combat, physical conflicts rely more heavily on several key mechanics related to attacking, maneuvering, and taking damage.

EVASION

Evasion represents a character’s ability to avoid attacks and other unwanted effects. Any roll made against a PC has a Difficulty equal to the target’s Evasion. A PC’s base Evasion is determined by their class, but can be modified by domain cards, equipment, conditions, and other effects.


Note: attacks rolled against adversaries use the target’s Difficulty instead of Evasion.


HIT POINTS & DAMAGE THRESHOLDS

Hit Points (HP) represent a character’s ability to withstand physical injury. When a character takes damage, they mark 1 to 3 HP, based on their damage thresholds:

A PC’s damage thresholds are calculated by adding their level to the listed damage thresholds of their equipped armor. A PC’s starting HP is based on their class, but they can gain additional Hit Points through advancements, features, and other effects.

An adversary’s Damage Thresholds and HP are listed in their stat blocks.

When a character marks their last Hit Point, they fall. If a PC falls, they make a death move.

Characters can clear Hit Points by taking downtime moves (see: Downtime) or by activating relevant special abilities or effects.


Optional Rule: Massive Damage

If a character ever takes damage equal to twice their Severe threshold, they mark 4 HP instead of 3.


STRESS

Stress represents how much mental, physical, and emotional strain a character can endure. Some special abilities or effects require the character activating them to mark Stress, and the GM can require a PC to mark Stress as a GM move or to represent the cost, complication, or consequence of an action roll.

When a character marks their last Stress, they become Vulnerable (see: Conditions) until they clear at least 1 Stress.

When a character must mark 1 or more Stress but can’t, they mark 1 HP instead. A character can’t use a move that requires them to mark Stress if all of their Stress is marked.

PCs can clear Stress by making downtime moves (see: Downtime). A PC’s maximum Stress is determined by their class, but they can increase it through advancements, abilities, and other effects.

ATTACKING

ATTACK ROLLS

An attack roll is an action roll intended to inflict harm. The trait that applies to an attack roll is specified by the weapon or spell being used. Unarmed attack rolls use either Strength or Finesse (GM’s choice). An attack roll’s Difficulty, unless otherwise noted, is equal to the Difficulty score of its target.

DAMAGE ROLLS

On a successful attack, roll damage. Damage is calculated from the damage roll listed in the attack’s description with the format “xdy+[modifier]” (e.g., for a spell that inflicts “1d8+2” damage, you roll an eight-sided and add 2 to the result; the damage dealt is equal to the total).

Any time an effect says to deal damage using your Spellcast trait, you roll a number of dice equal to your Spellcast trait.


Note: If your Spellcast trait is +0 or lower, you don’t roll anything.


For weapons, the number of damage dice you roll is equal to your Proficiency. Note that your Proficiency multiplies the number of dice you roll, but doesn’t affect the modifier. For example, a PC with Proficiency 2 and wielding a weapon with adamage rating of “d8+2” deals damage equal to “2d8+2” on a successful attack.

Successful unarmed attacks inflict [Proficiency]d4 damage.

CRITICAL DAMAGE

When you get a critical success (i.e., you roll matching values on your Duality Dice) on an attack roll, you deal extra damage.

Make the damage roll as usual, but add the maximum possible result of the damage dice to the final total. For instance, if an attack would normally deal 2d8+1 damage, a critical success would deal 2d8+1+16.

DAMAGE TYPES

There are two damage types: physical damage (phy) and magic damage (mag). Unless stated otherwise, mundane weapons and unarmed attacks deal physical damage, and spells deal magic damage.

RESISTANCE, IMMUNITY, AND DIRECT DAMAGE

If a target has resistance to a damage type, then they reduce incoming damage of that type by half before comparing it to their Hit Point Thresholds. If the target has additional ways of reducing incoming damage, such as marking Armor Slots, they apply the resistance effect first. The effects of multiple resistances to the same damage type do not stack.

If a target has immunity to a damage type, they ignore incoming damage of that type.

If an attack deals both physical and magic damage, a character can only benefit from resistance or immunity if they are resistant or immune to both damage types.

Direct damage is damage that can’t be reduced by marking Armor Slots.

MULTI-TARGET ATTACK ROLLS

If a spell or ability allows you to target multiple adversaries, make one attack roll and one damage roll, then apply the results to each target individually.

MULTIPLE DAMAGE SOURCES

Damage dealt simultaneously from multiple sources is always totaled before it’s compared to its target’s damage thresholds.


For example, if a PC with orc ancestry makes a successful attack against a target in Melee range and decides to spend a Hope to use their “Tusks” feature (which gives them an extra 1d6 damage on a damage roll), they would roll their normal weapon damage and add a d6 to the result, then deal that total damage to the adversary.


MAPS, RANGE, AND MOVEMENT

You can play Daggerheart using “theater of the mind” or maps and miniatures. The conversions below from abstract ranges to physical measurements assume 1 inch of map represents about 5 feet of fictional space.

Daggerheart uses the following ranges to translate fictional positioning into relative distance for the purposes of targeting, movement, and other game mechanics:

Range is measured from the source of an effect, such as the attacker or spellcaster, to the target or object of an effect.

A weapon, spell, ability, item, or other effect’s stated range is a maximum range; unless otherwise noted, it can be used at closer distances.


Optional Rule: Defined Ranges

If your table would rather operate with more precise range rules, you can use a 1-inch grid battle map during combat.

If you do, use the following guidelines for play:


MOVEMENT UNDER PRESSURE

When you’re under pressure or in danger and make an action roll, you can move to a location within Close range as part of that action. If you’re not already making an action roll, or if you want to move farther than your Close range, you need to succeed on an Agility Roll to safely reposition yourself.

An adversary can move within Close range for free as part of an action, or within Very Far range as a separate action.

AREA OF EFFECT

Unless stated otherwise, all the targets of a group effect must be within Very Close range of a single origin point within your effect’s range.

LINE OF SIGHT & COVER

Unless stated otherwise, a ranged attacker must have line of sight to their intended target to make an attack roll. If a partial obstruction lies between the attacker and target, the target has cover. Attacks made through cover are rolled with disadvantage. If the obstruction is total, there is no line of sight.

CONDITIONS

Conditions are effects that grant specific benefits or drawbacks to the target they are attached to.

STANDARD CONDITIONS

Daggerheart has three standard conditions:

HIDDEN

While you’re out of sight from all enemies and they don’t otherwise know your location, you gain the Hidden condition. Any rolls against a Hidden creature have disadvantage. After an adversary moves to where they would see you, you move into their line of sight, or you make an attack, you are no longer Hidden.

RESTRAINED

Restrained characters can’t move, but you can still take actions from their current position.

VULNERABLE

When a creature is Vulnerable, all rolls targeting them have advantage.

Some features can apply special or unique conditions, which work as described in the feature text.

Unless otherwise noted, the same condition can’t be applied more than once to the same target.

TEMPORARY TAGS & SPECIAL CONDITIONS

The temporary tag denotes a condition or effect that the affected creature can clear by making a move against it. When an affected PC makes a move to clear a temporary condition or effect, it normally requires a successful action roll using an appropriate trait. When an affected adversary makes a move to clear a temporary condition or effect, the GM puts the spotlight on the adversary and describes how they do it; this doesn’t require a roll but it does use up that adversary’s spotlight.

Special conditions are only cleared when specific requirements are met, such as completing a certain action or using a particular item. The requirements for clearing these conditions are stated in the text of the effect that applies the condition.

DOWNTIME

Between conflicts, the party can take a rest to recover expended resources and deepen their bonds. During a rest, each PC can make up to two downtime moves.

When the party rests, they must choose between a short rest and a long rest. If a party takes three short rests in a row, their next rest must be a long rest.

If a short rest is interrupted, such as by an adversary's attack, the characters don’t gain its benefits. If a long rest is interrupted, the characters only gain the benefits of a short rest.

A short rest lasts enough time for the party to catch its breath, about an hour in-world. Each player can move domain cards between their loadout and vault for free, then choose twice from the following list of downtime moves (players can choose the same move twice):

At the end of a short rest, any features or effects with a limited number of uses per rest refresh and any features or effects that last until your next rest expire.

A long rest is when the characters make camp and relax or sleep for several in-game hours. Each player can move domain cards between their loadout and vault for free, then choose twice from the following list of downtime moves (players can choose the same move twice):

At the end of a long rest, any features or effects with a limited number of uses per rest or per long rest refresh and any features or effects that last until your next rest or until your next long rest expire.

DOWNTIME CONSEQUENCES

On a short rest, the GM gains 1d4 Fear. On a long rest, they gain Fear equal to 1d4 + the number of PCs, and they can advance a long-term countdown of their choice.

DEATH

When a PC marks their last Hit Point, they must make a death move by choosing one of the following options:

If your character dies, work with the GM before the next session to create a new character at the current level of the rest of the party.

ADDITIONAL RULES

The following rules apply to many aspects of the game.

ROUNDING UP

This game doesn’t use fractions; if you need to round to a whole number, round up unless otherwise specified. When in doubt, resolve any ambiguity in favor of the PCs.

REROLLING DICE

When a feature allows you to reroll a die, you always take the new result unless the feature specifically says otherwise.

INCOMING DAMAGE

Incoming damage means the total damage from a single attack or source, before Armor Slots are marked.

SIMULTANEOUS EFFECTS

If the resolution order of multiple effects is unclear, the person in control of the effects (player or GM) decides what order to resolve them in.

STACKING EFFECTS

Unless stated otherwise, all effects beside conditions and advantage/disadvantage can stack.

ONGOING SPELL EFFECTS

If an effect doesn’t have a listed mechanical expiration, it only ends when decided by the controlling player, the GM, or the demands of the fiction.

SPENDING RESOURCES

Unless an effect states otherwise, you can’t spend Hope or mark Stress multiple times on the same feature to increase or repeat its effects on the same roll.

USING FEATURES AFTER A ROLL

If a feature allows you to affect a roll after the result has been totaled, you can use it after the GM declares whether the roll succeeds or fails, but not after the consequences unfold or another roll is made.

LEVELING UP

Your party levels up whenever the GM decides you’ve reached a narrative milestone (usually about every 3 sessions). All party members level up at the same time.

Daggerheart has 10 PC levels divided into 4 tiers:


→ Tier 1 encompasses level 1 only.

→ Tier 2 encompasses levels 2–4.

→ Tier 3 encompasses levels 5–7.

→ Tier 4 encompasses levels 8–10.


Your tier affects your damage thresholds, tier achievements, and access to advancements.

STEP ONE: TIER ACHIEVEMENTS

Take any applicable tier achievements

STEP TWO: ADVANCEMENTS

Choose any two advancements with at least one unmarked slot from your tier or below. Options with multiple slots can be chosen more than once. When you choose an advancement, mark one of its slots.

STEP THREE: DAMAGE THRESHOLDS

Increase all damage thresholds by 1.

STEP FOUR: DOMAIN CARDS

Acquire a new domain card at your level or lower from one of your class’s domains and add it to your loadout or vault. If your loadout is already full, you can’t add the new card to it until you move another into your vault. You can also exchange one domain card you’ve previously acquired for a different domain card of the same level or lower.

MULTICLASSING

Starting at level 5, you can choose multiclassing as an option when leveling up. When you multiclass, you choose an additional class, gain access to one of its domains, and acquire its class feature. Take the appropriate multiclass module and add it to the right side of your character sheet, then choose a foundation card from one of its subclasses. If your foundation cards specify different Spellcast traits, you can choose which one to apply when making a Spellcast roll.

Whenever you have the option to acquire a new domain card, you can choose from cards at or below half your current level (rounded up) from the domain you chose when you selected the multiclass advancement.

EQUIPMENT

Your equipped weapons and armor are the ones listed in the “Active Weapons” and “Active Armor” sections of your character sheet. Your character can only attack with weapons, benefit from armor, and gain features from items they have equipped. You can’t equip weapons or armor with a higher tier than you.

PCs can carry up to two additional weapons in the “Inventory Weapon” areas of the character sheet.

You can swap an Inventory Weapon with an Active Weapon at no cost during a rest or moment of calm; otherwise, you must mark a Stress to do so.

Your character can only have one Active Armor at a time.

They can’t equip armor while in danger or under pressure; otherwise, they can equip or unequip armor without cost.

Each armor has its own Armor Slots; if your character unequips their armor, track how many of its Armor Slots are marked. You can't carry armor in your inventory. When your character equips or unequips armor, recalculate your damage thresholds.

WEAPONS

All weapons have a tier, trait, range, damage die, damage type, and burden. Some weapons also have a feature.

CATEGORY

A weapon’s category specifies whether it is a Primary or Secondary weapon. Your character can only equip up to one weapon of each category at a time.

TRAIT

A weapon’s trait specifies which trait to use when making an attack roll with it.

RANGE

A weapon’s range specifies the maximum distance between the attacker and their target when attacking with it.

DAMAGE

A weapon’s damage indicates the size of the damage dice you roll on a successful attack with it; you roll a number of dice equal to your Proficiency. If the damage includes a flat modifier, this number is added to the total damage rolled, but is not altered or affected by Proficiency.

DAMAGE TYPE

A weapon’s damage type indicates whether it deals physical or magic damage. Weapons that deal magic damage can only be wielded by characters with a Spellcast trait.

BURDEN

A weapon’s burden indicates how many hands it occupies when equipped. Your character’s maximum burden is 2 hands.

FEATURE

A weapon’s feature is a special rule that stays in effect while the weapon is equipped.

You can throw an equipped weapon at a target within Very Close range, making the attack roll with Finesse. On a success, deal damage as usual for that weapon. Once thrown, the weapon is no longer considered equipped. Until you retrieve and re-equip it, you can’t attack with it or benefit from its features.

Combat Wheelchair

By Mark Thompson

The combat wheelchair is a ruleset designed to help you play a wheelchair user in Daggerheart. This section provides mechanics and narrative guidance for you to work from, but feel free to adapt the flavor text to best suit your character. Have fun with your character’s wheelchair design, and make it as unique or tailored to them as you please.

ACTION AND MOVEMENT

When describing how your character moves, you can use descriptions such as the following:

CONSEQUENCES

Here are some ways you might describe complications you encounter when your character uses their wheelchair:

GMs should avoid breaking a character's wheelchair or otherwise removing it from play as a consequence, unless everyone at the table, especially the wheelchair user’s player, gives their approval.

EVASION

Your character is assumed to be skilled in moving their wheelchair and navigating numerous situations in it. As a result, the only wheelchair that gives a penalty to a PC's Evasion is the Heavy Frame model.

BURDEN

All wheelchairs can be maneuvered using one or two hands outside of combat. However, when being used as a weapon, the chair is restricted to requiring one or two hands to perform attacks, depending on the model you’ve chosen. If you’re playing a character who has limited to no mobility in their arms, their wheelchair can be attuned to them by magical means. For example, your character might use a psychic link to guide the chair around like a pseudo-electric wheelchair. All the rules presented here can be tailored and adapted to any character's needs.

CHOOSING YOUR MODEL

All combat wheelchairs are equipped as Primary Weapons.

There are three models of wheelchair available: light, heavy, and arcane. You’re encouraged to consider the type of character you’re playing and the class they belong to, then choose the model that best matches that character concept.

ARMOR

Every armor has a name, base damage thresholds, and a base Armor Score. Some armor also has a feature.

While unarmored, your character’s base Armor Score is 0, their Major threshold is equal to their level, and their Severe threshold is equal to twice their level.

REDUCING INCOMING DAMAGE

When you take damage, you can mark one Armor Slot to reduce the number of Hit Points you would mark by one. If your character has an Armor Score of 0, you can’t mark Armor Slots. If an effect temporarily increases your Armor Score,

it increases your available Armor Slots by the same amount; when the effect ends, so does the availability of these Armor Slots.

LOOT

Loot comprises any consumables or reusable items the party acquires.

Items can be used until sold, discarded, or lost.

To generate a random item, choose a rarity, roll the designated dice, and match the total to the item in the table:

@UUID[RollTable.KKqUrMMXPpm7uhYT]{Loot}

Consumables

Consumables are loot that can only be used once. You can hold up to five of each consumable at a time. Using a consumable doesn’t require a roll unless required by the GM or the demands of the fiction.

To generate a random consumable, choose a rarity, roll the designated dice, and match the total to the item in the table:

@UUID[RollTable.wZXyi343PSVVwWB3]{Consumables}

GOLD

Gold is an abstract measurement of how much wealth a character has, and is measured in handfuls, bags, and chests, with 10 handfuls to 1 bag, and 10 bags to 1 chest. When you have marked all of the slots in a category and you gain another gold reward in that category, mark a slot in the following category and clear all the slots in the current one.

For example, if you have 9 handfuls and gain another, you instead mark 1 bag and erase all handfuls. If you have 9 bags and gain another, you mark 1 chest and erase all bags.

You can’t have more than 1 chest, so if all your Gold slots are marked, you’ll need to spend some of your gold or store it somewhere else before you can acquire more.


Optional Rule: Gold Coins

If your group wants to track gold with more granularity, you can add coins as your lowest denomination. Following the established pattern, 10 coins equal 1 handful.


" }, "video": { "controls": true, From 774b6dbdcca072d10af66a87b045dfa1b4030a5d Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Wed, 20 Aug 2025 02:56:00 +0200 Subject: [PATCH 02/66] Fixed so character dice rolls await the DiceSoNice animation before consuming resources (#1024) --- module/dice/dhRoll.mjs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 6d691c20..ac340c64 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -84,7 +84,7 @@ export default class DHRoll extends Roll { static async toMessage(roll, config) { const cls = getDocumentClass('ChatMessage'), - msg = { + msgData = { type: this.messageType, user: game.user.id, title: roll.title, @@ -94,8 +94,16 @@ export default class DHRoll extends Roll { rolls: [roll] }; config.selectedRollMode ??= game.settings.get('core', 'rollMode'); - if (roll._evaluated) return await cls.create(msg, { rollMode: config.selectedRollMode }); - return msg; + + if (roll._evaluated) { + const message = await cls.create(msgData, { rollMode: config.selectedRollMode }); + + if (game.modules.get('dice-so-nice')?.active) { + await game.dice3d.waitFor3DAnimationByMessageID(message.id); + } + + return message; + } else return msgData; } /** @inheritDoc */ From af250d7a61ee953d384a70b476d9a252fa222bdf Mon Sep 17 00:00:00 2001 From: joaquinpereyra98 <24190917+joaquinpereyra98@users.noreply.github.com> Date: Tue, 19 Aug 2025 22:07:35 -0300 Subject: [PATCH 03/66] [BUG] - Deleting items in the scrollable window causes scrollbar to reset to top (#1025) Fixes #977 Co-authored-by: Joaquin Pereyra --- .../applications/sheets/actors/adversary.mjs | 19 ++++++++--- .../applications/sheets/actors/character.mjs | 6 ++++ .../applications/sheets/actors/companion.mjs | 5 ++- .../sheets/actors/environment.mjs | 8 +++-- .../less/sheets/actors/adversary/effects.less | 17 ++++++++++ .../less/sheets/actors/companion/effects.less | 17 ++++++++++ .../environment/potentialAdversaries.less | 17 ++++++++++ styles/less/sheets/index.less | 3 ++ templates/sheets/actors/adversary/effects.hbs | 34 ++++++++++--------- templates/sheets/actors/companion/effects.hbs | 34 ++++++++++--------- .../environment/potentialAdversaries.hbs | 2 +- 11 files changed, 122 insertions(+), 40 deletions(-) create mode 100644 styles/less/sheets/actors/adversary/effects.less create mode 100644 styles/less/sheets/actors/companion/effects.less create mode 100644 styles/less/sheets/actors/environment/potentialAdversaries.less diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index a5d9d8a6..b47cb85a 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -25,11 +25,22 @@ export default class AdversarySheet extends DHBaseActorSheet { }; static PARTS = { - sidebar: { template: 'systems/daggerheart/templates/sheets/actors/adversary/sidebar.hbs' }, + sidebar: { + template: 'systems/daggerheart/templates/sheets/actors/adversary/sidebar.hbs', + scrollable: ['.shortcut-items-section'] + }, header: { template: 'systems/daggerheart/templates/sheets/actors/adversary/header.hbs' }, - features: { template: 'systems/daggerheart/templates/sheets/actors/adversary/features.hbs' }, - notes: { template: 'systems/daggerheart/templates/sheets/actors/adversary/notes.hbs' }, - effects: { template: 'systems/daggerheart/templates/sheets/actors/adversary/effects.hbs' } + features: { + template: 'systems/daggerheart/templates/sheets/actors/adversary/features.hbs', + scrollable: ['.feature-section'] + }, + notes: { + template: 'systems/daggerheart/templates/sheets/actors/adversary/notes.hbs' + }, + effects: { + template: 'systems/daggerheart/templates/sheets/actors/adversary/effects.hbs', + scrollable: ['.effects-sections'] + } }; /** @inheritdoc */ diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 92f6239b..0328f0dd 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -78,6 +78,7 @@ export default class CharacterSheet extends DHBaseActorSheet { static PARTS = { sidebar: { id: 'sidebar', + scrollable: ['.shortcut-items-section'], template: 'systems/daggerheart/templates/sheets/actors/character/sidebar.hbs' }, header: { @@ -86,22 +87,27 @@ export default class CharacterSheet extends DHBaseActorSheet { }, features: { id: 'features', + scrollable: ['.features-sections'], template: 'systems/daggerheart/templates/sheets/actors/character/features.hbs' }, loadout: { id: 'loadout', + scrollable: ['.items-section'], template: 'systems/daggerheart/templates/sheets/actors/character/loadout.hbs' }, inventory: { id: 'inventory', + scrollable: ['.items-section'], template: 'systems/daggerheart/templates/sheets/actors/character/inventory.hbs' }, biography: { id: 'biography', + scrollable: ['.items-section'], template: 'systems/daggerheart/templates/sheets/actors/character/biography.hbs' }, effects: { id: 'effects', + scrollable: ['.effects-sections'], template: 'systems/daggerheart/templates/sheets/actors/character/effects.hbs' } }; diff --git a/module/applications/sheets/actors/companion.mjs b/module/applications/sheets/actors/companion.mjs index 1105131d..2b82f50a 100644 --- a/module/applications/sheets/actors/companion.mjs +++ b/module/applications/sheets/actors/companion.mjs @@ -15,7 +15,10 @@ export default class DhCompanionSheet extends DHBaseActorSheet { static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/actors/companion/header.hbs' }, details: { template: 'systems/daggerheart/templates/sheets/actors/companion/details.hbs' }, - effects: { template: 'systems/daggerheart/templates/sheets/actors/companion/effects.hbs' } + effects: { + template: 'systems/daggerheart/templates/sheets/actors/companion/effects.hbs', + scrollable: ['.effects-sections'] + } }; /* -------------------------------------------- */ diff --git a/module/applications/sheets/actors/environment.mjs b/module/applications/sheets/actors/environment.mjs index 9fd003c6..0389d2c9 100644 --- a/module/applications/sheets/actors/environment.mjs +++ b/module/applications/sheets/actors/environment.mjs @@ -27,9 +27,13 @@ export default class DhpEnvironment extends DHBaseActorSheet { /**@override */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' }, - features: { template: 'systems/daggerheart/templates/sheets/actors/environment/features.hbs' }, + features: { + template: 'systems/daggerheart/templates/sheets/actors/environment/features.hbs', + scrollable: ['feature-section'] + }, potentialAdversaries: { - template: 'systems/daggerheart/templates/sheets/actors/environment/potentialAdversaries.hbs' + template: 'systems/daggerheart/templates/sheets/actors/environment/potentialAdversaries.hbs', + scrollable: ['items-sections'] }, notes: { template: 'systems/daggerheart/templates/sheets/actors/environment/notes.hbs' } }; diff --git a/styles/less/sheets/actors/adversary/effects.less b/styles/less/sheets/actors/adversary/effects.less new file mode 100644 index 00000000..4afe2454 --- /dev/null +++ b/styles/less/sheets/actors/adversary/effects.less @@ -0,0 +1,17 @@ +@import '../../../utils/colors.less'; + +.application.sheet.daggerheart.actor.dh-style.adversary { + .tab.effects { + .effects-sections { + display: flex; + flex-direction: column; + gap: 10px; + overflow-y: auto; + mask-image: linear-gradient(0deg, transparent 0%, black 5%); + padding-bottom: 20px; + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; + } + } +} diff --git a/styles/less/sheets/actors/companion/effects.less b/styles/less/sheets/actors/companion/effects.less new file mode 100644 index 00000000..12e1d847 --- /dev/null +++ b/styles/less/sheets/actors/companion/effects.less @@ -0,0 +1,17 @@ +@import '../../../utils/colors.less'; + +.application.sheet.daggerheart.actor.dh-style.companion { + .tab.effects { + .effects-sections { + display: flex; + flex-direction: column; + gap: 10px; + overflow-y: auto; + mask-image: linear-gradient(0deg, transparent 0%, black 5%); + padding-bottom: 20px; + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; + } + } +} diff --git a/styles/less/sheets/actors/environment/potentialAdversaries.less b/styles/less/sheets/actors/environment/potentialAdversaries.less new file mode 100644 index 00000000..6fd7af65 --- /dev/null +++ b/styles/less/sheets/actors/environment/potentialAdversaries.less @@ -0,0 +1,17 @@ +@import '../../../utils/colors.less'; + +.application.sheet.daggerheart.actor.dh-style.environment { + .tab.potentialAdversaries { + .items-section { + display: flex; + flex-direction: column; + gap: 10px; + overflow-y: auto; + mask-image: linear-gradient(0deg, transparent 0%, black 5%); + padding-bottom: 20px; + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; + } + } +} \ No newline at end of file diff --git a/styles/less/sheets/index.less b/styles/less/sheets/index.less index 1ffb92fe..991837c0 100644 --- a/styles/less/sheets/index.less +++ b/styles/less/sheets/index.less @@ -4,6 +4,7 @@ @import './actors/adversary/header.less'; @import './actors/adversary/sheet.less'; @import './actors/adversary/sidebar.less'; +@import './actors/adversary/effects.less'; @import './actors/character/biography.less'; @import './actors/character/effects.less'; @@ -17,9 +18,11 @@ @import './actors/companion/details.less'; @import './actors/companion/header.less'; @import './actors/companion/sheet.less'; +@import './actors/companion/effects.less'; @import './actors/environment/actions.less'; @import './actors/environment/header.less'; +@import './actors/environment/potentialAdversaries.less'; @import './actors/environment/sheet.less'; @import './items/beastform.less'; diff --git a/templates/sheets/actors/adversary/effects.hbs b/templates/sheets/actors/adversary/effects.hbs index 325610e6..cefb6e57 100644 --- a/templates/sheets/actors/adversary/effects.hbs +++ b/templates/sheets/actors/adversary/effects.hbs @@ -1,20 +1,22 @@
- {{> 'daggerheart.inventory-items' - title='DAGGERHEART.GENERAL.activeEffects' - type='effect' - isGlassy=true - collection=effects.actives - canCreate=true - hideResources=true - }} +
+ {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.activeEffects' + type='effect' + isGlassy=true + collection=effects.actives + canCreate=true + hideResources=true + }} - {{> 'daggerheart.inventory-items' - title='DAGGERHEART.GENERAL.inactiveEffects' - type='effect' - isGlassy=true - collection=effects.inactives - canCreate=true - hideResources=true - }} + {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.inactiveEffects' + type='effect' + isGlassy=true + collection=effects.inactives + canCreate=true + hideResources=true + }} +
\ No newline at end of file diff --git a/templates/sheets/actors/companion/effects.hbs b/templates/sheets/actors/companion/effects.hbs index 325610e6..cefb6e57 100644 --- a/templates/sheets/actors/companion/effects.hbs +++ b/templates/sheets/actors/companion/effects.hbs @@ -1,20 +1,22 @@
- {{> 'daggerheart.inventory-items' - title='DAGGERHEART.GENERAL.activeEffects' - type='effect' - isGlassy=true - collection=effects.actives - canCreate=true - hideResources=true - }} +
+ {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.activeEffects' + type='effect' + isGlassy=true + collection=effects.actives + canCreate=true + hideResources=true + }} - {{> 'daggerheart.inventory-items' - title='DAGGERHEART.GENERAL.inactiveEffects' - type='effect' - isGlassy=true - collection=effects.inactives - canCreate=true - hideResources=true - }} + {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.inactiveEffects' + type='effect' + isGlassy=true + collection=effects.inactives + canCreate=true + hideResources=true + }} +
\ No newline at end of file diff --git a/templates/sheets/actors/environment/potentialAdversaries.hbs b/templates/sheets/actors/environment/potentialAdversaries.hbs index cc246312..5f4d039e 100644 --- a/templates/sheets/actors/environment/potentialAdversaries.hbs +++ b/templates/sheets/actors/environment/potentialAdversaries.hbs @@ -3,7 +3,7 @@ data-tab='{{tabs.potentialAdversaries.id}}' data-group='{{tabs.potentialAdversaries.group}}' > -
+
{{#each document.system.potentialAdversaries as |category categoryId|}} {{> 'daggerheart.inventory-items' title=category.label From 218f180fa013fdf0cd0d4d86d068af5f43c15269 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Wed, 20 Aug 2025 10:13:51 +0200 Subject: [PATCH 04/66] [Fix] DiceSoNice DamageRolls (#1026) * Fixed so the 3d damage dice can be seen by everyone when not whispered * Fixed typo --- module/dice/damageRoll.mjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 427b6273..aa9e1d94 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -37,7 +37,13 @@ export default class DamageRoll extends DHRoll { Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll)) ), diceRoll = Roll.fromTerms([pool]); - await game.dice3d.showForRoll(diceRoll, game.user, true, chatMessage.whisper, chatMessage.blind); + await game.dice3d.showForRoll( + diceRoll, + game.user, + true, + chatMessage.whisper?.length > 0 ? chatMessage.whisper : null, + chatMessage.blind + ); } await super.buildPost(roll, config, message); if (config.source?.message) { From 7a6bbe34888dd4687f797565bca5001f318c3d59 Mon Sep 17 00:00:00 2001 From: Murilo Brito <91566541+moliloo@users.noreply.github.com> Date: Wed, 20 Aug 2025 05:14:46 -0300 Subject: [PATCH 05/66] add light theme for chat messages (#1016) --- styles/less/global/chat.less | 15 ++++- styles/less/global/inventory-item.less | 2 +- styles/less/ui/chat/ability-use.less | 40 ++++++++++-- styles/less/ui/chat/action.less | 31 ++++++++- styles/less/ui/chat/chat.less | 50 +++++++++++++- styles/less/ui/chat/downtime.less | 29 +++++++++ styles/less/ui/chat/sheet.less | 90 +++++++++++++++++++++----- styles/less/utils/colors.less | 1 + 8 files changed, 231 insertions(+), 27 deletions(-) diff --git a/styles/less/global/chat.less b/styles/less/global/chat.less index 37ec993d..bf29a05c 100644 --- a/styles/less/global/chat.less +++ b/styles/less/global/chat.less @@ -7,6 +7,15 @@ #chat-notifications .chat-log { .chat-message { background-image: url('../assets/parchments/dh-parchment-light.png'); + + .message-header .message-header-metadata .message-metadata, + .message-header .message-header-main .message-sub-header-container { + color: @dark; + } + + .message-header .message-header-main .message-sub-header-container h4 { + color: @dark-blue; + } } } } @@ -36,7 +45,7 @@ .message-metadata { font-family: @font-body; - color: light-dark(@dark, @beige); + color: @beige; } } @@ -59,14 +68,14 @@ display: flex; flex-direction: column; justify-content: space-between; - color: light-dark(@dark, @beige); + color: @beige; h4 { font-size: 16px; font-weight: bold; margin-bottom: 0; font-family: @font-subtitle; - color: light-dark(@dark-blue, @golden); + color: @golden; } } } diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index 6c58d31a..d63c658e 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -135,7 +135,7 @@ .label { gap: 4px; - color: @beige-80; + color: light-dark(@dark-80, @beige-80); } } } diff --git a/styles/less/ui/chat/ability-use.less b/styles/less/ui/chat/ability-use.less index 7993d29d..58897697 100644 --- a/styles/less/ui/chat/ability-use.less +++ b/styles/less/ui/chat/ability-use.less @@ -2,6 +2,38 @@ @import '../../utils/fonts.less'; @import '../../utils/spacing.less'; +.theme-light { + .daggerheart.chat.domain-card { + .domain-card-move .domain-card-header { + border-bottom: 1px solid @dark-blue; + + &:hover { + background: @dark-blue-10; + } + + .domain-label { + .title { + color: @dark-blue; + } + + .tags .tag { + background: @dark-15; + border: 1px solid @dark; + color: @dark; + } + } + + .fa-chevron-down { + color: @dark-blue; + } + } + + .description { + color: @dark; + } + } +} + .daggerheart.chat { &.domain-card { display: flex; @@ -41,7 +73,7 @@ border-bottom: 1px solid @golden; &:hover { - background: light-dark(@dark-blue-10, @golden-10); + background: @golden-10; cursor: pointer; transition: all 0.3s ease; } @@ -73,9 +105,9 @@ padding: 3px 5px; font-size: 12px; - background: light-dark(@dark-15, @beige-15); - border: 1px solid light-dark(@dark, @beige); - color: light-dark(@dark, @beige); + background: @beige-15; + border: 1px solid @beige; + color: @beige; border-radius: 3px; } } diff --git a/styles/less/ui/chat/action.less b/styles/less/ui/chat/action.less index 0200c9dc..82cc3210 100644 --- a/styles/less/ui/chat/action.less +++ b/styles/less/ui/chat/action.less @@ -2,6 +2,35 @@ @import '../../utils/fonts.less'; @import '../../utils/spacing.less'; +.theme-light { + .daggerheart.chat.action { + .action-move .action-section { + border-bottom: 1px solid @dark-blue; + + &:hover { + background: @dark-blue-10; + } + + .action-header { + .title { + color: @dark-blue; + } + .label { + color: @dark; + } + } + + .fa-chevron-down { + color: @dark-blue; + } + } + + .description { + color: @dark; + } + } +} + .daggerheart.chat { &.action { display: flex; @@ -34,7 +63,7 @@ border-bottom: 1px solid @golden; &:hover { - background: light-dark(@dark-blue-10, @golden-10); + background: @golden-10; cursor: pointer; transition: all 0.3s ease; } diff --git a/styles/less/ui/chat/chat.less b/styles/less/ui/chat/chat.less index 81af3d23..c6ed95ca 100644 --- a/styles/less/ui/chat/chat.less +++ b/styles/less/ui/chat/chat.less @@ -2,6 +2,49 @@ @import '../../utils/fonts.less'; @import '../../utils/spacing.less'; +.theme-light { + .daggerheart, + #chat-notifications { + --text-color: @dark-blue; + --bg-color: @dark-blue-40; + + .message-content .chat-roll { + .roll-part-header { + span, + span:before, + span:after { + color: @dark-blue; + } + &:before { + background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, @dark-blue 100%); + color: @dark-blue; + } + + &:after { + background: linear-gradient(90deg, @dark-blue 0%, rgba(0, 0, 0, 0) 100%); + color: @dark-blue; + } + } + .roll-section { + .roll-part-content { + .roll-result-value { + color: @dark-blue; + } + + .dice-tooltip .wrapper .roll-die { + color: @beige; + } + } + } + } + + .chat-message .roll-formula { + background: @dark-15; + color: @dark; + } + } +} + .daggerheart.chat { &.resource-roll { .reroll-message { @@ -27,8 +70,8 @@ .daggerheart, #chat-notifications { .chat-message { - --text-color: light-dark(@dark-blue, @golden); - --bg-color: light-dark(@dark-blue-40, @golden-40); + --text-color: @golden; + --bg-color: @golden-40; [data-use-perm='false'] { pointer-events: none; @@ -85,7 +128,7 @@ display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; - color: light-dark(@dark, @beige); + color: @beige; margin: 10px 0; span { @@ -427,6 +470,7 @@ gap: 5px; margin-top: 8px; button { + height: 32px; flex: 1; } } diff --git a/styles/less/ui/chat/downtime.less b/styles/less/ui/chat/downtime.less index 7c28c835..8b898c43 100644 --- a/styles/less/ui/chat/downtime.less +++ b/styles/less/ui/chat/downtime.less @@ -2,6 +2,35 @@ @import '../../utils/fonts.less'; @import '../../utils/spacing.less'; +.theme-light { + .daggerheart.chat.downtime { + .downtime-moves-list .downtime-move { + &:hover { + background: @dark-blue-10; + } + + .downtime-label { + border-bottom: 1px solid @dark-blue; + + .header-label .title { + color: @dark-blue; + } + .header-label .label { + color: @dark; + } + } + + .fa-chevron-down { + color: @dark-blue; + } + } + + .description { + color: @dark; + } + } +} + .daggerheart.chat { &.downtime { display: flex; diff --git a/styles/less/ui/chat/sheet.less b/styles/less/ui/chat/sheet.less index da66c12f..59fa39dc 100644 --- a/styles/less/ui/chat/sheet.less +++ b/styles/less/ui/chat/sheet.less @@ -1,6 +1,66 @@ @import '../../utils/colors.less'; @import '../../utils/fonts.less'; +.theme-light { + .chat-message .message-content { + color: @dark; + + blockquote { + border-left: 5px solid @dark-blue-40; + } + + a[href] { + color: @dark-blue; + } + + a[href]:hover, + a[href].active { + font-weight: bold; + text-shadow: 0 0 8px @dark-blue; + } + + button { + background: transparent; + border: 1px solid @dark-blue; + color: @dark-blue; + + &:hover { + background: @light-black; + color: @dark-blue; + } + + &:disabled { + background: transparent; + color: @dark-blue; + + &:hover { + background: transparent; + color: @dark-blue; + } + } + + &.reverted { + background: @dark-blue-10; + color: @dark-blue; + border: 1px solid @dark; + &:hover { + background: transparent; + color: @dark-blue; + } + img { + border-radius: 3px; + } + } + } + + .roll-buttons button { + height: 40px; + font-family: @font-body; + font-weight: bold; + } + } +} + .chat-message.dh-chat-message { .message-content { padding: 0; @@ -17,7 +77,7 @@ .message-content { padding: 0 8px; font-family: @font-body; - color: light-dark(@dark, @beige); + color: @beige; blockquote { border-left: 5px solid light-dark(@dark-blue-40, @golden-40); @@ -34,15 +94,15 @@ } button { - background: light-dark(transparent, @golden); - border: 1px solid light-dark(@dark-blue, @dark-blue); - color: light-dark(@dark-blue, @dark-blue); + background: @golden; + border: 1px solid @dark-blue; + color: @dark-blue; outline: none; box-shadow: none; &:hover { - background: light-dark(@light-black, @dark-blue); - color: light-dark(@dark-blue, @golden); + background: @dark-blue; + color: @golden; } &.glow { @@ -50,24 +110,24 @@ } &:disabled { - background: light-dark(transparent, @golden); - color: light-dark(@dark-blue, @dark-blue); + background: @golden; + color: @dark-blue; opacity: 0.6; cursor: not-allowed; &:hover { - background: light-dark(transparent, @golden); - color: light-dark(@dark-blue, @dark-blue); + background: @golden; + color: @dark-blue; } } &.reverted { - background: light-dark(@dark-blue-10, @golden-10); - color: light-dark(@dark-blue, @golden); - border: 1px solid light-dark(@dark, transparent); + background: @golden-10; + color: @golden; + border: 1px solid transparent; &:hover { - background: light-dark(transparent, @golden); - color: light-dark(@dark-blue, @dark-blue); + background: @golden; + color: @dark-blue; } img { border-radius: 3px; diff --git a/styles/less/utils/colors.less b/styles/less/utils/colors.less index dcc7cc5b..9ce201a3 100755 --- a/styles/less/utils/colors.less +++ b/styles/less/utils/colors.less @@ -53,6 +53,7 @@ @dark: #222; @dark-15: #22222215; @dark-40: #22222240; +@dark-80: #22222280; @dark-filter: brightness(0) saturate(100%); @deep-black: #0e0d15; From 60b55619e1035e5a10509e22055af079a76ac946 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Fri, 22 Aug 2025 01:05:09 +1000 Subject: [PATCH 06/66] [PR] [Feature] 652 Allow override range measurement settings (#1030) * Look for rangeMeasurementSettingsOverride on the scene to switch off DH global range measurement settings. * Part progress on adding config tab to scene config * Hard coded template; no value applied/saved * Flag fix * Use the flags setting * Clean up * Remove import * Better initialisation of PARTS and TABS * Fix localisation --------- Co-authored-by: Chris Ryan Co-authored-by: WBHarry --- daggerheart.mjs | 5 ++++ lang/en.json | 11 +++++++ module/applications/_module.mjs | 1 + module/applications/scene/_module.mjs | 1 + .../scene/sceneConfigSettings.mjs | 25 ++++++++++++++++ module/canvas/placeables/measuredTemplate.mjs | 30 +++++++++++++------ module/canvas/placeables/ruler.mjs | 6 ++-- module/canvas/placeables/tokenRuler.mjs | 6 ++-- module/systemRegistration/handlebars.mjs | 3 ++ templates/scene/dh-config.hbs | 9 ++++++ 10 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 module/applications/scene/_module.mjs create mode 100644 module/applications/scene/sceneConfigSettings.mjs create mode 100644 templates/scene/dh-config.hbs diff --git a/daggerheart.mjs b/daggerheart.mjs index 795764cc..9ed5feb1 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -145,6 +145,11 @@ Hooks.once('init', () => { // Make Compendium Dialog resizable foundry.applications.sidebar.apps.Compendium.DEFAULT_OPTIONS.window.resizable = true; + DocumentSheetConfig.registerSheet(foundry.documents.Scene, SYSTEM.id, applications.scene.DhSceneConfigSettings, { + makeDefault: true, + label: 'Daggerheart' + }); + settingsRegistration.registerDHSettings(); RegisterHandlebarsHelpers.registerHelpers(); diff --git a/lang/en.json b/lang/en.json index a7a615f5..d8a4b08f 100755 --- a/lang/en.json +++ b/lang/en.json @@ -26,6 +26,14 @@ "CONTROLS": { "inFront": "In Front" }, + "SCENE": { + "TABS": { + "SHEET": { + "dh": "Daggerheart" + } + } + }, + "DAGGERHEART": { "ACTIONS": { "TYPES": { @@ -2283,6 +2291,9 @@ "ResetSettings": { "resetConfirmationTitle": "Reset Settings", "resetConfirmationText": "Are you sure you want to reset the {settings}?" + }, + "Scene": { + "rangeMeasurementOverride": "Override Global Range Measurement Settings" } }, "UI": { diff --git a/module/applications/_module.mjs b/module/applications/_module.mjs index d4ceb229..3dd0c78f 100644 --- a/module/applications/_module.mjs +++ b/module/applications/_module.mjs @@ -2,6 +2,7 @@ export * as characterCreation from './characterCreation/_module.mjs'; export * as dialogs from './dialogs/_module.mjs'; export * as hud from './hud/_module.mjs'; export * as levelup from './levelup/_module.mjs'; +export * as scene from './scene/_module.mjs'; export * as settings from './settings/_module.mjs'; export * as sheets from './sheets/_module.mjs'; export * as sheetConfigs from './sheets-configs/_module.mjs'; diff --git a/module/applications/scene/_module.mjs b/module/applications/scene/_module.mjs new file mode 100644 index 00000000..6dc59081 --- /dev/null +++ b/module/applications/scene/_module.mjs @@ -0,0 +1 @@ +export { default as DhSceneConfigSettings } from './sceneConfigSettings.mjs'; \ No newline at end of file diff --git a/module/applications/scene/sceneConfigSettings.mjs b/module/applications/scene/sceneConfigSettings.mjs new file mode 100644 index 00000000..2ad7421a --- /dev/null +++ b/module/applications/scene/sceneConfigSettings.mjs @@ -0,0 +1,25 @@ +export default class DhSceneConfigSettings extends foundry.applications.sheets.SceneConfig { + constructor(options, ...args) { + super(options, ...args); + } + + static buildParts() { + const { footer, ...parts } = super.PARTS; + const tmpParts = { + ...parts, + dh: { template: "systems/daggerheart/templates/scene/dh-config.hbs" }, + footer + } + return tmpParts; + } + + static PARTS = DhSceneConfigSettings.buildParts(); + + static buildTabs() { + super.TABS.sheet.tabs.push({ id: "dh", icon: "fa-solid" }); + return super.TABS; + } + + static TABS = DhSceneConfigSettings.buildTabs(); + +} \ No newline at end of file diff --git a/module/canvas/placeables/measuredTemplate.mjs b/module/canvas/placeables/measuredTemplate.mjs index c9950650..49142685 100644 --- a/module/canvas/placeables/measuredTemplate.mjs +++ b/module/canvas/placeables/measuredTemplate.mjs @@ -10,29 +10,41 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur const splitRulerText = this.ruler.text.split(' '); if (splitRulerText.length > 0) { const rulerValue = Number(splitRulerText[0]); - const vagueLabel = this.constructor.getDistanceLabel(rulerValue, rangeMeasurementSettings); - this.ruler.text = vagueLabel; + const result = this.constructor.getRangeLabels(rulerValue, rangeMeasurementSettings); + this.ruler.text = result.distance + result.units ? (' ' + result.units) : ''; } } } - static getDistanceLabel(distance, settings) { + static getRangeLabels(distance, settings) { + let result = { distance: '', units: null } + const rangeMeasurementOverride = canvas.scene.flags.daggerheart?.rangeMeasurementOverride; + + if (rangeMeasurementOverride === true) { + result.distance = distance; + result.units = canvas.scene?.grid?.units; + return result + } if (distance <= settings.melee) { - return game.i18n.localize('DAGGERHEART.CONFIG.Range.melee.name'); + result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.melee.name'); + return result; } if (distance <= settings.veryClose) { - return game.i18n.localize('DAGGERHEART.CONFIG.Range.veryClose.name'); + result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.veryClose.name'); + return result; } if (distance <= settings.close) { - return game.i18n.localize('DAGGERHEART.CONFIG.Range.close.name'); + result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.close.name'); + return result; } if (distance <= settings.far) { - return game.i18n.localize('DAGGERHEART.CONFIG.Range.far.name'); + result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.far.name'); + return result; } if (distance > settings.far) { - return game.i18n.localize('DAGGERHEART.CONFIG.Range.veryFar.name'); + result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.veryFar.name'); } - return ''; + return result; } } diff --git a/module/canvas/placeables/ruler.mjs b/module/canvas/placeables/ruler.mjs index 6585a1cd..6e2f220d 100644 --- a/module/canvas/placeables/ruler.mjs +++ b/module/canvas/placeables/ruler.mjs @@ -8,9 +8,9 @@ export default class DhpRuler extends foundry.canvas.interaction.Ruler { const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement; if (range.enabled) { - const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range); - context.cost = { total: distance, units: null }; - context.distance = { total: distance, units: null }; + const result = DhMeasuredTemplate.getRangeLabels(waypoint.measurement.distance.toNearest(0.01), range); + context.cost = { total: result.distance, units: result.units }; + context.distance = { total: result.distance, units: result.units }; } return context; diff --git a/module/canvas/placeables/tokenRuler.mjs b/module/canvas/placeables/tokenRuler.mjs index ff8fc0d5..056953f8 100644 --- a/module/canvas/placeables/tokenRuler.mjs +++ b/module/canvas/placeables/tokenRuler.mjs @@ -8,9 +8,9 @@ export default class DhpTokenRuler extends foundry.canvas.placeables.tokens.Toke const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement; if (range.enabled) { - const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range); - context.cost = { total: distance, units: null }; - context.distance = { total: distance, units: null }; + const result = DhMeasuredTemplate.getRangeLabels(waypoint.measurement.distance.toNearest(0.01), range); + context.cost = { total: result.distance, units: result.units }; + context.distance = { total: result.distance, units: result.units }; } return context; diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs index ff741b91..cb7be42a 100644 --- a/module/systemRegistration/handlebars.mjs +++ b/module/systemRegistration/handlebars.mjs @@ -35,5 +35,8 @@ export const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/ui/chat/parts/damage-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/target-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/button-part.hbs', + + 'systems/daggerheart/templates/scene/dh-config.hbs', + ]); }; diff --git a/templates/scene/dh-config.hbs b/templates/scene/dh-config.hbs new file mode 100644 index 00000000..0d20c302 --- /dev/null +++ b/templates/scene/dh-config.hbs @@ -0,0 +1,9 @@ +
+
+
+ + +
+
+
\ No newline at end of file From 523ecb506b8f4b91d97f71b2859c7b36c36b0b79 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Fri, 22 Aug 2025 01:38:07 +0200 Subject: [PATCH 07/66] [Fix] ItemLink Fix (#1032) * . * . * Removed outcommented code * Raised to minor version * Added confirmation on import of old character data --- daggerheart.mjs | 3 ++ lang/en.json | 4 +- .../sheets/api/application-mixin.mjs | 1 - module/applications/sheets/api/base-item.mjs | 9 ++-- module/config/settingsConfig.mjs | 3 +- module/data/actor/character.mjs | 12 ++--- module/data/item/base.mjs | 50 ++++++------------- module/data/item/feature.mjs | 18 ------- module/documents/actor.mjs | 42 +++++++++++----- module/helpers/utils.mjs | 11 ++++ module/systemRegistration/_module.mjs | 1 + module/systemRegistration/migrations.mjs | 41 +++++++++++++++ module/systemRegistration/settings.mjs | 6 +++ system.json | 2 +- 14 files changed, 122 insertions(+), 81 deletions(-) create mode 100644 module/systemRegistration/migrations.mjs diff --git a/daggerheart.mjs b/daggerheart.mjs index 795764cc..96a6783b 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -13,6 +13,7 @@ import { enrichedDualityRoll } from './module/enrichers/DualityRollEnricher.mjs' import { registerCountdownHooks } from './module/data/countdowns.mjs'; import { handlebarsRegistration, + runMigrations, settingsRegistration, socketRegistration } from './module/systemRegistration/_module.mjs'; @@ -168,6 +169,8 @@ Hooks.on('ready', async () => { game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.welcomeMessage, true); } } + + runMigrations(); }); Hooks.once('dicesoniceready', () => {}); diff --git a/lang/en.json b/lang/en.json index 7daf6b14..6a34037d 100755 --- a/lang/en.json +++ b/lang/en.json @@ -193,7 +193,9 @@ "companionLevelup": { "confirmTitle": "Companion Levelup", "confirmText": "Would you like to level up your companion {name} by {levelChange} levels at this time? (You can do it manually later)" - } + }, + "InvalidOldCharacterImportTitle": "Old Character Import", + "InvalidOldCharacterImportText": "Character data exported prior to system version 1.1 will not generate a complete character. Do you wish to continue?" }, "Companion": { "FIELDS": { diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index ab1c9ab2..bdcee27a 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -591,7 +591,6 @@ export default function DHApplicationMixin(Base) { if (featureOnCharacter) { systemData = { originItemType: this.document.type, - originId: this.document.id, identifier: this.document.system.isMulticlass ? 'multiclass' : null }; } diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs index 9d7df6ee..e722b72e 100644 --- a/module/applications/sheets/api/base-item.mjs +++ b/module/applications/sheets/api/base-item.mjs @@ -149,12 +149,12 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { const { type } = target.dataset; const cls = foundry.documents.Item.implementation; + const multiclass = this.document.system.isMulticlass ? 'multiclass' : null; let systemData = {}; if (this.document.parent?.type === 'character') { systemData = { originItemType: this.document.type, - originId: this.document.id, - identifier: this.document.system.isMulticlass ? 'multiclass' : null + identifier: multiclass ?? type }; } @@ -275,14 +275,15 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { if (this.document.parent?.type === 'character') { const itemData = item.toObject(); + const multiclass = this.document.system.isMulticlass ? 'multiclass' : null; item = await cls.create( { ...itemData, + _stats: { compendiumSource: this.document.uuid }, system: { ...itemData.system, originItemType: this.document.type, - originId: this.document.id, - identifier: this.document.system.isMulticlass ? 'multiclass' : null + identifier: multiclass ?? target.dataset.type } }, { parent: this.document.parent } diff --git a/module/config/settingsConfig.mjs b/module/config/settingsConfig.mjs index dd8aeffe..76234a97 100644 --- a/module/config/settingsConfig.mjs +++ b/module/config/settingsConfig.mjs @@ -26,5 +26,6 @@ export const gameSettings = { Fear: 'ResourcesFear' }, LevelTiers: 'LevelTiers', - Countdowns: 'Countdowns' + Countdowns: 'Countdowns', + LastMigrationVersion: 'LastMigrationVersion' }; diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index bb6a8c65..7dd7993c 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -444,16 +444,12 @@ export default class DhCharacter extends BaseDataActor { } else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) { if (this.class.subclass) { const subclassState = this.class.subclass.system.featureState; - const subclass = - item.system.identifier === 'multiclass' ? this.multiclass.subclass : this.class.subclass; - const featureType = subclass - ? (subclass.system.features.find(x => x.item?.uuid === item.uuid)?.type ?? null) - : null; if ( - featureType === CONFIG.DH.ITEM.featureSubTypes.foundation || - (featureType === CONFIG.DH.ITEM.featureSubTypes.specialization && subclassState >= 2) || - (featureType === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3) + item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.foundation || + (item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.specialization && + subclassState >= 2) || + (item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3) ) { subclassFeatures.push(item); } diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index f0b22b44..f333537b 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -144,50 +144,30 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { } if (this.actor && this.actor.type === 'character' && this.features) { - const featureUpdates = {}; + const features = []; for (let f of this.features) { const fBase = f.item ?? f; const feature = fBase.system ? fBase : await foundry.utils.fromUuid(fBase.uuid); - const createData = foundry.utils.mergeObject( - feature.toObject(), - { - system: { - originItemType: this.parent.type, - originId: data._id, - identifier: this.isMulticlass ? 'multiclass' : null - } - }, - { inplace: false } + const multiclass = this.isMulticlass ? 'multiclass' : null; + features.push( + foundry.utils.mergeObject( + feature.toObject(), + { + _stats: { compendiumSource: fBase.uuid }, + system: { + originItemType: this.parent.type, + identifier: multiclass ?? (f.item ? f.type : null) + } + }, + { inplace: false } + ) ); - const [doc] = await this.actor.createEmbeddedDocuments('Item', [createData]); - - if (!featureUpdates.features) - featureUpdates.features = this.features.map(x => (x.item ? { ...x, item: x.item.uuid } : x.uuid)); - - if (f.item) { - const existingFeature = featureUpdates.features.find(x => x.item === f.item.uuid); - existingFeature.item = doc.uuid; - } else { - const replaceIndex = featureUpdates.features.findIndex(x => x === f.uuid); - featureUpdates.features.splice(replaceIndex, 1, doc.uuid); - } } - await this.updateSource(featureUpdates); + await this.actor.createEmbeddedDocuments('Item', features); } } - async _preDelete() { - if (!this.actor || this.actor.type !== 'character') return; - - const items = this.actor.items.filter(item => item.system.originId === this.parent.id); - if (items.length > 0) - await this.actor.deleteEmbeddedDocuments( - 'Item', - items.map(x => x.id) - ); - } - async _preUpdate(changed, options, userId) { const allowed = await super._preUpdate(changed, options, userId); if (allowed === false) return false; diff --git a/module/data/item/feature.mjs b/module/data/item/feature.mjs index 13c1d149..6e1aab41 100644 --- a/module/data/item/feature.mjs +++ b/module/data/item/feature.mjs @@ -1,5 +1,4 @@ import BaseDataItem from './base.mjs'; -import { ActionField, ActionsField } from '../fields/actionField.mjs'; export default class DHFeature extends BaseDataItem { /** @inheritDoc */ @@ -30,24 +29,7 @@ export default class DHFeature extends BaseDataItem { nullable: true, initial: null }), - originId: new fields.StringField({ nullable: true, initial: null }), identifier: new fields.StringField() }; } - - get spellcastingModifier() { - let traitValue = 0; - if (this.actor && this.originId && ['class', 'subclass'].includes(this.originItemType)) { - if (this.originItemType === 'subclass') { - traitValue = - this.actor.system.traits[this.actor.items.get(this.originId).system.spellcastingTrait]?.value ?? 0; - } else { - const { value: multiclass, subclass } = this.actor.system.multiclass; - const selectedSubclass = multiclass?.id === this.originId ? subclass : this.actor.system.class.subclass; - traitValue = this.actor.system.traits[selectedSubclass.system.spellcastingTrait]?.value ?? 0; - } - } - - return traitValue; - } } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 4a6d2d67..e1dd93af 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -1,7 +1,7 @@ import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs'; import { LevelOptionType } from '../data/levelTier.mjs'; import DHFeature from '../data/item/feature.mjs'; -import { damageKeyToNumber } from '../helpers/utils.mjs'; +import { damageKeyToNumber, versionCompare } from '../helpers/utils.mjs'; import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs'; export default class DhpActor extends Actor { @@ -27,7 +27,7 @@ export default class DhpActor extends Actor { /** @inheritDoc */ static migrateData(source) { - if(source.system?.attack && !source.system.attack.type) source.system.attack.type = "attack"; + if (source.system?.attack && !source.system.attack.type) source.system.attack.type = 'attack'; return super.migrateData(source); } @@ -571,19 +571,15 @@ export default class DhpActor extends Actor { if (armorSlotResult) { const { modifiedDamage, armorSpent, stressSpent } = armorSlotResult; updates.find(u => u.key === 'hitPoints').value = modifiedDamage; - if(armorSpent) { + if (armorSpent) { const armorUpdate = updates.find(u => u.key === 'armor'); - if(armorUpdate) - armorUpdate.value += armorSpent; - else - updates.push({ value: armorSpent, key: 'armor' }); + if (armorUpdate) armorUpdate.value += armorSpent; + else updates.push({ value: armorSpent, key: 'armor' }); } - if(stressSpent) { + if (stressSpent) { const stressUpdate = updates.find(u => u.key === 'stress'); - if(stressUpdate) - stressUpdate.value += stressSpent; - else - updates.push({ value: stressSpent, key: 'stress' }); + if (stressUpdate) stressUpdate.value += stressSpent; + else updates.push({ value: stressSpent, key: 'stress' }); } } } @@ -753,4 +749,26 @@ export default class DhpActor extends Actor { } } } + + /** @inheritdoc */ + async importFromJSON(json) { + if (!this.type === 'character') return await super.importFromJSON(json); + + if (!CONST.WORLD_DOCUMENT_TYPES.includes(this.documentName)) { + throw new Error('Only world Documents may be imported'); + } + + const parsedJSON = JSON.parse(json); + if (versionCompare(parsedJSON._stats.systemVersion, '1.1.0')) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.localize('DAGGERHEART.ACTORS.Character.InvalidOldCharacterImportTitle') + }, + content: game.i18n.localize('DAGGERHEART.ACTORS.Character.InvalidOldCharacterImportText') + }); + if (!confirmed) return; + } + + return await super.importFromJSON(json); + } } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 6f4e5a26..c8f4186f 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -420,3 +420,14 @@ export async function createEmbeddedItemsWithEffects(actor, baseData) { export const slugify = name => { return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', ''); }; + +export const versionCompare = (current, target) => { + const currentSplit = current.split('.').map(x => Number.parseInt(x)); + const targetSplit = target.split('.').map(x => Number.parseInt(x)); + for (var i = 0; i < currentSplit.length; i++) { + if (currentSplit[i] < targetSplit[i]) return true; + if (currentSplit[i] > targetSplit[i]) return false; + } + + return false; +}; diff --git a/module/systemRegistration/_module.mjs b/module/systemRegistration/_module.mjs index 38dcac4f..483a6b37 100644 --- a/module/systemRegistration/_module.mjs +++ b/module/systemRegistration/_module.mjs @@ -1,3 +1,4 @@ export { preloadHandlebarsTemplates as handlebarsRegistration } from './handlebars.mjs'; export * as settingsRegistration from './settings.mjs'; export * as socketRegistration from './socket.mjs'; +export { runMigrations } from './migrations.mjs'; diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs new file mode 100644 index 00000000..e84018fa --- /dev/null +++ b/module/systemRegistration/migrations.mjs @@ -0,0 +1,41 @@ +import { versionCompare } from '../helpers/utils.mjs'; + +export async function runMigrations() { + let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion); + if (!lastMigrationVersion) lastMigrationVersion = '1.0.6'; + + if (versionCompare(lastMigrationVersion, '1.1.0')) { + const compendiumActors = []; + for (let pack of game.packs) { + const documents = await pack.getDocuments(); + compendiumActors.push(...documents.filter(x => x.type === 'character')); + } + + [...compendiumActors, ...game.actors].forEach(actor => { + const items = actor.items.reduce((acc, item) => { + if (item.type === 'feature') { + const { originItemType, isMulticlass, identifier } = item.system; + const base = originItemType + ? actor.items.find( + x => x.type === originItemType && Boolean(isMulticlass) === Boolean(x.system.isMulticlass) + ) + : null; + if (base) { + const feature = base.system.features.find(x => x.item && x.item.uuid === item.uuid); + if (feature && identifier !== 'multiclass') { + acc.push({ _id: item.id, system: { identifier: feature.type } }); + } + } + } + + return acc; + }, []); + + actor.updateEmbeddedDocuments('Item', items); + }); + + lastMigrationVersion = '1.1.0'; + } + + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion); +} diff --git a/module/systemRegistration/settings.mjs b/module/systemRegistration/settings.mjs index 3d9ffc19..4828ebb0 100644 --- a/module/systemRegistration/settings.mjs +++ b/module/systemRegistration/settings.mjs @@ -91,6 +91,12 @@ const registerMenus = () => { }; const registerNonConfigSettings = () => { + game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, { + scope: 'world', + config: false, + type: String + }); + game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers, { scope: 'world', config: false, diff --git a/system.json b/system.json index e6b7650f..d72c53d5 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "1.0.6", + "version": "1.1.0", "compatibility": { "minimum": "13", "verified": "13.347", From 888cf9172bb69c0d444201527c408c9d6f53a825 Mon Sep 17 00:00:00 2001 From: Luiz HD Costa Date: Thu, 21 Aug 2025 22:35:36 -0300 Subject: [PATCH 08/66] [Community PR] Localize hardcoded text (#1002) * Localize remaining hardcoded user-facing strings * Introduce pluralize helper for localizing strings * Localize missing strings from ItemBrowser --- lang/en.json | 39 +++++- module/applications/ui/itemBrowser.mjs | 15 ++- module/config/itemBrowserConfig.mjs | 128 +++++++++---------- module/data/fields/action/costField.mjs | 2 +- module/data/fields/action/usesField.mjs | 2 +- module/helpers/handlebarsHelper.mjs | 21 ++- templates/sheets/actors/character/header.hbs | 2 +- templates/ui/chat/parts/target-part.hbs | 6 +- templates/ui/itemBrowser/itemBrowser.hbs | 14 +- 9 files changed, 147 insertions(+), 82 deletions(-) diff --git a/lang/en.json b/lang/en.json index 63b18219..1bddbad3 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2347,6 +2347,42 @@ "playerMessage": "{user} rerolled their {name}" } }, + "ItemBrowser": { + "title": "Daggerheart Compendium Browser", + "hint": "Select a Folder in sidebar to start browsing through the compendium", + "searchPlaceholder": "Search...", + "columnName": "Name", + "tooltipFilters": "Filters", + "tooltipErase": "Erase", + "difficultyMin": "Difficulty (Min)", + "difficultyMax": "Difficulty (Max)", + "hitPointsMin": "Hit Points (Min)", + "hitPointsMax": "Hit Points (Max)", + "stressMin": "Stress (Min)", + "stressMax": "Stress (Max)", + "armorScoreMin": "Armor Score (Min)", + "armorScoreMax": "Armor Score (Max)", + "levelMin": "Level (Min)", + "levelMax": "Level (Max)", + "recallCostMin": "Recall Cost (Min)", + "recallCostMax": "Recall Cost (Max)", + "evasionMin": "Evasion (Min)", + "evasionMax": "Evasion (Max)", + "subtype": "Subtype", + "folders": { + "adversaries": "Adversaries", + "ancestries": "Ancestries", + "equipment": "Equipment", + "classes": "Classes", + "subclasses": "Subclasses", + "domainCards": "Domain Cards", + "communities": "Communities", + "environments": "Environments", + "beastforms": "Beastforms", + "features": "Features", + "items": "Items" + } + }, "Notifications": { "adversaryMissing": "The linked adversary doesn't exist in the world.", "beastformInapplicable": "A beastform can only be applied to a Character.", @@ -2406,7 +2442,8 @@ "beastformEquipWeapon": "You cannot use weapons while in a Beastform.", "loadoutMaxReached": "You've reached maximum loadout. Move atleast one domain card to the vault, or increase the limit in homebrew settings if desired.", "domainMaxReached": "You've reached the maximum domains for the class. Increase the limit in homebrew settings if desired.", - "insufficientResources": "You have insufficient resources", + "insufficientResources": "You don't have enough resources to use that action.", + "actionNoUsesRemaining": "That action doesn't have remaining uses.", "multiclassAlreadyPresent": "You already have a class and multiclass", "subclassesAlreadyPresent": "You already have a class and multiclass subclass", "noDiceSystem": "Your selected dice {system} does not have a {faces} dice" diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index f0ad98db..3a31bd4e 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -154,7 +154,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { Object.values(config).forEach(c => { const folder = { id: c.id, - label: c.label, + label: game.i18n.localize(c.label), selected: (!parent || parent.selected) && this.selectedMenu.path[depth] === c.id }; folder.folders = c.folders @@ -173,11 +173,16 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { folderPath = `${compendium}.folders.${folderId}`, folderData = foundry.utils.getProperty(config, folderPath); + const columns = ItemBrowser.getFolderConfig(folderData).map(col => ({ + ...col, + label: game.i18n.localize(col.label) + })); + this.selectedMenu = { path: folderPath.split('.'), data: { ...folderData, - columns: ItemBrowser.getFolderConfig(folderData) + columns: columns } }; @@ -237,6 +242,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { else if (typeof f.choices === 'function') { f.choices = f.choices(); } + + // Clear field label so template uses our custom label parameter + if (f.field && f.label) { + f.field.label = undefined; + } + f.name ??= f.key; f.value = this.presets?.filter?.[f.name]?.value ?? null; }); diff --git a/module/config/itemBrowserConfig.mjs b/module/config/itemBrowserConfig.mjs index 6e3c0dea..0cb42d2c 100644 --- a/module/config/itemBrowserConfig.mjs +++ b/module/config/itemBrowserConfig.mjs @@ -3,63 +3,63 @@ export const typeConfig = { columns: [ { key: "system.tier", - label: "Tier" + label: "DAGGERHEART.GENERAL.Tiers.singular" }, { key: "system.type", - label: "Type" + label: "DAGGERHEART.GENERAL.type" } ], filters: [ { key: "system.tier", - label: "Tier", + label: "DAGGERHEART.GENERAL.Tiers.singular", field: 'system.api.models.actors.DhAdversary.schema.fields.tier' }, { key: "system.type", - label: "Type", + label: "DAGGERHEART.GENERAL.type", field: 'system.api.models.actors.DhAdversary.schema.fields.type' }, { key: "system.difficulty", name: "difficulty.min", - label: "Difficulty (Min)", + label: "DAGGERHEART.UI.ItemBrowser.difficultyMin", field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty', operator: "gte" }, { key: "system.difficulty", name: "difficulty.max", - label: "Difficulty (Max)", + label: "DAGGERHEART.UI.ItemBrowser.difficultyMax", field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty', operator: "lte" }, { key: "system.resources.hitPoints.max", name: "hp.min", - label: "Hit Points (Min)", + label: "DAGGERHEART.UI.ItemBrowser.hitPointsMin", field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max', operator: "gte" }, { key: "system.resources.hitPoints.max", name: "hp.max", - label: "Hit Points (Max)", + label: "DAGGERHEART.UI.ItemBrowser.hitPointsMax", field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max', operator: "lte" }, { key: "system.resources.stress.max", name: "stress.min", - label: "Stress (Min)", + label: "DAGGERHEART.UI.ItemBrowser.stressMin", field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max', operator: "gte" }, { key: "system.resources.stress.max", name: "stress.max", - label: "Stress (Max)", + label: "DAGGERHEART.UI.ItemBrowser.stressMax", field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max', operator: "lte" }, @@ -69,70 +69,70 @@ export const typeConfig = { columns: [ { key: "type", - label: "Type" + label: "DAGGERHEART.GENERAL.type" }, { key: "system.secondary", - label: "Subtype", + label: "DAGGERHEART.UI.ItemBrowser.subtype", format: (isSecondary) => isSecondary ? "secondary" : (isSecondary === false ? "primary" : '-') }, { key: "system.tier", - label: "Tier" + label: "DAGGERHEART.GENERAL.Tiers.singular" } ], filters: [ { key: "type", - label: "Type", + label: "DAGGERHEART.GENERAL.type", choices: () => CONFIG.Item.documentClass.TYPES.filter(t => ["armor", "weapon", "consumable", "loot"].includes(t)).map(t => ({ value: t, label: t })) }, { key: "system.secondary", - label: "Subtype", + label: "DAGGERHEART.UI.ItemBrowser.subtype", choices: [ - { value: false, label: "Primary Weapon"}, - { value: true, label: "Secondary Weapon"} + { value: false, label: "DAGGERHEART.ITEMS.Weapon.primaryWeapon" }, + { value: true, label: "DAGGERHEART.ITEMS.Weapon.secondaryWeapon" } ] }, { key: "system.tier", - label: "Tier", - choices: [{ value: "1", label: "1"}, { value: "2", label: "2"}, { value: "3", label: "3"}, { value: "4", label: "4"}] + label: "DAGGERHEART.GENERAL.Tiers.singular", + choices: [{ value: "1", label: "1" }, { value: "2", label: "2" }, { value: "3", label: "3" }, { value: "4", label: "4" }] }, { key: "system.burden", - label: "Burden", + label: "DAGGERHEART.GENERAL.burden", field: 'system.api.models.items.DHWeapon.schema.fields.burden' }, { key: "system.attack.roll.trait", - label: "Trait", + label: "DAGGERHEART.GENERAL.Trait.single", field: 'system.api.models.actions.actionsTypes.attack.schema.fields.roll.fields.trait' }, { key: "system.attack.range", - label: "Range", + label: "DAGGERHEART.GENERAL.range", field: 'system.api.models.actions.actionsTypes.attack.schema.fields.range' }, { key: "system.baseScore", name: "armor.min", - label: "Armor Score (Min)", + label: "DAGGERHEART.UI.ItemBrowser.armorScoreMin", field: 'system.api.models.items.DHArmor.schema.fields.baseScore', operator: "gte" }, { key: "system.baseScore", name: "armor.max", - label: "Armor Score (Max)", + label: "DAGGERHEART.UI.ItemBrowser.armorScoreMax", field: 'system.api.models.items.DHArmor.schema.fields.baseScore', operator: "lte" }, { key: "system.itemFeatures", - label: "Features", - choices: () => [...Object.entries(CONFIG.DH.ITEM.weaponFeatures), ...Object.entries(CONFIG.DH.ITEM.armorFeatures)].map(([k,v]) => ({ value: k, label: v.label})), + label: "DAGGERHEART.GENERAL.features", + choices: () => [...Object.entries(CONFIG.DH.ITEM.weaponFeatures), ...Object.entries(CONFIG.DH.ITEM.armorFeatures)].map(([k, v]) => ({ value: k, label: v.label })), operator: "contains3" } ] @@ -149,54 +149,54 @@ export const typeConfig = { columns: [ { key: "system.type", - label: "Type" + label: "DAGGERHEART.GENERAL.type" }, { key: "system.domain", - label: "Domain" + label: "DAGGERHEART.GENERAL.Domain.single" }, { key: "system.level", - label: "Level" + label: "DAGGERHEART.GENERAL.level" } ], filters: [ { key: "system.type", - label: "Type", + label: "DAGGERHEART.GENERAL.type", field: 'system.api.models.items.DHDomainCard.schema.fields.type' }, { key: "system.domain", - label: "Domain", + label: "DAGGERHEART.GENERAL.Domain.single", field: 'system.api.models.items.DHDomainCard.schema.fields.domain', operator: "contains2" }, { key: "system.level", name: "level.min", - label: "Level (Min)", + label: "DAGGERHEART.UI.ItemBrowser.levelMin", field: 'system.api.models.items.DHDomainCard.schema.fields.level', operator: "gte" }, { key: "system.level", name: "level.max", - label: "Level (Max)", + label: "DAGGERHEART.UI.ItemBrowser.levelMax", field: 'system.api.models.items.DHDomainCard.schema.fields.level', operator: "lte" }, { key: "system.recallCost", name: "recall.min", - label: "Recall Cost (Min)", + label: "DAGGERHEART.UI.ItemBrowser.recallCostMin", field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost', operator: "gte" }, { key: "system.recallCost", name: "recall.max", - label: "Recall Cost (Max)", + label: "DAGGERHEART.UI.ItemBrowser.recallCostMax", field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost', operator: "lte" } @@ -206,50 +206,50 @@ export const typeConfig = { columns: [ { key: "system.evasion", - label: "Evasion" + label: "DAGGERHEART.GENERAL.evasion" }, { key: "system.hitPoints", - label: "Hit Points" + label: "DAGGERHEART.GENERAL.HitPoints.plural" }, { key: "system.domains", - label: "Domains" + label: "DAGGERHEART.GENERAL.Domain.plural" } ], filters: [ { key: "system.evasion", name: "evasion.min", - label: "Evasion (Min)", + label: "DAGGERHEART.UI.ItemBrowser.evasionMin", field: 'system.api.models.items.DHClass.schema.fields.evasion', operator: "gte" }, { key: "system.evasion", name: "evasion.max", - label: "Evasion (Max)", + label: "DAGGERHEART.UI.ItemBrowser.evasionMax", field: 'system.api.models.items.DHClass.schema.fields.evasion', operator: "lte" }, { key: "system.hitPoints", name: "hp.min", - label: "Hit Points (Min)", + label: "DAGGERHEART.UI.ItemBrowser.hitPointsMin", field: 'system.api.models.items.DHClass.schema.fields.hitPoints', operator: "gte" }, { key: "system.hitPoints", name: "hp.max", - label: "Hit Points (Max)", + label: "DAGGERHEART.UI.ItemBrowser.hitPointsMax", field: 'system.api.models.items.DHClass.schema.fields.hitPoints', operator: "lte" }, { key: "system.domains", - label: "Domains", - choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label})), + label: "DAGGERHEART.GENERAL.Domain.plural", + choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label })), operator: "contains2" } ] @@ -258,14 +258,14 @@ export const typeConfig = { columns: [ { key: "id", - label: "Class", + label: "TYPES.Item.class", format: (id) => { return ""; } }, { key: "system.spellcastingTrait", - label: "Spellcasting Trait" + label: "DAGGERHEART.ITEMS.Subclass.spellcastingTrait" } ], filters: [] @@ -274,22 +274,22 @@ export const typeConfig = { columns: [ { key: "system.tier", - label: "Tier" + label: "DAGGERHEART.GENERAL.Tiers.singular" }, { key: "system.mainTrait", - label: "Main Trait" + label: "DAGGERHEART.GENERAL.Trait.single" } ], filters: [ { key: "system.tier", - label: "Tier", + label: "DAGGERHEART.GENERAL.Tiers.singular", field: 'system.api.models.items.DHBeastform.schema.fields.tier' }, { key: "system.mainTrait", - label: "Main Trait", + label: "DAGGERHEART.GENERAL.Trait.single", field: 'system.api.models.items.DHBeastform.schema.fields.mainTrait' } ] @@ -304,20 +304,20 @@ export const compendiumConfig = { "adversaries": { id: "adversaries", keys: ["adversaries"], - label: "Adversaries", + label: "DAGGERHEART.UI.ItemBrowser.folders.adversaries", type: ["adversary"], listType: "adversaries" }, "ancestries": { id: "ancestries", keys: ["ancestries"], - label: "Ancestries", + label: "DAGGERHEART.UI.ItemBrowser.folders.ancestries", type: ["ancestry"], folders: { "features": { id: "features", keys: ["ancestries"], - label: "Features", + label: "DAGGERHEART.UI.ItemBrowser.folders.features", type: ["feature"] } } @@ -325,26 +325,26 @@ export const compendiumConfig = { "equipments": { id: "equipments", keys: ["armors", "weapons", "consumables", "loot"], - label: "Equipment", + label: "DAGGERHEART.UI.ItemBrowser.folders.equipment", type: ["armor", "weapon", "consumable", "loot"], listType: "items" }, "classes": { id: "classes", keys: ["classes"], - label: "Classes", + label: "DAGGERHEART.UI.ItemBrowser.folders.classes", type: ["class"], folders: { "features": { id: "features", keys: ["classes"], - label: "Features", + label: "DAGGERHEART.UI.ItemBrowser.folders.features", type: ["feature"] }, "items": { id: "items", keys: ["classes"], - label: "Items", + label: "DAGGERHEART.UI.ItemBrowser.folders.items", type: ["armor", "weapon", "consumable", "loot"], listType: "items" } @@ -354,27 +354,27 @@ export const compendiumConfig = { "subclasses": { id: "subclasses", keys: ["subclasses"], - label: "Subclasses", + label: "DAGGERHEART.UI.ItemBrowser.folders.subclasses", type: ["subclass"], listType: "subclasses" }, "domains": { id: "domains", keys: ["domains"], - label: "Domain Cards", + label: "DAGGERHEART.UI.ItemBrowser.folders.domainCards", type: ["domainCard"], listType: "cards" }, "communities": { id: "communities", keys: ["communities"], - label: "Communities", + label: "DAGGERHEART.UI.ItemBrowser.folders.communities", type: ["community"], folders: { "features": { id: "features", keys: ["communities"], - label: "Features", + label: "DAGGERHEART.UI.ItemBrowser.folders.features", type: ["feature"] } } @@ -382,20 +382,20 @@ export const compendiumConfig = { "environments": { id: "environments", keys: ["environments"], - label: "Environments", + label: "DAGGERHEART.UI.ItemBrowser.folders.environments", type: ["environment"] }, "beastforms": { id: "beastforms", keys: ["beastforms"], - label: "Beastforms", + label: "DAGGERHEART.UI.ItemBrowser.folders.beastforms", type: ["beastform"], listType: "beastforms", folders: { "features": { id: "features", keys: ["beastforms"], - label: "Features", + label: "DAGGERHEART.UI.ItemBrowser.folders.features", type: ["feature"] } } diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index f4d942b1..c224fff0 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -25,7 +25,7 @@ export default class CostField extends fields.ArrayField { config.costs = CostField.calcCosts.call(this, costs); const hasCost = CostField.hasCost.call(this, config.costs); if (config.isFastForward && !hasCost) - return ui.notifications.warn("You don't have the resources to use that action."); + return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.insufficientResources')); return hasCost; } diff --git a/module/data/fields/action/usesField.mjs b/module/data/fields/action/usesField.mjs index 3993ca3b..5c2bfb61 100644 --- a/module/data/fields/action/usesField.mjs +++ b/module/data/fields/action/usesField.mjs @@ -25,7 +25,7 @@ export default class UsesField extends fields.SchemaField { if (uses && !uses.value) uses.value = 0; config.uses = uses; const hasUses = UsesField.hasUses.call(this, config.uses); - if (config.isFastForward && !hasUses) return ui.notifications.warn("That action doesn't have remaining uses."); + if (config.isFastForward && !hasUses) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.actionNoUsesRemaining')); return hasUses; } diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 171255e2..83220307 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -13,7 +13,8 @@ export default class RegisterHandlebarsHelpers { hasProperty: foundry.utils.hasProperty, getProperty: foundry.utils.getProperty, setVar: this.setVar, - empty: this.empty + empty: this.empty, + pluralize: this.pluralize }); } static add(a, b) { @@ -64,7 +65,7 @@ export default class RegisterHandlebarsHelpers { return isNumerical ? (!result ? 0 : Number(result)) : result; } - static setVar(name, value, context) { + static setVar(name, value) { this[name] = value; } @@ -72,4 +73,20 @@ export default class RegisterHandlebarsHelpers { if (!(typeof object === 'object')) return true; return Object.keys(object).length === 0; } + + /** + * Pluralize helper that returns the appropriate localized string based on count + * @param {number} count - The number to check for plurality + * @param {string} baseKey - The base localization key (e.g., "DAGGERHEART.GENERAL.Target") + * @returns {string} The localized singular or plural string + * + * Usage: {{pluralize currentTargets.length "DAGGERHEART.GENERAL.Target"}} + * Returns: "Target" if count is exactly 1, "Targets" if count is 0, 2+, or invalid + */ + static pluralize(count, baseKey) { + const numericCount = Number(count); + const isSingular = !isNaN(numericCount) && numericCount === 1; + const key = isSingular ? `${baseKey}.single` : `${baseKey}.plural`; + return game.i18n.localize(key); + } } diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 88a72fb8..e19c1dea 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -6,7 +6,7 @@ type='text' name='name' value='{{document.name}}' - placeholder='Actor Name' + placeholder='{{localize "DAGGERHEART.GENERAL.actorName"}}' /> diff --git a/templates/ui/chat/parts/target-part.hbs b/templates/ui/chat/parts/target-part.hbs index 82c71456..af7e93b0 100644 --- a/templates/ui/chat/parts/target-part.hbs +++ b/templates/ui/chat/parts/target-part.hbs @@ -1,11 +1,11 @@
-
Target
+
{{pluralize currentTargets.length "DAGGERHEART.GENERAL.Target"}}
{{#if (or (and targets.length (or (gt targetShort.hit 0) (gt targetShort.miss 0))) (and hasSave pendingSaves))}}
{{#if (or (gt targetShort.hit 0) (gt targetShort.miss 0))}} -
{{targetShort.hit}} {{#if (gt targetShort.hit 1)}}{{localize "DAGGERHEART.GENERAL.hit.single"}}{{else}}{{localize "DAGGERHEART.GENERAL.hit.plural"}}{{/if}}
-
{{targetShort.miss}} {{#if (gt targetShort.miss 1)}}{{localize "DAGGERHEART.GENERAL.miss.single"}}{{else}}{{localize "DAGGERHEART.GENERAL.miss.plural"}}{{/if}}
+
{{targetShort.hit}} {{pluralize targetShort.hit "DAGGERHEART.GENERAL.hit"}}
+
{{targetShort.miss}} {{pluralize targetShort.miss "DAGGERHEART.GENERAL.miss"}}
{{/if}} {{#if (and hasSave pendingSaves)}}
{{/if}}
diff --git a/templates/ui/itemBrowser/itemBrowser.hbs b/templates/ui/itemBrowser/itemBrowser.hbs index 000e4c03..ca0def19 100644 --- a/templates/ui/itemBrowser/itemBrowser.hbs +++ b/templates/ui/itemBrowser/itemBrowser.hbs @@ -17,12 +17,12 @@
- +
{{#if fieldFilter.length}} - + {{/if}} - +
@@ -55,9 +55,9 @@ {{#if menu.data.columns.length}}
-
Name
+
{{localize 'DAGGERHEART.UI.ItemBrowser.columnName'}}
{{#each menu.data.columns}} - {{label}} + {{localize label}} {{/each}}
{{/if}} @@ -82,8 +82,8 @@ {{!--
--}} {{else}}
-

Daggerheart Compendium Browser

- Select a Folder in sidebar to start browsing trought the compendium +

{{localize "DAGGERHEART.UI.ItemBrowser.title"}}

+ {{localize "DAGGERHEART.UI.ItemBrowser.hint"}}
{{/if}}
\ No newline at end of file From ee786544c75a970dc34d2364db600b932f6a72b7 Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Fri, 22 Aug 2025 12:22:47 +0200 Subject: [PATCH 09/66] Fix double damageKeyToNumber (#1044) --- module/documents/actor.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 0a68e9d1..4ef8b3b0 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -1,7 +1,7 @@ import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs'; import { LevelOptionType } from '../data/levelTier.mjs'; import DHFeature from '../data/item/feature.mjs'; -import { createScrollText, damageKeyToNumber, damageKeyToNumber, versionCompare } from '../helpers/utils.mjs'; +import { createScrollText, damageKeyToNumber, versionCompare } from '../helpers/utils.mjs'; import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs'; export default class DhpActor extends Actor { From 86d451f0d722665a030da4b23734007f0b0a09ba Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 23 Aug 2025 01:55:18 +0200 Subject: [PATCH 10/66] . (#1060) --- module/documents/activeEffect.mjs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index f46cc9db..b2896513 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -167,13 +167,11 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { if (subclass) { const featureState = subclass.system.featureState; - const featureType = subclass - ? (subclass.system.features.find(x => x.item?.uuid === this.parent.uuid)?.type ?? null) - : null; if ( - (featureType === CONFIG.DH.ITEM.featureSubTypes.specialization && featureState < 2) || - (featureType === CONFIG.DH.ITEM.featureSubTypes.mastery && featureState < 3) + (this.parent.system.identifier === CONFIG.DH.ITEM.featureSubTypes.specialization && + featureState < 2) || + (this.parent.system.identifier === CONFIG.DH.ITEM.featureSubTypes.mastery && featureState < 3) ) { this.transfer = false; } From 7016f71da7b810eb34a7ef32a333ead69e9c7159 Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Sat, 23 Aug 2025 02:05:16 +0200 Subject: [PATCH 11/66] Fix/spellcast modifier (#1061) * Temp ActionField attack type missing * Move missing attack type to getModel * Fix spellcast modifier bonus --- module/data/action/baseAction.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 5d3f7721..2f5935da 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -172,7 +172,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel dialog: { configure: hasRoll }, - type: this.type, + type: this.roll?.type ?? this.type, hasRoll: hasRoll, hasDamage: this.damage?.parts?.length && this.type !== 'healing', hasHealing: this.damage?.parts?.length && this.type === 'healing', From d5f7e17339dd0b24490be12064fe06f4fbebdf1c Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 23 Aug 2025 02:18:25 +0200 Subject: [PATCH 12/66] Added Custom Adversary Types (#1048) --- lang/en.json | 7 ++- .../settings/homebrewSettings.mjs | 54 +++++++++++++++++-- .../applications/sheets/actors/adversary.mjs | 4 ++ module/config/actorConfig.mjs | 5 ++ module/data/actor/adversary.mjs | 2 +- module/data/settings/Homebrew.mjs | 7 +++ styles/less/ui/index.less | 1 + .../ui/settings/homebrew-settings/types.less | 52 ++++++++++++++++++ .../settings/homebrew-settings/types.hbs | 28 ++++++++++ templates/sheets/actors/adversary/header.hbs | 4 +- templates/ui/tooltip/adversary.hbs | 2 +- 11 files changed, 156 insertions(+), 10 deletions(-) create mode 100644 styles/less/ui/settings/homebrew-settings/types.less create mode 100644 templates/settings/homebrew-settings/types.hbs diff --git a/lang/en.json b/lang/en.json index 1bddbad3..1c614fc2 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1900,7 +1900,8 @@ "tier4": "tier 4", "domains": "Domains", "downtime": "Downtime", - "rules": "Rules" + "rules": "Rules", + "types": "Types" }, "Tiers": { "singular": "Tier", @@ -2230,6 +2231,10 @@ "deleteDomain": "Delete Domain", "deleteDomainText": "Are you sure you want to delete the {name} domain? It will be immediately removed from all Actors in this world where it's currently used. Compendiums are not cleared.", "duplicateDomain": "There is already a domain with this identification." + }, + "adversaryType": { + "title": "Custom Adversary Types", + "newType": "Adversary Type" } }, "Menu": { diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index 3fa42afd..c2ac4a89 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -1,5 +1,6 @@ import { DhHomebrew } from '../../data/settings/_module.mjs'; import { slugify } from '../../helpers/utils.mjs'; + const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; export default class DhHomebrewSettings extends HandlebarsApplicationMixin(ApplicationV2) { @@ -10,11 +11,14 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).toObject() ); - this.selected = { - domain: null - }; + this.selected = this.#getDefaultAdversaryType(); } + #getDefaultAdversaryType = () => ({ + domain: null, + adversaryType: null + }); + get title() { return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title'); } @@ -35,6 +39,9 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli addDomain: this.addDomain, toggleSelectedDomain: this.toggleSelectedDomain, deleteDomain: this.deleteDomain, + addAdversaryType: this.addAdversaryType, + deleteAdversaryType: this.deleteAdversaryType, + selectAdversaryType: this.selectAdversaryType, save: this.save, reset: this.reset }, @@ -45,6 +52,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, settings: { template: 'systems/daggerheart/templates/settings/homebrew-settings/settings.hbs' }, domains: { template: 'systems/daggerheart/templates/settings/homebrew-settings/domains.hbs' }, + types: { template: 'systems/daggerheart/templates/settings/homebrew-settings/types.hbs' }, downtime: { template: 'systems/daggerheart/templates/settings/homebrew-settings/downtime.hbs' }, footer: { template: 'systems/daggerheart/templates/settings/homebrew-settings/footer.hbs' } }; @@ -52,12 +60,19 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli /** @inheritdoc */ static TABS = { main: { - tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'downtime' }], + tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'types' }, { id: 'downtime' }], initial: 'settings', labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } }; + changeTab(tab, group, options) { + super.changeTab(tab, group, options); + this.selected = this.#getDefaultAdversaryType(); + + this.render(); + } + async _prepareContext(_options) { const context = await super._prepareContext(_options); context.settingFields = this.settings; @@ -79,6 +94,11 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli context.configDomains = CONFIG.DH.DOMAIN.domains; context.homebrewDomains = this.settings.domains; break; + case 'types': + context.selectedAdversaryType = this.selected.adversaryType + ? { id: this.selected.adversaryType, ...this.settings.adversaryTypes[this.selected.adversaryType] } + : null; + break; } return context; @@ -301,6 +321,32 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli this.render(); } + static async addAdversaryType(_, target) { + const newId = foundry.utils.randomID(); + await this.settings.updateSource({ + [`adversaryTypes.${newId}`]: { + id: newId, + label: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.adversaryType.newType') + } + }); + + this.selected.adversaryType = newId; + this.render(); + } + + static async deleteAdversaryType(_, target) { + const { key } = target.dataset; + await this.settings.updateSource({ [`adversaryTypes.-=${key}`]: null }); + + this.selected.adversaryType = this.selected.adversaryType === key ? null : this.selected.adversaryType; + this.render(); + } + + static async selectAdversaryType(_, target) { + this.selected.adversaryType = this.selected.adversaryType === target.dataset.type ? null : target.dataset.type; + this.render(); + } + static async save() { await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject()); this.close(); diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index b47cb85a..f575a2f2 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -56,6 +56,7 @@ export default class AdversarySheet extends DHBaseActorSheet { async _prepareContext(options) { const context = await super._prepareContext(options); context.systemFields.attack.fields = this.document.system.attack.schema.fields; + return context; } @@ -65,6 +66,9 @@ export default class AdversarySheet extends DHBaseActorSheet { switch (partId) { case 'header': await this._prepareHeaderContext(context, options); + + const adversaryTypes = CONFIG.DH.ACTOR.allAdversaryTypes(); + context.adversaryType = game.i18n.localize(adversaryTypes[this.document.system.type].label); break; case 'notes': await this._prepareNotesContext(context, options); diff --git a/module/config/actorConfig.mjs b/module/config/actorConfig.mjs index 6453cd78..55f03789 100644 --- a/module/config/actorConfig.mjs +++ b/module/config/actorConfig.mjs @@ -157,6 +157,11 @@ export const adversaryTypes = { } }; +export const allAdversaryTypes = () => ({ + ...adversaryTypes, + ...game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).adversaryTypes +}); + export const environmentTypes = { exploration: { label: 'DAGGERHEART.CONFIG.EnvironmentType.exploration.label', diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index 80bcb43e..ba0693f7 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -27,7 +27,7 @@ export default class DhpAdversary extends BaseDataActor { }), type: new fields.StringField({ required: true, - choices: CONFIG.DH.ACTOR.adversaryTypes, + choices: CONFIG.DH.ACTOR.allAdversaryTypes, initial: CONFIG.DH.ACTOR.adversaryTypes.standard.id }), motivesAndTactics: new fields.StringField(), diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index e18fee39..0719b085 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -108,6 +108,13 @@ export default class DhHomebrew extends foundry.abstract.DataModel { }), description: new fields.HTMLField() }) + ), + adversaryTypes: new fields.TypedObjectField( + new fields.SchemaField({ + id: new fields.StringField({ required: true }), + label: new fields.StringField({ required: true, label: 'DAGGERHEART.GENERAL.label' }), + description: new fields.StringField() + }) ) }; } diff --git a/styles/less/ui/index.less b/styles/less/ui/index.less index 4a93feb6..49d1e009 100644 --- a/styles/less/ui/index.less +++ b/styles/less/ui/index.less @@ -21,3 +21,4 @@ @import './settings/settings.less'; @import './settings/homebrew-settings/domains.less'; +@import './settings/homebrew-settings/types.less'; diff --git a/styles/less/ui/settings/homebrew-settings/types.less b/styles/less/ui/settings/homebrew-settings/types.less new file mode 100644 index 00000000..d09431f7 --- /dev/null +++ b/styles/less/ui/settings/homebrew-settings/types.less @@ -0,0 +1,52 @@ +.theme-light .daggerheart.dh-style.setting.homebrew-settings .types.tab { + .adversary-types-container .adversary-type-container { + background-image: url('../assets/parchments/dh-parchment-light.png'); + } +} + +.daggerheart.dh-style.setting.homebrew-settings { + .types.tab { + .adversary-types-container { + width: 100%; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 4px; + + .adversary-type-container { + height: 2em; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + border: 1px solid; + border-radius: 6px; + padding: 0 8px; + border: 1px solid light-dark(@dark-blue, @golden); + color: light-dark(@dark, @beige); + background-image: url('../assets/parchments/dh-parchment-dark.png'); + cursor: pointer; + opacity: 0.6; + + &:hover { + opacity: 1; + } + + &.active { + opacity: 1; + background: var(--color-warm-2); + } + } + } + + .type-edit-container { + width: 100%; + display: flex; + flex-direction: column; + gap: 8px; + + textarea { + width: 100%; + } + } + } +} diff --git a/templates/settings/homebrew-settings/types.hbs b/templates/settings/homebrew-settings/types.hbs new file mode 100644 index 00000000..f9d3bba3 --- /dev/null +++ b/templates/settings/homebrew-settings/types.hbs @@ -0,0 +1,28 @@ +
+
+ + {{localize "DAGGERHEART.SETTINGS.Homebrew.adversaryType.title"}} + + + +
+ {{#each settingFields.adversaryTypes as |type key|}} +
+ {{type.label}} +
+
+ {{/each}} +
+ + {{#if selectedAdversaryType}} +
+ {{formGroup settingFields.schema.fields.adversaryTypes.element.fields.label name=(concat "adversaryTypes." selectedAdversaryType.id ".label") value=selectedAdversaryType.label localize=true }} + +
+ {{/if}} +
+
\ No newline at end of file diff --git a/templates/sheets/actors/adversary/header.hbs b/templates/sheets/actors/adversary/header.hbs index 8411dd93..e6f829b8 100644 --- a/templates/sheets/actors/adversary/header.hbs +++ b/templates/sheets/actors/adversary/header.hbs @@ -13,9 +13,7 @@
- - {{localize (concat 'DAGGERHEART.CONFIG.AdversaryType.' source.system.type '.label')}} - + {{adversaryType}}
{{#if (eq source.system.type 'horde')}}
diff --git a/templates/ui/tooltip/adversary.hbs b/templates/ui/tooltip/adversary.hbs index b400bd29..86c399c5 100644 --- a/templates/ui/tooltip/adversary.hbs +++ b/templates/ui/tooltip/adversary.hbs @@ -12,7 +12,7 @@
- {{#with (lookup config.ACTOR.adversaryTypes item.system.type) as | type |}} + {{#with (lookup adversaryTypes item.system.type) as | type |}}
{{localize type.label}}
{{/with}}
From b83adbf09ba0b427d1c0db739c57e102f800bf30 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Sat, 23 Aug 2025 18:32:17 +1000 Subject: [PATCH 13/66] Fixed the Cult Initiate Group Attack to use Fear instead of marking Stress (#1062) Co-authored-by: Chris Ryan --- .../adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json b/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json index fe8d090f..b46dc3a0 100644 --- a/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json +++ b/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json @@ -265,7 +265,7 @@ "cost": [ { "scalable": false, - "key": "stress", + "key": "fear", "value": 1, "keyIsID": false, "step": null @@ -281,7 +281,7 @@ "type": "self", "amount": null }, - "name": "Mark Stress", + "name": "Spend Fear", "img": "icons/creatures/abilities/tail-strike-bone-orange.webp", "range": "" } From 2d20fb0df447ba50fbe8b545f6f0a6c736489407 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 23 Aug 2025 13:18:14 +0200 Subject: [PATCH 14/66] Fixed multiclass --- module/data/actor/character.mjs | 5 +++-- module/data/item/base.mjs | 4 ++-- module/data/item/class.mjs | 9 --------- module/data/item/feature.mjs | 1 + module/documents/actor.mjs | 4 ++-- 5 files changed, 8 insertions(+), 15 deletions(-) diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 7dd7993c..7ddd878c 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -317,7 +317,7 @@ export default class DhCharacter extends BaseDataActor { } get multiclass() { - const value = this.parent.items.find(x => x.type === 'Class' && x.system.isMulticlass); + const value = this.parent.items.find(x => x.type === 'class' && x.system.isMulticlass); const subclass = this.parent.items.find(x => x.type === 'subclass' && x.system.isMulticlass); return { @@ -443,7 +443,8 @@ export default class DhCharacter extends BaseDataActor { classFeatures.push(item); } else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) { if (this.class.subclass) { - const subclassState = this.class.subclass.system.featureState; + const prop = item.system.multiclassOrigin ? 'multiclass' : 'class'; + const subclassState = this[prop].subclass.system.featureState; if ( item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.foundation || diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index f333537b..f8eae265 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -148,7 +148,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { for (let f of this.features) { const fBase = f.item ?? f; const feature = fBase.system ? fBase : await foundry.utils.fromUuid(fBase.uuid); - const multiclass = this.isMulticlass ? 'multiclass' : null; features.push( foundry.utils.mergeObject( feature.toObject(), @@ -156,7 +155,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { _stats: { compendiumSource: fBase.uuid }, system: { originItemType: this.parent.type, - identifier: multiclass ?? (f.item ? f.type : null) + identifier: f.item ? f.type : null, + multiclassOrigin: this.isMulticlass } }, { inplace: false } diff --git a/module/data/item/class.mjs b/module/data/item/class.mjs index 5e92d2fc..157f70c5 100644 --- a/module/data/item/class.mjs +++ b/module/data/item/class.mjs @@ -102,15 +102,6 @@ export default class DHClass extends BaseDataItem { if (allowed === false) return; } - _onDelete(options, userId) { - super._onDelete(options, userId); - - if (options.parent?.type === 'character') { - const path = `system.${this.isMulticlass ? 'multiclass' : 'class'}`; - foundry.utils.getProperty(options.parent, `${path}.subclass`)?.delete(); - } - } - async _preUpdate(changed, options, userId) { const allowed = await super._preUpdate(changed, options, userId); if (allowed === false) return false; diff --git a/module/data/item/feature.mjs b/module/data/item/feature.mjs index 6e1aab41..3b8fe064 100644 --- a/module/data/item/feature.mjs +++ b/module/data/item/feature.mjs @@ -29,6 +29,7 @@ export default class DHFeature extends BaseDataItem { nullable: true, initial: null }), + multiclassOrigin: new fields.BooleanField({ initial: false }), identifier: new fields.StringField() }; } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index e1dd93af..3325547c 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -164,10 +164,10 @@ export default class DhpActor extends Actor { if (multiclass) { const multiclassItem = this.items.find(x => x.uuid === multiclass.itemUuid); const multiclassFeatures = this.items.filter( - x => x.system.originItemType === 'class' && x.system.identifier === 'multiclass' + x => x.system.originItemType === 'class' && x.system.multiclassOrigin ); const subclassFeatures = this.items.filter( - x => x.system.originItemType === 'subclass' && x.system.identifier === 'multiclass' + x => x.system.originItemType === 'subclass' && x.system.multiclassOrigin ); this.deleteEmbeddedDocuments( From f76515eac1dddc2fca6c5f34ad574ed174089bb4 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 23 Aug 2025 13:20:02 +0200 Subject: [PATCH 15/66] Revert "Fixed multiclass" This reverts commit 2d20fb0df447ba50fbe8b545f6f0a6c736489407. --- module/data/actor/character.mjs | 5 ++--- module/data/item/base.mjs | 4 ++-- module/data/item/class.mjs | 9 +++++++++ module/data/item/feature.mjs | 1 - module/documents/actor.mjs | 4 ++-- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 7ddd878c..7dd7993c 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -317,7 +317,7 @@ export default class DhCharacter extends BaseDataActor { } get multiclass() { - const value = this.parent.items.find(x => x.type === 'class' && x.system.isMulticlass); + const value = this.parent.items.find(x => x.type === 'Class' && x.system.isMulticlass); const subclass = this.parent.items.find(x => x.type === 'subclass' && x.system.isMulticlass); return { @@ -443,8 +443,7 @@ export default class DhCharacter extends BaseDataActor { classFeatures.push(item); } else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) { if (this.class.subclass) { - const prop = item.system.multiclassOrigin ? 'multiclass' : 'class'; - const subclassState = this[prop].subclass.system.featureState; + const subclassState = this.class.subclass.system.featureState; if ( item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.foundation || diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index f8eae265..f333537b 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -148,6 +148,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { for (let f of this.features) { const fBase = f.item ?? f; const feature = fBase.system ? fBase : await foundry.utils.fromUuid(fBase.uuid); + const multiclass = this.isMulticlass ? 'multiclass' : null; features.push( foundry.utils.mergeObject( feature.toObject(), @@ -155,8 +156,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { _stats: { compendiumSource: fBase.uuid }, system: { originItemType: this.parent.type, - identifier: f.item ? f.type : null, - multiclassOrigin: this.isMulticlass + identifier: multiclass ?? (f.item ? f.type : null) } }, { inplace: false } diff --git a/module/data/item/class.mjs b/module/data/item/class.mjs index 157f70c5..5e92d2fc 100644 --- a/module/data/item/class.mjs +++ b/module/data/item/class.mjs @@ -102,6 +102,15 @@ export default class DHClass extends BaseDataItem { if (allowed === false) return; } + _onDelete(options, userId) { + super._onDelete(options, userId); + + if (options.parent?.type === 'character') { + const path = `system.${this.isMulticlass ? 'multiclass' : 'class'}`; + foundry.utils.getProperty(options.parent, `${path}.subclass`)?.delete(); + } + } + async _preUpdate(changed, options, userId) { const allowed = await super._preUpdate(changed, options, userId); if (allowed === false) return false; diff --git a/module/data/item/feature.mjs b/module/data/item/feature.mjs index 3b8fe064..6e1aab41 100644 --- a/module/data/item/feature.mjs +++ b/module/data/item/feature.mjs @@ -29,7 +29,6 @@ export default class DHFeature extends BaseDataItem { nullable: true, initial: null }), - multiclassOrigin: new fields.BooleanField({ initial: false }), identifier: new fields.StringField() }; } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 3325547c..e1dd93af 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -164,10 +164,10 @@ export default class DhpActor extends Actor { if (multiclass) { const multiclassItem = this.items.find(x => x.uuid === multiclass.itemUuid); const multiclassFeatures = this.items.filter( - x => x.system.originItemType === 'class' && x.system.multiclassOrigin + x => x.system.originItemType === 'class' && x.system.identifier === 'multiclass' ); const subclassFeatures = this.items.filter( - x => x.system.originItemType === 'subclass' && x.system.multiclassOrigin + x => x.system.originItemType === 'subclass' && x.system.identifier === 'multiclass' ); this.deleteEmbeddedDocuments( From 471cbd55df7aaf53430846b890848c33ffba84c9 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Sat, 23 Aug 2025 21:24:28 +1000 Subject: [PATCH 16/66] Fixes some measurement issues with templates (#1065) Co-authored-by: Chris Ryan --- module/canvas/placeables/measuredTemplate.mjs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/module/canvas/placeables/measuredTemplate.mjs b/module/canvas/placeables/measuredTemplate.mjs index 49142685..83cddfe1 100644 --- a/module/canvas/placeables/measuredTemplate.mjs +++ b/module/canvas/placeables/measuredTemplate.mjs @@ -10,38 +10,38 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur const splitRulerText = this.ruler.text.split(' '); if (splitRulerText.length > 0) { const rulerValue = Number(splitRulerText[0]); - const result = this.constructor.getRangeLabels(rulerValue, rangeMeasurementSettings); - this.ruler.text = result.distance + result.units ? (' ' + result.units) : ''; + const result = DhMeasuredTemplate.getRangeLabels(rulerValue, rangeMeasurementSettings); + this.ruler.text = result.distance + (result.units ? (' ' + result.units) : ''); } } } - static getRangeLabels(distance, settings) { - let result = { distance: '', units: null } + static getRangeLabels(distanceValue, settings) { + let result = { distance: distanceValue, units: '' } const rangeMeasurementOverride = canvas.scene.flags.daggerheart?.rangeMeasurementOverride; if (rangeMeasurementOverride === true) { - result.distance = distance; + result.distance = distanceValue; result.units = canvas.scene?.grid?.units; return result } - if (distance <= settings.melee) { + if (distanceValue <= settings.melee) { result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.melee.name'); return result; } - if (distance <= settings.veryClose) { + if (distanceValue <= settings.veryClose) { result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.veryClose.name'); return result; } - if (distance <= settings.close) { + if (distanceValue <= settings.close) { result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.close.name'); return result; } - if (distance <= settings.far) { + if (distanceValue <= settings.far) { result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.far.name'); return result; } - if (distance > settings.far) { + if (distanceValue > settings.far) { result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.veryFar.name'); } From 936c96a1bed04e9de4254bcd317fc6d8403cbbc3 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 23 Aug 2025 13:49:10 +0200 Subject: [PATCH 17/66] Fixed so enriched buttons are inline by default. Can be set to 'inline:true' to make them fit with the text better (#1068) --- module/enrichers/DamageEnricher.mjs | 12 ++++++++---- module/enrichers/DualityRollEnricher.mjs | 2 +- module/enrichers/TemplateEnricher.mjs | 8 ++++++-- styles/less/global/enrichment.less | 14 ++++++++++++++ styles/less/global/index.less | 1 + 5 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 styles/less/global/enrichment.less diff --git a/module/enrichers/DamageEnricher.mjs b/module/enrichers/DamageEnricher.mjs index 918edc39..a859be9f 100644 --- a/module/enrichers/DamageEnricher.mjs +++ b/module/enrichers/DamageEnricher.mjs @@ -2,7 +2,8 @@ export default function DhDamageEnricher(match, _options) { const parts = match[1].split('|').map(x => x.trim()); let value = null, - type = null; + type = null, + inline = false; parts.forEach(part => { const split = part.split(':').map(x => x.toLowerCase().trim()); @@ -14,16 +15,19 @@ export default function DhDamageEnricher(match, _options) { case 'type': type = split[1]; break; + case 'inline': + inline = true; + break; } } }); if (!value || !value) return match[0]; - return getDamageMessage(value, type, match[0]); + return getDamageMessage(value, type, inline, match[0]); } -function getDamageMessage(damage, type, defaultElement) { +function getDamageMessage(damage, type, inline, defaultElement) { const typeIcons = type .replace('[', '') .replace(']', '') @@ -40,7 +44,7 @@ function getDamageMessage(damage, type, defaultElement) { const dualityElement = document.createElement('span'); dualityElement.innerHTML = ` - `; diff --git a/styles/less/global/enrichment.less b/styles/less/global/enrichment.less new file mode 100644 index 00000000..2ad3975a --- /dev/null +++ b/styles/less/global/enrichment.less @@ -0,0 +1,14 @@ +.measured-template-button, +.enriched-damage-button, +.duality-roll-button { + display: inline; + + &.inline { + min-height: unset; + height: 18px; + } + + i { + font-size: 12px; + } +} diff --git a/styles/less/global/index.less b/styles/less/global/index.less index d6c8459e..5f955ce3 100644 --- a/styles/less/global/index.less +++ b/styles/less/global/index.less @@ -2,6 +2,7 @@ @import './dialog.less'; @import './chat.less'; @import './elements.less'; +@import './enrichment.less'; @import './tab-navigation.less'; @import './tab-form-footer.less'; @import './tab-actions.less'; From 0c3ebd6e11ed9ec57c5bbf4a04929609be055ee4 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 23 Aug 2025 13:55:31 +0200 Subject: [PATCH 18/66] Fixed description on Dracon and Orderborne (#1067) --- .../feature_Elemental_Breath_sRaE3CgkgjBF1UpV.json | 10 +++++----- .../feature_Dedicated_7aXWdH3gzaYREK0X.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/packs/ancestries/feature_Elemental_Breath_sRaE3CgkgjBF1UpV.json b/src/packs/ancestries/feature_Elemental_Breath_sRaE3CgkgjBF1UpV.json index ccd0f87e..9764c5b4 100644 --- a/src/packs/ancestries/feature_Elemental_Breath_sRaE3CgkgjBF1UpV.json +++ b/src/packs/ancestries/feature_Elemental_Breath_sRaE3CgkgjBF1UpV.json @@ -12,7 +12,7 @@ "type": "attack", "_id": "a6WROv0OKx0lbYVa", "systemPath": "actions", - "description": "", + "description": "

Choose an element for your breath (such as electricity, fire, or ice). You can use this breath against a target or group of targets within Very Close range, treating it as an Instinct weapon that deals d8 magic damage using your Proficiency.

", "chatDisplay": true, "actionType": "action", "cost": [], @@ -95,12 +95,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1753994055921, - "modifiedTime": 1753994120065, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1755938895948, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!sRaE3CgkgjBF1UpV" } diff --git a/src/packs/communities/feature_Dedicated_7aXWdH3gzaYREK0X.json b/src/packs/communities/feature_Dedicated_7aXWdH3gzaYREK0X.json index c93811a9..11ac8208 100644 --- a/src/packs/communities/feature_Dedicated_7aXWdH3gzaYREK0X.json +++ b/src/packs/communities/feature_Dedicated_7aXWdH3gzaYREK0X.json @@ -12,7 +12,7 @@ "type": "effect", "_id": "ZBVqSlsDUKf8uGrI", "systemPath": "actions", - "description": "", + "description": "

Record three sayings or values your upbringing instilled in you. Once per rest, when you describe how you’re embodying one of these principles through your current action, you can roll a d20 as your Hope Die

", "chatDisplay": true, "actionType": "action", "cost": [], @@ -41,12 +41,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754010247432, - "modifiedTime": 1754010247432, - "lastModifiedBy": "Q9NoTaEarn3VMS6Z" + "modifiedTime": 1755938935013, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "sort": 0, "ownership": { From a72d4583cd3167c72327f762a11b51bd2b0f0879 Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Sat, 23 Aug 2025 14:54:57 +0200 Subject: [PATCH 19/66] [PR]Add custom formula to weapon base attack (#964) * Add custom formula to weapon base attack * Remove log * Update weapon custom damage formula label + update font-size in px --- lang/en.json | 9 ++++++ module/data/fields/action/damageField.mjs | 12 ++++---- module/data/item/weapon.mjs | 4 +-- styles/less/dialog/beastform/sheet.less | 4 +-- .../selections-container.less | 20 ++++++------- .../damage-reduction-container.less | 4 +-- .../less/dialog/dice-roll/roll-selection.less | 8 ++--- .../dialog/downtime/downtime-container.less | 6 ++-- .../dialog/level-up/selections-container.less | 8 ++--- .../dialog/level-up/summary-container.less | 6 ++-- .../less/dialog/level-up/tiers-container.less | 4 +-- styles/less/dialog/reroll-dialog/sheet.less | 6 ++-- styles/less/global/chat.less | 2 +- styles/less/global/dialog.less | 2 +- styles/less/global/elements.less | 18 ++++++------ styles/less/global/inventory-item.less | 29 ++++++++++--------- styles/less/global/item-header.less | 6 ++-- styles/less/global/prose-mirror.less | 8 ++--- .../character-settings/sheet.less | 2 +- styles/less/sheets-settings/header.less | 2 +- .../less/sheets/actors/adversary/header.less | 6 ++-- .../less/sheets/actors/adversary/sidebar.less | 12 ++++---- .../less/sheets/actors/character/header.less | 16 +++++----- .../sheets/actors/character/inventory.less | 2 +- .../less/sheets/actors/character/loadout.less | 2 +- .../less/sheets/actors/character/sidebar.less | 10 +++---- .../less/sheets/actors/companion/details.less | 6 ++-- .../less/sheets/actors/companion/header.less | 6 ++-- .../sheets/actors/environment/header.less | 8 ++--- styles/less/ui/chat/ability-use.less | 4 +-- styles/less/ui/chat/action.less | 4 +-- styles/less/ui/chat/chat.less | 4 +-- styles/less/ui/chat/downtime.less | 4 +-- .../less/ui/combat-sidebar/token-actions.less | 2 +- styles/less/ui/countdown/sheet.less | 2 +- styles/less/ui/item-browser/item-browser.less | 10 +++---- .../settings/homebrew-settings/domains.less | 4 +-- styles/less/ui/settings/settings.less | 6 ++-- styles/less/utils/mixin.less | 6 ++-- styles/less/ux/autocomplete/autocomplete.less | 4 +-- styles/less/ux/tooltip/tooltip.less | 2 +- templates/sheets/items/weapon/header.hbs | 6 +++- templates/sheets/items/weapon/settings.hbs | 15 +++++++--- 43 files changed, 161 insertions(+), 140 deletions(-) diff --git a/lang/en.json b/lang/en.json index 1c614fc2..beeffd41 100755 --- a/lang/en.json +++ b/lang/en.json @@ -72,6 +72,14 @@ "exactHint": "The Character's Tier is used if empty", "label": "Beastform" }, + "damage": { + "multiplier": "Multiplier", + "flatMultiplier": "Flat Multiplier" + }, + "general": { + "customFormula": "Custom Formula", + "formula": "Formula" + }, "displayInChat": "Display in chat" }, "RollField": { @@ -1929,6 +1937,7 @@ "continue": "Continue", "criticalSuccess": "Critical Success", "criticalShort": "Critical", + "custom": "Custom", "d20Roll": "D20 Roll", "damage": "Damage", "damageRoll": "Damage Roll", diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index cf327204..7f1b61e9 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -23,14 +23,14 @@ export class DHActionDiceData extends foundry.abstract.DataModel { multiplier: new fields.StringField({ choices: CONFIG.DH.GENERAL.multiplierTypes, initial: 'prof', - label: 'Multiplier' + label: "DAGGERHEART.ACTIONS.Config.damage.multiplier" }), - flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }), - dice: new fields.StringField({ choices: CONFIG.DH.GENERAL.diceTypes, initial: 'd6', label: 'Dice' }), - bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }), + flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: "DAGGERHEART.ACTIONS.Config.damage.flatMultiplier" }), + dice: new fields.StringField({ choices: CONFIG.DH.GENERAL.diceTypes, initial: 'd6', label: "DAGGERHEART.GENERAL.Dice.single" }), + bonus: new fields.NumberField({ nullable: true, initial: null, label: "DAGGERHEART.GENERAL.bonus" }), custom: new fields.SchemaField({ - enabled: new fields.BooleanField({ label: 'Custom Formula' }), - formula: new FormulaField({ label: 'Formula', initial: '' }) + enabled: new fields.BooleanField({ label: "DAGGERHEART.ACTIONS.Config.general.customFormula" }), + formula: new FormulaField({ label: "DAGGERHEART.ACTIONS.Config.general.formula", initial: '' }) }) }; } diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 66025cc5..ee4b997c 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -199,8 +199,8 @@ export default class DHWeapon extends AttachableItem { ]; for (const { value, type } of attack.damage.parts) { - const parts = [value.dice]; - if (value.bonus) parts.push(value.bonus.signedString()); + const parts = value.custom.enabled ? [game.i18n.localize("DAGGERHEART.GENERAL.custom")] : [value.dice]; + if (!value.custom.enabled && value.bonus) parts.push(value.bonus.signedString()); if (type.size > 0) { const typeTags = Array.from(type) diff --git a/styles/less/dialog/beastform/sheet.less b/styles/less/dialog/beastform/sheet.less index c13ee95e..9e87f53b 100644 --- a/styles/less/dialog/beastform/sheet.less +++ b/styles/less/dialog/beastform/sheet.less @@ -41,7 +41,7 @@ display: flex; flex-wrap: wrap; text-align: center; - font-size: 16px; + font-size: var(--font-size-16); margin: 0 4px; border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; @@ -135,7 +135,7 @@ align-items: center; i { - font-size: 24px; + font-size: var(--font-size-24); } } diff --git a/styles/less/dialog/character-creation/selections-container.less b/styles/less/dialog/character-creation/selections-container.less index bc7a6987..3b93313a 100644 --- a/styles/less/dialog/character-creation/selections-container.less +++ b/styles/less/dialog/character-creation/selections-container.less @@ -75,7 +75,7 @@ label { position: absolute; - font-size: 18px; + font-size: var(--font-size-18); font-weight: bold; padding: 0 2px; background-image: url(../assets/parchments/dh-parchment-light.png); @@ -141,7 +141,7 @@ .ancestry-preview-feature { flex: 1; - font-size: 14px; + font-size: var(--font-size-14); white-space: wrap; padding: 0 2px; border: 1px solid light-dark(@golden, @dark-blue); @@ -178,7 +178,7 @@ legend { margin-left: auto; margin-right: auto; - font-size: 28px; + font-size: var(--font-size-28); font-weight: bold; padding: 0 8px; } @@ -191,7 +191,7 @@ justify-content: center; legend { - font-size: 20px; + font-size: var(--font-size-20); white-space: nowrap; } @@ -343,7 +343,7 @@ border-radius: 50%; height: 20px; width: 20px; - font-size: 14px; + font-size: var(--font-size-14); display: flex; align-items: center; justify-content: center; @@ -358,7 +358,7 @@ .descriptor { position: absolute; bottom: -8px; - font-size: 12px; + font-size: var(--font-size-12); border-radius: 8px; width: 56px; text-align: center; @@ -400,7 +400,7 @@ legend { margin-left: auto; margin-right: auto; - font-size: 28px; + font-size: var(--font-size-28); font-weight: bold; padding: 0 8px; white-space: nowrap; @@ -444,7 +444,7 @@ label { position: absolute; top: -8px; - font-size: 12px; + font-size: var(--font-size-12); white-space: nowrap; border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; @@ -472,7 +472,7 @@ legend { margin-left: auto; margin-right: auto; - font-size: 12px; + font-size: var(--font-size-12); } .suggestion-inner-container { @@ -490,7 +490,7 @@ label { position: absolute; top: -2px; - font-size: 12px; + font-size: var(--font-size-12); } img { diff --git a/styles/less/dialog/damage-reduction/damage-reduction-container.less b/styles/less/dialog/damage-reduction/damage-reduction-container.less index 9e1d1472..2f343fb3 100644 --- a/styles/less/dialog/damage-reduction/damage-reduction-container.less +++ b/styles/less/dialog/damage-reduction/damage-reduction-container.less @@ -76,7 +76,7 @@ border-radius: 6px; height: 26px; padding: 0 1px; - font-size: 18px; + font-size: var(--font-size-18); display: flex; align-items: center; justify-content: center; @@ -108,7 +108,7 @@ border-radius: 6px; height: 26px; padding: 0 4px; - font-size: 18px; + font-size: var(--font-size-18); display: flex; align-items: center; justify-content: center; diff --git a/styles/less/dialog/dice-roll/roll-selection.less b/styles/less/dialog/dice-roll/roll-selection.less index 9113bc03..a0ac42b6 100644 --- a/styles/less/dialog/dice-roll/roll-selection.less +++ b/styles/less/dialog/dice-roll/roll-selection.less @@ -23,7 +23,7 @@ width: auto; opacity: 0.3; border-radius: 50%; - font-size: 18px; + font-size: var(--font-size-18); font-weight: bold; &:hover { @@ -74,7 +74,7 @@ font-family: @font-subtitle; font-style: normal; font-weight: 700; - font-size: 16px; + font-size: var(--font-size-16); line-height: 19px; color: light-dark(@dark, @beige); @@ -102,7 +102,7 @@ .label { font-style: normal; font-weight: 400; - font-size: 14px; + font-size: var(--font-size-14); line-height: 17px; } @@ -127,7 +127,7 @@ .label { font-style: normal; font-weight: 400; - font-size: 14px; + font-size: var(--font-size-14); line-height: 17px; } diff --git a/styles/less/dialog/downtime/downtime-container.less b/styles/less/dialog/downtime/downtime-container.less index f9f8df17..16edd3b0 100644 --- a/styles/less/dialog/downtime/downtime-container.less +++ b/styles/less/dialog/downtime/downtime-container.less @@ -35,7 +35,7 @@ gap: 4px; .activity-marker { - font-size: 8px; + font-size: .5rem; flex: none; color: light-dark(@dark-blue, @golden); margin-right: 4px; @@ -54,7 +54,7 @@ } .activity-selected-marker { - font-size: 14px; + font-size: var(--font-size-14); border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; color: light-dark(@dark, @beige); @@ -71,7 +71,7 @@ display: grid; grid-template-columns: 1fr 1fr; gap: 4px; - font-size: 12px; + font-size: var(--font-size-12); &.wide { grid-template-columns: 1fr 1fr 1fr 1fr; diff --git a/styles/less/dialog/level-up/selections-container.less b/styles/less/dialog/level-up/selections-container.less index 96cadd29..6a551865 100644 --- a/styles/less/dialog/level-up/selections-container.less +++ b/styles/less/dialog/level-up/selections-container.less @@ -21,7 +21,7 @@ background: light-dark(@dark-blue-40, @golden-40); border-radius: 3px; padding: 5px; - font-size: 16px; + font-size: var(--font-size-16); gap: 4px; width: 100%; @@ -38,7 +38,7 @@ display: flex; align-items: center; justify-content: center; - font-size: 12px; + font-size: var(--font-size-12); } } } @@ -96,7 +96,7 @@ width: 54px; border-radius: 50%; border: 2px solid; - font-size: 48px; + font-size: var(--font-size-48); display: flex; align-items: center; justify-content: center; @@ -133,7 +133,7 @@ .levelup-selections-title { margin-left: auto; margin-right: auto; - font-size: 22px; + font-size: 1.375rem; font-weight: bold; padding: 0 12px; } diff --git a/styles/less/dialog/level-up/summary-container.less b/styles/less/dialog/level-up/summary-container.less index f192d5ec..d67abff6 100644 --- a/styles/less/dialog/level-up/summary-container.less +++ b/styles/less/dialog/level-up/summary-container.less @@ -41,7 +41,7 @@ display: flex; align-items: center; gap: 4px; - font-size: 14px; + font-size: var(--font-size-14); color: light-dark(@dark, @beige); } @@ -49,7 +49,7 @@ display: flex; align-items: center; gap: 4px; - font-size: 16px; + font-size: var(--font-size-16); color: light-dark(@dark, @beige); margin-bottom: 5px; } @@ -62,7 +62,7 @@ border: 2px solid; border-radius: 3px; padding: 0 4px; - font-size: 14px; + font-size: var(--font-size-14); color: light-dark(@dark, @beige); } } diff --git a/styles/less/dialog/level-up/tiers-container.less b/styles/less/dialog/level-up/tiers-container.less index d4efa46b..270b9b80 100644 --- a/styles/less/dialog/level-up/tiers-container.less +++ b/styles/less/dialog/level-up/tiers-container.less @@ -21,7 +21,7 @@ legend { margin-left: auto; margin-right: auto; - font-size: 22px; + font-size: 1.375rem; font-weight: bold; padding: 0 12px; } @@ -60,7 +60,7 @@ } .checkbox-group-label { - font-size: 12px; + font-size: var(--font-size-12); font-style: italic; } } diff --git a/styles/less/dialog/reroll-dialog/sheet.less b/styles/less/dialog/reroll-dialog/sheet.less index f8687009..71c94d80 100644 --- a/styles/less/dialog/reroll-dialog/sheet.less +++ b/styles/less/dialog/reroll-dialog/sheet.less @@ -37,7 +37,7 @@ display: flex; align-items: center; justify-content: center; - font-size: 22px; + font-size: 1.375rem; opacity: 0.8; &.selected { @@ -99,12 +99,12 @@ &:before, &:after { line-height: 12px; - font-size: 12px; + font-size: var(--font-size-12); } } i { - font-size: 10px; + font-size: var(--font-size-10); } } } diff --git a/styles/less/global/chat.less b/styles/less/global/chat.less index bf29a05c..d2822b1b 100644 --- a/styles/less/global/chat.less +++ b/styles/less/global/chat.less @@ -71,7 +71,7 @@ color: @beige; h4 { - font-size: 16px; + font-size: var(--font-size-16); font-weight: bold; margin-bottom: 0; font-family: @font-subtitle; diff --git a/styles/less/global/dialog.less b/styles/less/global/dialog.less index 42fdb07e..f164b701 100644 --- a/styles/less/global/dialog.less +++ b/styles/less/global/dialog.less @@ -50,7 +50,7 @@ .formula-label { font-style: normal; font-weight: 500; - font-size: 14px; + font-size: var(--font-size-14); line-height: 17px; white-space: nowrap; color: light-dark(@dark, @beige); diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index 2f4912c5..397420b2 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -165,7 +165,7 @@ z-index: 1; .remove { - font-size: 10px; + font-size: var(--font-size-10); } } } @@ -400,7 +400,7 @@ display: flex; flex-direction: column; white-space: nowrap; - font-size: 14px; + font-size: var(--font-size-14); font-weight: 400; &.modifier-label { @@ -527,7 +527,7 @@ font-family: @font-body; margin-top: 4px; color: light-dark(#14142599, #efe6d850); - font-size: 12px; + font-size: var(--font-size-12); padding-left: 3px; } } @@ -541,7 +541,7 @@ text-align: center; } .title-hint { - font-size: 12px; + font-size: var(--font-size-12); font-variant: small-caps; text-align: center; } @@ -605,7 +605,7 @@ align-items: center; label { - font-size: 16px; + font-size: var(--font-size-16); } .form-fields { @@ -776,7 +776,7 @@ .preview-text-container { padding: 10px 0; text-align: center; - font-size: 16px; + font-size: var(--font-size-16); color: light-dark(@beige, @dark); background-image: url(../assets/parchments/dh-parchment-light.png); border-radius: 0 0 4px 4px; @@ -799,14 +799,14 @@ justify-content: center; .preview-add-icon { - font-size: 40px; + font-size: var(--font-size-40); color: light-dark(@dark-blue-50, @beige-50); } .preview-empty-subtext { position: absolute; bottom: 5%; - font-size: 10px; + font-size: var(--font-size-10); font-variant: small-caps; text-align: center; font-style: italic; @@ -821,7 +821,7 @@ width: 54px; border-radius: 50%; border: 2px solid; - font-size: 48px; + font-size: var(--font-size-48); display: flex; align-items: center; justify-content: center; diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index d63c658e..e221f4e7 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -105,7 +105,7 @@ align-self: center; .item-name { - font-size: 14px; + font-size: var(--font-size-14); .expanded-icon { display: none; @@ -121,9 +121,10 @@ .label { display: flex; flex-direction: row; - justify-content: center; align-items: center; - font-size: 12px; + font-size: var(--font-size-12); + flex-wrap: wrap; + justify-content: start; } .tag { @@ -179,18 +180,18 @@ overflow: hidden; h1 { - font-size: 32px; + font-size: var(--font-size-32); } h2 { - font-size: 28px; + font-size: var(--font-size-28); font-weight: 600; } h3 { - font-size: 20px; + font-size: var(--font-size-20); font-weight: 600; } h4 { - font-size: 16px; + font-size: var(--font-size-16); color: @beige; font-weight: 600; } @@ -231,7 +232,7 @@ label { color: light-dark(white, black); filter: drop-shadow(0 0 1px light-dark(@dark-blue, @golden)); - font-size: 18px; + font-size: var(--font-size-18); } img { @@ -243,7 +244,7 @@ text-shadow: 0 0 3px white; filter: drop-shadow(0 1px white); color: black; - font-size: 26px; + font-size: 1.625rem; } } } @@ -310,7 +311,7 @@ .card-name { font-style: normal; font-weight: 400; - font-size: 12px; + font-size: var(--font-size-12); line-height: 15px; color: @beige; @@ -351,7 +352,7 @@ gap: 4px; .resource-edit { - font-size: 14px; + font-size: var(--font-size-14); } } @@ -363,7 +364,7 @@ i { flex: none; - font-size: 14px; + font-size: var(--font-size-14); } input { @@ -383,7 +384,7 @@ color: light-dark(white, black); filter: drop-shadow(0 0 1px light-dark(@dark-blue, @golden)); z-index: 2; - font-size: 18px; + font-size: var(--font-size-18); cursor: pointer; } @@ -397,7 +398,7 @@ text-shadow: 0 0 3px white; filter: drop-shadow(0 1px white); color: black; - font-size: 26px; + font-size: 1.625rem; } } } diff --git a/styles/less/global/item-header.less b/styles/less/global/item-header.less index 7b2c907f..073762e0 100755 --- a/styles/less/global/item-header.less +++ b/styles/less/global/item-header.less @@ -35,7 +35,7 @@ width: 80%; .item-name input[type='text'] { - font-size: 32px; + font-size: var(--font-size-32); height: 42px; text-align: center; width: 90%; @@ -103,7 +103,7 @@ transition: all 0.3s ease; .recall-label { - font-size: 14px; + font-size: var(--font-size-14); opacity: 0; margin-right: 0.3rem; transition: all 0.3s ease; @@ -141,7 +141,7 @@ .item-name { input[type='text'] { - font-size: 32px; + font-size: var(--font-size-32); height: 42px; text-align: center; width: 90%; diff --git a/styles/less/global/prose-mirror.less b/styles/less/global/prose-mirror.less index cb7933a4..506fb8b7 100644 --- a/styles/less/global/prose-mirror.less +++ b/styles/less/global/prose-mirror.less @@ -12,18 +12,18 @@ scrollbar-width: thin; scrollbar-color: light-dark(@dark-blue, @golden) transparent; h1 { - font-size: 32px; + font-size: var(--font-size-32); } h2 { - font-size: 28px; + font-size: var(--font-size-28); font-weight: 600; } h3 { - font-size: 20px; + font-size: var(--font-size-20); font-weight: 600; } h4 { - font-size: 16px; + font-size: var(--font-size-16); color: @beige; font-weight: 600; } diff --git a/styles/less/sheets-settings/character-settings/sheet.less b/styles/less/sheets-settings/character-settings/sheet.less index 78bbf9c5..f0c7c94e 100644 --- a/styles/less/sheets-settings/character-settings/sheet.less +++ b/styles/less/sheets-settings/character-settings/sheet.less @@ -33,7 +33,7 @@ div { filter: drop-shadow(0 0 3px black); text-shadow: 0 0 3px black; - font-size: 12px; + font-size: var(--font-size-12); } input { diff --git a/styles/less/sheets-settings/header.less b/styles/less/sheets-settings/header.less index 6ac2663f..82f3c488 100644 --- a/styles/less/sheets-settings/header.less +++ b/styles/less/sheets-settings/header.less @@ -10,7 +10,7 @@ font-family: @font-subtitle; font-style: normal; font-weight: 700; - font-size: 24px; + font-size: var(--font-size-24); margin: 0; text-align: center; color: light-dark(@dark-blue, @golden); diff --git a/styles/less/sheets/actors/adversary/header.less b/styles/less/sheets/actors/adversary/header.less index 22a769ae..d4a7812e 100644 --- a/styles/less/sheets/actors/adversary/header.less +++ b/styles/less/sheets/actors/adversary/header.less @@ -18,7 +18,7 @@ flex: 1; input[type='text'] { - font-size: 32px; + font-size: var(--font-size-32); height: 42px; text-align: start; border: 1px solid transparent; @@ -42,7 +42,7 @@ justify-content: center; align-items: center; padding: 3px 5px; - font-size: 12px; + font-size: var(--font-size-12); font: @font-body; background: light-dark(@dark-15, @beige-15); @@ -55,7 +55,7 @@ flex-direction: row; justify-content: center; align-items: center; - font-size: 12px; + font-size: var(--font-size-12); } } diff --git a/styles/less/sheets/actors/adversary/sidebar.less b/styles/less/sheets/actors/adversary/sidebar.less index 70cd92ed..ab15fa46 100644 --- a/styles/less/sheets/actors/adversary/sidebar.less +++ b/styles/less/sheets/actors/adversary/sidebar.less @@ -74,7 +74,7 @@ height: 30px; h4 { - font-size: 14px; + font-size: var(--font-size-14); font-weight: bold; text-transform: uppercase; color: light-dark(@dark-blue, @golden); @@ -255,7 +255,7 @@ font-weight: bold; text-align: center; line-height: 18px; - font-size: 12px; + font-size: var(--font-size-12); color: light-dark(@beige, @dark-blue); } } @@ -295,7 +295,7 @@ align-items: center; h3 { - font-size: 20px; + font-size: var(--font-size-20); } } .items-list { @@ -315,7 +315,7 @@ align-items: center; h3 { - font-size: 20px; + font-size: var(--font-size-20); } } @@ -337,7 +337,7 @@ .experience-name { width: 180px; text-align: start; - font-size: 14px; + font-size: var(--font-size-14); color: light-dark(@dark, @beige); } } @@ -345,7 +345,7 @@ .experience-value { height: 25px; width: 35px; - font-size: 14px; + font-size: var(--font-size-14); color: light-dark(@dark, @beige); align-content: center; text-align: center; diff --git a/styles/less/sheets/actors/character/header.less b/styles/less/sheets/actors/character/header.less index 2d261a6a..80089cf7 100644 --- a/styles/less/sheets/actors/character/header.less +++ b/styles/less/sheets/actors/character/header.less @@ -41,7 +41,7 @@ flex: 1; input[type='text'] { - font-size: 32px; + font-size: var(--font-size-32); height: 42px; text-align: start; border: 1px solid transparent; @@ -72,7 +72,7 @@ .level-button { color: light-dark(@dark, @beige); - font-size: 18px; + font-size: var(--font-size-18); line-height: 1; min-height: unset; height: min-content; @@ -97,7 +97,7 @@ justify-content: space-between; padding: 5px 0; margin-bottom: 10px; - font-size: 12px; + font-size: var(--font-size-12); color: light-dark(@dark-blue, @golden); .missing-header-feature { @@ -158,7 +158,7 @@ height: 30px; h4 { - font-size: 14px; + font-size: var(--font-size-14); font-weight: bold; text-transform: uppercase; color: light-dark(@dark-blue, @golden); @@ -170,7 +170,7 @@ gap: 5px; .label { - font-size: 14px; + font-size: var(--font-size-14); font-weight: bold; text-transform: uppercase; color: light-dark(@dark-blue, @golden); @@ -205,7 +205,7 @@ align-items: center; padding-top: 5px; color: light-dark(@dark-blue, @golden); - font-size: 14px; + font-size: var(--font-size-14); font-weight: 600; align-items: center; justify-content: center; @@ -213,14 +213,14 @@ i { line-height: 17px; - font-size: 10px; + font-size: var(--font-size-10); } } .trait-value { font-style: normal; font-weight: 400; - font-size: 20px; + font-size: var(--font-size-20); text-align: center; } } diff --git a/styles/less/sheets/actors/character/inventory.less b/styles/less/sheets/actors/character/inventory.less index 0870c0c3..9fd48921 100644 --- a/styles/less/sheets/actors/character/inventory.less +++ b/styles/less/sheets/actors/character/inventory.less @@ -41,7 +41,7 @@ height: 32px; position: absolute; right: 20px; - font-size: 16px; + font-size: var(--font-size-16); z-index: 1; color: light-dark(@dark-blue-50, @beige-50); } diff --git a/styles/less/sheets/actors/character/loadout.less b/styles/less/sheets/actors/character/loadout.less index 35dffb79..08246efb 100644 --- a/styles/less/sheets/actors/character/loadout.less +++ b/styles/less/sheets/actors/character/loadout.less @@ -41,7 +41,7 @@ height: 32px; position: absolute; right: 20px; - font-size: 16px; + font-size: var(--font-size-16); z-index: 1; color: light-dark(@dark-blue-50, @beige-50); } diff --git a/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less index 3ff8576d..3d244cdd 100644 --- a/styles/less/sheets/actors/character/sidebar.less +++ b/styles/less/sheets/actors/character/sidebar.less @@ -89,7 +89,7 @@ transition: all 0.3s ease; .spellcast-label { - font-size: 14px; + font-size: var(--font-size-14); opacity: 0; margin-right: 0.3rem; transition: all 0.3s ease; @@ -258,7 +258,7 @@ text-align: center; line-height: 18px; color: light-dark(@beige, @dark-blue); - font-size: 12px; + font-size: var(--font-size-12); } } .status-value { @@ -402,7 +402,7 @@ font-weight: bold; text-align: center; line-height: 18px; - font-size: 12px; + font-size: var(--font-size-12); color: light-dark(@beige, @dark-blue); } } @@ -424,7 +424,7 @@ height: 30px; h4 { - font-size: 14px; + font-size: var(--font-size-14); font-weight: bold; text-transform: uppercase; color: light-dark(@dark-blue, @golden); @@ -490,7 +490,7 @@ .experience-value { height: 25px; width: 35px; - font-size: 14px; + font-size: var(--font-size-14); color: light-dark(@dark, @beige); align-content: center; text-align: center; diff --git a/styles/less/sheets/actors/companion/details.less b/styles/less/sheets/actors/companion/details.less index 9823825f..2df14b23 100644 --- a/styles/less/sheets/actors/companion/details.less +++ b/styles/less/sheets/actors/companion/details.less @@ -16,7 +16,7 @@ width: 100%; h3 { - font-size: 20px; + font-size: var(--font-size-20); } } .items-list { @@ -58,7 +58,7 @@ .experience-name { width: 180px; text-align: start; - font-size: 14px; + font-size: var(--font-size-14); color: light-dark(@dark, @beige); } } @@ -66,7 +66,7 @@ .experience-value { height: 25px; width: 35px; - font-size: 14px; + font-size: var(--font-size-14); color: light-dark(@dark, @beige); align-content: center; text-align: center; diff --git a/styles/less/sheets/actors/companion/header.less b/styles/less/sheets/actors/companion/header.less index 240f9df8..b85a1819 100644 --- a/styles/less/sheets/actors/companion/header.less +++ b/styles/less/sheets/actors/companion/header.less @@ -24,7 +24,7 @@ margin-bottom: -30px; input[type='text'] { - font-size: 24px; + font-size: var(--font-size-24); height: 32px; text-align: center; border: 1px solid transparent; @@ -78,7 +78,7 @@ font-weight: bold; text-align: center; line-height: 18px; - font-size: 12px; + font-size: var(--font-size-12); color: light-dark(@beige, @dark-blue); } } @@ -209,7 +209,7 @@ .level-button { color: light-dark(@dark, @beige); - font-size: 18px; + font-size: var(--font-size-18); line-height: 1; min-height: unset; height: min-content; diff --git a/styles/less/sheets/actors/environment/header.less b/styles/less/sheets/actors/environment/header.less index 0ac361a1..670f6c92 100644 --- a/styles/less/sheets/actors/environment/header.less +++ b/styles/less/sheets/actors/environment/header.less @@ -39,7 +39,7 @@ justify-content: center; align-items: center; padding: 3px 5px; - font-size: 12px; + font-size: var(--font-size-12); font: @font-body; background: light-dark(@dark-15, @beige-15); @@ -52,7 +52,7 @@ flex-direction: row; justify-content: center; align-items: center; - font-size: 12px; + font-size: var(--font-size-12); } } @@ -100,7 +100,7 @@ font-weight: bold; text-align: center; line-height: 18px; - font-size: 12px; + font-size: var(--font-size-12); color: light-dark(@beige, @dark-blue); } } @@ -108,7 +108,7 @@ .item-name { input[type='text'] { - font-size: 32px; + font-size: var(--font-size-32); height: 42px; text-align: start; transition: all 0.3s ease; diff --git a/styles/less/ui/chat/ability-use.less b/styles/less/ui/chat/ability-use.less index 58897697..d313638f 100644 --- a/styles/less/ui/chat/ability-use.less +++ b/styles/less/ui/chat/ability-use.less @@ -87,7 +87,7 @@ gap: 5px; .title { - font-size: 20px; + font-size: var(--font-size-20); color: @golden; font-weight: 700; } @@ -103,7 +103,7 @@ justify-content: center; align-items: center; padding: 3px 5px; - font-size: 12px; + font-size: var(--font-size-12); background: @beige-15; border: 1px solid @beige; diff --git a/styles/less/ui/chat/action.less b/styles/less/ui/chat/action.less index 82cc3210..a849315a 100644 --- a/styles/less/ui/chat/action.less +++ b/styles/less/ui/chat/action.less @@ -81,13 +81,13 @@ gap: 5px; .title { - font-size: 20px; + font-size: var(--font-size-20); color: @golden; font-weight: 700; } .label { - font-size: 12px; + font-size: var(--font-size-12); color: @beige; margin: 0; } diff --git a/styles/less/ui/chat/chat.less b/styles/less/ui/chat/chat.less index c6ed95ca..9eb35cd6 100644 --- a/styles/less/ui/chat/chat.less +++ b/styles/less/ui/chat/chat.less @@ -49,7 +49,7 @@ &.resource-roll { .reroll-message { text-align: center; - font-size: 18px; + font-size: var(--font-size-18); margin-bottom: 0; } } @@ -191,7 +191,7 @@ position: absolute; top: 0; right: 0; - font-size: 10px; + font-size: var(--font-size-10); z-index: 2; filter: drop-shadow(0 0 3px black); } diff --git a/styles/less/ui/chat/downtime.less b/styles/less/ui/chat/downtime.less index 8b898c43..ca29e85f 100644 --- a/styles/less/ui/chat/downtime.less +++ b/styles/less/ui/chat/downtime.less @@ -82,12 +82,12 @@ .header-label { padding: 8px; .title { - font-size: 16px; + font-size: var(--font-size-16); color: @golden; font-weight: 700; } .label { - font-size: 12px; + font-size: var(--font-size-12); color: @beige; margin: 0; } diff --git a/styles/less/ui/combat-sidebar/token-actions.less b/styles/less/ui/combat-sidebar/token-actions.less index 6fc84d29..41fb38ab 100644 --- a/styles/less/ui/combat-sidebar/token-actions.less +++ b/styles/less/ui/combat-sidebar/token-actions.less @@ -17,7 +17,7 @@ display: flex; align-items: center; justify-content: center; - font-size: 10px; + font-size: var(--font-size-10); padding: 8px; --button-size: 0; diff --git a/styles/less/ui/countdown/sheet.less b/styles/less/ui/countdown/sheet.less index 1692773e..0ce7c4af 100644 --- a/styles/less/ui/countdown/sheet.less +++ b/styles/less/ui/countdown/sheet.less @@ -47,7 +47,7 @@ position: absolute; top: 8px; right: 8px; - font-size: 18px; + font-size: var(--font-size-18); } .countdown-container { diff --git a/styles/less/ui/item-browser/item-browser.less b/styles/less/ui/item-browser/item-browser.less index 7be45b8c..d891e73d 100644 --- a/styles/less/ui/item-browser/item-browser.less +++ b/styles/less/ui/item-browser/item-browser.less @@ -161,7 +161,7 @@ height: 32px; position: absolute; right: 20px; - font-size: 16px; + font-size: var(--font-size-16); z-index: 1; color: light-dark(@dark-blue-50, @beige-50); } @@ -304,18 +304,18 @@ gap: 5px; h1 { - font-size: 32px; + font-size: var(--font-size-32); } h2 { - font-size: 28px; + font-size: var(--font-size-28); font-weight: 600; } h3 { - font-size: 20px; + font-size: var(--font-size-20); font-weight: 600; } h4 { - font-size: 16px; + font-size: var(--font-size-16); color: @beige; font-weight: 600; } diff --git a/styles/less/ui/settings/homebrew-settings/domains.less b/styles/less/ui/settings/homebrew-settings/domains.less index c2ba3ef7..84e013c2 100644 --- a/styles/less/ui/settings/homebrew-settings/domains.less +++ b/styles/less/ui/settings/homebrew-settings/domains.less @@ -44,7 +44,7 @@ border-radius: 50%; width: 24px; height: 24px; - font-size: 12px; + font-size: var(--font-size-12); } } } @@ -118,7 +118,7 @@ button { border-radius: 50%; - font-size: 12px; + font-size: var(--font-size-12); height: 24px; width: 24px; margin-right: 4px; diff --git a/styles/less/ui/settings/settings.less b/styles/less/ui/settings/settings.less index 8062ff73..cee5475f 100644 --- a/styles/less/ui/settings/settings.less +++ b/styles/less/ui/settings/settings.less @@ -53,7 +53,7 @@ } i { - font-size: 18px; + font-size: var(--font-size-18); } } } @@ -80,7 +80,7 @@ width: 80%; .item-name input[type='text'] { - font-size: 32px; + font-size: var(--font-size-32); height: 42px; text-align: center; width: 90%; @@ -117,7 +117,7 @@ label { position: absolute; top: -7px; - font-size: 12px; + font-size: var(--font-size-12); font-variant: petite-caps; z-index: 2; } diff --git a/styles/less/utils/mixin.less b/styles/less/utils/mixin.less index 2f1aa907..49e97a1f 100644 --- a/styles/less/utils/mixin.less +++ b/styles/less/utils/mixin.less @@ -30,7 +30,7 @@ align-items: center; h3 { - font-size: 20px; + font-size: var(--font-size-20); } } @@ -70,7 +70,7 @@ h4 { font-family: @font-body; - font-size: 14px; + font-size: var(--font-size-14); border: none; font-weight: 700; margin: 0; @@ -80,7 +80,7 @@ h5 { font-family: @font-body; - font-size: 14px; + font-size: var(--font-size-14); margin: 0; font-weight: normal; } diff --git a/styles/less/ux/autocomplete/autocomplete.less b/styles/less/ux/autocomplete/autocomplete.less index 868b4f43..808a8972 100644 --- a/styles/less/ux/autocomplete/autocomplete.less +++ b/styles/less/ux/autocomplete/autocomplete.less @@ -22,12 +22,12 @@ .group { font-weight: bold; - font-size: 14px; + font-size: var(--font-size-14); padding-left: 8px; } li[role='option'] { - font-size: 14px; + font-size: var(--font-size-14); padding-left: 10px; cursor: pointer; diff --git a/styles/less/ux/tooltip/tooltip.less b/styles/less/ux/tooltip/tooltip.less index 43f47da5..1d7079ee 100644 --- a/styles/less/ux/tooltip/tooltip.less +++ b/styles/less/ux/tooltip/tooltip.less @@ -85,7 +85,7 @@ gap: 8px; .tooltip-chip { - font-size: 18px; + font-size: var(--font-size-18); padding: 2px 4px; border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; diff --git a/templates/sheets/items/weapon/header.hbs b/templates/sheets/items/weapon/header.hbs index ee0198ba..349a9516 100644 --- a/templates/sheets/items/weapon/header.hbs +++ b/templates/sheets/items/weapon/header.hbs @@ -14,7 +14,11 @@ - {{localize (concat 'DAGGERHEART.CONFIG.Range.' source.system.attack.range '.name')}} - - {{source.system.attack.damage.parts.0.value.dice}}{{#if source.system.attack.damage.parts.0.value.bonus}} + {{source.system.attack.damage.parts.0.value.bonus}}{{/if}} + {{#if source.system.attack.damage.parts.0.value.custom.enabled}} + {{localize "DAGGERHEART.GENERAL.custom"}} + {{else}} + {{source.system.attack.damage.parts.0.value.dice}}{{#if source.system.attack.damage.parts.0.value.bonus}} + {{source.system.attack.damage.parts.0.value.bonus}}{{/if}} + {{/if}} ( {{#each source.system.attack.damage.parts.0.type}} {{localize (concat 'DAGGERHEART.CONFIG.DamageType.' this '.abbreviation')}} diff --git a/templates/sheets/items/weapon/settings.hbs b/templates/sheets/items/weapon/settings.hbs index b2738c75..f9499221 100644 --- a/templates/sheets/items/weapon/settings.hbs +++ b/templates/sheets/items/weapon/settings.hbs @@ -21,10 +21,17 @@ {{#with systemFields.attack.fields.damage.fields.parts.element.fields as | fields | }} {{#with (lookup ../document.system.attack.damage.parts 0) as | source | }} {{localize "DAGGERHEART.GENERAL.damage"}} - {{localize "DAGGERHEART.GENERAL.Dice.single"}} - {{formInput fields.value.fields.dice value=source.value.dice name="system.attack.damage.parts.0.value.dice"}} - {{localize "DAGGERHEART.GENERAL.bonus"}} - {{formInput fields.value.fields.bonus value=source.value.bonus name="system.attack.damage.parts.0.value.bonus" localize=true}} + {{localize "DAGGERHEART.ACTIONS.Config.general.customFormula"}} + {{formInput fields.value.fields.custom.fields.enabled value=source.value.custom.enabled name="system.attack.damage.parts.0.value.custom.enabled"}} + {{#if source.value.custom.enabled}} + {{localize "DAGGERHEART.ACTIONS.Config.general.formula"}} + {{formInput fields.value.fields.custom.fields.formula value=source.value.custom.formula name="system.attack.damage.parts.0.value.custom.formula"}} + {{else}} + {{localize "DAGGERHEART.GENERAL.Dice.single"}} + {{formInput fields.value.fields.dice value=source.value.dice name="system.attack.damage.parts.0.value.dice"}} + {{localize "DAGGERHEART.GENERAL.bonus"}} + {{formInput fields.value.fields.bonus value=source.value.bonus name="system.attack.damage.parts.0.value.bonus" localize=true}} + {{/if}} {{localize "DAGGERHEART.GENERAL.type"}} {{formInput fields.type value=source.type name="system.attack.damage.parts.0.type" localize=true}} {{localize "DAGGERHEART.CONFIG.DamageType.direct.name"}} From 0b2694b007711444192f583bc0daf211f1a44e6b Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 23 Aug 2025 23:50:10 +0200 Subject: [PATCH 20/66] Improved feature request label (#1072) --- .../ISSUE_TEMPLATE/{feature_report.md => feature_request.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/ISSUE_TEMPLATE/{feature_report.md => feature_request.md} (76%) diff --git a/.github/ISSUE_TEMPLATE/feature_report.md b/.github/ISSUE_TEMPLATE/feature_request.md similarity index 76% rename from .github/ISSUE_TEMPLATE/feature_report.md rename to .github/ISSUE_TEMPLATE/feature_request.md index df00ba37..35710e2b 100644 --- a/.github/ISSUE_TEMPLATE/feature_report.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,6 +1,6 @@ --- -name: Feature report -about: Create a feature report for suggestions on improving the system +name: Feature request +about: Create a feature request for suggestions on improving the system title: "[Feature] " labels: enhancement, discussion, maybe type: feature From 46ea4addd0710708e7d27b82dbb817a49e8db0a6 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 24 Aug 2025 02:13:15 +0200 Subject: [PATCH 21/66] [Fix] Subclass Links (#1069) * Attempt at making subclass aware of its class * Update compendium classes/subclasses * Fixed multiclass * Update compenidum browser subclass class filter * Added migration for subclass.linkedClass * Using foundry's isNewer function rather than custom one * Added migration for existing actor features --------- Co-authored-by: Dapoolp --- lang/en.json | 3 +- .../characterCreation/characterCreation.mjs | 7 +- module/applications/sheets/items/class.mjs | 15 + module/applications/ui/itemBrowser.mjs | 9 +- module/config/itemBrowserConfig.mjs | 484 +++++++++--------- module/data/actor/character.mjs | 6 +- module/data/item/base.mjs | 4 +- module/data/item/feature.mjs | 1 + module/data/item/subclass.mjs | 4 +- module/documents/actor.mjs | 8 +- module/helpers/utils.mjs | 11 - module/systemRegistration/migrations.mjs | 44 +- .../classes/class_Bard_vegl3bFOq3pcFTWT.json | 8 +- .../classes/class_Druid_ZNwUTCyGCEcidZFv.json | 8 +- .../class_Guardian_nRAyoC0fOzXPDa4z.json | 8 +- .../class_Ranger_BTyfve69LKqoOi9S.json | 8 +- .../classes/class_Rogue_CvHlkHZfpMiCz5uT.json | 8 +- .../class_Seraph_5ZnlJ5bEoyOTkUJv.json | 8 +- .../class_Sorcerer_DchOzHcWIJE9FKcR.json | 8 +- .../class_Warrior_xCUWwJz4WSthvLfy.json | 8 +- .../class_Wizard_5LwX4m8ziY3F1ZGC.json | 8 +- .../subclass_Beastbound_TIUsIlTS1WkK5vr2.json | 11 +- ...ss_Call_Of_The_Brave_NAFU9roaVG7f3RNJ.json | 11 +- ...s_Call_Of_The_Slayer_bcNe5qP3o6CKadhK.json | 11 +- ...class_Divine_Wielder_M5mpGoAj8LRkylrY.json | 11 +- ...ass_Elemental_Origin_wg1H0hROc2acHwZh.json | 11 +- ...subclass_Nightwalker_h161OSIK24Up4qNd.json | 11 +- ...bclass_Primal_Origin_GLpRVxnY5E82khxH.json | 11 +- ..._School_Of_Knowledge_qqQlgCqhOivUFoQn.json | 11 +- ...bclass_School_Of_War_4y9Ph7RsCIAbkwTk.json | 11 +- .../subclass_Stalwart_rKRxFBlkbh9cDK8K.json | 11 +- .../subclass_Syndicate_95QxNZwgyEm1LqdG.json | 11 +- .../subclass_Troubadour_ld8MIvk0xVJydSBz.json | 11 +- .../subclass_Vengeance_SUo8NPBPO8aN193u.json | 11 +- ...ss_Warden_of_Renewal_xp0XMjYT85Q7E90o.json | 11 +- ...rden_of_the_Elements_W9hs5kxOWeY7eA4Q.json | 11 +- .../subclass_Wayfinder_zsUglcU4NgZ8tNgZ.json | 11 +- ...lass_Winged_Sentinel_y7ERWRIpJsdP9Re4.json | 11 +- .../subclass_Wordsmith_XTSODVM8st75Os8M.json | 11 +- styles/less/ui/item-browser/item-browser.less | 2 + system.json | 2 +- 41 files changed, 479 insertions(+), 391 deletions(-) diff --git a/lang/en.json b/lang/en.json index 6a34037d..f310a01d 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2371,7 +2371,8 @@ "insufficientResources": "You have insufficient resources", "multiclassAlreadyPresent": "You already have a class and multiclass", "subclassesAlreadyPresent": "You already have a class and multiclass subclass", - "noDiceSystem": "Your selected dice {system} does not have a {faces} dice" + "noDiceSystem": "Your selected dice {system} does not have a {faces} dice", + "subclassAlreadyLinked": "{name} is already a subclass in the class {class}. Remove it from there if you want it to be a subclass to this class." }, "Tooltip": { "disableEffect": "Disable Effect", diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index f9f832e8..ccb6f7c0 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -432,12 +432,17 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl } }; - if (type == 'domains') + if (type === 'domains') presets.filter = { 'level.max': { key: 'level.max', value: 1 }, 'system.domain': { key: 'system.domain', value: this.setup.class?.system.domains ?? null } }; + if (type === 'subclasses') + presets.filter = { + 'system.linkedClass.uuid': { key: 'system.linkedClass.uuid', value: this.setup.class?.uuid } + }; + if (equipment.includes(type)) presets.filter = { 'system.tier': { key: 'system.tier', value: 1 }, diff --git a/module/applications/sheets/items/class.mjs b/module/applications/sheets/items/class.mjs index 01f4249a..033c63e6 100644 --- a/module/applications/sheets/items/class.mjs +++ b/module/applications/sheets/items/class.mjs @@ -119,6 +119,15 @@ export default class ClassSheet extends DHBaseItemSheet { const itemType = data.data ? data.type : item.type; const target = event.target.closest('fieldset.drop-section'); if (itemType === 'subclass') { + if (item.system.linkedClass) { + return ui.notifications.warn( + game.i18n.format('DAGGERHEART.UI.Notifications.subclassAlreadyLinked', { + name: item.name, + class: this.document.name + }) + ); + } + await item.update({ 'system.linkedClass': this.document.uuid }); await this.document.update({ 'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid] }); @@ -181,6 +190,12 @@ export default class ClassSheet extends DHBaseItemSheet { static async #removeItemFromCollection(_event, element) { const { uuid, target } = element.dataset; const prop = foundry.utils.getProperty(this.document.system, target); + + if (target === 'subclasses') { + const subclass = await foundry.utils.fromUuid(uuid); + await subclass.update({ 'system.linkedClass': null }); + } + await this.document.update({ [`system.${target}`]: prop.filter(i => i.uuid !== uuid).map(x => x.uuid) }); } diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index f0ad98db..9b9bef91 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -235,7 +235,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { filters.forEach(f => { if (typeof f.field === 'string') f.field = foundry.utils.getProperty(game, f.field); else if (typeof f.choices === 'function') { - f.choices = f.choices(); + f.choices = f.choices(this.items); } f.name ??= f.key; f.value = this.presets?.filter?.[f.name]?.value ?? null; @@ -248,11 +248,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { /* -------------------------------------------- */ /** - * Create and initialize search filter instances for the inventory and loadout sections. + * Create and initialize search filter instance. * - * Sets up two {@link foundry.applications.ux.SearchFilter} instances: - * - One for the inventory, which filters items in the inventory grid. - * - One for the loadout, which filters items in the loadout/card grid. * @private */ _createSearchFilter() { @@ -339,7 +336,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { item = this.items.find(i => i.uuid === itemUUID); if (!item) continue; - + const matchesMenu = this.fieldFilter.length === 0 || this.fieldFilter.every( diff --git a/module/config/itemBrowserConfig.mjs b/module/config/itemBrowserConfig.mjs index 6e3c0dea..e40c989b 100644 --- a/module/config/itemBrowserConfig.mjs +++ b/module/config/itemBrowserConfig.mjs @@ -2,404 +2,422 @@ export const typeConfig = { adversaries: { columns: [ { - key: "system.tier", - label: "Tier" + key: 'system.tier', + label: 'Tier' }, { - key: "system.type", - label: "Type" + key: 'system.type', + label: 'Type' } ], filters: [ { - key: "system.tier", - label: "Tier", + key: 'system.tier', + label: 'Tier', field: 'system.api.models.actors.DhAdversary.schema.fields.tier' }, { - key: "system.type", - label: "Type", + key: 'system.type', + label: 'Type', field: 'system.api.models.actors.DhAdversary.schema.fields.type' }, { - key: "system.difficulty", - name: "difficulty.min", - label: "Difficulty (Min)", + key: 'system.difficulty', + name: 'difficulty.min', + label: 'Difficulty (Min)', field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty', - operator: "gte" + operator: 'gte' }, { - key: "system.difficulty", - name: "difficulty.max", - label: "Difficulty (Max)", + key: 'system.difficulty', + name: 'difficulty.max', + label: 'Difficulty (Max)', field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty', - operator: "lte" + operator: 'lte' }, { - key: "system.resources.hitPoints.max", - name: "hp.min", - label: "Hit Points (Min)", + key: 'system.resources.hitPoints.max', + name: 'hp.min', + label: 'Hit Points (Min)', field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max', - operator: "gte" + operator: 'gte' }, { - key: "system.resources.hitPoints.max", - name: "hp.max", - label: "Hit Points (Max)", + key: 'system.resources.hitPoints.max', + name: 'hp.max', + label: 'Hit Points (Max)', field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max', - operator: "lte" + operator: 'lte' }, { - key: "system.resources.stress.max", - name: "stress.min", - label: "Stress (Min)", + key: 'system.resources.stress.max', + name: 'stress.min', + label: 'Stress (Min)', field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max', - operator: "gte" + operator: 'gte' }, { - key: "system.resources.stress.max", - name: "stress.max", - label: "Stress (Max)", + key: 'system.resources.stress.max', + name: 'stress.max', + label: 'Stress (Max)', field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max', - operator: "lte" - }, + operator: 'lte' + } ] }, items: { columns: [ { - key: "type", - label: "Type" + key: 'type', + label: 'Type' }, { - key: "system.secondary", - label: "Subtype", - format: (isSecondary) => isSecondary ? "secondary" : (isSecondary === false ? "primary" : '-') + key: 'system.secondary', + label: 'Subtype', + format: isSecondary => (isSecondary ? 'secondary' : isSecondary === false ? 'primary' : '-') }, { - key: "system.tier", - label: "Tier" + key: 'system.tier', + label: 'Tier' } ], filters: [ { - key: "type", - label: "Type", - choices: () => CONFIG.Item.documentClass.TYPES.filter(t => ["armor", "weapon", "consumable", "loot"].includes(t)).map(t => ({ value: t, label: t })) + key: 'type', + label: 'Type', + choices: () => + CONFIG.Item.documentClass.TYPES.filter(t => + ['armor', 'weapon', 'consumable', 'loot'].includes(t) + ).map(t => ({ value: t, label: t })) }, { - key: "system.secondary", - label: "Subtype", + key: 'system.secondary', + label: 'Subtype', choices: [ - { value: false, label: "Primary Weapon"}, - { value: true, label: "Secondary Weapon"} + { value: false, label: 'Primary Weapon' }, + { value: true, label: 'Secondary Weapon' } ] }, { - key: "system.tier", - label: "Tier", - choices: [{ value: "1", label: "1"}, { value: "2", label: "2"}, { value: "3", label: "3"}, { value: "4", label: "4"}] + key: 'system.tier', + label: 'Tier', + choices: [ + { value: '1', label: '1' }, + { value: '2', label: '2' }, + { value: '3', label: '3' }, + { value: '4', label: '4' } + ] }, { - key: "system.burden", - label: "Burden", + key: 'system.burden', + label: 'Burden', field: 'system.api.models.items.DHWeapon.schema.fields.burden' }, { - key: "system.attack.roll.trait", - label: "Trait", + key: 'system.attack.roll.trait', + label: 'Trait', field: 'system.api.models.actions.actionsTypes.attack.schema.fields.roll.fields.trait' }, { - key: "system.attack.range", - label: "Range", + key: 'system.attack.range', + label: 'Range', field: 'system.api.models.actions.actionsTypes.attack.schema.fields.range' }, { - key: "system.baseScore", - name: "armor.min", - label: "Armor Score (Min)", + key: 'system.baseScore', + name: 'armor.min', + label: 'Armor Score (Min)', field: 'system.api.models.items.DHArmor.schema.fields.baseScore', - operator: "gte" + operator: 'gte' }, { - key: "system.baseScore", - name: "armor.max", - label: "Armor Score (Max)", + key: 'system.baseScore', + name: 'armor.max', + label: 'Armor Score (Max)', field: 'system.api.models.items.DHArmor.schema.fields.baseScore', - operator: "lte" + operator: 'lte' }, { - key: "system.itemFeatures", - label: "Features", - choices: () => [...Object.entries(CONFIG.DH.ITEM.weaponFeatures), ...Object.entries(CONFIG.DH.ITEM.armorFeatures)].map(([k,v]) => ({ value: k, label: v.label})), - operator: "contains3" + key: 'system.itemFeatures', + label: 'Features', + choices: () => + [ + ...Object.entries(CONFIG.DH.ITEM.weaponFeatures), + ...Object.entries(CONFIG.DH.ITEM.armorFeatures) + ].map(([k, v]) => ({ value: k, label: v.label })), + operator: 'contains3' } ] }, features: { - columns: [ - - ], - filters: [ - - ] + columns: [], + filters: [] }, cards: { columns: [ { - key: "system.type", - label: "Type" + key: 'system.type', + label: 'Type' }, { - key: "system.domain", - label: "Domain" + key: 'system.domain', + label: 'Domain' }, { - key: "system.level", - label: "Level" + key: 'system.level', + label: 'Level' } ], filters: [ { - key: "system.type", - label: "Type", + key: 'system.type', + label: 'Type', field: 'system.api.models.items.DHDomainCard.schema.fields.type' }, { - key: "system.domain", - label: "Domain", + key: 'system.domain', + label: 'Domain', field: 'system.api.models.items.DHDomainCard.schema.fields.domain', - operator: "contains2" + operator: 'contains2' }, { - key: "system.level", - name: "level.min", - label: "Level (Min)", + key: 'system.level', + name: 'level.min', + label: 'Level (Min)', field: 'system.api.models.items.DHDomainCard.schema.fields.level', - operator: "gte" + operator: 'gte' }, { - key: "system.level", - name: "level.max", - label: "Level (Max)", + key: 'system.level', + name: 'level.max', + label: 'Level (Max)', field: 'system.api.models.items.DHDomainCard.schema.fields.level', - operator: "lte" + operator: 'lte' }, { - key: "system.recallCost", - name: "recall.min", - label: "Recall Cost (Min)", + key: 'system.recallCost', + name: 'recall.min', + label: 'Recall Cost (Min)', field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost', - operator: "gte" + operator: 'gte' }, { - key: "system.recallCost", - name: "recall.max", - label: "Recall Cost (Max)", + key: 'system.recallCost', + name: 'recall.max', + label: 'Recall Cost (Max)', field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost', - operator: "lte" + operator: 'lte' } ] }, classes: { columns: [ { - key: "system.evasion", - label: "Evasion" + key: 'system.evasion', + label: 'Evasion' }, { - key: "system.hitPoints", - label: "Hit Points" + key: 'system.hitPoints', + label: 'Hit Points' }, { - key: "system.domains", - label: "Domains" + key: 'system.domains', + label: 'Domains' } ], filters: [ { - key: "system.evasion", - name: "evasion.min", - label: "Evasion (Min)", + key: 'system.evasion', + name: 'evasion.min', + label: 'Evasion (Min)', field: 'system.api.models.items.DHClass.schema.fields.evasion', - operator: "gte" + operator: 'gte' }, { - key: "system.evasion", - name: "evasion.max", - label: "Evasion (Max)", + key: 'system.evasion', + name: 'evasion.max', + label: 'Evasion (Max)', field: 'system.api.models.items.DHClass.schema.fields.evasion', - operator: "lte" + operator: 'lte' }, { - key: "system.hitPoints", - name: "hp.min", - label: "Hit Points (Min)", + key: 'system.hitPoints', + name: 'hp.min', + label: 'Hit Points (Min)', field: 'system.api.models.items.DHClass.schema.fields.hitPoints', - operator: "gte" + operator: 'gte' }, { - key: "system.hitPoints", - name: "hp.max", - label: "Hit Points (Max)", + key: 'system.hitPoints', + name: 'hp.max', + label: 'Hit Points (Max)', field: 'system.api.models.items.DHClass.schema.fields.hitPoints', - operator: "lte" + operator: 'lte' }, { - key: "system.domains", - label: "Domains", - choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label})), - operator: "contains2" + key: 'system.domains', + label: 'Domains', + choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label })), + operator: 'contains2' } ] }, subclasses: { columns: [ { - key: "id", - label: "Class", - format: (id) => { - return ""; - } + key: 'system.linkedClass', + label: 'Class', + format: linkedClass => linkedClass.name }, { - key: "system.spellcastingTrait", - label: "Spellcasting Trait" - } - ], - filters: [] - }, - beastforms: { - columns: [ - { - key: "system.tier", - label: "Tier" - }, - { - key: "system.mainTrait", - label: "Main Trait" + key: 'system.spellcastingTrait', + label: 'Spellcasting Trait' } ], filters: [ { - key: "system.tier", - label: "Tier", + key: 'system.linkedClass.uuid', + label: 'Class', + choices: (items) => { + const list = items.map(item => ({ value: item.system.linkedClass.uuid, label: item.system.linkedClass.name })); + return list.reduce((a,c) => { + if(!(a.find(i => i.value === c.value))) a.push(c); + return a; + }, []); + } + } + ] + }, + beastforms: { + columns: [ + { + key: 'system.tier', + label: 'Tier' + }, + { + key: 'system.mainTrait', + label: 'Main Trait' + } + ], + filters: [ + { + key: 'system.tier', + label: 'Tier', field: 'system.api.models.items.DHBeastform.schema.fields.tier' }, { - key: "system.mainTrait", - label: "Main Trait", + key: 'system.mainTrait', + label: 'Main Trait', field: 'system.api.models.items.DHBeastform.schema.fields.mainTrait' } ] } -} +}; export const compendiumConfig = { - "daggerheart": { - id: "daggerheart", - label: "DAGGERHEART", + daggerheart: { + id: 'daggerheart', + label: 'DAGGERHEART', folders: { - "adversaries": { - id: "adversaries", - keys: ["adversaries"], - label: "Adversaries", - type: ["adversary"], - listType: "adversaries" + adversaries: { + id: 'adversaries', + keys: ['adversaries'], + label: 'Adversaries', + type: ['adversary'], + listType: 'adversaries' }, - "ancestries": { - id: "ancestries", - keys: ["ancestries"], - label: "Ancestries", - type: ["ancestry"], + ancestries: { + id: 'ancestries', + keys: ['ancestries'], + label: 'Ancestries', + type: ['ancestry'], folders: { - "features": { - id: "features", - keys: ["ancestries"], - label: "Features", - type: ["feature"] + features: { + id: 'features', + keys: ['ancestries'], + label: 'Features', + type: ['feature'] } } }, - "equipments": { - id: "equipments", - keys: ["armors", "weapons", "consumables", "loot"], - label: "Equipment", - type: ["armor", "weapon", "consumable", "loot"], - listType: "items" + equipments: { + id: 'equipments', + keys: ['armors', 'weapons', 'consumables', 'loot'], + label: 'Equipment', + type: ['armor', 'weapon', 'consumable', 'loot'], + listType: 'items' }, - "classes": { - id: "classes", - keys: ["classes"], - label: "Classes", - type: ["class"], + classes: { + id: 'classes', + keys: ['classes'], + label: 'Classes', + type: ['class'], folders: { - "features": { - id: "features", - keys: ["classes"], - label: "Features", - type: ["feature"] + features: { + id: 'features', + keys: ['classes'], + label: 'Features', + type: ['feature'] }, - "items": { - id: "items", - keys: ["classes"], - label: "Items", - type: ["armor", "weapon", "consumable", "loot"], - listType: "items" + items: { + id: 'items', + keys: ['classes'], + label: 'Items', + type: ['armor', 'weapon', 'consumable', 'loot'], + listType: 'items' } }, - listType: "classes" + listType: 'classes' }, - "subclasses": { - id: "subclasses", - keys: ["subclasses"], - label: "Subclasses", - type: ["subclass"], - listType: "subclasses" + subclasses: { + id: 'subclasses', + keys: ['subclasses'], + label: 'Subclasses', + type: ['subclass'], + listType: 'subclasses' }, - "domains": { - id: "domains", - keys: ["domains"], - label: "Domain Cards", - type: ["domainCard"], - listType: "cards" + domains: { + id: 'domains', + keys: ['domains'], + label: 'Domain Cards', + type: ['domainCard'], + listType: 'cards' }, - "communities": { - id: "communities", - keys: ["communities"], - label: "Communities", - type: ["community"], + communities: { + id: 'communities', + keys: ['communities'], + label: 'Communities', + type: ['community'], folders: { - "features": { - id: "features", - keys: ["communities"], - label: "Features", - type: ["feature"] + features: { + id: 'features', + keys: ['communities'], + label: 'Features', + type: ['feature'] } } }, - "environments": { - id: "environments", - keys: ["environments"], - label: "Environments", - type: ["environment"] + environments: { + id: 'environments', + keys: ['environments'], + label: 'Environments', + type: ['environment'] }, - "beastforms": { - id: "beastforms", - keys: ["beastforms"], - label: "Beastforms", - type: ["beastform"], - listType: "beastforms", + beastforms: { + id: 'beastforms', + keys: ['beastforms'], + label: 'Beastforms', + type: ['beastform'], + listType: 'beastforms', folders: { - "features": { - id: "features", - keys: ["beastforms"], - label: "Features", - type: ["feature"] + features: { + id: 'features', + keys: ['beastforms'], + label: 'Features', + type: ['feature'] } } } } } -} \ No newline at end of file +}; diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 7dd7993c..ddcc5bf5 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -317,7 +317,7 @@ export default class DhCharacter extends BaseDataActor { } get multiclass() { - const value = this.parent.items.find(x => x.type === 'Class' && x.system.isMulticlass); + const value = this.parent.items.find(x => x.type === 'class' && x.system.isMulticlass); const subclass = this.parent.items.find(x => x.type === 'subclass' && x.system.isMulticlass); return { @@ -443,7 +443,9 @@ export default class DhCharacter extends BaseDataActor { classFeatures.push(item); } else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) { if (this.class.subclass) { - const subclassState = this.class.subclass.system.featureState; + const prop = item.system.multiclassOrigin ? 'multiclass' : 'class'; + const subclassState = this[prop].subclass?.system?.featureState; + if (!subclassState) continue; if ( item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.foundation || diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index f333537b..f8eae265 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -148,7 +148,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { for (let f of this.features) { const fBase = f.item ?? f; const feature = fBase.system ? fBase : await foundry.utils.fromUuid(fBase.uuid); - const multiclass = this.isMulticlass ? 'multiclass' : null; features.push( foundry.utils.mergeObject( feature.toObject(), @@ -156,7 +155,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { _stats: { compendiumSource: fBase.uuid }, system: { originItemType: this.parent.type, - identifier: multiclass ?? (f.item ? f.type : null) + identifier: f.item ? f.type : null, + multiclassOrigin: this.isMulticlass } }, { inplace: false } diff --git a/module/data/item/feature.mjs b/module/data/item/feature.mjs index 6e1aab41..3b8fe064 100644 --- a/module/data/item/feature.mjs +++ b/module/data/item/feature.mjs @@ -29,6 +29,7 @@ export default class DHFeature extends BaseDataItem { nullable: true, initial: null }), + multiclassOrigin: new fields.BooleanField({ initial: false }), identifier: new fields.StringField() }; } diff --git a/module/data/item/subclass.mjs b/module/data/item/subclass.mjs index 46b83753..e16a15f7 100644 --- a/module/data/item/subclass.mjs +++ b/module/data/item/subclass.mjs @@ -1,3 +1,4 @@ +import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; import ItemLinkFields from '../fields/itemLinkFields.mjs'; import BaseDataItem from './base.mjs'; @@ -25,7 +26,8 @@ export default class DHSubclass extends BaseDataItem { }), features: new ItemLinkFields(), featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }), - isMulticlass: new fields.BooleanField({ initial: false }) + isMulticlass: new fields.BooleanField({ initial: false }), + linkedClass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true, initial: null }) }; } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index e1dd93af..9a22eb44 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -1,7 +1,7 @@ import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs'; import { LevelOptionType } from '../data/levelTier.mjs'; import DHFeature from '../data/item/feature.mjs'; -import { damageKeyToNumber, versionCompare } from '../helpers/utils.mjs'; +import { damageKeyToNumber } from '../helpers/utils.mjs'; import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs'; export default class DhpActor extends Actor { @@ -164,10 +164,10 @@ export default class DhpActor extends Actor { if (multiclass) { const multiclassItem = this.items.find(x => x.uuid === multiclass.itemUuid); const multiclassFeatures = this.items.filter( - x => x.system.originItemType === 'class' && x.system.identifier === 'multiclass' + x => x.system.originItemType === 'class' && x.system.multiclassOrigin ); const subclassFeatures = this.items.filter( - x => x.system.originItemType === 'subclass' && x.system.identifier === 'multiclass' + x => x.system.originItemType === 'subclass' && x.system.multiclassOrigin ); this.deleteEmbeddedDocuments( @@ -759,7 +759,7 @@ export default class DhpActor extends Actor { } const parsedJSON = JSON.parse(json); - if (versionCompare(parsedJSON._stats.systemVersion, '1.1.0')) { + if (foundry.utils.isNewerVersion('1.1.0', parsedJSON._stats.systemVersion)) { const confirmed = await foundry.applications.api.DialogV2.confirm({ window: { title: game.i18n.localize('DAGGERHEART.ACTORS.Character.InvalidOldCharacterImportTitle') diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index c8f4186f..6f4e5a26 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -420,14 +420,3 @@ export async function createEmbeddedItemsWithEffects(actor, baseData) { export const slugify = name => { return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', ''); }; - -export const versionCompare = (current, target) => { - const currentSplit = current.split('.').map(x => Number.parseInt(x)); - const targetSplit = target.split('.').map(x => Number.parseInt(x)); - for (var i = 0; i < currentSplit.length; i++) { - if (currentSplit[i] < targetSplit[i]) return true; - if (currentSplit[i] > targetSplit[i]) return false; - } - - return false; -}; diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index e84018fa..da0dcb5d 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -1,10 +1,8 @@ -import { versionCompare } from '../helpers/utils.mjs'; - export async function runMigrations() { let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion); if (!lastMigrationVersion) lastMigrationVersion = '1.0.6'; - if (versionCompare(lastMigrationVersion, '1.1.0')) { + if (foundry.utils.isNewerVersion('1.1.0', lastMigrationVersion)) { const compendiumActors = []; for (let pack of game.packs) { const documents = await pack.getDocuments(); @@ -37,5 +35,45 @@ export async function runMigrations() { lastMigrationVersion = '1.1.0'; } + if (foundry.utils.isNewerVersion('1.1.1', lastMigrationVersion)) { + const compendiumClasses = []; + const compendiumActors = []; + for (let pack of game.packs) { + const documents = await pack.getDocuments(); + compendiumClasses.push(...documents.filter(x => x.type === 'class')); + compendiumActors.push(...documents.filter(x => x.type === 'character')); + } + + [...compendiumActors, ...game.actors.filter(x => x.type === 'character')].forEach(char => { + const multiclass = char.items.find(x => x.type === 'class' && x.system.isMulticlass); + const multiclassSubclass = multiclass.system.subclasses.length > 0 ? multiclass.system.subclasses[0] : null; + char.items.forEach(item => { + if (item.type === 'feature' && item.system.identifier === 'multiclass') { + const base = item.system.originItemType === 'class' ? multiclass : multiclassSubclass; + if (base) { + const baseFeature = base.system.features.find(x => x.item.name === item.name); + if (baseFeature) { + item.update({ + system: { + multiclassOrigin: true, + identifier: baseFeature.type + } + }); + } + } + } + }); + }); + + const worldClasses = game.items.filter(x => x.type === 'class'); + for (let classVal of [...compendiumClasses, ...worldClasses]) { + for (let subclass of classVal.system.subclasses) { + await subclass.update({ 'system.linkedClass': classVal.uuid }); + } + } + + lastMigrationVersion = '1.1.1'; + } + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion); } diff --git a/src/packs/classes/class_Bard_vegl3bFOq3pcFTWT.json b/src/packs/classes/class_Bard_vegl3bFOq3pcFTWT.json index 836e49b5..5ffd8a11 100644 --- a/src/packs/classes/class_Bard_vegl3bFOq3pcFTWT.json +++ b/src/packs/classes/class_Bard_vegl3bFOq3pcFTWT.json @@ -70,12 +70,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754174600538, - "modifiedTime": 1754325498779, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943467705, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_id": "vegl3bFOq3pcFTWT", "sort": 300000, diff --git a/src/packs/classes/class_Druid_ZNwUTCyGCEcidZFv.json b/src/packs/classes/class_Druid_ZNwUTCyGCEcidZFv.json index 800598a6..086d363e 100644 --- a/src/packs/classes/class_Druid_ZNwUTCyGCEcidZFv.json +++ b/src/packs/classes/class_Druid_ZNwUTCyGCEcidZFv.json @@ -72,12 +72,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754222247012, - "modifiedTime": 1754325498779, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943479440, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!ZNwUTCyGCEcidZFv" } diff --git a/src/packs/classes/class_Guardian_nRAyoC0fOzXPDa4z.json b/src/packs/classes/class_Guardian_nRAyoC0fOzXPDa4z.json index 2d76ccfb..b50f86ef 100644 --- a/src/packs/classes/class_Guardian_nRAyoC0fOzXPDa4z.json +++ b/src/packs/classes/class_Guardian_nRAyoC0fOzXPDa4z.json @@ -68,12 +68,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754246931974, - "modifiedTime": 1754325498779, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943488697, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!nRAyoC0fOzXPDa4z" } diff --git a/src/packs/classes/class_Ranger_BTyfve69LKqoOi9S.json b/src/packs/classes/class_Ranger_BTyfve69LKqoOi9S.json index 17fb6b2d..62e51c7d 100644 --- a/src/packs/classes/class_Ranger_BTyfve69LKqoOi9S.json +++ b/src/packs/classes/class_Ranger_BTyfve69LKqoOi9S.json @@ -68,12 +68,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754268869310, - "modifiedTime": 1754325517617, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943505024, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!BTyfve69LKqoOi9S" } diff --git a/src/packs/classes/class_Rogue_CvHlkHZfpMiCz5uT.json b/src/packs/classes/class_Rogue_CvHlkHZfpMiCz5uT.json index 617ea0f0..146ad340 100644 --- a/src/packs/classes/class_Rogue_CvHlkHZfpMiCz5uT.json +++ b/src/packs/classes/class_Rogue_CvHlkHZfpMiCz5uT.json @@ -72,12 +72,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754325275832, - "modifiedTime": 1754500637635, - "lastModifiedBy": "Q9NoTaEarn3VMS6Z" + "modifiedTime": 1755943515533, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!CvHlkHZfpMiCz5uT" } diff --git a/src/packs/classes/class_Seraph_5ZnlJ5bEoyOTkUJv.json b/src/packs/classes/class_Seraph_5ZnlJ5bEoyOTkUJv.json index b88da6c2..6a2b70a1 100644 --- a/src/packs/classes/class_Seraph_5ZnlJ5bEoyOTkUJv.json +++ b/src/packs/classes/class_Seraph_5ZnlJ5bEoyOTkUJv.json @@ -68,12 +68,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754351482530, - "modifiedTime": 1754355938087, - "lastModifiedBy": "Q9NoTaEarn3VMS6Z" + "modifiedTime": 1755943523935, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!5ZnlJ5bEoyOTkUJv" } diff --git a/src/packs/classes/class_Sorcerer_DchOzHcWIJE9FKcR.json b/src/packs/classes/class_Sorcerer_DchOzHcWIJE9FKcR.json index 15e9bcce..0d3d71ac 100644 --- a/src/packs/classes/class_Sorcerer_DchOzHcWIJE9FKcR.json +++ b/src/packs/classes/class_Sorcerer_DchOzHcWIJE9FKcR.json @@ -76,12 +76,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754349743129, - "modifiedTime": 1754350005553, - "lastModifiedBy": "Q9NoTaEarn3VMS6Z" + "modifiedTime": 1755943536635, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!DchOzHcWIJE9FKcR" } diff --git a/src/packs/classes/class_Warrior_xCUWwJz4WSthvLfy.json b/src/packs/classes/class_Warrior_xCUWwJz4WSthvLfy.json index 230c3a70..2d120f8e 100644 --- a/src/packs/classes/class_Warrior_xCUWwJz4WSthvLfy.json +++ b/src/packs/classes/class_Warrior_xCUWwJz4WSthvLfy.json @@ -72,12 +72,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754255776706, - "modifiedTime": 1754325510730, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943545980, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!xCUWwJz4WSthvLfy" } diff --git a/src/packs/classes/class_Wizard_5LwX4m8ziY3F1ZGC.json b/src/packs/classes/class_Wizard_5LwX4m8ziY3F1ZGC.json index 9a5790db..0955e68a 100644 --- a/src/packs/classes/class_Wizard_5LwX4m8ziY3F1ZGC.json +++ b/src/packs/classes/class_Wizard_5LwX4m8ziY3F1ZGC.json @@ -72,12 +72,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754253505323, - "modifiedTime": 1754325500455, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943555087, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!5LwX4m8ziY3F1ZGC" } diff --git a/src/packs/subclasses/subclass_Beastbound_TIUsIlTS1WkK5vr2.json b/src/packs/subclasses/subclass_Beastbound_TIUsIlTS1WkK5vr2.json index 824572e9..622fb36a 100644 --- a/src/packs/subclasses/subclass_Beastbound_TIUsIlTS1WkK5vr2.json +++ b/src/packs/subclasses/subclass_Beastbound_TIUsIlTS1WkK5vr2.json @@ -30,7 +30,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.BTyfve69LKqoOi9S" }, "effects": [], "sort": 0, @@ -43,12 +44,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754268237448, - "modifiedTime": 1754268308097, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943503629, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!TIUsIlTS1WkK5vr2" } diff --git a/src/packs/subclasses/subclass_Call_Of_The_Brave_NAFU9roaVG7f3RNJ.json b/src/packs/subclasses/subclass_Call_Of_The_Brave_NAFU9roaVG7f3RNJ.json index 51718237..46f7f2af 100644 --- a/src/packs/subclasses/subclass_Call_Of_The_Brave_NAFU9roaVG7f3RNJ.json +++ b/src/packs/subclasses/subclass_Call_Of_The_Brave_NAFU9roaVG7f3RNJ.json @@ -26,7 +26,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.xCUWwJz4WSthvLfy" }, "effects": [], "sort": 0, @@ -39,12 +40,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754256077777, - "modifiedTime": 1754256954656, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1755943544886, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!NAFU9roaVG7f3RNJ" } diff --git a/src/packs/subclasses/subclass_Call_Of_The_Slayer_bcNe5qP3o6CKadhK.json b/src/packs/subclasses/subclass_Call_Of_The_Slayer_bcNe5qP3o6CKadhK.json index ab0419d8..bbcf6ca6 100644 --- a/src/packs/subclasses/subclass_Call_Of_The_Slayer_bcNe5qP3o6CKadhK.json +++ b/src/packs/subclasses/subclass_Call_Of_The_Slayer_bcNe5qP3o6CKadhK.json @@ -22,7 +22,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.xCUWwJz4WSthvLfy" }, "effects": [], "sort": 0, @@ -35,12 +36,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754256112978, - "modifiedTime": 1754256959532, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1755943545973, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!bcNe5qP3o6CKadhK" } diff --git a/src/packs/subclasses/subclass_Divine_Wielder_M5mpGoAj8LRkylrY.json b/src/packs/subclasses/subclass_Divine_Wielder_M5mpGoAj8LRkylrY.json index e5f1f4c8..9a7b9c96 100644 --- a/src/packs/subclasses/subclass_Divine_Wielder_M5mpGoAj8LRkylrY.json +++ b/src/packs/subclasses/subclass_Divine_Wielder_M5mpGoAj8LRkylrY.json @@ -26,7 +26,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.5ZnlJ5bEoyOTkUJv" }, "effects": [], "sort": 0, @@ -39,12 +40,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754352806098, - "modifiedTime": 1754354057333, - "lastModifiedBy": "Q9NoTaEarn3VMS6Z" + "modifiedTime": 1755943522722, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!M5mpGoAj8LRkylrY" } diff --git a/src/packs/subclasses/subclass_Elemental_Origin_wg1H0hROc2acHwZh.json b/src/packs/subclasses/subclass_Elemental_Origin_wg1H0hROc2acHwZh.json index b1e5f67e..f1f85e42 100644 --- a/src/packs/subclasses/subclass_Elemental_Origin_wg1H0hROc2acHwZh.json +++ b/src/packs/subclasses/subclass_Elemental_Origin_wg1H0hROc2acHwZh.json @@ -22,7 +22,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.DchOzHcWIJE9FKcR" }, "effects": [], "sort": 0, @@ -35,12 +36,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754349604941, - "modifiedTime": 1754349648910, - "lastModifiedBy": "Q9NoTaEarn3VMS6Z" + "modifiedTime": 1755943535524, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!wg1H0hROc2acHwZh" } diff --git a/src/packs/subclasses/subclass_Nightwalker_h161OSIK24Up4qNd.json b/src/packs/subclasses/subclass_Nightwalker_h161OSIK24Up4qNd.json index 8da59a4c..49f9a520 100644 --- a/src/packs/subclasses/subclass_Nightwalker_h161OSIK24Up4qNd.json +++ b/src/packs/subclasses/subclass_Nightwalker_h161OSIK24Up4qNd.json @@ -30,7 +30,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.CvHlkHZfpMiCz5uT" }, "effects": [], "sort": 0, @@ -43,12 +44,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754322815758, - "modifiedTime": 1754323509061, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943514465, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!h161OSIK24Up4qNd" } diff --git a/src/packs/subclasses/subclass_Primal_Origin_GLpRVxnY5E82khxH.json b/src/packs/subclasses/subclass_Primal_Origin_GLpRVxnY5E82khxH.json index e3f38604..37619136 100644 --- a/src/packs/subclasses/subclass_Primal_Origin_GLpRVxnY5E82khxH.json +++ b/src/packs/subclasses/subclass_Primal_Origin_GLpRVxnY5E82khxH.json @@ -22,7 +22,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.DchOzHcWIJE9FKcR" }, "effects": [], "sort": 0, @@ -35,12 +36,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754349604941, - "modifiedTime": 1754349673276, - "lastModifiedBy": "Q9NoTaEarn3VMS6Z" + "modifiedTime": 1755943536628, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!GLpRVxnY5E82khxH" } diff --git a/src/packs/subclasses/subclass_School_Of_Knowledge_qqQlgCqhOivUFoQn.json b/src/packs/subclasses/subclass_School_Of_Knowledge_qqQlgCqhOivUFoQn.json index 7dcbcf35..7ed07d06 100644 --- a/src/packs/subclasses/subclass_School_Of_Knowledge_qqQlgCqhOivUFoQn.json +++ b/src/packs/subclasses/subclass_School_Of_Knowledge_qqQlgCqhOivUFoQn.json @@ -34,7 +34,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.5LwX4m8ziY3F1ZGC" }, "effects": [], "sort": 0, @@ -47,12 +48,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754253538384, - "modifiedTime": 1754254543287, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1755943553625, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!qqQlgCqhOivUFoQn" } diff --git a/src/packs/subclasses/subclass_School_Of_War_4y9Ph7RsCIAbkwTk.json b/src/packs/subclasses/subclass_School_Of_War_4y9Ph7RsCIAbkwTk.json index 03af87fc..856031d8 100644 --- a/src/packs/subclasses/subclass_School_Of_War_4y9Ph7RsCIAbkwTk.json +++ b/src/packs/subclasses/subclass_School_Of_War_4y9Ph7RsCIAbkwTk.json @@ -34,7 +34,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.5LwX4m8ziY3F1ZGC" }, "effects": [], "sort": 0, @@ -47,12 +48,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754253587683, - "modifiedTime": 1754254721869, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1755943555081, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!4y9Ph7RsCIAbkwTk" } diff --git a/src/packs/subclasses/subclass_Stalwart_rKRxFBlkbh9cDK8K.json b/src/packs/subclasses/subclass_Stalwart_rKRxFBlkbh9cDK8K.json index 0e9ea4df..b75f88d0 100644 --- a/src/packs/subclasses/subclass_Stalwart_rKRxFBlkbh9cDK8K.json +++ b/src/packs/subclasses/subclass_Stalwart_rKRxFBlkbh9cDK8K.json @@ -34,7 +34,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.nRAyoC0fOzXPDa4z" }, "effects": [], "sort": 0, @@ -47,12 +48,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754245881893, - "modifiedTime": 1754245958817, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943487549, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!rKRxFBlkbh9cDK8K" } diff --git a/src/packs/subclasses/subclass_Syndicate_95QxNZwgyEm1LqdG.json b/src/packs/subclasses/subclass_Syndicate_95QxNZwgyEm1LqdG.json index 8d7285d4..2cef9bbd 100644 --- a/src/packs/subclasses/subclass_Syndicate_95QxNZwgyEm1LqdG.json +++ b/src/packs/subclasses/subclass_Syndicate_95QxNZwgyEm1LqdG.json @@ -22,7 +22,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.CvHlkHZfpMiCz5uT" }, "effects": [], "sort": 0, @@ -35,12 +36,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754323643089, - "modifiedTime": 1754323735227, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943515526, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!95QxNZwgyEm1LqdG" } diff --git a/src/packs/subclasses/subclass_Troubadour_ld8MIvk0xVJydSBz.json b/src/packs/subclasses/subclass_Troubadour_ld8MIvk0xVJydSBz.json index e50ecfa8..eb51d60e 100644 --- a/src/packs/subclasses/subclass_Troubadour_ld8MIvk0xVJydSBz.json +++ b/src/packs/subclasses/subclass_Troubadour_ld8MIvk0xVJydSBz.json @@ -21,7 +21,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.vegl3bFOq3pcFTWT" }, "effects": [], "ownership": { @@ -33,12 +34,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754174653653, - "modifiedTime": 1754236659263, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943465827, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_id": "ld8MIvk0xVJydSBz", "sort": 100000, diff --git a/src/packs/subclasses/subclass_Vengeance_SUo8NPBPO8aN193u.json b/src/packs/subclasses/subclass_Vengeance_SUo8NPBPO8aN193u.json index 53f7cf96..fedf5f1a 100644 --- a/src/packs/subclasses/subclass_Vengeance_SUo8NPBPO8aN193u.json +++ b/src/packs/subclasses/subclass_Vengeance_SUo8NPBPO8aN193u.json @@ -26,7 +26,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.nRAyoC0fOzXPDa4z" }, "effects": [], "sort": 0, @@ -39,12 +40,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754246011733, - "modifiedTime": 1754246076491, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943488691, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!SUo8NPBPO8aN193u" } diff --git a/src/packs/subclasses/subclass_Warden_of_Renewal_xp0XMjYT85Q7E90o.json b/src/packs/subclasses/subclass_Warden_of_Renewal_xp0XMjYT85Q7E90o.json index 08421469..abe97d5e 100644 --- a/src/packs/subclasses/subclass_Warden_of_Renewal_xp0XMjYT85Q7E90o.json +++ b/src/packs/subclasses/subclass_Warden_of_Renewal_xp0XMjYT85Q7E90o.json @@ -29,7 +29,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.ZNwUTCyGCEcidZFv" }, "effects": [], "folder": "AZWrSJzGXltzQhAJ", @@ -43,12 +44,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754221346981, - "modifiedTime": 1754236671909, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943479431, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!xp0XMjYT85Q7E90o" } diff --git a/src/packs/subclasses/subclass_Warden_of_the_Elements_W9hs5kxOWeY7eA4Q.json b/src/packs/subclasses/subclass_Warden_of_the_Elements_W9hs5kxOWeY7eA4Q.json index 0e4972f6..43642e89 100644 --- a/src/packs/subclasses/subclass_Warden_of_the_Elements_W9hs5kxOWeY7eA4Q.json +++ b/src/packs/subclasses/subclass_Warden_of_the_Elements_W9hs5kxOWeY7eA4Q.json @@ -21,7 +21,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.ZNwUTCyGCEcidZFv" }, "effects": [], "folder": "AZWrSJzGXltzQhAJ", @@ -35,12 +36,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754221102716, - "modifiedTime": 1754236671090, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943478132, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!W9hs5kxOWeY7eA4Q" } diff --git a/src/packs/subclasses/subclass_Wayfinder_zsUglcU4NgZ8tNgZ.json b/src/packs/subclasses/subclass_Wayfinder_zsUglcU4NgZ8tNgZ.json index 4f9a2289..a308d587 100644 --- a/src/packs/subclasses/subclass_Wayfinder_zsUglcU4NgZ8tNgZ.json +++ b/src/packs/subclasses/subclass_Wayfinder_zsUglcU4NgZ8tNgZ.json @@ -26,7 +26,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.BTyfve69LKqoOi9S" }, "effects": [], "sort": 0, @@ -39,12 +40,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754268318903, - "modifiedTime": 1754268377047, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943505016, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!zsUglcU4NgZ8tNgZ" } diff --git a/src/packs/subclasses/subclass_Winged_Sentinel_y7ERWRIpJsdP9Re4.json b/src/packs/subclasses/subclass_Winged_Sentinel_y7ERWRIpJsdP9Re4.json index 6da6a117..f71a1fcb 100644 --- a/src/packs/subclasses/subclass_Winged_Sentinel_y7ERWRIpJsdP9Re4.json +++ b/src/packs/subclasses/subclass_Winged_Sentinel_y7ERWRIpJsdP9Re4.json @@ -26,7 +26,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.5ZnlJ5bEoyOTkUJv" }, "effects": [], "sort": 0, @@ -39,12 +40,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754354451615, - "modifiedTime": 1754355901649, - "lastModifiedBy": "Q9NoTaEarn3VMS6Z" + "modifiedTime": 1755943523928, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_key": "!items!y7ERWRIpJsdP9Re4" } diff --git a/src/packs/subclasses/subclass_Wordsmith_XTSODVM8st75Os8M.json b/src/packs/subclasses/subclass_Wordsmith_XTSODVM8st75Os8M.json index f4c35265..6c975ca6 100644 --- a/src/packs/subclasses/subclass_Wordsmith_XTSODVM8st75Os8M.json +++ b/src/packs/subclasses/subclass_Wordsmith_XTSODVM8st75Os8M.json @@ -25,7 +25,8 @@ } ], "featureState": 1, - "isMulticlass": false + "isMulticlass": false, + "linkedClass": "Compendium.daggerheart.classes.Item.vegl3bFOq3pcFTWT" }, "effects": [], "ownership": { @@ -37,12 +38,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.0", "createdTime": 1754174655078, - "modifiedTime": 1754236660088, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1755943467695, + "lastModifiedBy": "tt3PwMBXcTLCtIQU" }, "_id": "XTSODVM8st75Os8M", "sort": 200000, diff --git a/styles/less/ui/item-browser/item-browser.less b/styles/less/ui/item-browser/item-browser.less index df35d60b..407a3116 100644 --- a/styles/less/ui/item-browser/item-browser.less +++ b/styles/less/ui/item-browser/item-browser.less @@ -356,6 +356,8 @@ display: grid; grid-template-rows: 0fr; transition: all 0.3s ease-in-out; + width: 100%; + .wrapper { overflow: hidden; display: grid; diff --git a/system.json b/system.json index d72c53d5..31130bb8 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "1.1.0", + "version": "1.1.1", "compatibility": { "minimum": "13", "verified": "13.347", From fbeff1b90863c1fecdc42b6ea65a7d433db47d86 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 24 Aug 2025 02:21:00 +0200 Subject: [PATCH 22/66] The basic attack damage shown in the sidebar is modified if horde damage is active (#1071) --- module/data/action/attackAction.mjs | 8 +++++--- module/data/actor/adversary.mjs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index 62463d15..68afc23b 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -51,11 +51,13 @@ export default class DHAttackAction extends DHDamageAction { const labels = []; const { roll, range, damage } = this; - if (roll.trait) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`)) + if (roll.trait) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`)); if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`)); - for (const { value, type } of damage.parts) { - const str = Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {}); + const useAltDamage = this.actor?.effects?.find(x => x.type === 'horde')?.active; + for (const { value, valueAlt, type } of damage.parts) { + const usedValue = useAltDamage ? valueAlt : value; + const str = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {}); const icons = Array.from(type) .map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon) diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index e64c64f3..a4dfddf5 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -129,7 +129,7 @@ export default class DhpAdversary extends BaseDataActor { CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation ).hordeDamage; - if (autoHordeDamage && changes.system?.resources?.hitPoints?.value) { + if (autoHordeDamage && changes.system?.resources?.hitPoints?.value !== undefined) { const hordeActiveEffect = this.parent.effects.find(x => x.type === 'horde'); if (hordeActiveEffect) { const halfHP = Math.ceil(this.resources.hitPoints.max / 2); From 16173363d4450f4e0c9856b2bc58a1317bd85f8e Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 24 Aug 2025 02:24:57 +0200 Subject: [PATCH 23/66] [Feature] 687 - Companion Improvements (#1049) * Companions can't be put into CombatState anymore. Companions now have a Action Roll button * Added handling for multiselect toggleCombat --- module/applications/hud/tokenHUD.mjs | 25 +++++++++- .../applications/sheets/actors/companion.mjs | 47 +++++++++++++++++++ .../less/sheets/actors/companion/details.less | 13 +++++ templates/sheets/actors/companion/details.hbs | 3 ++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/module/applications/hud/tokenHUD.mjs b/module/applications/hud/tokenHUD.mjs index 5c29260b..48d5ac89 100644 --- a/module/applications/hud/tokenHUD.mjs +++ b/module/applications/hud/tokenHUD.mjs @@ -1,6 +1,9 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { static DEFAULT_OPTIONS = { - classes: ['daggerheart'] + classes: ['daggerheart'], + actions: { + combat: DHTokenHUD.#onToggleCombat + } }; /** @override */ @@ -11,8 +14,14 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { } }; + static #nonCombatTypes = ['environment', 'companion']; + async _prepareContext(options) { const context = await super._prepareContext(options); + + context.canToggleCombat = DHTokenHUD.#nonCombatTypes.includes(this.actor.type) + ? false + : context.canToggleCombat; context.systemStatusEffects = Object.keys(context.statusEffects).reduce((acc, key) => { const effect = context.statusEffects[key]; if (effect.systemEffect) acc[key] = effect; @@ -36,6 +45,20 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { return context; } + static async #onToggleCombat() { + const tokens = canvas.tokens.controlled + .filter(t => !t.actor || !DHTokenHUD.#nonCombatTypes.includes(t.actor.type)) + .map(t => t.document); + if (!this.object.controlled) tokens.push(this.document); + + try { + if (this.document.inCombat) await TokenDocument.implementation.deleteCombatants(tokens); + else await TokenDocument.implementation.createCombatants(tokens); + } catch (err) { + ui.notifications.warn(err.message); + } + } + _getStatusEffectChoices() { // Include all HUD-enabled status effects const choices = {}; diff --git a/module/applications/sheets/actors/companion.mjs b/module/applications/sheets/actors/companion.mjs index 2b82f50a..82aee312 100644 --- a/module/applications/sheets/actors/companion.mjs +++ b/module/applications/sheets/actors/companion.mjs @@ -8,6 +8,7 @@ export default class DhCompanionSheet extends DHBaseActorSheet { classes: ['actor', 'companion'], position: { width: 340 }, actions: { + actionRoll: DhCompanionSheet.#actionRoll, levelManagement: DhCompanionSheet.#levelManagement } }; @@ -45,6 +46,52 @@ export default class DhCompanionSheet extends DHBaseActorSheet { /* Application Clicks Actions */ /* -------------------------------------------- */ + /** + * + */ + static async #actionRoll(event) { + const partner = this.actor.system.partner; + const config = { + event, + title: `${game.i18n.localize('DAGGERHEART.GENERAL.Roll.action')}: ${this.actor.name}`, + headerTitle: `Companion ${game.i18n.localize('DAGGERHEART.GENERAL.Roll.action')}`, + roll: { + trait: partner.system.spellcastModifierTrait?.key + }, + hasRoll: true, + data: partner.getRollData() + }; + + const result = await partner.diceRoll(config); + this.consumeResource(result?.costs); + } + + // Remove when Action Refactor part #2 done + async consumeResource(costs) { + if (!costs?.length) return; + + const partner = this.actor.system.partner; + const usefulResources = { + ...foundry.utils.deepClone(partner.system.resources), + fear: { + value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), + max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, + reversed: false + } + }; + const resources = game.system.api.fields.ActionFields.CostField.getRealCosts(costs).map(c => { + const resource = usefulResources[c.key]; + return { + key: c.key, + value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1), + target: resource.target, + keyIsID: resource.keyIsID + }; + }); + + await partner.modifyResource(resources); + } + /** * Opens the companions level management window. * @type {ApplicationClickAction} diff --git a/styles/less/sheets/actors/companion/details.less b/styles/less/sheets/actors/companion/details.less index 2df14b23..cbdc25e6 100644 --- a/styles/less/sheets/actors/companion/details.less +++ b/styles/less/sheets/actors/companion/details.less @@ -77,4 +77,17 @@ } } } + + .action-section { + display: flex; + padding: 0 10px; + margin-top: 20px; + width: 100%; + + button { + height: 40px; + width: 100%; + font-weight: 600; + } + } } diff --git a/templates/sheets/actors/companion/details.hbs b/templates/sheets/actors/companion/details.hbs index dd28e745..eee2122c 100644 --- a/templates/sheets/actors/companion/details.hbs +++ b/templates/sheets/actors/companion/details.hbs @@ -56,4 +56,7 @@
{{/each}} +
+ +
\ No newline at end of file From 661808b5f153522d3b4374ea99694633b949c86a Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 24 Aug 2025 02:33:54 +0200 Subject: [PATCH 24/66] Fixed so observer permission doesn't allow using or editing anything (#1073) --- module/applications/sheets/actors/character.mjs | 9 +++++++++ styles/less/sheets/actors/character/sheet.less | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 0328f0dd..465da28d 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -15,6 +15,8 @@ export default class CharacterSheet extends DHBaseActorSheet { static DEFAULT_OPTIONS = { classes: ['character'], 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: { toggleVault: CharacterSheet.#toggleVault, rollAttribute: CharacterSheet.#rollAttribute, @@ -148,6 +150,13 @@ export default class CharacterSheet extends DHBaseActorSheet { .querySelector('.level-value') ?.addEventListener('change', event => this.document.updateLevel(Number(event.currentTarget.value))); + const observer = this.document.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER, { + exact: true + }); + if (observer) { + this.element.querySelector('.window-content').classList.add('viewMode'); + } + this._createFilterMenus(); this._createSearchFilter(); } diff --git a/styles/less/sheets/actors/character/sheet.less b/styles/less/sheets/actors/character/sheet.less index 68792c99..ee6580fd 100644 --- a/styles/less/sheets/actors/character/sheet.less +++ b/styles/less/sheets/actors/character/sheet.less @@ -11,6 +11,21 @@ padding-bottom: 0; overflow-x: auto; + &.viewMode { + button:not(.btn-toggle-view), + input:not(.search), + .controls, + .character-sidebar-sheet, + .img-portait, + .name-row, + .hope-section, + .downtime-section, + .character-traits, + .card-list { + pointer-events: none; + } + } + .character-sidebar-sheet { grid-row: 1 / span 2; grid-column: 1; From 2a0d748b5ee06133e3bfe9fb976b2ed59a6d46b3 Mon Sep 17 00:00:00 2001 From: Murilo Brito <91566541+moliloo@users.noreply.github.com> Date: Sat, 23 Aug 2025 21:52:18 -0300 Subject: [PATCH 25/66] [Bugfix] Fix light theme to roll messages (#1066) * fix light theme to roll messages * fix light theme issues * finally fix issues --------- Co-authored-by: WBHarry --- lang/en.json | 2 +- module/canvas/placeables/measuredTemplate.mjs | 6 +- module/data/fields/action/damageField.mjs | 20 +- module/data/item/weapon.mjs | 2 +- .../dialog/downtime/downtime-container.less | 2 +- styles/less/ui/chat/ability-use.less | 288 +++++++++--------- styles/less/ui/chat/action.less | 249 +++++++-------- styles/less/ui/chat/chat.less | 137 +++++++-- styles/less/ui/chat/downtime.less | 220 ++++++------- styles/less/ui/chat/sheet.less | 14 +- templates/ui/chat/resource-roll.hbs | 6 +- 11 files changed, 523 insertions(+), 423 deletions(-) diff --git a/lang/en.json b/lang/en.json index beeffd41..26eec167 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2385,7 +2385,7 @@ "subtype": "Subtype", "folders": { "adversaries": "Adversaries", - "ancestries": "Ancestries", + "ancestries": "Ancestries", "equipment": "Equipment", "classes": "Classes", "subclasses": "Subclasses", diff --git a/module/canvas/placeables/measuredTemplate.mjs b/module/canvas/placeables/measuredTemplate.mjs index 83cddfe1..f3691bb8 100644 --- a/module/canvas/placeables/measuredTemplate.mjs +++ b/module/canvas/placeables/measuredTemplate.mjs @@ -11,19 +11,19 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur if (splitRulerText.length > 0) { const rulerValue = Number(splitRulerText[0]); const result = DhMeasuredTemplate.getRangeLabels(rulerValue, rangeMeasurementSettings); - this.ruler.text = result.distance + (result.units ? (' ' + result.units) : ''); + this.ruler.text = result.distance + (result.units ? ' ' + result.units : ''); } } } static getRangeLabels(distanceValue, settings) { - let result = { distance: distanceValue, units: '' } + let result = { distance: distanceValue, units: '' }; const rangeMeasurementOverride = canvas.scene.flags.daggerheart?.rangeMeasurementOverride; if (rangeMeasurementOverride === true) { result.distance = distanceValue; result.units = canvas.scene?.grid?.units; - return result + return result; } if (distanceValue <= settings.melee) { result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.melee.name'); diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 7f1b61e9..4b4dee7d 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -23,14 +23,22 @@ export class DHActionDiceData extends foundry.abstract.DataModel { multiplier: new fields.StringField({ choices: CONFIG.DH.GENERAL.multiplierTypes, initial: 'prof', - label: "DAGGERHEART.ACTIONS.Config.damage.multiplier" + label: 'DAGGERHEART.ACTIONS.Config.damage.multiplier' }), - flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: "DAGGERHEART.ACTIONS.Config.damage.flatMultiplier" }), - dice: new fields.StringField({ choices: CONFIG.DH.GENERAL.diceTypes, initial: 'd6', label: "DAGGERHEART.GENERAL.Dice.single" }), - bonus: new fields.NumberField({ nullable: true, initial: null, label: "DAGGERHEART.GENERAL.bonus" }), + flatMultiplier: new fields.NumberField({ + nullable: true, + initial: 1, + label: 'DAGGERHEART.ACTIONS.Config.damage.flatMultiplier' + }), + dice: new fields.StringField({ + choices: CONFIG.DH.GENERAL.diceTypes, + initial: 'd6', + label: 'DAGGERHEART.GENERAL.Dice.single' + }), + bonus: new fields.NumberField({ nullable: true, initial: null, label: 'DAGGERHEART.GENERAL.bonus' }), custom: new fields.SchemaField({ - enabled: new fields.BooleanField({ label: "DAGGERHEART.ACTIONS.Config.general.customFormula" }), - formula: new FormulaField({ label: "DAGGERHEART.ACTIONS.Config.general.formula", initial: '' }) + enabled: new fields.BooleanField({ label: 'DAGGERHEART.ACTIONS.Config.general.customFormula' }), + formula: new FormulaField({ label: 'DAGGERHEART.ACTIONS.Config.general.formula', initial: '' }) }) }; } diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index ee4b997c..07a5c028 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -199,7 +199,7 @@ export default class DHWeapon extends AttachableItem { ]; for (const { value, type } of attack.damage.parts) { - const parts = value.custom.enabled ? [game.i18n.localize("DAGGERHEART.GENERAL.custom")] : [value.dice]; + const parts = value.custom.enabled ? [game.i18n.localize('DAGGERHEART.GENERAL.custom')] : [value.dice]; if (!value.custom.enabled && value.bonus) parts.push(value.bonus.signedString()); if (type.size > 0) { diff --git a/styles/less/dialog/downtime/downtime-container.less b/styles/less/dialog/downtime/downtime-container.less index 16edd3b0..eb615ef0 100644 --- a/styles/less/dialog/downtime/downtime-container.less +++ b/styles/less/dialog/downtime/downtime-container.less @@ -35,7 +35,7 @@ gap: 4px; .activity-marker { - font-size: .5rem; + font-size: 0.5rem; flex: none; color: light-dark(@dark-blue, @golden); margin-right: 4px; diff --git a/styles/less/ui/chat/ability-use.less b/styles/less/ui/chat/ability-use.less index d313638f..88302d0d 100644 --- a/styles/less/ui/chat/ability-use.less +++ b/styles/less/ui/chat/ability-use.less @@ -1,144 +1,144 @@ -@import '../../utils/colors.less'; -@import '../../utils/fonts.less'; -@import '../../utils/spacing.less'; - -.theme-light { - .daggerheart.chat.domain-card { - .domain-card-move .domain-card-header { - border-bottom: 1px solid @dark-blue; - - &:hover { - background: @dark-blue-10; - } - - .domain-label { - .title { - color: @dark-blue; - } - - .tags .tag { - background: @dark-15; - border: 1px solid @dark; - color: @dark; - } - } - - .fa-chevron-down { - color: @dark-blue; - } - } - - .description { - color: @dark; - } - } -} - -.daggerheart.chat { - &.domain-card { - display: flex; - flex-direction: column; - align-items: center; - - .card-img { - width: 100%; - height: 200px; - mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 90%, transparent 100%); - object-fit: cover; - } - - details[open] { - .fa-chevron-down { - transform: rotate(180deg); - transition: all 0.3s ease; - } - } - - .domain-card-move { - width: 100%; - - .fa-chevron-down { - transition: all 0.3s ease; - margin-left: auto; - } - - .domain-card-header { - display: flex; - flex-direction: row; - align-items: center; - margin: 8px; - padding-bottom: 5px; - width: -webkit-fill-available; - gap: 5px; - border-bottom: 1px solid @golden; - - &:hover { - background: @golden-10; - cursor: pointer; - transition: all 0.3s ease; - } - - .domain-label { - display: flex; - flex-direction: column; - width: 100%; - padding-bottom: 5px; - width: -webkit-fill-available; - gap: 5px; - - .title { - font-size: var(--font-size-20); - color: @golden; - font-weight: 700; - } - - .tags { - display: flex; - gap: 10px; - flex-wrap: wrap; - - .tag { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - padding: 3px 5px; - font-size: var(--font-size-12); - - background: @beige-15; - border: 1px solid @beige; - color: @beige; - border-radius: 3px; - } - } - } - } - } - - .description { - padding: 8px; - } - - .ability-card-footer { - display: flex; - flex-wrap: wrap; - gap: 5px; - width: 100%; - padding: 0 8px; - - button { - height: 40px; - flex: 1 1 calc(50% - 5px); - - &:nth-last-child(1):nth-child(odd) { - flex-basis: 100%; - } - } - - .ability-card-action-cost { - margin: auto; - font-size: 1.5em; - } - } - } -} +@import '../../utils/colors.less'; +@import '../../utils/fonts.less'; +@import '../../utils/spacing.less'; + +#interface.theme-light { + .daggerheart.chat.domain-card { + .domain-card-move .domain-card-header { + border-bottom: 1px solid @dark-blue; + + &:hover { + background: @dark-blue-10; + } + + .domain-label { + .title { + color: @dark-blue; + } + + .tags .tag { + background: @dark-15; + border: 1px solid @dark; + color: @dark; + } + } + + .fa-chevron-down { + color: @dark-blue; + } + } + + .description { + color: @dark; + } + } +} + +.daggerheart.chat { + &.domain-card { + display: flex; + flex-direction: column; + align-items: center; + + .card-img { + width: 100%; + height: 200px; + mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 90%, transparent 100%); + object-fit: cover; + } + + details[open] { + .fa-chevron-down { + transform: rotate(180deg); + transition: all 0.3s ease; + } + } + + .domain-card-move { + width: 100%; + + .fa-chevron-down { + transition: all 0.3s ease; + margin-left: auto; + } + + .domain-card-header { + display: flex; + flex-direction: row; + align-items: center; + margin: 8px; + padding-bottom: 5px; + width: -webkit-fill-available; + gap: 5px; + border-bottom: 1px solid @golden; + + &:hover { + background: @golden-10; + cursor: pointer; + transition: all 0.3s ease; + } + + .domain-label { + display: flex; + flex-direction: column; + width: 100%; + padding-bottom: 5px; + width: -webkit-fill-available; + gap: 5px; + + .title { + font-size: var(--font-size-20); + color: @golden; + font-weight: 700; + } + + .tags { + display: flex; + gap: 10px; + flex-wrap: wrap; + + .tag { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + padding: 3px 5px; + font-size: var(--font-size-12); + + background: @beige-15; + border: 1px solid @beige; + color: @beige; + border-radius: 3px; + } + } + } + } + } + + .description { + padding: 8px; + } + + .ability-card-footer { + display: flex; + flex-wrap: wrap; + gap: 5px; + width: 100%; + padding: 0 8px; + + button { + height: 40px; + flex: 1 1 calc(50% - 5px); + + &:nth-last-child(1):nth-child(odd) { + flex-basis: 100%; + } + } + + .ability-card-action-cost { + margin: auto; + font-size: 1.5em; + } + } + } +} diff --git a/styles/less/ui/chat/action.less b/styles/less/ui/chat/action.less index a849315a..817b0acd 100644 --- a/styles/less/ui/chat/action.less +++ b/styles/less/ui/chat/action.less @@ -1,124 +1,125 @@ -@import '../../utils/colors.less'; -@import '../../utils/fonts.less'; -@import '../../utils/spacing.less'; - -.theme-light { - .daggerheart.chat.action { - .action-move .action-section { - border-bottom: 1px solid @dark-blue; - - &:hover { - background: @dark-blue-10; - } - - .action-header { - .title { - color: @dark-blue; - } - .label { - color: @dark; - } - } - - .fa-chevron-down { - color: @dark-blue; - } - } - - .description { - color: @dark; - } - } -} - -.daggerheart.chat { - &.action { - display: flex; - flex-direction: column; - align-items: center; - - details[open] { - .fa-chevron-down { - transform: rotate(180deg); - transition: all 0.3s ease; - } - } - - .action-move { - width: 100%; - - .fa-chevron-down { - transition: all 0.3s ease; - margin-left: auto; - } - - .action-section { - display: flex; - flex-direction: row; - align-items: center; - margin: 8px 8px 0; - padding-bottom: 5px; - width: -webkit-fill-available; - gap: 5px; - border-bottom: 1px solid @golden; - - &:hover { - background: @golden-10; - cursor: pointer; - transition: all 0.3s ease; - } - - .action-img { - width: 40px; - height: 40px; - border-radius: 3px; - object-fit: cover; - } - - .action-header { - display: flex; - flex-direction: column; - gap: 5px; - - .title { - font-size: var(--font-size-20); - color: @golden; - font-weight: 700; - } - - .label { - font-size: var(--font-size-12); - color: @beige; - margin: 0; - } - } - } - } - - .description { - padding: 8px; - } - - .ability-card-footer { - display: flex; - flex-wrap: wrap; - gap: 5px; - width: 100%; - padding: 0 8px; - - button { - height: 40px; - flex: 1 1 calc(50% - 5px); - - &:nth-last-child(1):nth-child(odd) { - flex-basis: 100%; - } - } - - .ability-card-action-cost { - margin: auto; - font-size: 1.5em; - } - } - } -} +@import '../../utils/colors.less'; +@import '../../utils/fonts.less'; +@import '../../utils/spacing.less'; + +#interface.theme-light { + .daggerheart.chat.action { + .action-move .action-section { + border-bottom: 1px solid @dark-blue; + + &:hover { + background: @dark-blue-10; + } + + .action-header { + .title { + color: @dark-blue; + } + .label { + color: @dark; + } + } + + .fa-chevron-down { + color: @dark-blue; + } + } + + .description { + color: @dark; + } + } +} + +.daggerheart.chat { + &.action { + display: flex; + flex-direction: column; + align-items: center; + + details[open] { + .fa-chevron-down { + transform: rotate(180deg); + transition: all 0.3s ease; + } + } + + .action-move { + width: 100%; + + .fa-chevron-down { + transition: all 0.3s ease; + margin-left: auto; + } + + .action-section { + display: flex; + flex-direction: row; + align-items: center; + margin: 8px 8px 0; + padding-bottom: 5px; + width: -webkit-fill-available; + gap: 5px; + border-bottom: 1px solid @golden; + + &:hover { + background: @golden-10; + cursor: pointer; + transition: all 0.3s ease; + } + + .action-img { + width: 40px; + height: 40px; + border-radius: 3px; + object-fit: cover; + } + + .action-header { + display: flex; + flex-direction: column; + gap: 5px; + color: @beige; + + .title { + font-size: var(--font-size-20); + color: @golden; + font-weight: 700; + } + + .label { + font-size: var(--font-size-12); + color: @beige; + margin: 0; + } + } + } + } + + .description { + padding: 8px; + } + + .ability-card-footer { + display: flex; + flex-wrap: wrap; + gap: 5px; + width: 100%; + padding: 0 8px; + + button { + height: 40px; + flex: 1 1 calc(50% - 5px); + + &:nth-last-child(1):nth-child(odd) { + flex-basis: 100%; + } + } + + .ability-card-action-cost { + margin: auto; + font-size: 1.5em; + } + } + } +} diff --git a/styles/less/ui/chat/chat.less b/styles/less/ui/chat/chat.less index 9eb35cd6..828e2774 100644 --- a/styles/less/ui/chat/chat.less +++ b/styles/less/ui/chat/chat.less @@ -2,52 +2,137 @@ @import '../../utils/fonts.less'; @import '../../utils/spacing.less'; -.theme-light { - .daggerheart, - #chat-notifications { +#interface.theme-light { + .daggerheart.chat-sidebar .chat-log, + #chat-notifications .chat-log { --text-color: @dark-blue; --bg-color: @dark-blue-40; - .message-content .chat-roll { - .roll-part-header { - span, - span:before, - span:after { - color: @dark-blue; - } - &:before { - background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, @dark-blue 100%); - color: @dark-blue; + .chat-message { + .roll-formula { + background: @dark-15; + color: @dark; + } + + &.duality { + background-image: url(../assets/parchments/dh-parchment-dark.png); + + .message-content { + color: @beige; } - &:after { - background: linear-gradient(90deg, @dark-blue 0%, rgba(0, 0, 0, 0) 100%); - color: @dark-blue; + .roll-formula { + background: @dark-15; + color: @dark; + } + + .message-header { + .message-sub-header-container { + color: @beige; + h4 { + color: @golden; + } + } + .message-header-metadata { + .message-metadata { + color: @beige; + } + } + } + + &.hope { + --text-color: @golden; + --bg-color: @golden-40; + .message-header, + .message-content { + background-color: @golden-bg; + } + .roll-formula { + background: var(--bg-color); + color: var(--text-color); + } + } + + &.fear { + --text-color: @chat-blue; + --bg-color: @chat-blue-40; + .message-header, + .message-content { + background-color: @chat-blue-bg; + } + .roll-formula { + background: var(--bg-color); + color: var(--text-color); + } + } + + &.critical { + --text-color: @chat-purple; + --bg-color: @chat-purple-40; + .message-header, + .message-content { + background-color: @chat-purple-bg; + } + .roll-formula { + background: var(--bg-color); + color: var(--text-color); + } } } - .roll-section { - .roll-part-content { - .roll-result-value { + + &:not(.duality) { + .font-20 { + color: @dark; + } + + .roll-die { + color: @beige; + } + + fieldset { + color: @dark-blue; + border-color: @dark-blue; + + legend { color: @dark-blue; } + } + .chat-roll { + .roll-part-header { + color: @dark-blue; - .dice-tooltip .wrapper .roll-die { - color: @beige; + span::before, + span::after { + color: @dark-blue; + } + + &:before { + background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, @dark-blue 100%); + } + + &:after { + background: linear-gradient(90deg, @dark-blue 0%, rgba(0, 0, 0, 0) 100%); + } + } + + .roll-part-content { + &.dice-result { + color: @dark; + } + .roll-result-container { + color: @dark-blue; + } } } } } - - .chat-message .roll-formula { - background: @dark-15; - color: @dark; - } } } .daggerheart.chat { &.resource-roll { .reroll-message { + color: @beige; text-align: center; font-size: var(--font-size-18); margin-bottom: 0; diff --git a/styles/less/ui/chat/downtime.less b/styles/less/ui/chat/downtime.less index ca29e85f..5496a2a3 100644 --- a/styles/less/ui/chat/downtime.less +++ b/styles/less/ui/chat/downtime.less @@ -1,110 +1,110 @@ -@import '../../utils/colors.less'; -@import '../../utils/fonts.less'; -@import '../../utils/spacing.less'; - -.theme-light { - .daggerheart.chat.downtime { - .downtime-moves-list .downtime-move { - &:hover { - background: @dark-blue-10; - } - - .downtime-label { - border-bottom: 1px solid @dark-blue; - - .header-label .title { - color: @dark-blue; - } - .header-label .label { - color: @dark; - } - } - - .fa-chevron-down { - color: @dark-blue; - } - } - - .description { - color: @dark; - } - } -} - -.daggerheart.chat { - &.downtime { - display: flex; - flex-direction: column; - align-items: center; - - details[open] { - .fa-chevron-down { - transform: rotate(180deg); - transition: all 0.3s ease; - } - } - - .downtime-moves-list { - display: flex; - flex-direction: column; - gap: 5px; - width: 100%; - - .fa-chevron-down { - transition: all 0.3s ease; - margin-left: auto; - } - - .downtime-move { - width: 100%; - - .downtime-label { - display: flex; - align-items: center; - gap: 5px; - border-bottom: 1px solid @golden; - margin: 0 8px; - padding-bottom: 5px; - width: -webkit-fill-available; - - &:hover { - background: light-dark(@dark-blue-10, @golden-10); - cursor: pointer; - transition: all 0.3s ease; - } - - .downtime-image { - width: 40px; - height: 40px; - border-radius: 3px; - } - - .header-label { - padding: 8px; - .title { - font-size: var(--font-size-16); - color: @golden; - font-weight: 700; - } - .label { - font-size: var(--font-size-12); - color: @beige; - margin: 0; - } - } - } - - .description { - padding: 8px; - } - } - - .action-use-button { - width: -webkit-fill-available; - margin: 0 8px; - font-weight: 600; - height: 40px; - } - } - } -} +@import '../../utils/colors.less'; +@import '../../utils/fonts.less'; +@import '../../utils/spacing.less'; + +#interface.theme-light { + .daggerheart.chat.downtime { + .downtime-moves-list .downtime-move { + &:hover { + background: @dark-blue-10; + } + + .downtime-label { + border-bottom: 1px solid @dark-blue; + + .header-label .title { + color: @dark-blue; + } + .header-label .label { + color: @dark; + } + } + + .fa-chevron-down { + color: @dark-blue; + } + } + + .description { + color: @dark; + } + } +} + +.daggerheart.chat { + &.downtime { + display: flex; + flex-direction: column; + align-items: center; + + details[open] { + .fa-chevron-down { + transform: rotate(180deg); + transition: all 0.3s ease; + } + } + + .downtime-moves-list { + display: flex; + flex-direction: column; + gap: 5px; + width: 100%; + + .fa-chevron-down { + transition: all 0.3s ease; + margin-left: auto; + } + + .downtime-move { + width: 100%; + + .downtime-label { + display: flex; + align-items: center; + gap: 5px; + border-bottom: 1px solid @golden; + margin: 0 8px; + padding-bottom: 5px; + width: -webkit-fill-available; + + &:hover { + background: light-dark(@dark-blue-10, @golden-10); + cursor: pointer; + transition: all 0.3s ease; + } + + .downtime-image { + width: 40px; + height: 40px; + border-radius: 3px; + } + + .header-label { + padding: 8px; + .title { + font-size: var(--font-size-16); + color: @golden; + font-weight: 700; + } + .label { + font-size: var(--font-size-12); + color: @beige; + margin: 0; + } + } + } + + .description { + padding: 8px; + } + } + + .action-use-button { + width: -webkit-fill-available; + margin: 0 8px; + font-weight: 600; + height: 40px; + } + } + } +} diff --git a/styles/less/ui/chat/sheet.less b/styles/less/ui/chat/sheet.less index 59fa39dc..3d47a9b5 100644 --- a/styles/less/ui/chat/sheet.less +++ b/styles/less/ui/chat/sheet.less @@ -1,8 +1,8 @@ @import '../../utils/colors.less'; @import '../../utils/fonts.less'; -.theme-light { - .chat-message .message-content { +#interface.theme-light { + .chat-message:not(.duality) .message-content { color: @dark; blockquote { @@ -58,6 +58,12 @@ font-family: @font-body; font-weight: bold; } + + .dice-roll .dice-formula, + .dice-roll .dice-total { + background: @dark-blue-40; + color: @dark-blue; + } } } @@ -158,8 +164,8 @@ .dice-roll .dice-total { box-shadow: none; border: none; - background: light-dark(@dark-blue-40, @golden-40); - color: light-dark(@dark-blue, @golden); + background: @golden-10; + color: @golden; font-weight: 600; align-content: center; } diff --git a/templates/ui/chat/resource-roll.hbs b/templates/ui/chat/resource-roll.hbs index b6e1f3b0..dc41d18c 100644 --- a/templates/ui/chat/resource-roll.hbs +++ b/templates/ui/chat/resource-roll.hbs @@ -1,3 +1,3 @@ -
-
{{localize "DAGGERHEART.UI.Chat.resourceRoll.playerMessage" user=user name=name }}
-
\ No newline at end of file + \ No newline at end of file From afdffb672a660b114fa7f4ecf15e228c83182bf1 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Sun, 24 Aug 2025 11:38:09 +1000 Subject: [PATCH 26/66] [PR] [Fix] Homebrew Settings, Initial Trait Modifiers improvements and clean up (#1035) * Remove old homebrew settings template file * Improve the Initial Trait Modifiers config display * Remove font setting --------- Co-authored-by: Chris Ryan --- styles/less/ui/settings/settings.less | 31 ++----- templates/settings/homebrew-settings.hbs | 90 ------------------- .../settings/homebrew-settings/settings.hbs | 17 ++-- 3 files changed, 19 insertions(+), 119 deletions(-) delete mode 100644 templates/settings/homebrew-settings.hbs diff --git a/styles/less/ui/settings/settings.less b/styles/less/ui/settings/settings.less index cee5475f..5dd203db 100644 --- a/styles/less/ui/settings/settings.less +++ b/styles/less/ui/settings/settings.less @@ -16,6 +16,12 @@ } } + &.six-columns { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr; + gap: 2px; + } + &.start-align { align-self: flex-start; } @@ -103,28 +109,9 @@ gap: 4px; } - .trait-array-container { - display: flex; - justify-content: space-evenly; - gap: 8px; - margin-bottom: 16px; - - .trait-array-item { - position: relative; - display: flex; - justify-content: center; - - label { - position: absolute; - top: -7px; - font-size: var(--font-size-12); - font-variant: petite-caps; - z-index: 2; - } - - input { - text-align: center; - } + .trait-item { + input { + text-align: center; } } diff --git a/templates/settings/homebrew-settings.hbs b/templates/settings/homebrew-settings.hbs deleted file mode 100644 index 4e2c442f..00000000 --- a/templates/settings/homebrew-settings.hbs +++ /dev/null @@ -1,90 +0,0 @@ -
-
-

{{localize 'DAGGERHEART.SETTINGS.Menu.homebrew.name'}}

-
- {{formGroup settingFields.schema.fields.maxFear value=settingFields._source.maxFear localize=true}} - {{formGroup settingFields.schema.fields.maxDomains value=settingFields._source.maxDomains localize=true}} - {{formGroup settingFields.schema.fields.maxLoadout value=settingFields._source.maxLoadout localize=true}} -
- -

{{localize "DAGGERHEART.SETTINGS.Homebrew.FIELDS.traitArray.label"}}

-
- {{#each settingFields._source.traitArray as |trait index|}} -
- - -
- {{/each}} -
- -
- - {{localize "DAGGERHEART.SETTINGS.Homebrew.currency.title"}} - - {{formGroup settingFields.schema.fields.currency.fields.enabled value=settingFields._source.currency.enabled localize=true}} - {{formGroup settingFields.schema.fields.currency.fields.title value=settingFields._source.currency.title localize=true}} - {{formGroup settingFields.schema.fields.currency.fields.coins value=settingFields._source.currency.coins localize=true}} - {{formGroup settingFields.schema.fields.currency.fields.handfuls value=settingFields._source.currency.handfuls localize=true}} - {{formGroup settingFields.schema.fields.currency.fields.bags value=settingFields._source.currency.bags localize=true}} - {{formGroup settingFields.schema.fields.currency.fields.chests value=settingFields._source.currency.chests localize=true}} - -
- -
- {{localize "DAGGERHEART.SETTINGS.Homebrew.downtimeMoves"}} - -
- - {{localize "DAGGERHEART.APPLICATIONS.Downtime.longRest.title"}} - - - - -
- -
- -
-
- -
- {{#each settingFields._source.restMoves.longRest.moves as |move id|}} - {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="longRest" id=id }} - {{/each}} -
-
- -
- - {{localize "DAGGERHEART.APPLICATIONS.Downtime.shortRest.title"}} - - - - -
- -
- -
-
- -
- {{#each settingFields._source.restMoves.shortRest.moves as |move id|}} - {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="shortRest" id=id }} - {{/each}} -
-
-
- -
- - -
-
- \ No newline at end of file diff --git a/templates/settings/homebrew-settings/settings.hbs b/templates/settings/homebrew-settings/settings.hbs index 1c7c787d..893e1bc3 100644 --- a/templates/settings/homebrew-settings/settings.hbs +++ b/templates/settings/homebrew-settings/settings.hbs @@ -12,15 +12,18 @@ {{formGroup settingFields.schema.fields.maxLoadout value=settingFields._source.maxLoadout localize=true}}
-

{{localize "DAGGERHEART.SETTINGS.Homebrew.FIELDS.traitArray.label"}}

-
+
+ + {{localize "DAGGERHEART.SETTINGS.Homebrew.FIELDS.traitArray.label"}} + + {{#each settingFields._source.traitArray as |trait index|}} -
- - -
+
+ {{localize "DAGGERHEART.GENERAL.Modifier.single"}} {{add index 1}} + +
{{/each}} -
+
From 5a38e28a846b137de1db774d6e8bc82c5a058598 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 24 Aug 2025 13:36:46 +0200 Subject: [PATCH 27/66] Fixed the migration to respect null for multiclass (#1075) --- module/systemRegistration/migrations.mjs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index da0dcb5d..134c8714 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -3,8 +3,13 @@ export async function runMigrations() { if (!lastMigrationVersion) lastMigrationVersion = '1.0.6'; if (foundry.utils.isNewerVersion('1.1.0', lastMigrationVersion)) { + const lockedPacks = []; const compendiumActors = []; for (let pack of game.packs) { + if (pack.locked) { + lockedPacks.push(pack.collection); + await pack.configure({ locked: false }); + } const documents = await pack.getDocuments(); compendiumActors.push(...documents.filter(x => x.type === 'character')); } @@ -32,13 +37,23 @@ export async function runMigrations() { actor.updateEmbeddedDocuments('Item', items); }); + for (let packId of lockedPacks) { + const pack = game.packs.get(packId); + await pack.configure({ locked: true }); + } + lastMigrationVersion = '1.1.0'; } if (foundry.utils.isNewerVersion('1.1.1', lastMigrationVersion)) { + const lockedPacks = []; const compendiumClasses = []; const compendiumActors = []; for (let pack of game.packs) { + if (pack.locked) { + lockedPacks.push(pack.collection); + await pack.configure({ locked: false }); + } const documents = await pack.getDocuments(); compendiumClasses.push(...documents.filter(x => x.type === 'class')); compendiumActors.push(...documents.filter(x => x.type === 'character')); @@ -46,7 +61,8 @@ export async function runMigrations() { [...compendiumActors, ...game.actors.filter(x => x.type === 'character')].forEach(char => { const multiclass = char.items.find(x => x.type === 'class' && x.system.isMulticlass); - const multiclassSubclass = multiclass.system.subclasses.length > 0 ? multiclass.system.subclasses[0] : null; + const multiclassSubclass = + multiclass?.system?.subclasses?.length > 0 ? multiclass.system.subclasses[0] : null; char.items.forEach(item => { if (item.type === 'feature' && item.system.identifier === 'multiclass') { const base = item.system.originItemType === 'class' ? multiclass : multiclassSubclass; @@ -72,6 +88,11 @@ export async function runMigrations() { } } + for (let packId of lockedPacks) { + const pack = game.packs.get(packId); + await pack.configure({ locked: true }); + } + lastMigrationVersion = '1.1.1'; } From d686ac1e0cdc1ce88a6952c3d9641bea12c3d951 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 24 Aug 2025 18:40:06 +0200 Subject: [PATCH 28/66] . (#1056) --- module/data/actor/base.mjs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index 5b225228..0e9dd2d8 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -115,11 +115,16 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel { const typeForDefeated = ['character', 'adversary', 'companion'].find(x => x === this.parent.type); if (defeatedSettings.enabled && typeForDefeated) { const resource = typeForDefeated === 'companion' ? 'stress' : 'hitPoints'; - if (changes.system.resources[resource]) { - const becameMax = changes.system.resources[resource].value === this.resources[resource].max; + const resourceValue = changes.system.resources[resource]; + if ( + resourceValue && + this.resources[resource].max && + resourceValue.value !== this.resources[resource].value + ) { + const becameMax = resourceValue.value === this.resources[resource].max; const wasMax = this.resources[resource].value === this.resources[resource].max && - this.resources[resource].value !== changes.system.resources[resource].value; + this.resources[resource].value !== resourceValue.value; if (becameMax) { this.parent.toggleDefeated(true); } else if (wasMax) { From d24fef7494da497e8d94245287a43849c61a29c5 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 24 Aug 2025 19:15:42 +0200 Subject: [PATCH 29/66] [Fix] Hints For Drag/Drop (#1076) * Added hints for drag-drop areas * Changed to drag-area style. Updated localization --- lang/en.json | 5 +++++ styles/less/global/global.less | 12 ++++++++++++ styles/less/global/index.less | 1 + .../environment-settings/adversaries.less | 13 ------------- .../less/sheets/actors/character/inventory.less | 4 ---- styles/less/sheets/actors/character/loadout.less | 4 ---- styles/less/ui/item-browser/item-browser.less | 8 ++------ .../environment-settings/adversaries.hbs | 2 +- templates/sheets/items/class/features.hbs | 3 +++ templates/sheets/items/class/settings.hbs | 15 +++++++++++++++ 10 files changed, 39 insertions(+), 28 deletions(-) create mode 100644 styles/less/global/global.less diff --git a/lang/en.json b/lang/en.json index f310a01d..7efca151 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1894,6 +1894,7 @@ "amount": "Amount", "any": "Any", "armor": "Armor", + "armors": "Armors", "armorScore": "Armor Score", "activeEffects": "Active Effects", "armorSlots": "Armor Slots", @@ -1944,6 +1945,7 @@ "inactiveEffects": "Inactive Effects", "inventory": "Inventory", "itemResource": "Item Resource", + "items": "Items", "label": "Label", "level": "Level", "levelShort": "Lv", @@ -1955,6 +1957,7 @@ "plural": "Miss" }, "maxWithThing": "Max {thing}", + "missingDragDropThing": "Drop {thing} here", "multiclass": "Multiclass", "newCategory": "New Category", "none": "None", @@ -1976,6 +1979,7 @@ "scalable": "Scalable", "situationalBonus": "Situational Bonus", "stress": "Stress", + "subclasses": "Subclasses", "success": "Success", "take": "Take", "Target": { @@ -1993,6 +1997,7 @@ "used": "Used", "uses": "Uses", "value": "Value", + "weapons": "Weapons", "withThing": "With {thing}" }, "ITEMS": { diff --git a/styles/less/global/global.less b/styles/less/global/global.less new file mode 100644 index 00000000..5bf32746 --- /dev/null +++ b/styles/less/global/global.less @@ -0,0 +1,12 @@ +.drag-area { + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + width: 100%; + height: 40px; + border: 1px dashed light-dark(@dark-blue-50, @beige-50); + border-radius: 3px; + color: light-dark(@dark-blue-50, @beige-50); + font-family: @font-body; +} diff --git a/styles/less/global/index.less b/styles/less/global/index.less index 5f955ce3..db61304a 100644 --- a/styles/less/global/index.less +++ b/styles/less/global/index.less @@ -3,6 +3,7 @@ @import './chat.less'; @import './elements.less'; @import './enrichment.less'; +@import './global.less'; @import './tab-navigation.less'; @import './tab-form-footer.less'; @import './tab-actions.less'; diff --git a/styles/less/sheets-settings/environment-settings/adversaries.less b/styles/less/sheets-settings/environment-settings/adversaries.less index 8dc12c87..1a27eaca 100644 --- a/styles/less/sheets-settings/environment-settings/adversaries.less +++ b/styles/less/sheets-settings/environment-settings/adversaries.less @@ -34,18 +34,5 @@ width: 100%; } } - - .adversaries-dragger { - display: flex; - align-items: center; - justify-content: center; - box-sizing: border-box; - width: 100%; - height: 40px; - border: 1px dashed light-dark(@dark-blue-50, @beige-50); - border-radius: 3px; - color: light-dark(@dark-blue-50, @beige-50); - font-family: @font-body; - } } } diff --git a/styles/less/sheets/actors/character/inventory.less b/styles/less/sheets/actors/character/inventory.less index 48bdd682..d8c225ad 100644 --- a/styles/less/sheets/actors/character/inventory.less +++ b/styles/less/sheets/actors/character/inventory.less @@ -27,10 +27,6 @@ outline: 2px solid light-dark(@dark, @golden); } - &:placeholder { - color: light-dark(@dark-blue-50, @beige-50); - } - &::-webkit-search-cancel-button { -webkit-appearance: none; display: none; diff --git a/styles/less/sheets/actors/character/loadout.less b/styles/less/sheets/actors/character/loadout.less index 98fe9eed..24a53959 100644 --- a/styles/less/sheets/actors/character/loadout.less +++ b/styles/less/sheets/actors/character/loadout.less @@ -27,10 +27,6 @@ outline: 2px solid light-dark(@dark, @golden); } - &:placeholder { - color: light-dark(@dark-blue-50, @beige-50); - } - &::-webkit-search-cancel-button { -webkit-appearance: none; display: none; diff --git a/styles/less/ui/item-browser/item-browser.less b/styles/less/ui/item-browser/item-browser.less index 407a3116..7d708e1f 100644 --- a/styles/less/ui/item-browser/item-browser.less +++ b/styles/less/ui/item-browser/item-browser.less @@ -148,10 +148,6 @@ outline: 2px solid light-dark(@dark, @golden); } - &:placeholder { - color: light-dark(@dark-blue-50, @beige-50); - } - &::-webkit-search-cancel-button { -webkit-appearance: none; display: none; @@ -187,7 +183,7 @@ } &:has(+ .subfolder-list):after { - content: "+"; + content: '+'; } } @@ -357,7 +353,7 @@ grid-template-rows: 0fr; transition: all 0.3s ease-in-out; width: 100%; - + .wrapper { overflow: hidden; display: grid; diff --git a/templates/sheets-settings/environment-settings/adversaries.hbs b/templates/sheets-settings/environment-settings/adversaries.hbs index 3464a745..4d8b5122 100644 --- a/templates/sheets-settings/environment-settings/adversaries.hbs +++ b/templates/sheets-settings/environment-settings/adversaries.hbs @@ -25,7 +25,7 @@ {{/each}} -
+
{{localize "DAGGERHEART.GENERAL.dropActorsHere"}}
diff --git a/templates/sheets/items/class/features.hbs b/templates/sheets/items/class/features.hbs index 4e6a7e6f..6ed449dc 100644 --- a/templates/sheets/items/class/features.hbs +++ b/templates/sheets/items/class/features.hbs @@ -27,6 +27,9 @@
{{localize "TYPES.Item.subclass"}}
+ {{#unless source.system.subclasses}} +
{{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "DAGGERHEART.GENERAL.subclasses")}}
+ {{/unless}} {{#each source.system.subclasses as |subclass index|}}
  • diff --git a/templates/sheets/items/class/settings.hbs b/templates/sheets/items/class/settings.hbs index 9ac2b14f..8e8254ee 100644 --- a/templates/sheets/items/class/settings.hbs +++ b/templates/sheets/items/class/settings.hbs @@ -45,6 +45,8 @@ {{#unless (eq document.parent.type 'character')}}{{/unless}}
  • + {{else}} +
    {{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "DAGGERHEART.GENERAL.weapons")}}
    {{/if}}
    @@ -60,6 +62,8 @@ {{#unless (eq document.parent.type 'character')}}{{/unless}} + {{else}} +
    {{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "DAGGERHEART.GENERAL.weapons")}}
    {{/if}} @@ -75,6 +79,8 @@ {{#unless (eq document.parent.type 'character')}}{{/unless}} + {{else}} +
    {{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "DAGGERHEART.GENERAL.armors")}}
    {{/if}} @@ -85,6 +91,9 @@
    {{localize "DAGGERHEART.GENERAL.take"}}
    + {{#unless source.system.inventory.take}} +
    {{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "DAGGERHEART.GENERAL.items")}}
    + {{/unless}} {{#each source.system.inventory.take}} {{#if this}}
    @@ -102,6 +111,9 @@
    {{localize "DAGGERHEART.ITEMS.Class.guide.inventory.thenChoose"}}
    + {{#unless source.system.inventory.choiceA}} +
    {{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "DAGGERHEART.GENERAL.items")}}
    + {{/unless}} {{#each source.system.inventory.choiceA}} {{#if this}}
    @@ -119,6 +131,9 @@
    {{localize "DAGGERHEART.ITEMS.Class.guide.inventory.andEither"}}
    + {{#unless source.system.inventory.choiceB}} +
    {{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "DAGGERHEART.GENERAL.items")}}
    + {{/unless}} {{#each source.system.inventory.choiceB}} {{#if this}}
    From 7a1d259e825300ee1bf5c154a37d280a2b42d773 Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Sun, 24 Aug 2025 20:55:35 +0200 Subject: [PATCH 30/66] Fix/1022 (#1079) * Temp ActionField attack type missing * Move missing attack type to getModel * Show hit/miss to GM and hide to players if no OBSERVER permission --- module/data/chat-message/adversaryRoll.mjs | 5 +++-- templates/ui/chat/parts/target-part.hbs | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/module/data/chat-message/adversaryRoll.mjs b/module/data/chat-message/adversaryRoll.mjs index 1447acb8..de203a6e 100644 --- a/module/data/chat-message/adversaryRoll.mjs +++ b/module/data/chat-message/adversaryRoll.mjs @@ -112,7 +112,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { this.currentTargets = this.getTargetList(); // this.registerTargetHook(); - if (this.targetMode === true && this.hasRoll) { + if (this.hasRoll) { this.targetShort = this.targets.reduce( (a, c) => { if (c.hit) a.hit += 1; @@ -126,7 +126,8 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { } this.canViewSecret = this.parent.speakerActor?.testUserPermission(game.user, 'OBSERVER'); - this.canButtonApply = game.user.isGM; + this.canButtonApply = game.user.isGM; //temp + this.isGM = game.user.isGM; //temp } getTargetList() { diff --git a/templates/ui/chat/parts/target-part.hbs b/templates/ui/chat/parts/target-part.hbs index 82c71456..ec3ea997 100644 --- a/templates/ui/chat/parts/target-part.hbs +++ b/templates/ui/chat/parts/target-part.hbs @@ -1,6 +1,6 @@
    Target
    - {{#if (or (and targets.length (or (gt targetShort.hit 0) (gt targetShort.miss 0))) (and hasSave pendingSaves))}} + {{#if isGM}}
    {{#if (or (gt targetShort.hit 0) (gt targetShort.miss 0))}} @@ -30,8 +30,8 @@
    {{name}}
    - {{#if (and ../targetMode ../hasRoll)}} -
    + {{#if (and ../hasRoll (hasProperty this "hit"))}} +
    {{#if hit}} {{localize "DAGGERHEART.GENERAL.hit.single"}} {{else}} From f480027b9573e379768d85264985065d7f42c208 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:09:21 +0200 Subject: [PATCH 31/66] Improved text color on chat-message flavor texts (#1080) --- styles/less/global/chat.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/less/global/chat.less b/styles/less/global/chat.less index 37ec993d..d7fee8e5 100644 --- a/styles/less/global/chat.less +++ b/styles/less/global/chat.less @@ -77,7 +77,7 @@ .flavor-text { font-size: var(--font-size-12); line-height: 20px; - color: var(--color-dark-4); + color: light-dark(@dark, @beige); text-align: center; display: block; } From 9aba4dc66dbf5b4213803db3189064b30f3eac86 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 26 Aug 2025 01:17:58 +0200 Subject: [PATCH 32/66] Corrected pendant name (#1085) --- ...n => loot_Strange_Dirty_Pendant_LriTeh9hkwzEaCr1.json} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename src/packs/classes/{loot_Strange_Dirty_Penant_LriTeh9hkwzEaCr1.json => loot_Strange_Dirty_Pendant_LriTeh9hkwzEaCr1.json} (80%) diff --git a/src/packs/classes/loot_Strange_Dirty_Penant_LriTeh9hkwzEaCr1.json b/src/packs/classes/loot_Strange_Dirty_Pendant_LriTeh9hkwzEaCr1.json similarity index 80% rename from src/packs/classes/loot_Strange_Dirty_Penant_LriTeh9hkwzEaCr1.json rename to src/packs/classes/loot_Strange_Dirty_Pendant_LriTeh9hkwzEaCr1.json index ba307e05..3ca7b938 100644 --- a/src/packs/classes/loot_Strange_Dirty_Penant_LriTeh9hkwzEaCr1.json +++ b/src/packs/classes/loot_Strange_Dirty_Pendant_LriTeh9hkwzEaCr1.json @@ -1,6 +1,6 @@ { "folder": "1gQpPaxdgvCxYlLs", - "name": "Strange Dirty Penant", + "name": "Strange Dirty Pendant", "type": "loot", "_id": "LriTeh9hkwzEaCr1", "img": "icons/equipment/neck/necklace-carved-stone-spiral.webp", @@ -20,12 +20,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754222150005, - "modifiedTime": 1754246254311, - "lastModifiedBy": "LgnbNMLaxandgMQq" + "modifiedTime": 1756136989085, + "lastModifiedBy": "HKcDBUU22bYKtQmH" }, "_key": "!items!LriTeh9hkwzEaCr1" } From 2aeb25503360f2371fd323be7cd9a48a2b648cde Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 26 Aug 2025 01:27:38 +0200 Subject: [PATCH 33/66] Corrected so secondary weapons will show that they are secondary in the tooltip (#1082) --- templates/ui/tooltip/weapon.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/ui/tooltip/weapon.hbs b/templates/ui/tooltip/weapon.hbs index bf414e5c..582bfc50 100644 --- a/templates/ui/tooltip/weapon.hbs +++ b/templates/ui/tooltip/weapon.hbs @@ -6,7 +6,7 @@
    -
    {{#if item.system.secondaryWeapon}}{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon"}}{{else}}{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon"}}{{/if}}
    +
    {{#if item.system.secondary}}{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon"}}{{else}}{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon"}}{{/if}}
    From 0a5828c8fac09947b6f9f48bffb999c1880fed72 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 26 Aug 2025 01:54:07 +0200 Subject: [PATCH 34/66] [Fix] Character Sheet Subclass Compendium Preset (#1083) * Added the preset for the subclass Compendium Browser from character sheet * Raised version --- module/applications/sheets/actors/character.mjs | 9 +++++++++ system.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 4f7d947b..c860e9e9 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -607,6 +607,15 @@ export default class CharacterSheet extends DHBaseActorSheet { const presets = { compendium: 'daggerheart', folder: key, + filter: + key === 'subclasses' + ? { + 'system.linkedClass.uuid': { + key: 'system.linkedClass.uuid', + value: this.document.system.class.value._stats.compendiumSource + } + } + : undefined, render: { noFolder: true } diff --git a/system.json b/system.json index 31130bb8..8e5a7610 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "1.1.1", + "version": "1.1.2", "compatibility": { "minimum": "13", "verified": "13.347", From ff65a85458f3aaf7d7bf56525dba762357ed0435 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 26 Aug 2025 02:04:22 +0200 Subject: [PATCH 35/66] [Fix] 1086 - Enrichment Buttons (#1087) * Fixed so enriched buttons are button type * Fixed DamageEnrihed button --- module/enrichers/DamageEnricher.mjs | 7 ++++++- module/enrichers/DualityRollEnricher.mjs | 2 +- module/enrichers/TemplateEnricher.mjs | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/module/enrichers/DamageEnricher.mjs b/module/enrichers/DamageEnricher.mjs index a859be9f..a52c4b31 100644 --- a/module/enrichers/DamageEnricher.mjs +++ b/module/enrichers/DamageEnricher.mjs @@ -44,7 +44,7 @@ function getDamageMessage(damage, type, inline, defaultElement) { const dualityElement = document.createElement('span'); dualityElement.innerHTML = ` - `; From 76d753cd880b0bcb037b777a56be22541b67e0db Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 26 Aug 2025 02:14:09 +0200 Subject: [PATCH 36/66] Fixed so subclasses can be dragged to the sheet when they're stored in a folder (#1081) --- module/data/item/subclass.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/module/data/item/subclass.mjs b/module/data/item/subclass.mjs index e16a15f7..375588fb 100644 --- a/module/data/item/subclass.mjs +++ b/module/data/item/subclass.mjs @@ -52,8 +52,7 @@ export default class DHSubclass extends BaseDataItem { async _preCreate(data, options, user) { if (this.actor?.type === 'character') { - const dataUuid = - data.uuid ?? (data.folder ? `Compendium.daggerheart.subclasses.Item.${data._id}` : `Item.${data._id}`); + const dataUuid = data.uuid ?? data._stats.compendiumSource ?? `Item.${data._id}`; if (this.actor.system.class.subclass) { if (this.actor.system.multiclass.subclass) { ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassesAlreadyPresent')); From 3386a9d61d6cb71769852c1f61564f7fd2c08c0a Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 26 Aug 2025 14:13:31 +0200 Subject: [PATCH 37/66] [Feature] 1031 - Action Names In Chat (#1039) * Fixed so that actions don't print the actor name in chat along with its own name * . --- module/data/action/baseAction.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index d46bd776..ea619bcf 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -163,7 +163,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel const hasRoll = this.getUseHasRoll(byPass); return { event, - title: `${this.item.name}: ${game.i18n.localize(this.name)}`, + title: `${this.item instanceof CONFIG.Actor.documentClass ? '' : `${this.item.name}: `}${game.i18n.localize(this.name)}`, source: { item: this.item._id, action: this._id, From aaf6c689fc052bf9958470287e0e4ba980b6c1fa Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Tue, 26 Aug 2025 14:22:40 +0200 Subject: [PATCH 38/66] Add hint css (#1059) * Add hint css * Move .hint css class to global --- styles/less/global/elements.less | 19 ++++++++++++++++++- styles/less/global/global.less | 14 ++++++++++++++ styles/less/ui/settings/settings.less | 3 ++- .../settings/automation-settings/general.hbs | 2 +- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index 397420b2..399715ca 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -603,9 +603,12 @@ display: flex; justify-content: space-between; align-items: center; + gap: .25rem .5rem; + flex-wrap: wrap; label { - font-size: var(--font-size-16); + font-size: var(--font-size-14); + font-weight: normal; } .form-fields { @@ -613,6 +616,20 @@ gap: 4px; align-items: center; } + + &.setting-two-values { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: .25rem .5rem; + + .form-group { + justify-content: end; + } + + .hint { + grid-column: 1 / -1; + } + } } } diff --git a/styles/less/global/global.less b/styles/less/global/global.less index 5bf32746..cf8431b5 100644 --- a/styles/less/global/global.less +++ b/styles/less/global/global.less @@ -10,3 +10,17 @@ color: light-dark(@dark-blue-50, @beige-50); font-family: @font-body; } + +.daggerheart.dh-style { + .hint { + flex: 0 0 100%; + margin: 0; + color: var(--color-form-hint); + } + + &:hover { + .hint { + color: var(--color-form-hint-hover); + } + } +} \ No newline at end of file diff --git a/styles/less/ui/settings/settings.less b/styles/less/ui/settings/settings.less index 5dd203db..2ac8bfb0 100644 --- a/styles/less/ui/settings/settings.less +++ b/styles/less/ui/settings/settings.less @@ -4,7 +4,7 @@ fieldset { display: flex; flex-direction: column; - gap: 4px; + gap: .5rem; &.two-columns { display: grid; @@ -32,6 +32,7 @@ display: flex; align-items: center; gap: 8px; + flex-wrap: nowrap; } .settings-items { diff --git a/templates/settings/automation-settings/general.hbs b/templates/settings/automation-settings/general.hbs index 04d08a9f..211ee68e 100644 --- a/templates/settings/automation-settings/general.hbs +++ b/templates/settings/automation-settings/general.hbs @@ -3,7 +3,7 @@ data-tab="{{tabs.general.id}}" data-group="{{tabs.general.group}}" > -
    +
    {{formGroup settingFields.schema.fields.hopeFear.fields.gm value=settingFields._source.hopeFear.gm localize=true}} {{formGroup settingFields.schema.fields.hopeFear.fields.players value=settingFields._source.hopeFear.players localize=true}} From ff396cd2f0f44c7a8a458a52cce2560c3bb42cab Mon Sep 17 00:00:00 2001 From: CPTN_Cosmo Date: Tue, 26 Aug 2025 14:42:07 +0200 Subject: [PATCH 39/66] added note to translations (#1093) * added note to translations * clarified note --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b6f4097e..80c01bce 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,6 +17,10 @@ We welcome contributions of all kinds: 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 From dc54fdc096bd08d2176fc1cd9c48309386dce281 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 26 Aug 2025 20:30:27 +0200 Subject: [PATCH 40/66] [Fix] 1091 - Improve Homebrew Domain Container (#1095) * Simple fix for now * Corrected to 2 rows of height * Corrected scrollbar --- styles/less/ui/settings/homebrew-settings/domains.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/styles/less/ui/settings/homebrew-settings/domains.less b/styles/less/ui/settings/homebrew-settings/domains.less index e0e0dcd7..893f58be 100644 --- a/styles/less/ui/settings/homebrew-settings/domains.less +++ b/styles/less/ui/settings/homebrew-settings/domains.less @@ -54,6 +54,10 @@ display: grid; grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr; gap: 8px; + max-height: 184px; + overflow: auto; + scrollbar-width: thin; + scrollbar-color: light-dark(#18162e, #f3c267) transparent; .domain-container { position: relative; From 8e5dd2237085cb43fd08b65586e3f21a0b5f94ff Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Wed, 27 Aug 2025 02:12:48 +0200 Subject: [PATCH 41/66] [Fix] 900 - Compendium Self Targetting (#1077) * Fixed classes and subclasses * Went over DomainCards * Everything except de-facto-items * Fixed items * Fixed merge conflicts --- module/config/itemConfig.mjs | 3 + .../feature_Retract_UFR67BUOhNGLFyg9.json | 10 +-- .../feature_Wings_WquAjoOcso8lwySW.json | 10 +-- ...eature_Armored_Shell_nDQZdIF2epKlhauX.json | 10 +-- .../feature_Rampage_8upqfcZvi7b5hRLE.json | 70 +++++++++++++--- .../feature_Takedown_0ey4kM9ssj2otHvb.json | 23 +++--- ...ature_Frontline_Tank_YS1g7YdWwOaS629x.json | 10 +-- ...eature_Hold_Them_Off_2Cyb9ZeuAesf5Sb3.json | 10 +-- .../feature_No_Mercy_njj2C3tMDeCHHOoh.json | 10 +-- ...ature_Ranger_s_Focus_ncLx2P8BOUtrAD38.json | 60 ++++++++++++-- ...eature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json | 16 ++-- ...feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json | 36 +-------- ...inCard_Bold_Presence_tdsL00yTSLNgZWs6.json | 18 ++--- ...inCard_Book_of_Ronin_SZMNR3uGNinJcN4N.json | 10 +-- ...inCard_Book_of_Sitil_eq8VNqYMRHhF9xw9.json | 10 +-- ...nCard_Book_of_Yarrow_J1ovx2FpNDvPq1o6.json | 8 +- ...inCard_Breaking_Blow_8UANBgSdhMZ0sqfO.json | 8 +- ...nCard_Cloaking_Blast_Zhw7PtK8nMPlsOqD.json | 10 +-- ...inCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json | 10 +-- ...ainCard_Deadly_Focus_xxZOXC4tiZQ6kg1e.json | 36 ++++----- ...Card_Force_of_Nature_LzVpMkD5I4QeaIHf.json | 10 +-- .../domainCard_Frenzy_MMl7abdGRLl7TJLO.json | 10 +-- ...omainCard_Full_Surge_SgvjJfMyubZowPxS.json | 18 ++--- ...inCard_Hold_the_Line_kdFoLo3KXwn4LqTG.json | 26 +++--- ...ard_Hypnotic_Shimmer_2ZeuCGVatQdPOVC6.json | 8 +- ...Card_Lead_by_Example_YWCRplmtwpCjpq5i.json | 8 +- ...rd_Overwhelming_Aura_iEBLySZD9z8CLdz7.json | 10 +-- .../domainCard_Rage_Up_GRL0cvs96vrTDckZ.json | 20 ++--- ...nCard_Rain_of_Blades_Ucenef6JpjQxwXni.json | 8 +- ...nCard_Salvation_Beam_4uAFGp3LxiC07woC.json | 8 +- .../domainCard_Smite_U1uWJE94HZVudujz.json | 10 +-- ..._Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json | 10 +-- ...omainCard_Thorn_Skin_oUipGK84E2KjoKqh.json | 10 +-- ...ard_Uncanny_Disguise_TV56wSysbU5xAlOa.json | 10 +-- ...inCard_Unleash_Chaos_o62i0QdbUDIiAhSq.json | 10 +-- ...Card_Vanishing_Dodge_GBMIElIpk4cvk1Bd.json | 8 +- ...inCard_Veil_of_Night_gV4L5ZZmfPrEbIDh.json | 10 +-- .../folders_Splendor_TL1TutmbeCVJ06nR.json | 8 +- .../folders_Valor_yPVeShe47ETIqs9q.json | 8 +- ...mable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json | 10 +-- ...umable_Attune_Potion_JGD3M9hBHtVAA8XP.json | 8 +- ...e_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json | 10 +-- ...mable_Bolster_Potion_FOPQNqXbiVO0ilYL.json | 8 +- ...consumable_Death_Tea_xDnJeF1grkmKck8Q.json | 10 +-- ...able_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json | 10 +-- ...nsumable_Featherbone_DpxEMpwfasEBpORU.json | 64 +++++++++++++-- ...onsumable_Gill_Salve_Nvbb9mze6o5D0AEg.json | 64 +++++++++++++-- .../loot_Glamour_Stone_Pj17cvdJ1XG1jv6I.json | 68 +++++++++++++--- ...loot_Piercing_Arrows_I63LTFD6GXHgyGpR.json | 8 +- ...loot_Premium_Bedroll_QGYPNBIufpBguwjC.json | 10 +-- ...t_Ring_of_Resistance_aUqRifqR5JXXa1dN.json | 79 +++++++++++++++++-- ...loot_Ring_of_Silence_K1ysGnTpNyxPu5Au.json | 43 +++++----- ...ture_Act_of_Reprisal_k7vvMJtEcxMWUUrW.json | 8 +- .../feature_Adrenaline_uByM34yQlw38yf1V.json | 37 +-------- ...eature_Battle_Bonded_hWsKyed1vfILg0I8.json | 54 ++++++++++++- ...feature_Elementalist_dPcqKN5NeDkjB1HW.json | 28 ++++--- ...ure_Elusive_Predator_Cjtc43V3IzAmfIFG.json | 54 ++++++++++++- ...re_Ruthless_Predator_Qny2J3R35bvC0Cey.json | 10 +-- ...ature_Shadow_Stepper_hAwTXjhyphiE3aeW.json | 10 +-- ...eature_Vanishing_Act_iyIg1VLwO8C6jvFZ.json | 18 ++--- 60 files changed, 786 insertions(+), 413 deletions(-) diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs index cee4bc52..7e9f75ea 100644 --- a/module/config/itemConfig.mjs +++ b/module/config/itemConfig.mjs @@ -865,6 +865,9 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.description', img: 'icons/commodities/currency/coins-crown-stack-gold.webp', + target: { + type: 'self' + }, // Should cost handful of gold, effects: [ { diff --git a/src/packs/ancestries/feature_Retract_UFR67BUOhNGLFyg9.json b/src/packs/ancestries/feature_Retract_UFR67BUOhNGLFyg9.json index 59e33ce4..0e3e6555 100644 --- a/src/packs/ancestries/feature_Retract_UFR67BUOhNGLFyg9.json +++ b/src/packs/ancestries/feature_Retract_UFR67BUOhNGLFyg9.json @@ -23,12 +23,12 @@ }, "effects": [], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Mark Stress", "img": "icons/magic/defensive/shield-barrier-flaming-diamond-teal.webp", - "range": "" + "range": "self" } }, "originItemType": null, @@ -111,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753996513763, - "modifiedTime": 1755394440308, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756040910699, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!UFR67BUOhNGLFyg9" } diff --git a/src/packs/ancestries/feature_Wings_WquAjoOcso8lwySW.json b/src/packs/ancestries/feature_Wings_WquAjoOcso8lwySW.json index 7bc73dc6..87aa36a9 100644 --- a/src/packs/ancestries/feature_Wings_WquAjoOcso8lwySW.json +++ b/src/packs/ancestries/feature_Wings_WquAjoOcso8lwySW.json @@ -31,12 +31,12 @@ }, "effects": [], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Mark Stress", "img": "icons/creatures/abilities/wing-batlike-white-blue.webp", - "range": "" + "range": "self" } }, "originItemType": null, @@ -113,10 +113,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753994723305, - "modifiedTime": 1755394368487, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756040981649, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!WquAjoOcso8lwySW" } diff --git a/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json b/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json index b4d711a5..26534190 100644 --- a/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json +++ b/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json @@ -24,7 +24,7 @@ ], "uses": { "value": null, - "max": null, + "max": "", "recovery": null }, "effects": [ @@ -39,7 +39,7 @@ }, "name": "Retract", "img": "icons/creatures/reptiles/turtle-shell-glowing-green.webp", - "range": "" + "range": "self" } }, "originItemType": null, @@ -149,10 +149,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753580983699, - "modifiedTime": 1755395623887, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756041050697, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "nDQZdIF2epKlhauX", "sort": 2200000, diff --git a/src/packs/beastforms/feature_Rampage_8upqfcZvi7b5hRLE.json b/src/packs/beastforms/feature_Rampage_8upqfcZvi7b5hRLE.json index 3ac9731d..e9b157ba 100644 --- a/src/packs/beastforms/feature_Rampage_8upqfcZvi7b5hRLE.json +++ b/src/packs/beastforms/feature_Rampage_8upqfcZvi7b5hRLE.json @@ -24,22 +24,22 @@ ], "uses": { "value": null, - "max": null, + "max": "", "recovery": null }, "effects": [ { - "_id": "5TX0hHFKZHvBeWne", + "_id": "A7l4JEBC1FFQajsN", "onSave": false } ], "target": { - "type": null, + "type": "self", "amount": null }, - "name": "Gain Proficiency", + "name": "Mark Stress", "img": "icons/creatures/abilities/bear-roar-bite-brown.webp", - "range": "" + "range": "self" } }, "originItemType": null, @@ -51,7 +51,59 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "name": "Rampage", + "img": "icons/creatures/claws/claw-bear-paw-swipe-red.webp", + "origin": "Compendium.daggerheart.beastforms.Item.8upqfcZvi7b5hRLE", + "transfer": false, + "_id": "A7l4JEBC1FFQajsN", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "changes": [ + { + "key": "system.proficiency", + "mode": 2, + "value": "1", + "priority": null + } + ], + "disabled": false, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

    Before you make an attack roll, you can mark a Stress to gain a +1 bonus to your Proficiency for that attack.

    ", + "tint": "#ffffff", + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.347", + "systemId": "daggerheart", + "systemVersion": "1.1.0", + "createdTime": 1756041194412, + "modifiedTime": 1756041218646, + "lastModifiedBy": "vUIbuan0U50nfKBE" + }, + "_key": "!items.effects!8upqfcZvi7b5hRLE.A7l4JEBC1FFQajsN" + } + ], "folder": "uU8bIoZvXge0rLaU", "ownership": { "default": 0, @@ -64,10 +116,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753582591417, - "modifiedTime": 1755395649724, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756041228102, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "8upqfcZvi7b5hRLE", "sort": 2000000, diff --git a/src/packs/beastforms/feature_Takedown_0ey4kM9ssj2otHvb.json b/src/packs/beastforms/feature_Takedown_0ey4kM9ssj2otHvb.json index a1a14198..f31ed781 100644 --- a/src/packs/beastforms/feature_Takedown_0ey4kM9ssj2otHvb.json +++ b/src/packs/beastforms/feature_Takedown_0ey4kM9ssj2otHvb.json @@ -24,7 +24,7 @@ ], "uses": { "value": null, - "max": null, + "max": "", "recovery": null }, "damage": { @@ -58,7 +58,7 @@ "includeBase": false }, "target": { - "type": "any", + "type": "self", "amount": null }, "effects": [], @@ -84,7 +84,7 @@ }, "name": "Attack", "img": "icons/creatures/abilities/paw-print-orange.webp", - "range": "" + "range": "self" } }, "originItemType": null, @@ -101,7 +101,7 @@ "name": "Takedown", "img": "icons/creatures/abilities/paw-print-orange.webp", "origin": "Item.okjlLUwEdNOKeUk3", - "transfer": false, + "transfer": true, "_id": "hE6ciIusvKEtUQ8U", "type": "base", "system": {}, @@ -113,7 +113,7 @@ "priority": null } ], - "disabled": false, + "disabled": true, "duration": { "startTime": null, "combat": null, @@ -123,7 +123,7 @@ "startRound": null, "startTurn": null }, - "description": "", + "description": "

    You gain a +2 bonus to your Proficiency for this attack and the target must mark a Stress.

    ", "tint": "#ffffff", "statuses": [], "sort": 0, @@ -132,10 +132,11 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", - "lastModifiedBy": null + "lastModifiedBy": "vUIbuan0U50nfKBE", + "modifiedTime": 1756041368153 }, "_key": "!items.effects!0ey4kM9ssj2otHvb.hE6ciIusvKEtUQ8U" } @@ -152,10 +153,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753621786000, - "modifiedTime": 1755395696040, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756041242273, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "0ey4kM9ssj2otHvb", "sort": 600000, diff --git a/src/packs/classes/feature_Frontline_Tank_YS1g7YdWwOaS629x.json b/src/packs/classes/feature_Frontline_Tank_YS1g7YdWwOaS629x.json index a0b56f5f..701f8eb9 100644 --- a/src/packs/classes/feature_Frontline_Tank_YS1g7YdWwOaS629x.json +++ b/src/packs/classes/feature_Frontline_Tank_YS1g7YdWwOaS629x.json @@ -60,7 +60,7 @@ "includeBase": false }, "target": { - "type": "any", + "type": "self", "amount": null }, "effects": [], @@ -81,7 +81,7 @@ }, "name": "Spend Hope", "img": "icons/magic/defensive/shield-barrier-flaming-pentagon-orange.webp", - "range": "" + "range": "self" } }, "originItemType": null, @@ -105,10 +105,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1754246687097, - "modifiedTime": 1755391259920, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756034159296, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!YS1g7YdWwOaS629x" } diff --git a/src/packs/classes/feature_Hold_Them_Off_2Cyb9ZeuAesf5Sb3.json b/src/packs/classes/feature_Hold_Them_Off_2Cyb9ZeuAesf5Sb3.json index f2e99b8f..3a640f48 100644 --- a/src/packs/classes/feature_Hold_Them_Off_2Cyb9ZeuAesf5Sb3.json +++ b/src/packs/classes/feature_Hold_Them_Off_2Cyb9ZeuAesf5Sb3.json @@ -5,14 +5,14 @@ "_id": "2Cyb9ZeuAesf5Sb3", "img": "icons/magic/defensive/barrier-shield-dome-deflect-teal.webp", "system": { - "description": "

    Spend 3 Hope when you succeed on an attack with a weapon to use that same roll against two additional adversaries within range of the attack.

    ", + "description": "

    Spend 3 Hope when you succeed on an attack with a weapon to use that same roll against two additional adversaries within range of the attack.

    ", "resource": null, "actions": { "yhVUna5biFAN0o2Y": { "type": "effect", "_id": "yhVUna5biFAN0o2Y", "systemPath": "actions", - "description": "

    Spend 3 Hope when you succeed on an attack with a weapon to use that same roll against two additional adversaries within range of the attack.

    ", + "description": "

    Spend 3 Hope when you succeed on an attack with a weapon to use that same roll against two additional adversaries within range of the attack.

    ", "chatDisplay": true, "actionType": "action", "cost": [ @@ -60,10 +60,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1754268431889, - "modifiedTime": 1755391281371, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756034191317, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!2Cyb9ZeuAesf5Sb3" } diff --git a/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json b/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json index b131fcdf..7d689fe3 100644 --- a/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json +++ b/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json @@ -38,12 +38,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Spend Hope", "img": "icons/magic/holy/barrier-shield-winged-blue.webp", - "range": "" + "range": "self" } }, "originItemType": null, @@ -119,10 +119,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1754257270096, - "modifiedTime": 1755391439746, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756034406042, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!njj2C3tMDeCHHOoh" } diff --git a/src/packs/classes/feature_Ranger_s_Focus_ncLx2P8BOUtrAD38.json b/src/packs/classes/feature_Ranger_s_Focus_ncLx2P8BOUtrAD38.json index 4f798663..d3d10bc0 100644 --- a/src/packs/classes/feature_Ranger_s_Focus_ncLx2P8BOUtrAD38.json +++ b/src/packs/classes/feature_Ranger_s_Focus_ncLx2P8BOUtrAD38.json @@ -29,7 +29,12 @@ "max": "", "recovery": null }, - "effects": [], + "effects": [ + { + "_id": "SXi2dQWqpwY9fap4", + "onSave": false + } + ], "target": { "type": "any", "amount": null @@ -47,7 +52,52 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "name": "Ranger's Focus", + "img": "icons/magic/perception/eye-ringed-green.webp", + "origin": "Compendium.daggerheart.classes.Item.ncLx2P8BOUtrAD38", + "transfer": false, + "_id": "SXi2dQWqpwY9fap4", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "changes": [], + "disabled": false, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

    Until this feature ends or the ranger makes a different creature their Focus, they gain the following benefits against this adversary:

    • They know precisely what direction they are in.

    • When they deal damage to them, the adverasry must mark a Stress.

    • When they fail an attack against them, they can end their Ranger’s Focus feature to reroll their Duality Dice.

    ", + "tint": "#ffffff", + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.347", + "systemId": "daggerheart", + "systemVersion": "1.1.0", + "createdTime": 1756034238996, + "modifiedTime": 1756034332923, + "lastModifiedBy": "vUIbuan0U50nfKBE" + }, + "_key": "!items.effects!ncLx2P8BOUtrAD38.SXi2dQWqpwY9fap4" + } + ], "sort": 0, "ownership": { "default": 0, @@ -60,10 +110,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1754268505051, - "modifiedTime": 1755391289388, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756034239047, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!ncLx2P8BOUtrAD38" } diff --git a/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json b/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json index 244f46c5..fcc178d0 100644 --- a/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json +++ b/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json @@ -36,7 +36,7 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Spend Hope", @@ -76,7 +76,7 @@ "priority": null } ], - "disabled": true, + "disabled": false, "duration": { "startTime": null, "combat": null, @@ -95,12 +95,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754323997794, - "modifiedTime": 1754351805065, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1756034385415, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items.effects!hVaaPIjxoextIgSL.hhVjBro2osGDTT5g" } @@ -117,10 +117,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1754323951411, - "modifiedTime": 1755391319440, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756034357629, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!hVaaPIjxoextIgSL" } diff --git a/src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json b/src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json index eb637edd..8984606c 100644 --- a/src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json +++ b/src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json @@ -7,35 +7,7 @@ "system": { "description": "

    When you succeed on an attack while Cloaked or while an ally is within Melee range of your target, add a number of d6s equal to your tier to your damage roll.

    • Level 1 -> Tier 1

    • Levels 2–4 -> Tier 2

    • Levels 5–7 -> Tier 3

    • Levels 8–10 -> Tier 4

    ", "resource": null, - "actions": { - "SfctrIW5KjH4nq5G": { - "type": "effect", - "_id": "SfctrIW5KjH4nq5G", - "systemPath": "actions", - "description": "

    When you succeed on an attack while Cloaked or while an ally is within Melee range of your target, add a number of d6s equal to your tier to your damage roll.

    • Level 1 -> Tier 1

    • Levels 2–4 -> Tier 2

    • Levels 5–7 -> Tier 3

    • Levels 8–10 -> Tier 4

    ", - "chatDisplay": true, - "actionType": "action", - "cost": [], - "uses": { - "value": null, - "max": "", - "recovery": null - }, - "effects": [ - { - "_id": "380jFzw756qSy5ae", - "onSave": false - } - ], - "target": { - "type": "any", - "amount": null - }, - "name": "Use Sneak Attack", - "img": "icons/skills/melee/strike-dagger-skull-white.webp", - "range": "" - } - }, + "actions": {}, "originItemType": null, "originId": null, "attribution": { @@ -115,10 +87,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1754324216454, - "modifiedTime": 1755391313725, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756034698108, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!5QqpEwmwkPfZHpMW" } diff --git a/src/packs/domains/domainCard_Bold_Presence_tdsL00yTSLNgZWs6.json b/src/packs/domains/domainCard_Bold_Presence_tdsL00yTSLNgZWs6.json index 164ffb48..c83c1fde 100644 --- a/src/packs/domains/domainCard_Bold_Presence_tdsL00yTSLNgZWs6.json +++ b/src/packs/domains/domainCard_Bold_Presence_tdsL00yTSLNgZWs6.json @@ -43,7 +43,7 @@ }, "name": "Spend Hope", "img": "icons/magic/holy/barrier-shield-winged-blue.webp", - "range": "" + "range": "self" }, "OdRTXMOXhZFiZHES": { "type": "effect", @@ -81,10 +81,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784418, - "modifiedTime": 1755429846358, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038980509, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "tdsL00yTSLNgZWs6", "sort": 3400000, @@ -93,7 +93,7 @@ "name": "Bold Presence", "img": "icons/magic/holy/barrier-shield-winged-blue.webp", "origin": "Compendium.daggerheart.domains.Item.tdsL00yTSLNgZWs6", - "transfer": true, + "transfer": false, "_id": "2XEYhuAcRGTtqvED", "type": "base", "system": { @@ -112,7 +112,7 @@ "priority": null } ], - "disabled": true, + "disabled": false, "duration": { "startTime": null, "combat": null, @@ -131,12 +131,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754241531511, - "modifiedTime": 1754241605959, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1756038989398, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items.effects!tdsL00yTSLNgZWs6.2XEYhuAcRGTtqvED" } diff --git a/src/packs/domains/domainCard_Book_of_Ronin_SZMNR3uGNinJcN4N.json b/src/packs/domains/domainCard_Book_of_Ronin_SZMNR3uGNinJcN4N.json index b6f8bfcc..862d5da9 100644 --- a/src/packs/domains/domainCard_Book_of_Ronin_SZMNR3uGNinJcN4N.json +++ b/src/packs/domains/domainCard_Book_of_Ronin_SZMNR3uGNinJcN4N.json @@ -28,7 +28,7 @@ "includeBase": false }, "target": { - "type": "any", + "type": "self", "amount": null }, "effects": [ @@ -59,7 +59,7 @@ }, "name": "Transform", "img": "icons/containers/barrels/barrel-reinforced-cherry-brown.webp", - "range": "" + "range": "self" }, "h7i4ZuDYuYLnjze6": { "type": "attack", @@ -126,10 +126,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784424, - "modifiedTime": 1755429102985, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038121293, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "SZMNR3uGNinJcN4N", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Book_of_Sitil_eq8VNqYMRHhF9xw9.json b/src/packs/domains/domainCard_Book_of_Sitil_eq8VNqYMRHhF9xw9.json index 4ee7c45c..3c60a08d 100644 --- a/src/packs/domains/domainCard_Book_of_Sitil_eq8VNqYMRHhF9xw9.json +++ b/src/packs/domains/domainCard_Book_of_Sitil_eq8VNqYMRHhF9xw9.json @@ -30,12 +30,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Adjust Appearance", "img": "icons/magic/defensive/shield-barrier-blades-teal.webp", - "range": "" + "range": "self" }, "wBQkw3P4Esj6kOx2": { "type": "effect", @@ -132,10 +132,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784424, - "modifiedTime": 1755428993189, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756037990822, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "eq8VNqYMRHhF9xw9", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Book_of_Yarrow_J1ovx2FpNDvPq1o6.json b/src/packs/domains/domainCard_Book_of_Yarrow_J1ovx2FpNDvPq1o6.json index 8cef1c1e..8d91c1ee 100644 --- a/src/packs/domains/domainCard_Book_of_Yarrow_J1ovx2FpNDvPq1o6.json +++ b/src/packs/domains/domainCard_Book_of_Yarrow_J1ovx2FpNDvPq1o6.json @@ -89,7 +89,7 @@ }, "name": "Magic Immunity", "img": "icons/magic/defensive/barrier-shield-dome-deflect-teal.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -105,10 +105,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784429, - "modifiedTime": 1755429115570, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038143042, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "J1ovx2FpNDvPq1o6", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Breaking_Blow_8UANBgSdhMZ0sqfO.json b/src/packs/domains/domainCard_Breaking_Blow_8UANBgSdhMZ0sqfO.json index 567d7d57..3781989d 100644 --- a/src/packs/domains/domainCard_Breaking_Blow_8UANBgSdhMZ0sqfO.json +++ b/src/packs/domains/domainCard_Breaking_Blow_8UANBgSdhMZ0sqfO.json @@ -41,7 +41,7 @@ ], "target": { "type": "any", - "amount": null + "amount": 1 }, "name": "Mark Stress", "img": "icons/skills/wounds/bone-broken-knee-beam.webp", @@ -61,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784431, - "modifiedTime": 1755428345979, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756037935304, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "8UANBgSdhMZ0sqfO", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Cloaking_Blast_Zhw7PtK8nMPlsOqD.json b/src/packs/domains/domainCard_Cloaking_Blast_Zhw7PtK8nMPlsOqD.json index f19833c5..5314de9e 100644 --- a/src/packs/domains/domainCard_Cloaking_Blast_Zhw7PtK8nMPlsOqD.json +++ b/src/packs/domains/domainCard_Cloaking_Blast_Zhw7PtK8nMPlsOqD.json @@ -40,12 +40,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Become Cloaked", "img": "icons/magic/perception/shadow-stealth-eyes-purple.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -61,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784435, - "modifiedTime": 1755428020354, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756037373705, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "Zhw7PtK8nMPlsOqD", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json b/src/packs/domains/domainCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json index 9d243ade..82d68f9c 100644 --- a/src/packs/domains/domainCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json +++ b/src/packs/domains/domainCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json @@ -36,7 +36,7 @@ "includeBase": false }, "target": { - "type": "any", + "type": "self", "amount": null }, "effects": [ @@ -67,7 +67,7 @@ }, "name": "Tekaira's Armored Beetles: Stress", "img": "icons/creatures/invertebrates/wasp-swarm-attack.webp", - "range": "" + "range": "self" }, "533qzPIjcccpiMey": { "type": "effect", @@ -195,10 +195,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784438, - "modifiedTime": 1755429481288, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038512929, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "rZPH0BY8Sznc9sFG", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Deadly_Focus_xxZOXC4tiZQ6kg1e.json b/src/packs/domains/domainCard_Deadly_Focus_xxZOXC4tiZQ6kg1e.json index df82f1aa..9246896d 100644 --- a/src/packs/domains/domainCard_Deadly_Focus_xxZOXC4tiZQ6kg1e.json +++ b/src/packs/domains/domainCard_Deadly_Focus_xxZOXC4tiZQ6kg1e.json @@ -26,17 +26,17 @@ }, "effects": [ { - "_id": "6sR46Hd554DiLHy4", + "_id": "TFWiAUDCfGaax5MU", "onSave": false } ], "target": { - "type": "any", - "amount": null + "type": "self", + "amount": 1 }, "name": "Focus", "img": "icons/skills/targeting/crosshair-pointed-orange.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -52,20 +52,20 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784443, - "modifiedTime": 1755428127375, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756037604544, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "xxZOXC4tiZQ6kg1e", "sort": 3400000, "effects": [ { "name": "Deadly Focus", - "img": "systems/daggerheart/assets/icons/domains/domain-card/blade.png", + "img": "icons/skills/targeting/crosshair-pointed-orange.webp", "origin": "Compendium.daggerheart.domains.Item.xxZOXC4tiZQ6kg1e", - "transfer": true, - "_id": "6sR46Hd554DiLHy4", + "transfer": false, + "_id": "TFWiAUDCfGaax5MU", "type": "base", "system": { "rangeDependence": { @@ -83,7 +83,7 @@ "priority": null } ], - "disabled": true, + "disabled": false, "duration": { "startTime": null, "combat": null, @@ -93,7 +93,7 @@ "startRound": null, "startTurn": null }, - "description": "", + "description": "

    Until you attack another creature, you defeat the target, or the battle ends, gain a +1 bonus to your Proficiency.

    ", "tint": "#ffffff", "statuses": [], "sort": 0, @@ -102,14 +102,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1754244951545, - "modifiedTime": 1754304242570, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "systemVersion": "1.1.0", + "createdTime": 1756037548255, + "modifiedTime": 1756037588317, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, - "_key": "!items.effects!xxZOXC4tiZQ6kg1e.6sR46Hd554DiLHy4" + "_key": "!items.effects!xxZOXC4tiZQ6kg1e.TFWiAUDCfGaax5MU" } ], "ownership": { diff --git a/src/packs/domains/domainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json b/src/packs/domains/domainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json index ff9b1ebf..9aefa1bf 100644 --- a/src/packs/domains/domainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json +++ b/src/packs/domains/domainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json @@ -38,12 +38,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Transform", "img": "icons/magic/nature/elemental-plant-humanoid.webp", - "range": "" + "range": "self" }, "1rLoYS90AZizJujS": { "type": "effect", @@ -89,10 +89,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784456, - "modifiedTime": 1755429633230, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038760003, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "LzVpMkD5I4QeaIHf", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Frenzy_MMl7abdGRLl7TJLO.json b/src/packs/domains/domainCard_Frenzy_MMl7abdGRLl7TJLO.json index 87dddb15..c6323551 100644 --- a/src/packs/domains/domainCard_Frenzy_MMl7abdGRLl7TJLO.json +++ b/src/packs/domains/domainCard_Frenzy_MMl7abdGRLl7TJLO.json @@ -31,12 +31,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Frenzy", "img": "icons/magic/fire/projectile-wave-yellow.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -52,10 +52,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784459, - "modifiedTime": 1755428186983, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756037770198, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "MMl7abdGRLl7TJLO", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Full_Surge_SgvjJfMyubZowPxS.json b/src/packs/domains/domainCard_Full_Surge_SgvjJfMyubZowPxS.json index 15ffb49e..b8ac3547 100644 --- a/src/packs/domains/domainCard_Full_Surge_SgvjJfMyubZowPxS.json +++ b/src/packs/domains/domainCard_Full_Surge_SgvjJfMyubZowPxS.json @@ -43,7 +43,7 @@ }, "name": "Mark Stress", "img": "icons/magic/control/buff-flight-wings-runes-purple.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -59,10 +59,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784460, - "modifiedTime": 1755429931329, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756039042961, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "SgvjJfMyubZowPxS", "sort": 3400000, @@ -71,7 +71,7 @@ "name": "Full Surge", "img": "icons/magic/symbols/ring-circle-smoke-blue.webp", "origin": "Compendium.daggerheart.domains.Item.SgvjJfMyubZowPxS", - "transfer": true, + "transfer": false, "_id": "H5q5iYImr69TfZcp", "type": "base", "system": { @@ -120,7 +120,7 @@ "priority": null } ], - "disabled": true, + "disabled": false, "duration": { "startTime": null, "combat": null, @@ -139,12 +139,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754242182511, - "modifiedTime": 1754242229676, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1756039048089, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items.effects!SgvjJfMyubZowPxS.H5q5iYImr69TfZcp" } diff --git a/src/packs/domains/domainCard_Hold_the_Line_kdFoLo3KXwn4LqTG.json b/src/packs/domains/domainCard_Hold_the_Line_kdFoLo3KXwn4LqTG.json index b572243f..6ea5b5c2 100644 --- a/src/packs/domains/domainCard_Hold_the_Line_kdFoLo3KXwn4LqTG.json +++ b/src/packs/domains/domainCard_Hold_the_Line_kdFoLo3KXwn4LqTG.json @@ -43,7 +43,7 @@ }, "name": "Spend Hope", "img": "icons/magic/holy/barrier-shield-winged-blue.webp", - "range": "" + "range": "self" }, "SKG6Gu0uJZxtYTnz": { "type": "effect", @@ -87,10 +87,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784468, - "modifiedTime": 1755429945423, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756039072992, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "kdFoLo3KXwn4LqTG", "sort": 3400000, @@ -99,7 +99,7 @@ "name": "Hold the Line", "img": "icons/magic/defensive/shield-barrier-blue.webp", "origin": "Compendium.daggerheart.domains.Item.kdFoLo3KXwn4LqTG", - "transfer": true, + "transfer": false, "_id": "y82y3nUStyL8DIJL", "type": "base", "system": { @@ -111,7 +111,7 @@ } }, "changes": [], - "disabled": true, + "disabled": false, "duration": { "startTime": null, "combat": null, @@ -130,18 +130,18 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754242295342, - "modifiedTime": 1754242434181, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1756039077113, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items.effects!kdFoLo3KXwn4LqTG.y82y3nUStyL8DIJL" }, { "name": "Hold the Line", - "img": "systems/daggerheart/assets/icons/domains/domain-card/valor.png", + "img": "icons/skills/melee/shield-block-bash-blue.webp", "origin": "Compendium.daggerheart.domains.Item.kdFoLo3KXwn4LqTG", "transfer": false, "_id": "WTYg0b8nE1XbnMiA", @@ -176,12 +176,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754242400483, - "modifiedTime": 1754242409060, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1756039151398, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items.effects!kdFoLo3KXwn4LqTG.WTYg0b8nE1XbnMiA" } diff --git a/src/packs/domains/domainCard_Hypnotic_Shimmer_2ZeuCGVatQdPOVC6.json b/src/packs/domains/domainCard_Hypnotic_Shimmer_2ZeuCGVatQdPOVC6.json index 7579ac02..d7ad18c6 100644 --- a/src/packs/domains/domainCard_Hypnotic_Shimmer_2ZeuCGVatQdPOVC6.json +++ b/src/packs/domains/domainCard_Hypnotic_Shimmer_2ZeuCGVatQdPOVC6.json @@ -14,7 +14,7 @@ "type": "attack", "_id": "kLMAuyZktmohOSXa", "systemPath": "actions", - "description": "

    Make a Spellcast Roll against all adversaries in front of you within Close range. Once per rest on a success, create an illusion of flashing colors and lights that temporarily Stuns targets you succeed against and forces them to mark a Stress. While Stunned, they can’t use reactions and can’t take any other actions until they clear this condition.

    ", + "description": "

    Make a Spellcast Roll against all adversaries in front of you within Close range. Once per rest on a success, create an illusion of flashing colors and lights that temporarily Stuns targets you succeed against and forces them to mark a Stress. While Stunned, they can’t use reactions and can’t take any other actions until they clear this condition.

    @Template[type:infront|range:c]

    ", "chatDisplay": true, "actionType": "action", "cost": [], @@ -102,10 +102,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784471, - "modifiedTime": 1755429167606, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038232979, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "2ZeuCGVatQdPOVC6", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Lead_by_Example_YWCRplmtwpCjpq5i.json b/src/packs/domains/domainCard_Lead_by_Example_YWCRplmtwpCjpq5i.json index fa6f3c10..2566febc 100644 --- a/src/packs/domains/domainCard_Lead_by_Example_YWCRplmtwpCjpq5i.json +++ b/src/packs/domains/domainCard_Lead_by_Example_YWCRplmtwpCjpq5i.json @@ -39,7 +39,7 @@ ], "target": { "type": "any", - "amount": null + "amount": 1 }, "name": "Mark Stress", "img": "icons/magic/control/buff-flight-wings-runes-purple.webp", @@ -59,10 +59,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784475, - "modifiedTime": 1755429951881, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756039181179, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "YWCRplmtwpCjpq5i", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Overwhelming_Aura_iEBLySZD9z8CLdz7.json b/src/packs/domains/domainCard_Overwhelming_Aura_iEBLySZD9z8CLdz7.json index 72a79735..e3dee02a 100644 --- a/src/packs/domains/domainCard_Overwhelming_Aura_iEBLySZD9z8CLdz7.json +++ b/src/packs/domains/domainCard_Overwhelming_Aura_iEBLySZD9z8CLdz7.json @@ -38,7 +38,7 @@ "includeBase": false }, "target": { - "type": "any", + "type": "self", "amount": null }, "effects": [ @@ -69,7 +69,7 @@ }, "name": "Cast", "img": "icons/magic/holy/angel-winged-humanoid-blue.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -85,10 +85,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784491, - "modifiedTime": 1755429784343, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038903709, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "iEBLySZD9z8CLdz7", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json b/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json index d9d67737..140e570d 100644 --- a/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json +++ b/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json @@ -14,7 +14,7 @@ "type": "effect", "_id": "LmjwPg03xLnoGTHm", "systemPath": "actions", - "description": "", + "description": "

    You can mark a Stress to gain a bonus to your damage roll equal to twice your Strength.

    ", "chatDisplay": true, "actionType": "action", "cost": [ @@ -22,8 +22,8 @@ "scalable": false, "key": "stress", "value": 1, - "step": 1, "keyIsID": false, + "step": null, "consumeOnSuccess": false } ], @@ -40,18 +40,18 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Mark 1 Stress", "img": "icons/magic/control/silhouette-aura-energy.webp", - "range": "" + "range": "self" }, "fKY9NcYBwCFwMsgV": { "type": "effect", "_id": "fKY9NcYBwCFwMsgV", "systemPath": "actions", - "description": "", + "description": "

    You can mark a Stress to gain a bonus to your damage roll equal to twice your Strength.

    ", "chatDisplay": true, "actionType": "action", "cost": [ @@ -77,12 +77,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Mark 2 Stress", "img": "icons/magic/control/silhouette-aura-energy.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -98,10 +98,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784496, - "modifiedTime": 1755428155116, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756037744719, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "GRL0cvs96vrTDckZ", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Rain_of_Blades_Ucenef6JpjQxwXni.json b/src/packs/domains/domainCard_Rain_of_Blades_Ucenef6JpjQxwXni.json index 630960f4..03337fa2 100644 --- a/src/packs/domains/domainCard_Rain_of_Blades_Ucenef6JpjQxwXni.json +++ b/src/packs/domains/domainCard_Rain_of_Blades_Ucenef6JpjQxwXni.json @@ -14,7 +14,7 @@ "type": "attack", "_id": "WTnjKQs2uI1TuF9r", "systemPath": "actions", - "description": "

    Spend a Hope to make a Spellcast Roll and conjure throwing blades that strike out at all targets within Very Close range. Targets you succeed against take d8+2 magic damage using your Proficiency.

    ", + "description": "

    Spend a Hope to make a Spellcast Roll and conjure throwing blades that strike out at all targets within Very Close range. Targets you succeed against take d8+2 magic damage using your Proficiency.

    @Template[type:emanation|range:vc]

    ", "chatDisplay": true, "actionType": "action", "cost": [ @@ -154,10 +154,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784497, - "modifiedTime": 1755429293162, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038335767, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "Ucenef6JpjQxwXni", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Salvation_Beam_4uAFGp3LxiC07woC.json b/src/packs/domains/domainCard_Salvation_Beam_4uAFGp3LxiC07woC.json index baaba0a6..c8efc258 100644 --- a/src/packs/domains/domainCard_Salvation_Beam_4uAFGp3LxiC07woC.json +++ b/src/packs/domains/domainCard_Salvation_Beam_4uAFGp3LxiC07woC.json @@ -14,7 +14,7 @@ "type": "healing", "_id": "dmnB4ZMSk8lsB8Lg", "systemPath": "actions", - "description": "

    Make a Spellcast Roll (16). On a success, mark any number of Stress to target a line of allies within Far range. You can clear Hit Points on the targets equal to the number of Stress marked, divided among them however you’d like.

    ", + "description": "

    Make a Spellcast Roll (16). On a success, mark any number of Stress to target a line of allies within Far range. You can clear Hit Points on the targets equal to the number of Stress marked, divided among them however you’d like.

    @Template[type:ray|range:f]

    ", "chatDisplay": true, "actionType": "action", "cost": [ @@ -101,10 +101,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784508, - "modifiedTime": 1755429792958, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038941872, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "4uAFGp3LxiC07woC", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Smite_U1uWJE94HZVudujz.json b/src/packs/domains/domainCard_Smite_U1uWJE94HZVudujz.json index 245db4a2..7b966db2 100644 --- a/src/packs/domains/domainCard_Smite_U1uWJE94HZVudujz.json +++ b/src/packs/domains/domainCard_Smite_U1uWJE94HZVudujz.json @@ -40,12 +40,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Charge Smite", "img": "icons/skills/melee/sword-winged-holy-orange.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -61,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784519, - "modifiedTime": 1755429734196, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038851464, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "U1uWJE94HZVudujz", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json b/src/packs/domains/domainCard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json index 02d43c56..a7af630e 100644 --- a/src/packs/domains/domainCard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json +++ b/src/packs/domains/domainCard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json @@ -38,12 +38,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Become Spectral", "img": "icons/magic/unholy/silhouette-light-fire-blue.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -59,10 +59,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784520, - "modifiedTime": 1755429431607, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038467712, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "iQhgqmLwhcSTYnvr", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Thorn_Skin_oUipGK84E2KjoKqh.json b/src/packs/domains/domainCard_Thorn_Skin_oUipGK84E2KjoKqh.json index 6c56de38..41103c18 100644 --- a/src/packs/domains/domainCard_Thorn_Skin_oUipGK84E2KjoKqh.json +++ b/src/packs/domains/domainCard_Thorn_Skin_oUipGK84E2KjoKqh.json @@ -33,12 +33,12 @@ }, "effects": [], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Spend Hope", "img": "icons/magic/nature/thorns-hand-glow-green.webp", - "range": "" + "range": "self" }, "IVAyyVf8gqFWB7bP": { "type": "effect", @@ -92,10 +92,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784531, - "modifiedTime": 1755429528391, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038664007, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "oUipGK84E2KjoKqh", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json b/src/packs/domains/domainCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json index 1d0456ce..be34704d 100644 --- a/src/packs/domains/domainCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json +++ b/src/packs/domains/domainCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json @@ -28,7 +28,7 @@ "includeBase": false }, "target": { - "type": "any", + "type": "self", "amount": null }, "effects": [ @@ -39,7 +39,7 @@ ], "name": "Don Facade", "img": "icons/magic/control/debuff-energy-hold-pink.webp", - "range": "" + "range": "self" }, "OoNND7VcWoBQdtFK": { "type": "effect", @@ -91,10 +91,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784537, - "modifiedTime": 1755429298602, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038348939, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "TV56wSysbU5xAlOa", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Unleash_Chaos_o62i0QdbUDIiAhSq.json b/src/packs/domains/domainCard_Unleash_Chaos_o62i0QdbUDIiAhSq.json index 8137b243..757aca62 100644 --- a/src/packs/domains/domainCard_Unleash_Chaos_o62i0QdbUDIiAhSq.json +++ b/src/packs/domains/domainCard_Unleash_Chaos_o62i0QdbUDIiAhSq.json @@ -126,12 +126,12 @@ }, "effects": [], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Replenish Tokens", "img": "icons/commodities/gems/gem-faceted-diamond-blue.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -147,10 +147,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784537, - "modifiedTime": 1755427920648, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756037237479, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "o62i0QdbUDIiAhSq", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Vanishing_Dodge_GBMIElIpk4cvk1Bd.json b/src/packs/domains/domainCard_Vanishing_Dodge_GBMIElIpk4cvk1Bd.json index 29ba6f90..d5284f5a 100644 --- a/src/packs/domains/domainCard_Vanishing_Dodge_GBMIElIpk4cvk1Bd.json +++ b/src/packs/domains/domainCard_Vanishing_Dodge_GBMIElIpk4cvk1Bd.json @@ -38,7 +38,7 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Vanish", @@ -59,10 +59,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784539, - "modifiedTime": 1755429382850, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038425727, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "GBMIElIpk4cvk1Bd", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Veil_of_Night_gV4L5ZZmfPrEbIDh.json b/src/packs/domains/domainCard_Veil_of_Night_gV4L5ZZmfPrEbIDh.json index 3e0a4623..ad78023c 100644 --- a/src/packs/domains/domainCard_Veil_of_Night_gV4L5ZZmfPrEbIDh.json +++ b/src/packs/domains/domainCard_Veil_of_Night_gV4L5ZZmfPrEbIDh.json @@ -28,7 +28,7 @@ "includeBase": false }, "target": { - "type": "any", + "type": "self", "amount": null }, "effects": [ @@ -59,7 +59,7 @@ }, "name": "Cast", "img": "icons/magic/unholy/barrier-shield-glowing-pink.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -75,10 +75,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753922784541, - "modifiedTime": 1755429328234, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756038378309, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_id": "gV4L5ZZmfPrEbIDh", "sort": 3400000, diff --git a/src/packs/domains/folders_Splendor_TL1TutmbeCVJ06nR.json b/src/packs/domains/folders_Splendor_TL1TutmbeCVJ06nR.json index c0870ecf..87cc7469 100644 --- a/src/packs/domains/folders_Splendor_TL1TutmbeCVJ06nR.json +++ b/src/packs/domains/folders_Splendor_TL1TutmbeCVJ06nR.json @@ -6,18 +6,18 @@ "sorting": "m", "_id": "TL1TutmbeCVJ06nR", "description": "", - "sort": 800000, + "sort": 900000, "flags": {}, "_stats": { "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1748717133557, - "modifiedTime": 1752681441081, - "lastModifiedBy": "YNJ4HgHtFrTI89mx" + "modifiedTime": 1756038952388, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!folders!TL1TutmbeCVJ06nR" } diff --git a/src/packs/domains/folders_Valor_yPVeShe47ETIqs9q.json b/src/packs/domains/folders_Valor_yPVeShe47ETIqs9q.json index 0a65d51f..f6361e50 100644 --- a/src/packs/domains/folders_Valor_yPVeShe47ETIqs9q.json +++ b/src/packs/domains/folders_Valor_yPVeShe47ETIqs9q.json @@ -6,18 +6,18 @@ "sorting": "m", "_id": "yPVeShe47ETIqs9q", "description": "", - "sort": 900000, + "sort": 800000, "flags": {}, "_stats": { "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1748717138700, - "modifiedTime": 1752681443619, - "lastModifiedBy": "YNJ4HgHtFrTI89mx" + "modifiedTime": 1756038952388, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!folders!yPVeShe47ETIqs9q" } diff --git a/src/packs/items/consumables/consumable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json b/src/packs/items/consumables/consumable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json index 62049319..4f68e0d7 100644 --- a/src/packs/items/consumables/consumable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json +++ b/src/packs/items/consumables/consumable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json @@ -30,12 +30,12 @@ }, "effects": [], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Stitch", "img": "icons/skills/trades/textiles-stitching-leather-brown.webp", - "range": "" + "range": "self" } }, "consumeOnUse": true, @@ -59,10 +59,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753588973384, - "modifiedTime": 1755432710896, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756041701211, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!VlbsCjvvLNfTzNXb" } diff --git a/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json b/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json index 9e316540..64db5be0 100644 --- a/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json +++ b/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json @@ -32,7 +32,7 @@ }, "name": "Drink", "img": "icons/consumables/potions/bottle-conical-corked-purple.webp", - "range": "" + "range": "self" } }, "consumeOnUse": true, @@ -101,10 +101,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753587033468, - "modifiedTime": 1755432536588, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756041711386, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!JGD3M9hBHtVAA8XP" } diff --git a/src/packs/items/consumables/consumable_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json b/src/packs/items/consumables/consumable_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json index a1b70dc6..7d7596eb 100644 --- a/src/packs/items/consumables/consumable_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json +++ b/src/packs/items/consumables/consumable_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json @@ -22,12 +22,12 @@ }, "effects": [], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Drink", "img": "icons/consumables/potions/potion-tube-corked-bat-gold-red.webp", - "range": "" + "range": "veryFar" } }, "consumeOnUse": true, @@ -51,10 +51,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753589954973, - "modifiedTime": 1755432804800, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756041739719, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!pDGzmczoTlKGmKgd" } diff --git a/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json b/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json index 7519c7ab..70d93553 100644 --- a/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json +++ b/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json @@ -32,7 +32,7 @@ }, "name": "Drink", "img": "icons/consumables/potions/potion-vial-tube-yellow.webp", - "range": "" + "range": "self" } }, "consumeOnUse": true, @@ -101,10 +101,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753586850134, - "modifiedTime": 1755432522101, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756041748451, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!FOPQNqXbiVO0ilYL" } diff --git a/src/packs/items/consumables/consumable_Death_Tea_xDnJeF1grkmKck8Q.json b/src/packs/items/consumables/consumable_Death_Tea_xDnJeF1grkmKck8Q.json index e0b8a28c..25f7a45d 100644 --- a/src/packs/items/consumables/consumable_Death_Tea_xDnJeF1grkmKck8Q.json +++ b/src/packs/items/consumables/consumable_Death_Tea_xDnJeF1grkmKck8Q.json @@ -27,12 +27,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Drink", "img": "icons/consumables/drinks/wine-amphora-clay-gray.webp", - "range": "" + "range": "self" } }, "consumeOnUse": true, @@ -101,10 +101,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753592717630, - "modifiedTime": 1755433085832, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756041829971, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!xDnJeF1grkmKck8Q" } diff --git a/src/packs/items/consumables/consumable_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json b/src/packs/items/consumables/consumable_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json index 9389ca85..ee696b5d 100644 --- a/src/packs/items/consumables/consumable_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json +++ b/src/packs/items/consumables/consumable_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json @@ -11,13 +11,13 @@ "type": "attack", "_id": "rcHEz3ImUDwo6lPC", "systemPath": "actions", - "description": "", + "description": "

    You can drink this tea to unleash a fiery breath attack. Make an Instinct Roll against all adversaries in front of you within Close range. Targets you succeed against take d20 physical damage using your Proficiency.

    @Template[type:emanation|range:c]

    ", "chatDisplay": true, "actionType": "action", "cost": [], "uses": { "value": null, - "max": null, + "max": "", "recovery": null }, "damage": { @@ -100,10 +100,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753591525829, - "modifiedTime": 1755432974525, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756041882255, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!wM18PWWW2Ami4fBG" } diff --git a/src/packs/items/consumables/consumable_Featherbone_DpxEMpwfasEBpORU.json b/src/packs/items/consumables/consumable_Featherbone_DpxEMpwfasEBpORU.json index 31a5b7fd..fbdb9c17 100644 --- a/src/packs/items/consumables/consumable_Featherbone_DpxEMpwfasEBpORU.json +++ b/src/packs/items/consumables/consumable_Featherbone_DpxEMpwfasEBpORU.json @@ -20,14 +20,19 @@ "max": "", "recovery": null }, - "effects": [], + "effects": [ + { + "_id": "VfJIJBW96e45xQHY", + "onSave": false + } + ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Use", "img": "icons/commodities/bones/bones-stack-worn-brown.webp", - "range": "" + "range": "self" } }, "consumeOnUse": true, @@ -37,7 +42,52 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "name": "Featherbone", + "img": "icons/commodities/bones/bones-stack-worn-brown.webp", + "origin": "Compendium.daggerheart.consumables.Item.DpxEMpwfasEBpORU", + "transfer": false, + "_id": "VfJIJBW96e45xQHY", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "changes": [], + "disabled": false, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

    You can use this bone to control your falling speed for a number of minutes equal to your level.

    ", + "tint": "#ffffff", + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.347", + "systemId": "daggerheart", + "systemVersion": "1.1.0", + "createdTime": 1756041997551, + "modifiedTime": 1756042000517, + "lastModifiedBy": "vUIbuan0U50nfKBE" + }, + "_key": "!items.effects!DpxEMpwfasEBpORU.VfJIJBW96e45xQHY" + } + ], "folder": null, "sort": 0, "ownership": { @@ -51,10 +101,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753590624634, - "modifiedTime": 1755432889504, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756042004101, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!DpxEMpwfasEBpORU" } diff --git a/src/packs/items/consumables/consumable_Gill_Salve_Nvbb9mze6o5D0AEg.json b/src/packs/items/consumables/consumable_Gill_Salve_Nvbb9mze6o5D0AEg.json index 5e770de0..dd7ce092 100644 --- a/src/packs/items/consumables/consumable_Gill_Salve_Nvbb9mze6o5D0AEg.json +++ b/src/packs/items/consumables/consumable_Gill_Salve_Nvbb9mze6o5D0AEg.json @@ -20,14 +20,19 @@ "max": "", "recovery": null }, - "effects": [], + "effects": [ + { + "_id": "5rL9CY5GO9SJcEZq", + "onSave": false + } + ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Apply", "img": "icons/commodities/materials/bowl-powder-blue.webp", - "range": "" + "range": "self" } }, "consumeOnUse": true, @@ -37,7 +42,52 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "name": "Gill Salve", + "img": "icons/commodities/materials/bowl-powder-blue.webp", + "origin": "Compendium.daggerheart.consumables.Item.Nvbb9mze6o5D0AEg", + "transfer": false, + "_id": "5rL9CY5GO9SJcEZq", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "changes": [], + "disabled": false, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

    You can apply this salve to your neck to breathe underwater for a number of minutes equal to your level.

    ", + "tint": "#ffffff", + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.347", + "systemId": "daggerheart", + "systemVersion": "1.1.0", + "createdTime": 1756041968799, + "modifiedTime": 1756041978849, + "lastModifiedBy": "vUIbuan0U50nfKBE" + }, + "_key": "!items.effects!Nvbb9mze6o5D0AEg.5rL9CY5GO9SJcEZq" + } + ], "folder": null, "sort": 0, "ownership": { @@ -51,10 +101,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753589241094, - "modifiedTime": 1755432719146, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756041983578, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!Nvbb9mze6o5D0AEg" } diff --git a/src/packs/items/loot/loot_Glamour_Stone_Pj17cvdJ1XG1jv6I.json b/src/packs/items/loot/loot_Glamour_Stone_Pj17cvdJ1XG1jv6I.json index 6b233757..fa76826b 100644 --- a/src/packs/items/loot/loot_Glamour_Stone_Pj17cvdJ1XG1jv6I.json +++ b/src/packs/items/loot/loot_Glamour_Stone_Pj17cvdJ1XG1jv6I.json @@ -11,7 +11,7 @@ "type": "effect", "_id": "ATQgH12mufTOQLKs", "systemPath": "actions", - "description": "", + "description": "

    Activate this pebble-sized stone to memorize the appearance of someone you can see. Spend a Hope to magically recreate this guise on yourself as an illusion.

    ", "chatDisplay": true, "actionType": "action", "cost": [ @@ -25,17 +25,22 @@ ], "uses": { "value": null, - "max": null, + "max": "", "recovery": null }, - "effects": [], + "effects": [ + { + "_id": "K5SB6tfuDkdVaQYe", + "onSave": false + } + ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Create Illusion", "img": "icons/commodities/treasure/token-engraved-purple-glowing.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -44,7 +49,52 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "name": "Glamour Stone", + "img": "icons/commodities/treasure/token-engraved-purple-glowing.webp", + "origin": "Compendium.daggerheart.loot.Item.Pj17cvdJ1XG1jv6I", + "transfer": false, + "_id": "K5SB6tfuDkdVaQYe", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "changes": [], + "disabled": false, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

    Activate this pebble-sized stone to memorize the appearance of someone you can see. Spend a Hope to magically recreate this guise on yourself as an illusion.

    ", + "tint": "#ffffff", + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.347", + "systemId": "daggerheart", + "systemVersion": "1.1.0", + "createdTime": 1756042876194, + "modifiedTime": 1756042884422, + "lastModifiedBy": "vUIbuan0U50nfKBE" + }, + "_key": "!items.effects!Pj17cvdJ1XG1jv6I.K5SB6tfuDkdVaQYe" + } + ], "folder": null, "sort": 0, "ownership": { @@ -58,10 +108,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753625773657, - "modifiedTime": 1755431995057, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756042876216, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!Pj17cvdJ1XG1jv6I" } diff --git a/src/packs/items/loot/loot_Piercing_Arrows_I63LTFD6GXHgyGpR.json b/src/packs/items/loot/loot_Piercing_Arrows_I63LTFD6GXHgyGpR.json index 9a056431..26845282 100644 --- a/src/packs/items/loot/loot_Piercing_Arrows_I63LTFD6GXHgyGpR.json +++ b/src/packs/items/loot/loot_Piercing_Arrows_I63LTFD6GXHgyGpR.json @@ -11,7 +11,7 @@ "type": "effect", "_id": "DW5AqEM0F8XaUqpn", "systemPath": "actions", - "description": "", + "description": "

    Three times per rest when you succeed on an attack with one of these arrows, you can add your Proficiency to the damage roll.

    ", "chatDisplay": true, "actionType": "action", "cost": [], @@ -108,10 +108,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753625947079, - "modifiedTime": 1755432020809, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756042964027, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!I63LTFD6GXHgyGpR" } diff --git a/src/packs/items/loot/loot_Premium_Bedroll_QGYPNBIufpBguwjC.json b/src/packs/items/loot/loot_Premium_Bedroll_QGYPNBIufpBguwjC.json index f000d904..1a2b7f62 100644 --- a/src/packs/items/loot/loot_Premium_Bedroll_QGYPNBIufpBguwjC.json +++ b/src/packs/items/loot/loot_Premium_Bedroll_QGYPNBIufpBguwjC.json @@ -17,11 +17,11 @@ "cost": [], "uses": { "value": null, - "max": null, + "max": "", "recovery": null }, "target": { - "type": "any", + "type": "self", "amount": null }, "effects": [], @@ -89,10 +89,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753624827945, - "modifiedTime": 1755431895118, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756042976231, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!QGYPNBIufpBguwjC" } diff --git a/src/packs/items/loot/loot_Ring_of_Resistance_aUqRifqR5JXXa1dN.json b/src/packs/items/loot/loot_Ring_of_Resistance_aUqRifqR5JXXa1dN.json index 628fe395..1fa60198 100644 --- a/src/packs/items/loot/loot_Ring_of_Resistance_aUqRifqR5JXXa1dN.json +++ b/src/packs/items/loot/loot_Ring_of_Resistance_aUqRifqR5JXXa1dN.json @@ -11,7 +11,7 @@ "type": "effect", "_id": "4swwe8SZeh2KyPtl", "systemPath": "actions", - "description": "", + "description": "

    Once per long rest, you can activate this ring after a successful attack against you to halve the damage.

    ", "chatDisplay": true, "actionType": "action", "cost": [], @@ -20,14 +20,19 @@ "max": 1, "recovery": "longRest" }, - "effects": [], + "effects": [ + { + "_id": "Sj7uGf560T7u4rlX", + "onSave": false + } + ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Activate", "img": "icons/equipment/finger/ring-shield-silver.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -36,7 +41,65 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "name": "Ring of Resistance", + "img": "icons/equipment/finger/ring-shield-silver.webp", + "origin": "Compendium.daggerheart.loot.Item.aUqRifqR5JXXa1dN", + "transfer": false, + "_id": "Sj7uGf560T7u4rlX", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "changes": [ + { + "key": "system.resistance.magical.resistance", + "mode": 5, + "value": "1", + "priority": null + }, + { + "key": "system.resistance.physical.resistance", + "mode": 5, + "value": "1", + "priority": null + } + ], + "disabled": false, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

    Once per long rest, you can activate this ring after a successful attack against you to halve the damage.

    ", + "tint": "#ffffff", + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.347", + "systemId": "daggerheart", + "systemVersion": "1.1.0", + "createdTime": 1756043003002, + "modifiedTime": 1756043032046, + "lastModifiedBy": "vUIbuan0U50nfKBE" + }, + "_key": "!items.effects!aUqRifqR5JXXa1dN.Sj7uGf560T7u4rlX" + } + ], "folder": null, "sort": 0, "ownership": { @@ -50,10 +113,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753638222884, - "modifiedTime": 1755432185583, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756043010729, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!aUqRifqR5JXXa1dN" } diff --git a/src/packs/items/loot/loot_Ring_of_Silence_K1ysGnTpNyxPu5Au.json b/src/packs/items/loot/loot_Ring_of_Silence_K1ysGnTpNyxPu5Au.json index b6de3da5..7d38f5a2 100644 --- a/src/packs/items/loot/loot_Ring_of_Silence_K1ysGnTpNyxPu5Au.json +++ b/src/packs/items/loot/loot_Ring_of_Silence_K1ysGnTpNyxPu5Au.json @@ -11,7 +11,7 @@ "type": "effect", "_id": "Y4mvuJ4tncrKhyjY", "systemPath": "actions", - "description": "", + "description": "

    Spend a Hope to activate this ring. Your footsteps are silent until your next rest.

    ", "chatDisplay": true, "actionType": "action", "cost": [ @@ -28,14 +28,19 @@ "max": 1, "recovery": "shortRest" }, - "effects": [], + "effects": [ + { + "_id": "9JSLRu7amQ4iuIO7", + "onSave": false + } + ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Activate", "img": "icons/equipment/finger/ring-ball-purple.webp", - "range": "" + "range": "self" } }, "attribution": { @@ -47,9 +52,11 @@ "effects": [ { "name": "Ring of Silence", - "type": "base", - "_id": "aCt3QjdeTREZAlEa", "img": "icons/equipment/finger/ring-ball-purple.webp", + "origin": "Compendium.daggerheart.loot.Item.K1ysGnTpNyxPu5Au", + "transfer": false, + "_id": "9JSLRu7amQ4iuIO7", + "type": "base", "system": { "rangeDependence": { "enabled": false, @@ -59,7 +66,7 @@ } }, "changes": [], - "disabled": true, + "disabled": false, "duration": { "startTime": null, "combat": null, @@ -69,10 +76,8 @@ "startRound": null, "startTurn": null }, - "description": "

    Your footsteps are silent until your next rest.

    ", - "origin": null, + "description": "

    Your footsteps are silent until your next rest.

    ", "tint": "#ffffff", - "transfer": true, "statuses": [], "sort": 0, "flags": {}, @@ -80,14 +85,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753990311274, - "modifiedTime": 1753990345889, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "systemVersion": "1.1.0", + "createdTime": 1756043091753, + "modifiedTime": 1756043101742, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, - "_key": "!items.effects!K1ysGnTpNyxPu5Au.aCt3QjdeTREZAlEa" + "_key": "!items.effects!K1ysGnTpNyxPu5Au.9JSLRu7amQ4iuIO7" } ], "folder": null, @@ -103,10 +108,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1753637775628, - "modifiedTime": 1755432151648, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756043091776, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!K1ysGnTpNyxPu5Au" } diff --git a/src/packs/subclasses/feature_Act_of_Reprisal_k7vvMJtEcxMWUUrW.json b/src/packs/subclasses/feature_Act_of_Reprisal_k7vvMJtEcxMWUUrW.json index 441bf12a..a73eb3d7 100644 --- a/src/packs/subclasses/feature_Act_of_Reprisal_k7vvMJtEcxMWUUrW.json +++ b/src/packs/subclasses/feature_Act_of_Reprisal_k7vvMJtEcxMWUUrW.json @@ -29,7 +29,7 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Mark Adversary", @@ -98,10 +98,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1754243143650, - "modifiedTime": 1755391854504, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756035614382, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!k7vvMJtEcxMWUUrW" } diff --git a/src/packs/subclasses/feature_Adrenaline_uByM34yQlw38yf1V.json b/src/packs/subclasses/feature_Adrenaline_uByM34yQlw38yf1V.json index c1b11484..e15129b0 100644 --- a/src/packs/subclasses/feature_Adrenaline_uByM34yQlw38yf1V.json +++ b/src/packs/subclasses/feature_Adrenaline_uByM34yQlw38yf1V.json @@ -7,36 +7,7 @@ "system": { "description": "

    While you're Vulnerable, add your level to your damage rolls.

    ", "resource": null, - "actions": { - "UgffABhuobRDP7MQ": { - "type": "effect", - "_id": "UgffABhuobRDP7MQ", - "systemPath": "actions", - "description": "

    While you're Vulnerable, add your level to your damage rolls.

    ", - "chatDisplay": true, - "actionType": "action", - "cost": [], - "uses": { - "value": null, - "max": "", - "recovery": null, - "consumeOnSuccess": false - }, - "effects": [ - { - "_id": "HMx9uZ54mvMiH95x", - "onSave": false - } - ], - "target": { - "type": "self", - "amount": null - }, - "name": "Apply", - "img": "icons/magic/unholy/hand-marked-pink.webp", - "range": "" - } - }, + "actions": {}, "originItemType": null, "originId": null, "attribution": { @@ -116,10 +87,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1754319984350, - "modifiedTime": 1755392070263, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756035779655, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!uByM34yQlw38yf1V" } diff --git a/src/packs/subclasses/feature_Battle_Bonded_hWsKyed1vfILg0I8.json b/src/packs/subclasses/feature_Battle_Bonded_hWsKyed1vfILg0I8.json index 259a25da..1efb24e2 100644 --- a/src/packs/subclasses/feature_Battle_Bonded_hWsKyed1vfILg0I8.json +++ b/src/packs/subclasses/feature_Battle_Bonded_hWsKyed1vfILg0I8.json @@ -16,7 +16,59 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "name": "Battle Bonded", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "_id": "IZhakv6EuG8DO4a3", + "img": "icons/creatures/mammals/humanoid-wolf-dog-blue.webp", + "changes": [ + { + "key": "system.evasion", + "mode": 2, + "value": "2", + "priority": null + } + ], + "disabled": true, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

    When an adversary attacks you while they’re within your companion’s Melee range, you gain a +2 bonus to your Evasion against the attack.

    ", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.347", + "systemId": "daggerheart", + "systemVersion": "1.1.0", + "createdTime": 1756035661033, + "modifiedTime": 1756035696385, + "lastModifiedBy": "vUIbuan0U50nfKBE" + }, + "_key": "!items.effects!hWsKyed1vfILg0I8.IZhakv6EuG8DO4a3" + } + ], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/subclasses/feature_Elementalist_dPcqKN5NeDkjB1HW.json b/src/packs/subclasses/feature_Elementalist_dPcqKN5NeDkjB1HW.json index dd2d8597..ac661a74 100644 --- a/src/packs/subclasses/feature_Elementalist_dPcqKN5NeDkjB1HW.json +++ b/src/packs/subclasses/feature_Elementalist_dPcqKN5NeDkjB1HW.json @@ -38,12 +38,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Bonus to Roll", "img": "icons/sundries/gaming/dice-runed-tan.webp", - "range": "" + "range": "self" }, "S7HvFD3qIR3ifJRL": { "type": "effect", @@ -75,12 +75,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Bonus to Damage", "img": "icons/sundries/gaming/dice-runed-tan.webp", - "range": "" + "range": "self" } }, "originItemType": null, @@ -115,7 +115,7 @@ "priority": null } ], - "disabled": true, + "disabled": false, "duration": { "startTime": null, "combat": null, @@ -134,10 +134,11 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", - "lastModifiedBy": null + "lastModifiedBy": "vUIbuan0U50nfKBE", + "modifiedTime": 1756035533156 }, "_key": "!items.effects!dPcqKN5NeDkjB1HW.EY87mY6ULfIt3XC8" }, @@ -170,7 +171,7 @@ "priority": null } ], - "disabled": true, + "disabled": false, "duration": { "startTime": null, "combat": null, @@ -189,10 +190,11 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", - "lastModifiedBy": null + "lastModifiedBy": "vUIbuan0U50nfKBE", + "modifiedTime": 1756035536128 }, "_key": "!items.effects!dPcqKN5NeDkjB1HW.WwibpgaO6Kkks7aZ" } @@ -209,10 +211,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1754349507020, - "modifiedTime": 1755392284524, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756035526712, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!dPcqKN5NeDkjB1HW" } diff --git a/src/packs/subclasses/feature_Elusive_Predator_Cjtc43V3IzAmfIFG.json b/src/packs/subclasses/feature_Elusive_Predator_Cjtc43V3IzAmfIFG.json index fed36288..d0787ec3 100644 --- a/src/packs/subclasses/feature_Elusive_Predator_Cjtc43V3IzAmfIFG.json +++ b/src/packs/subclasses/feature_Elusive_Predator_Cjtc43V3IzAmfIFG.json @@ -16,7 +16,59 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "name": "Elusive Predator", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "_id": "X4llFOcAcdJLbear", + "img": "icons/creatures/mammals/beast-horned-scaled-glowing-orange.webp", + "changes": [ + { + "key": "system.evasion", + "mode": 2, + "value": "2", + "priority": null + } + ], + "disabled": true, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

    When your Focus makes an attack against you, you gain a +2 bonus to your Evasion against the attack.

    ", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.347", + "systemId": "daggerheart", + "systemVersion": "1.1.0", + "createdTime": 1756035709943, + "modifiedTime": 1756035734622, + "lastModifiedBy": "vUIbuan0U50nfKBE" + }, + "_key": "!items.effects!Cjtc43V3IzAmfIFG.X4llFOcAcdJLbear" + } + ], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/subclasses/feature_Ruthless_Predator_Qny2J3R35bvC0Cey.json b/src/packs/subclasses/feature_Ruthless_Predator_Qny2J3R35bvC0Cey.json index 8ac4675a..c58ad931 100644 --- a/src/packs/subclasses/feature_Ruthless_Predator_Qny2J3R35bvC0Cey.json +++ b/src/packs/subclasses/feature_Ruthless_Predator_Qny2J3R35bvC0Cey.json @@ -36,12 +36,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Mark Stress", "img": "icons/creatures/mammals/wolf-shadow-black.webp", - "range": "" + "range": "self" } }, "originItemType": null, @@ -117,10 +117,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1754266926055, - "modifiedTime": 1755391927605, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756035422344, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!Qny2J3R35bvC0Cey" } diff --git a/src/packs/subclasses/feature_Shadow_Stepper_hAwTXjhyphiE3aeW.json b/src/packs/subclasses/feature_Shadow_Stepper_hAwTXjhyphiE3aeW.json index c46446d0..de0f54b7 100644 --- a/src/packs/subclasses/feature_Shadow_Stepper_hAwTXjhyphiE3aeW.json +++ b/src/packs/subclasses/feature_Shadow_Stepper_hAwTXjhyphiE3aeW.json @@ -36,12 +36,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Mark Stress", "img": "icons/magic/unholy/projectile-smoke-trail-pink.webp", - "range": "" + "range": "far" } }, "originItemType": null, @@ -110,10 +110,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1754318976447, - "modifiedTime": 1755392035163, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756035450401, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!hAwTXjhyphiE3aeW" } diff --git a/src/packs/subclasses/feature_Vanishing_Act_iyIg1VLwO8C6jvFZ.json b/src/packs/subclasses/feature_Vanishing_Act_iyIg1VLwO8C6jvFZ.json index d56182e9..57ae1815 100644 --- a/src/packs/subclasses/feature_Vanishing_Act_iyIg1VLwO8C6jvFZ.json +++ b/src/packs/subclasses/feature_Vanishing_Act_iyIg1VLwO8C6jvFZ.json @@ -36,12 +36,12 @@ } ], "target": { - "type": "any", + "type": "self", "amount": null }, "name": "Mark Stress", "img": "icons/magic/unholy/strike-hand-glow-pink.webp", - "range": "" + "range": "self" } }, "originItemType": null, @@ -57,7 +57,7 @@ "name": "Vanishing Act: Cloaked", "img": "icons/magic/unholy/strike-hand-glow-pink.webp", "origin": "Compendium.daggerheart.subclasses.Item.iyIg1VLwO8C6jvFZ", - "transfer": true, + "transfer": false, "_id": "czrwqq44sEr0uJ8O", "type": "base", "system": { @@ -88,12 +88,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754322584884, - "modifiedTime": 1754352062188, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1756035920880, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items.effects!iyIg1VLwO8C6jvFZ.czrwqq44sEr0uJ8O" } @@ -110,10 +110,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.1.0", "createdTime": 1754321406972, - "modifiedTime": 1755392101482, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756035929726, + "lastModifiedBy": "vUIbuan0U50nfKBE" }, "_key": "!items!iyIg1VLwO8C6jvFZ" } From 1eb3ff11c090b8d78d0f461cac93ff89fb364a61 Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Wed, 27 Aug 2025 09:00:18 +0200 Subject: [PATCH 42/66] Task/415 action refactor part 2 (#1094) --- lang/en.json | 37 +- module/applications/dialogs/d20RollDialog.mjs | 54 ++- .../settings/automationSettings.mjs | 3 +- .../applications/sheets/actors/adversary.mjs | 4 +- .../applications/sheets/actors/character.mjs | 26 +- .../sheets/api/application-mixin.mjs | 14 +- module/applications/ui/chatLog.mjs | 95 +---- module/applications/ui/fearTracker.mjs | 15 +- module/config/settingsConfig.mjs | 19 + module/data/action/baseAction.mjs | 383 +++++++----------- module/data/action/beastformAction.mjs | 5 +- module/data/action/damageAction.mjs | 60 --- module/data/action/macroAction.mjs | 11 - module/data/chat-message/_modules.mjs | 2 +- .../{adversaryRoll.mjs => actorRoll.mjs} | 2 +- module/data/fields/action/_module.mjs | 1 - module/data/fields/action/beastformField.mjs | 99 +++++ module/data/fields/action/costField.mjs | 104 ++++- module/data/fields/action/damageField.mjs | 142 +++++++ module/data/fields/action/effectsField.mjs | 90 ++++ module/data/fields/action/healingField.mjs | 12 - module/data/fields/action/macroField.mjs | 22 + module/data/fields/action/rangeField.mjs | 11 +- module/data/fields/action/rollField.mjs | 87 ++-- module/data/fields/action/saveField.mjs | 155 +++++++ module/data/fields/action/targetField.mjs | 52 ++- module/data/fields/action/usesField.mjs | 48 ++- module/data/settings/Automation.mjs | 66 +++ module/dice/d20Roll.mjs | 7 +- module/dice/damageRoll.mjs | 4 +- module/dice/dhRoll.mjs | 11 +- module/dice/dualityRoll.mjs | 8 +- module/documents/actor.mjs | 2 +- module/documents/chatMessage.mjs | 134 +++--- module/systemRegistration/socket.mjs | 10 +- styles/less/global/elements.less | 1 + styles/less/global/global.less | 2 +- styles/less/ui/settings/settings.less | 2 +- templates/actionTypes/beastform.hbs | 11 +- templates/actionTypes/damage.hbs | 8 +- templates/actionTypes/save.hbs | 13 +- templates/dialogs/dice-roll/rollSelection.hbs | 18 +- .../settings/automation-settings/roll.hbs | 22 + .../action-settings/effect.hbs | 1 - templates/ui/chat/parts/button-part.hbs | 6 +- templates/ui/chat/parts/damage-part.hbs | 4 +- templates/ui/chat/parts/target-part.hbs | 2 +- 47 files changed, 1244 insertions(+), 641 deletions(-) rename module/data/chat-message/{adversaryRoll.mjs => actorRoll.mjs} (98%) delete mode 100644 module/data/fields/action/healingField.mjs create mode 100644 templates/settings/automation-settings/roll.hbs diff --git a/lang/en.json b/lang/en.json index 36b68ad7..d3d707d6 100755 --- a/lang/en.json +++ b/lang/en.json @@ -96,6 +96,7 @@ "attackName": "Attack Name", "includeBase": { "label": "Include Item Damage" }, "multiplier": "Multiplier", + "saveHint": "Set a default Trait to enable Reaction Roll. It can be changed later in Reaction Roll Dialog.", "resultBased": { "label": "Formula based on Hope/Fear result." }, @@ -310,7 +311,8 @@ "toLoadout": "Send to Loadout", "toVault": "Send to Vault", "unequip": "Unequip", - "useItem": "Use Item" + "useItem": "Use Item", + "cancelBeastform": "Cancel Beastform" }, "Countdown": { "addCountdown": "Add Countdown", @@ -1908,6 +1910,7 @@ "tier4": "tier 4", "domains": "Domains", "downtime": "Downtime", + "roll": "Roll", "rules": "Rules", "types": "Types" }, @@ -1961,6 +1964,7 @@ "fear": "Fear", "features": "Features", "formula": "Formula", + "gm": "GM", "healing": "Healing", "healingRoll": "Healing Roll", "hit": { @@ -1997,6 +2001,10 @@ "none": "None", "noTarget": "No current target", "partner": "Partner", + "player": { + "single": "Player", + "plurial": "Players" + }, "proficiency": "Proficiency", "quantity": "Quantity", "range": "Range", @@ -2022,6 +2030,7 @@ }, "title": "Title", "total": "Total", + "traitModifier": "Trait Modifier", "true": "True", "type": "Type", "unarmed": "Unarmed", @@ -2204,10 +2213,35 @@ "playerCanEditSheet": { "label": "Players Can Manually Edit Character Settings", "hint": "Players are allowed to access the manual Character Settings and change their statistics beyond the rules." + }, + "roll": { + "roll": { + "label": "Roll", + "hint": "Auto behavior for rolls like Attack, Spellcast, etc." + }, + "damage": { + "label": "Damage/Healing Roll", + "hint": "Auto behavior for Damage & Healing rolls after the Attack/Spellcast." + }, + "save": { + "label": "Reaction Roll", + "hint": "Auto behavior if a Reaction Roll is needed. Targets must be selected before the action is made" + }, + "damageApply": { + "label": "Apply Damage/Healing", + "hint": "Automatically apply damages & healings. Targets must be selected before the action is made and Reaction Roll Automation must be different than Never. Bypass users permissions." + }, + "effect": { + "label": "Apply Effects", + "hint": "Automatically apply effects. Targets must be selected before the action is made and Reaction Roll Automation must be different than Never. Bypass users permissions." + } } }, "defeated": { "title": "Defeated Handling" + }, + "roll": { + "title": "Actions" } }, "Homebrew": { @@ -2407,6 +2441,7 @@ "beastformInapplicable": "A beastform can only be applied to a Character.", "beastformAlreadyApplied": "The character already has a beastform applied!", "noTargetsSelected": "No targets are selected.", + "noTargetsSelectedOrPerm": "No targets are selected or with the update permission.", "attackTargetDoesNotExist": "The target token no longer exists", "insufficentAdvancements": "You don't have enough advancements left.", "noAssignedPlayerCharacter": "You have no assigned character.", diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index d445f24c..ed8fd6a1 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -1,3 +1,5 @@ +import { abilities } from "../../config/actorConfig.mjs"; + const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; export default class D20RollDialog extends HandlebarsApplicationMixin(ApplicationV2) { @@ -7,7 +9,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.roll = roll; this.config = config; this.config.experiences = []; - this.reactionOverride = config.roll?.type === 'reaction'; + this.reactionOverride = config.actionType === 'reaction'; if (config.source?.action) { this.item = config.data.parent.items.get(config.source.item) ?? config.data.parent; @@ -20,7 +22,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio static DEFAULT_OPTIONS = { tag: 'form', - id: 'roll-selection', + // id: 'roll-selection', classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'roll-selection'], position: { width: 'auto' @@ -42,7 +44,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio }; get title() { - return this.config.title; + return `${this.config.title}${this.actor ? `: ${this.actor.name}` : ''}`; } get actor() { @@ -113,15 +115,21 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio context.isLite = this.config.roll?.lite; context.extraFormula = this.config.extraFormula; context.formula = this.roll.constructFormula(this.config); + if(this.actor.system.traits) context.abilities = this.getTraitModifiers(); - context.showReaction = !context.rollConfig.type && context.rollType === 'DualityRoll'; + context.showReaction = !this.config.roll?.type && context.rollType === 'DualityRoll'; context.reactionOverride = this.reactionOverride; } return context; } + getTraitModifiers() { + return Object.values(abilities).map(a => ({ id: a.id, label: `${game.i18n.localize(a.label)} (${this.actor.system.traits[a.id]?.value.signedString() ?? 0})` })) + } + static updateRollConfiguration(event, _, formData) { const { ...rest } = foundry.utils.expandObject(formData.object); + this.config.selectedRollMode = rest.selectedRollMode; if (this.config.costs) { @@ -133,6 +141,12 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.roll[key] = value; }); } + if(rest.hasOwnProperty("trait")) { + this.config.roll.trait = rest.trait; + this.config.title = game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { + ability: game.i18n.localize(abilities[this.config.roll.trait]?.label) + }); + } this.config.extraFormula = rest.extraFormula; this.render(); } @@ -151,31 +165,29 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.config.experiences.indexOf(button.dataset.key) > -1 ? this.config.experiences.filter(x => x !== button.dataset.key) : [...this.config.experiences, button.dataset.key]; - if (this.config?.data?.parent?.type === 'character' || this.config?.data?.parent?.type === 'companion') { - this.config.costs = - this.config.costs.indexOf(this.config.costs.find(c => c.extKey === button.dataset.key)) > -1 - ? this.config.costs.filter(x => x.extKey !== button.dataset.key) - : [ - ...this.config.costs, - { - extKey: button.dataset.key, - key: 'hope', - value: 1, - name: this.config.data?.experiences?.[button.dataset.key]?.name - } - ]; - } + this.config.costs = + this.config.costs.indexOf(this.config.costs.find(c => c.extKey === button.dataset.key)) > -1 + ? this.config.costs.filter(x => x.extKey !== button.dataset.key) + : [ + ...this.config.costs, + { + extKey: button.dataset.key, + key: this.config?.data?.parent?.isNPC ? 'fear' : 'hope', + value: 1, + name: this.config.data?.experiences?.[button.dataset.key]?.name + } + ]; this.render(); } static toggleReaction() { if (this.config.roll) { this.reactionOverride = !this.reactionOverride; - this.config.roll.type = this.reactionOverride + this.config.actionType = this.reactionOverride ? CONFIG.DH.ITEM.actionTypes.reaction.id - : this.config.roll.type === CONFIG.DH.ITEM.actionTypes.reaction.id + : this.config.actionType === CONFIG.DH.ITEM.actionTypes.reaction.id ? null - : this.config.roll.type; + : this.config.actionType; this.render(); } } diff --git a/module/applications/settings/automationSettings.mjs b/module/applications/settings/automationSettings.mjs index 0157e016..4407897d 100644 --- a/module/applications/settings/automationSettings.mjs +++ b/module/applications/settings/automationSettings.mjs @@ -35,13 +35,14 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App header: { template: 'systems/daggerheart/templates/settings/automation-settings/header.hbs' }, general: { template: 'systems/daggerheart/templates/settings/automation-settings/general.hbs' }, rules: { template: 'systems/daggerheart/templates/settings/automation-settings/rules.hbs' }, + roll: { template: 'systems/daggerheart/templates/settings/automation-settings/roll.hbs' }, footer: { template: 'systems/daggerheart/templates/settings/automation-settings/footer.hbs' } }; /** @inheritdoc */ static TABS = { main: { - tabs: [{ id: 'general' }, { id: 'rules' }], + tabs: [{ id: 'general' }, { id: 'rules' }, { id: 'roll' }], initial: 'general', labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index f575a2f2..64f48d02 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -146,9 +146,9 @@ export default class AdversarySheet extends DHBaseActorSheet { title: `Reaction Roll: ${this.actor.name}`, headerTitle: 'Adversary Reaction Roll', roll: { - type: 'reaction' + type: 'trait' }, - type: 'trait', + actionType: 'reaction', hasRoll: true, data: this.actor.getRollData() }; diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 6c2d7c95..308faee7 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -679,31 +679,7 @@ export default class CharacterSheet extends DHBaseActorSheet { }) }); - this.consumeResource(result?.costs); - } - - // Remove when Action Refactor part #2 done - async consumeResource(costs) { - if (!costs?.length) return; - const usefulResources = { - ...foundry.utils.deepClone(this.actor.system.resources), - fear: { - value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), - max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, - reversed: false - } - }; - const resources = game.system.api.fields.ActionFields.CostField.getRealCosts(costs).map(c => { - const resource = usefulResources[c.key]; - return { - key: c.key, - value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1), - target: resource.target, - keyIsID: resource.keyIsID - }; - }); - - await this.actor.modifyResource(resources); + if(result) game.system.api.fields.ActionFields.CostField.execute.call(this, result); } //TODO: redo toggleEquipItem method diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index de9fe53d..7f338ac1 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -412,6 +412,16 @@ export default function DHApplicationMixin(Base) { ]; if (usable) { + options.unshift({ + name: 'DAGGERHEART.APPLICATIONS.ContextMenu.cancelBeastform', + icon: 'fa-solid fa-ban', + condition: target => { + const doc = getDocFromElementSync(target); + return doc && doc.system?.actions?.some(a => a.type === "beastform"); + }, + callback: async target => game.system.api.fields.ActionFields.BeastformField.handleActiveTransformations.call(await getDocFromElement(target)) + }); + options.unshift({ name: 'DAGGERHEART.GENERAL.damage', icon: 'fa-solid fa-explosion', @@ -422,7 +432,9 @@ export default function DHApplicationMixin(Base) { callback: async (target, event) => { const doc = await getDocFromElement(target), action = doc?.system?.attack ?? doc; - return action && action.use(event, { byPassRoll: true }); + const config = action.prepareConfig(event); + config.hasRoll = false; + return action && action.workflow.get("damage").execute(config, null, true); } }); diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index a80974ed..21762818 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -1,5 +1,3 @@ -import { emitAsGM, GMUpdateEvent } from '../../systemRegistration/socket.mjs'; - export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog { constructor(options) { super(options); @@ -55,21 +53,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo } addChatListeners = async (app, html, data) => { - html.querySelectorAll('.duality-action-damage').forEach(element => - element.addEventListener('click', event => this.onRollDamage(event, data.message)) - ); - html.querySelectorAll('.target-save').forEach(element => - element.addEventListener('click', event => this.onRollSave(event, data.message)) - ); - html.querySelectorAll('.roll-all-save-button').forEach(element => - element.addEventListener('click', event => this.onRollAllSave(event, data.message)) - ); html.querySelectorAll('.simple-roll-button').forEach(element => element.addEventListener('click', event => this.onRollSimple(event, data.message)) ); - html.querySelectorAll('.healing-button').forEach(element => - element.addEventListener('click', event => this.onHealing(event, data.message)) - ); html.querySelectorAll('.ability-use-button').forEach(element => element.addEventListener('click', event => this.abilityUseButton(event, data.message)) ); @@ -90,80 +76,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo super.close(options); } - async getActor(uuid) { - return await foundry.utils.fromUuid(uuid); - } - - getAction(actor, itemId, actionId) { - const item = actor.items.get(itemId), - action = - actor.system.attack?._id === actionId - ? actor.system.attack - : item.system.attack?._id === actionId - ? item.system.attack - : item?.system?.actions?.get(actionId); - return action; - } - - async onRollDamage(event, message) { - event.stopPropagation(); - const actor = await this.getActor(message.system.source.actor); - if(!actor.isOwner) return true; - if (message.system.source.item && message.system.source.action) { - const action = this.getAction(actor, message.system.source.item, message.system.source.action); - if (!action || !action?.rollDamage) return; - await action.rollDamage(event, message); - } - } - - async onRollSave(event, message) { - event.stopPropagation(); - const actor = await this.getActor(message.system.source.actor), - tokenId = event.target.closest('[data-token]')?.dataset.token, - token = game.canvas.tokens.get(tokenId); - if (!token?.actor || !token.isOwner) return true; - if (message.system.source.item && message.system.source.action) { - const action = this.getAction(actor, message.system.source.item, message.system.source.action); - if (!action || !action?.hasSave) return; - action.rollSave(token.actor, event, message).then(result => - emitAsGM( - GMUpdateEvent.UpdateSaveMessage, - action.updateSaveMessage.bind(action, result, message, token.id), - { - action: action.uuid, - message: message._id, - token: token.id, - result - } - ) - ); - } - } - - async onRollAllSave(event, message) { - event.stopPropagation(); - if (!game.user.isGM) return; - const targets = event.target.parentElement.querySelectorAll('[data-token] .target-save'); - const actor = await this.getActor(message.system.source.actor), - action = this.getAction(actor, message.system.source.item, message.system.source.action); - targets.forEach(async el => { - const tokenId = el.closest('[data-token]')?.dataset.token, - token = game.canvas.tokens.get(tokenId); - if (!token.actor) return; - if (game.user === token.actor.owner) el.dispatchEvent(new PointerEvent('click', { shiftKey: true })); - else { - token.actor.owner - .query('reactionRoll', { - actionId: action.uuid, - actorId: token.actor.uuid, - event, - message - }) - .then(result => action.updateSaveMessage(result, message, token.id)); - } - }); - } - async onRollSimple(event, message) { const buttonType = event.target.dataset.type ?? 'damage', total = message.rolls.reduce((a, c) => a + Roll.fromJSON(c).total, 0), @@ -197,8 +109,11 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo item.system.attack?.id === event.currentTarget.id ? item.system.attack : item.system.actions.get(event.currentTarget.id); - if (event.currentTarget.dataset.directDamage) action.use(event, { byPassRoll: true }); - else action.use(event); + if (event.currentTarget.dataset.directDamage) { + const config = action.prepareConfig(event); + config.hasRoll = false; + action.workflow.get("damage").execute(config, null, true); + } else action.use(event); } async actionUseButton(event, message) { diff --git a/module/applications/ui/fearTracker.mjs b/module/applications/ui/fearTracker.mjs index ace2bbb2..a346aa66 100644 --- a/module/applications/ui/fearTracker.mjs +++ b/module/applications/ui/fearTracker.mjs @@ -78,7 +78,7 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV /** @override */ async _preRender(context, options) { - if (this.currentFear > this.maxFear) + if (this.currentFear > this.maxFear && game.user.isGM) await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear, this.maxFear); } @@ -107,18 +107,5 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV async updateFear(value) { return emitAsGM(GMUpdateEvent.UpdateFear, game.settings.set.bind(game.settings, CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), value); - /* if(!game.user.isGM) - await game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.GMUpdate, - data: { - action: GMUpdateEvent.UpdateFear, - update: value - } - }); - else - game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear, value); */ - /* if (!game.user.isGM) return; - value = Math.max(0, Math.min(this.maxFear, value)); - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear, value); */ } } diff --git a/module/config/settingsConfig.mjs b/module/config/settingsConfig.mjs index 76234a97..0f148aeb 100644 --- a/module/config/settingsConfig.mjs +++ b/module/config/settingsConfig.mjs @@ -29,3 +29,22 @@ export const gameSettings = { Countdowns: 'Countdowns', LastMigrationVersion: 'LastMigrationVersion' }; + +export const actionAutomationChoices = { + never: { + id: "never", + label: "Never" + }, + showDialog: { + id: "showDialog", + label: "Show Dialog only" + }, + // npcOnly: { + // id: "npcOnly", + // label: "Always for non-characters" + // }, + always: { + id: "always", + label: "Always" + } +} diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index ea619bcf..6e522ceb 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -1,14 +1,9 @@ import DhpActor from '../../documents/actor.mjs'; import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs'; import { ActionMixin } from '../fields/actionField.mjs'; -import { abilities } from '../../config/actorConfig.mjs'; const fields = foundry.data.fields; -/* - !!! I'm currently refactoring the whole Action thing, it's a WIP !!! -*/ - /* ToDo - Target Check / Target Picker @@ -20,6 +15,7 @@ const fields = foundry.data.fields; export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel) { static extraSchemas = ['cost', 'uses', 'range']; + /** @inheritDoc */ static defineSchema() { const schemaFields = { _id: new fields.DocumentIdField({ initial: () => foundry.utils.randomID() }), @@ -37,31 +33,76 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel }; this.extraSchemas.forEach(s => { - let clsField; - if ((clsField = this.getActionField(s))) schemaFields[s] = new clsField(); + let clsField = this.getActionField(s); + if (clsField) + schemaFields[s] = new clsField(); }); return schemaFields; } + /** + * Create a Map containing each Action step based on fields define in schema. Ordered by Fields order property. + * + * Each step can be called individually as long as needed config is provided. + * Ex: .workflow.get("damage").execute(config) + * @returns {Map} + */ + defineWorkflow() { + const workflow = new Map(); + this.constructor.extraSchemas.forEach(s => { + let clsField = this.constructor.getActionField(s); + if (clsField?.execute) { + workflow.set(s, { order: clsField.order, execute: clsField.execute.bind(this) } ); + if( s === "damage" ) workflow.set("applyDamage", { order: 75, execute: clsField.applyDamage.bind(this) } ); + } + }); + return new Map([...workflow.entries()].sort(([aKey, aValue], [bKey, bValue]) => aValue.order - bValue.order)); + } + + /** + * Getter returning the workflow property or creating it the first time the property is called + */ + get workflow() { + if ( this.hasOwnProperty("_workflow") ) return this._workflow; + const workflow = Object.freeze(this.defineWorkflow()); + Object.defineProperty(this, "_workflow", {value: workflow, writable: false}); + return workflow; + } + + /** + * Get the Field class from ActionFields global config + * @param {string} name Field short name, equal to Action property + * @returns Action Field + */ static getActionField(name) { const field = game.system.api.fields.ActionFields[`${name.capitalize()}Field`]; return fields.DataField.isPrototypeOf(field) && field; } + /** @inheritDoc */ prepareData() { this.name = this.name || game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[this.type].name); this.img = this.img ?? this.parent?.parent?.img; } + /** + * Get Action ID + */ get id() { return this._id; } + /** + * Return Item the action is attached too. + */ get item() { return this.parent.parent; } + /** + * Return the first Actor parent found. + */ get actor() { return this.item instanceof DhpActor ? this.item @@ -74,6 +115,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return 'trait'; } + /** + * Prepare base data based on Action Type & Parent Type + * @param {object} parent + * @returns {object} + */ static getSourceConfig(parent) { const updateSource = {}; if (parent?.parent?.type === 'weapon' && this === game.system.api.models.actions.actionsTypes.attack) { @@ -96,6 +142,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return updateSource; } + /** + * Obtain a data object used to evaluate any dice rolls associated with this particular Action + * @param {object} [data ={}] Optional data object from previous configuration/rolls + * @returns {object} + */ getRollData(data = {}) { if (!this.actor) return null; const actorData = this.actor.getRollData(false); @@ -111,19 +162,30 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return actorData; } - async use(event, options = {}) { + /** + * Execute each part of the Action workflow in order, calling a specific event before and after each part. + * @param {object} config Config object usually created from prepareConfig method + */ + async executeWorkflow(config) { + for(const [key, part] of this.workflow) { + if (Hooks.call(`${CONFIG.DH.id}.pre${key.capitalize()}Action`, this, config) === false) return; + if(await part.execute(config) === false) return; + if (Hooks.call(`${CONFIG.DH.id}.post${key.capitalize()}Action`, this, config) === false) return; + } + } + + /** + * Main method to use the Action + * @param {Event} event Event from the button used to trigger the Action + * @returns {object} + */ + async use(event) { if (!this.actor) throw new Error("An Action can't be used outside of an Actor context."); if (this.chatDisplay) await this.toChat(); - let { byPassRoll } = options, - config = this.prepareConfig(event, byPassRoll); - for (let i = 0; i < this.constructor.extraSchemas.length; i++) { - let clsField = this.constructor.getActionField(this.constructor.extraSchemas[i]); - if (clsField?.prepareConfig) { - const keep = clsField.prepareConfig.call(this, config); - if (config.isFastForward && !keep) return; - } - } + + let config = this.prepareConfig(event); + if(!config) return; if (Hooks.call(`${CONFIG.DH.id}.preUseAction`, this, config) === false) return; @@ -132,36 +194,22 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel config = await D20RollDialog.configure(null, config); if (!config) return; } - - if (config.hasRoll) { - const rollConfig = this.prepareRoll(config); - config.roll = rollConfig; - config = await this.actor.diceRoll(config); - if (!config) return; - } - - if (this.doFollowUp(config)) { - if (this.rollDamage && this.damage.parts.length) await this.rollDamage(event, config); - else if (this.trigger) await this.trigger(event, config); - else if (this.hasSave || this.hasEffect) { - const roll = new CONFIG.Dice.daggerheart.DHRoll(''); - roll._evaluated = true; - await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config); - } - } - - // Consume resources - await this.consume(config); + + // Execute the Action Worflow in order based of schema fields + await this.executeWorkflow(config); if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return; return config; } - /* */ - prepareConfig(event, byPass = false) { - const hasRoll = this.getUseHasRoll(byPass); - return { + /** + * Create the basic config common to every action type + * @param {Event} event Event from the button used to trigger the Action + * @returns {object} + */ + prepareBaseConfig(event) { + const config = { event, title: `${this.item instanceof CONFIG.Actor.documentClass ? '' : `${this.item.name}: `}${game.i18n.localize(this.name)}`, source: { @@ -169,238 +217,95 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel action: this._id, actor: this.actor.uuid }, - dialog: { - configure: hasRoll - }, - type: this.roll?.type ?? this.type, - hasRoll: hasRoll, - hasDamage: this.damage?.parts?.length && this.type !== 'healing', - hasHealing: this.damage?.parts?.length && this.type === 'healing', - hasEffect: !!this.effects?.length, - isDirect: !!this.damage?.direct, + dialog: {}, + actionType: this.actionType, + hasRoll: this.hasRoll, + hasDamage: this.hasDamage, + hasHealing: this.hasHealing, + hasEffect: this.hasEffect, hasSave: this.hasSave, + isDirect: !!this.damage?.direct, selectedRollMode: game.settings.get('core', 'rollMode'), - isFastForward: event.shiftKey, data: this.getRollData(), - evaluate: hasRoll + evaluate: this.hasRoll }; + DHBaseAction.applyKeybindings(config); + return config; } + /** + * Create the config for that action used for its workflow + * @param {Event} event Event from the button used to trigger the Action + * @returns {object} + */ + prepareConfig(event) { + const config = this.prepareBaseConfig(event); + for(const clsField of Object.values(this.schema.fields)) { + if (clsField?.prepareConfig) + if(clsField.prepareConfig.call(this, config) === false) return false; + } + return config; + } + + /** + * Method used to know if a configuration dialog must be shown or not when there is no roll. + * @param {*} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @returns {boolean} + */ requireConfigurationDialog(config) { return !config.event.shiftKey && !config.hasRoll && (config.costs?.length || config.uses); } - prepareRoll() { - const roll = { - baseModifiers: this.roll.getModifier(), - label: 'Attack', - type: this.actionType, - difficulty: this.roll?.difficulty, - formula: this.roll.getFormula(), - advantage: CONFIG.DH.ACTIONS.advantageState[this.roll.advState].value - }; - if (this.roll?.type === 'diceSet' || !this.hasRoll) roll.lite = true; - - return roll; - } - - doFollowUp(config) { - return !config.hasRoll; - } - + /** + * Consume Action configured resources & uses. + * That method is only used when we want those resources to be consumed outside of the use method workflow. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @param {boolean} successCost + */ async consume(config, successCost = false) { - const actor = this.actor.system.partner ?? this.actor, - usefulResources = { - ...foundry.utils.deepClone(actor.system.resources), - fear: { - value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), - max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, - reversed: false - } - }; + await this.workflow.get("cost")?.execute(config, successCost); + await this.workflow.get("uses")?.execute(config, successCost); - for (var cost of config.costs) { - if (cost.keyIsID) { - usefulResources[cost.key] = { - value: cost.value, - target: this.parent.parent, - keyIsID: true - }; - } - } - - const resources = game.system.api.fields.ActionFields.CostField.getRealCosts(config.costs) - .filter( - c => - (!successCost && (!c.consumeOnSuccess || config.roll?.success)) || - (successCost && c.consumeOnSuccess) - ) - .reduce((a, c) => { - const resource = usefulResources[c.key]; - if (resource) { - a.push({ - key: c.key, - value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1), - target: resource.target, - keyIsID: resource.keyIsID - }); - return a; - } - }, []); - - await actor.modifyResource(resources); - if ( - config.uses?.enabled && - ((!successCost && (!config.uses?.consumeOnSuccess || config.roll?.success)) || - (successCost && config.uses?.consumeOnSuccess)) - ) - this.update({ 'uses.value': this.uses.value + 1 }); - - if (config.roll?.success || successCost) { + if (config.roll && !config.roll.success && successCost) { setTimeout(() => { (config.message ?? config.parent).update({ 'system.successConsumed': true }); }, 50); } } - /* */ - /* ROLL */ - getUseHasRoll(byPass = false) { - return this.hasRoll && !byPass; + /** + * Set if a configuration dialog must be shown or not if a special keyboard key is pressed. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + */ + static applyKeybindings(config) { + config.dialog.configure ??= !(config.event.shiftKey || config.event.altKey || config.event.ctrlKey); } + /** + * Getters to know which parts the action is composed of. A field can exist but configured to not be used. + * @returns {boolean} If that part is in the action. + */ + get hasRoll() { return !!this.roll?.type; } - get modifiers() { - if (!this.actor) return []; - const modifiers = []; - /** Placeholder for specific bonuses **/ - return modifiers; + get hasDamage() { + return this.damage?.parts?.length && this.type !== 'healing' } - /* ROLL */ - /* SAVE */ + get hasHealing() { + return this.damage?.parts?.length && this.type === 'healing' + } + get hasSave() { return !!this.save?.trait; } - /* SAVE */ - /* EFFECTS */ get hasEffect() { return this.effects?.length > 0; } - async applyEffects(event, data, targets) { - targets ??= data.system.targets; - const force = true; /* Where should this come from? */ - if (!this.effects?.length || !targets.length) return; - let effects = this.effects; - targets.forEach(async token => { - if (!token.hit && !force) return; - if (this.hasSave && token.saved.success === true) { - effects = this.effects.filter(e => e.onSave === true); - } - if (!effects.length) return; - effects.forEach(async e => { - const actor = canvas.tokens.get(token.id)?.actor, - effect = this.item.effects.get(e._id); - if (!actor || !effect) return; - await this.applyEffect(effect, actor); - }); - }); - } - - async applyEffect(effect, actor) { - const existingEffect = actor.effects.find(e => e.origin === effect.uuid); - if (existingEffect) { - return effect.update( - foundry.utils.mergeObject({ - ...effect.constructor.getInitialDuration(), - disabled: false - }) - ); - } - - // Otherwise, create a new effect on the target - const effectData = foundry.utils.mergeObject({ - ...effect.toObject(), - disabled: false, - transfer: false, - origin: effect.uuid - }); - await ActiveEffect.implementation.create(effectData, { parent: actor }); - } - /* EFFECTS */ - - /* SAVE */ - async rollSave(actor, event, message) { - if (!actor) return; - const title = actor.isNPC - ? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll') - : game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { - ability: game.i18n.localize(abilities[this.save.trait]?.label) - }); - return actor.diceRoll({ - event, - title, - roll: { - trait: this.save.trait, - difficulty: this.save.difficulty ?? this.actor?.baseSaveDifficulty, - type: 'reaction' - }, - type: 'trait', - hasRoll: true, - data: actor.getRollData() - }); - } - - updateSaveMessage(result, message, targetId) { - if (!result) return; - const updateMsg = this.updateChatMessage.bind(this, message, targetId, { - result: result.roll.total, - success: result.roll.success - }); - if (game.modules.get('dice-so-nice')?.active) - game.dice3d.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id).then(() => updateMsg()); - else updateMsg(); - } - - static rollSaveQuery({ actionId, actorId, event, message }) { - return new Promise(async (resolve, reject) => { - const actor = await fromUuid(actorId), - action = await fromUuid(actionId); - if (!actor || !actor?.isOwner) reject(); - action.rollSave(actor, event, message).then(result => resolve(result)); - }); - } - /* SAVE */ - - async updateChatMessage(message, targetId, changes, chain = true) { - setTimeout(async () => { - const chatMessage = ui.chat.collection.get(message._id); - - await chatMessage.update({ - flags: { - [game.system.id]: { - reactionRolls: { - [targetId]: changes - } - } - } - }); - }, 100); - if (chain) { - if (message.system.source.message) - this.updateChatMessage(ui.chat.collection.get(message.system.source.message), targetId, changes, false); - const relatedChatMessages = ui.chat.collection.filter(c => c.system.source?.message === message._id); - relatedChatMessages.forEach(c => { - this.updateChatMessage(c, targetId, changes, false); - }); - } - } - /** * Generates a list of localized tags for this action. * @returns {string[]} An array of localized tag strings. diff --git a/module/data/action/beastformAction.mjs b/module/data/action/beastformAction.mjs index 8c2dd31e..657cfde2 100644 --- a/module/data/action/beastformAction.mjs +++ b/module/data/action/beastformAction.mjs @@ -1,10 +1,9 @@ -import BeastformDialog from '../../applications/dialogs/beastformDialog.mjs'; import DHBaseAction from './baseAction.mjs'; export default class DhBeastformAction extends DHBaseAction { static extraSchemas = [...super.extraSchemas, 'beastform']; - async use(event, options) { + /* async use(event, options) { const beastformConfig = this.prepareBeastformConfig(); const abort = await this.handleActiveTransformations(); @@ -82,5 +81,5 @@ export default class DhBeastformAction extends DHBaseAction { beastformEffects.map(x => x.id) ); return existingEffects; - } + } */ } diff --git a/module/data/action/damageAction.mjs b/module/data/action/damageAction.mjs index 7deeb006..b4b3e17c 100644 --- a/module/data/action/damageAction.mjs +++ b/module/data/action/damageAction.mjs @@ -1,65 +1,5 @@ -import { setsEqual } from '../../helpers/utils.mjs'; import DHBaseAction from './baseAction.mjs'; export default class DHDamageAction extends DHBaseAction { static extraSchemas = [...super.extraSchemas, 'damage', 'target', 'effects']; - - getFormulaValue(part, data) { - let formulaValue = part.value; - - if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt; - - const isAdversary = this.actor.type === 'adversary'; - if (isAdversary && this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) { - const hasHordeDamage = this.actor.effects.find(x => x.type === 'horde'); - if (hasHordeDamage && !hasHordeDamage.disabled) return part.valueAlt; - } - - return formulaValue; - } - - formatFormulas(formulas, systemData) { - const formattedFormulas = []; - formulas.forEach(formula => { - if (isNaN(formula.formula)) - formula.formula = Roll.replaceFormulaData(formula.formula, this.getRollData(systemData)); - const same = formattedFormulas.find( - f => setsEqual(f.damageTypes, formula.damageTypes) && f.applyTo === formula.applyTo - ); - if (same) same.formula += ` + ${formula.formula}`; - else formattedFormulas.push(formula); - }); - return formattedFormulas; - } - - async rollDamage(event, data) { - const systemData = data.system ?? data; - - let formulas = this.damage.parts.map(p => ({ - formula: this.getFormulaValue(p, systemData).getFormula(this.actor), - damageTypes: p.applyTo === 'hitPoints' && !p.type.size ? new Set(['physical']) : p.type, - applyTo: p.applyTo - })); - - if (!formulas.length) return; - - formulas = this.formatFormulas(formulas, systemData); - - delete systemData.evaluate; - const config = { - ...systemData, - roll: formulas, - dialog: {}, - data: this.getRollData() - }; - if (this.hasSave) config.onSave = this.save.damageMod; - if (data.system) { - config.source.message = data._id; - config.directDamage = false; - } else { - config.directDamage = true; - } - - return CONFIG.Dice.daggerheart.DamageRoll.build(config); - } } diff --git a/module/data/action/macroAction.mjs b/module/data/action/macroAction.mjs index 58b8eba5..b8338cd8 100644 --- a/module/data/action/macroAction.mjs +++ b/module/data/action/macroAction.mjs @@ -2,15 +2,4 @@ import DHBaseAction from './baseAction.mjs'; export default class DHMacroAction extends DHBaseAction { static extraSchemas = [...super.extraSchemas, 'macro']; - - async trigger(event, ...args) { - const fixUUID = !this.macro.includes('Macro.') ? `Macro.${this.macro}` : this.macro, - macro = await fromUuid(fixUUID); - try { - if (!macro) throw new Error(`No macro found for the UUID: ${this.macro}.`); - macro.execute(); - } catch (error) { - ui.notifications.error(error); - } - } } diff --git a/module/data/chat-message/_modules.mjs b/module/data/chat-message/_modules.mjs index a4e2c1fd..36c6ee9d 100644 --- a/module/data/chat-message/_modules.mjs +++ b/module/data/chat-message/_modules.mjs @@ -1,5 +1,5 @@ import DHAbilityUse from "./abilityUse.mjs"; -import DHActorRoll from "./adversaryRoll.mjs"; +import DHActorRoll from "./actorRoll.mjs"; export const config = { abilityUse: DHAbilityUse, diff --git a/module/data/chat-message/adversaryRoll.mjs b/module/data/chat-message/actorRoll.mjs similarity index 98% rename from module/data/chat-message/adversaryRoll.mjs rename to module/data/chat-message/actorRoll.mjs index d87bab6a..b6512fbd 100644 --- a/module/data/chat-message/adversaryRoll.mjs +++ b/module/data/chat-message/actorRoll.mjs @@ -95,7 +95,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { } registerTargetHook() { - if (!this.parent.isAuthor) return; + if (!this.parent.isAuthor || !this.hasTarget) return; if (this.targetMode && this.parent.targetHook !== null) { Hooks.off('targetToken', this.parent.targetHook); return (this.parent.targetHook = null); diff --git a/module/data/fields/action/_module.mjs b/module/data/fields/action/_module.mjs index e6caa963..7a33e147 100644 --- a/module/data/fields/action/_module.mjs +++ b/module/data/fields/action/_module.mjs @@ -6,6 +6,5 @@ export { default as EffectsField } from './effectsField.mjs'; export { default as SaveField } from './saveField.mjs'; export { default as BeastformField } from './beastformField.mjs'; export { default as DamageField } from './damageField.mjs'; -export { default as HealingField } from './healingField.mjs'; export { default as RollField } from './rollField.mjs'; export { default as MacroField } from './macroField.mjs'; diff --git a/module/data/fields/action/beastformField.mjs b/module/data/fields/action/beastformField.mjs index 832bd9f6..62c735d0 100644 --- a/module/data/fields/action/beastformField.mjs +++ b/module/data/fields/action/beastformField.mjs @@ -1,6 +1,13 @@ +import BeastformDialog from "../../../applications/dialogs/beastformDialog.mjs"; + const fields = foundry.data.fields; export default class BeastformField extends fields.SchemaField { + /** + * Action Workflow order + */ + static order = 90; + constructor(options = {}, context = {}) { const beastformFields = { tierAccess: new fields.SchemaField({ @@ -27,4 +34,96 @@ export default class BeastformField extends fields.SchemaField { }; super(beastformFields, options, context); } + + /** + * Beastform Transformation Action Workflow part. + * Must be called within Action context or similar. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + */ + static async execute(config) { + // Should not be useful anymore here + await BeastformField.handleActiveTransformations.call(this); + + const { selected, evolved, hybrid } = await BeastformDialog.configure(config, this.item); + if (!selected) return false; + + return await BeastformField.transform.call(this, selected, evolved, hybrid); + } + + /** + * Update Action Workflow config object. + * Must be called within Action context. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + */ + prepareConfig(config) { + if(this.actor.effects.find(x => x.type === 'beastform')) { + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.beastformAlreadyApplied')); + return false; + } + + const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers; + const actorLevel = this.actor.system.levelData.level.current; + const actorTier = + Object.values(settingsTiers).find( + tier => actorLevel >= tier.levels.start && actorLevel <= tier.levels.end + ) ?? 1; + + config.tierLimit = this.beastform.tierAccess.exact ?? actorTier; + } + + /** + * TODO by Harry + * @param {*} selectedForm + * @param {*} evolvedData + * @param {*} hybridData + * @returns + */ + static async transform(selectedForm, evolvedData, hybridData) { + const formData = evolvedData?.form ? evolvedData.form.toObject() : selectedForm.toObject(); + const beastformEffect = formData.effects.find(x => x.type === 'beastform'); + if (!beastformEffect) { + ui.notifications.error('DAGGERHEART.UI.Notifications.beastformMissingEffect'); + return false; + } + + if (evolvedData?.form) { + const evolvedForm = selectedForm.effects.find(x => x.type === 'beastform'); + if (!evolvedForm) { + ui.notifications.error('DAGGERHEART.UI.Notifications.beastformMissingEffect'); + return false; + } + + beastformEffect.changes = [...beastformEffect.changes, ...evolvedForm.changes]; + formData.system.features = [...formData.system.features, ...selectedForm.system.features.map(x => x.uuid)]; + } + + if (selectedForm.system.beastformType === CONFIG.DH.ITEM.beastformTypes.hybrid.id) { + formData.system.advantageOn = Object.values(hybridData.advantages).reduce((advantages, formCategory) => { + Object.keys(formCategory).forEach(advantageKey => { + advantages[advantageKey] = formCategory[advantageKey]; + }); + return advantages; + }, {}); + formData.system.features = [ + ...formData.system.features, + ...Object.values(hybridData.features).flatMap(x => Object.keys(x)) + ]; + } + + this.actor.createEmbeddedDocuments('Item', [formData]); + } + + /** + * Remove existing beastform effect and return true if there was one + * @returns {boolean} + */ + static async handleActiveTransformations() { + const beastformEffects = this.actor.effects.filter(x => x.type === 'beastform'); + const existingEffects = beastformEffects.length > 0; + await this.actor.deleteEmbeddedDocuments( + 'ActiveEffect', + beastformEffects.map(x => x.id) + ); + return existingEffects; + } } diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index c224fff0..ca942909 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -1,6 +1,12 @@ const fields = foundry.data.fields; export default class CostField extends fields.ArrayField { + /** + * Action Workflow order + */ + static order = 150; + + /** @inheritDoc */ constructor(options = {}, context = {}) { const element = new fields.SchemaField({ key: new fields.StringField({ @@ -20,15 +26,80 @@ export default class CostField extends fields.ArrayField { super(element, options, context); } - static prepareConfig(config) { + /** + * Cost Consumption Action Workflow part. + * Consume configured action resources. + * Must be called within Action context or similar. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @param {boolean} [successCost=false] Consume only resources configured as "On Success only" if not already consumed. + */ + static async execute(config, successCost = false) { + const actor= this.actor.system.partner ?? this.actor, + usefulResources = { + ...foundry.utils.deepClone(actor.system.resources), + fear: { + value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), + max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, + reversed: false + } + }; + + if(this.parent?.parent) { + for (var cost of config.costs) { + if (cost.keyIsID) { + usefulResources[cost.key] = { + value: cost.value, + target: this.parent.parent, + keyIsID: true + }; + } + } + } + + const resources = CostField.getRealCosts(config.costs) + .filter( + c => + (!successCost && (!c.consumeOnSuccess || config.roll?.success)) || + (successCost && c.consumeOnSuccess) + ) + .reduce((a, c) => { + const resource = usefulResources[c.key]; + if (resource) { + a.push({ + key: c.key, + value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1), + target: resource.target, + keyIsID: resource.keyIsID + }); + return a; + } + }, []); + + await actor.modifyResource(resources); + } + + /** + * Update Action Workflow config object. + * Must be called within Action context or similar. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @returns {boolean} Return false if fast-forwarded and no more uses. + */ + prepareConfig(config) { const costs = this.cost?.length ? foundry.utils.deepClone(this.cost) : []; config.costs = CostField.calcCosts.call(this, costs); const hasCost = CostField.hasCost.call(this, config.costs); - if (config.isFastForward && !hasCost) - return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.insufficientResources')); - return hasCost; + if (config.dialog.configure === false && !hasCost) { + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.insufficientResources')); + return hasCost; + } } + /** + * + * Must be called within Action context. + * @param {*} costs + * @returns + */ static calcCosts(costs) { const resources = CostField.getResources.call(this, costs); return costs.map(c => { @@ -40,13 +111,19 @@ export default class CostField extends fields.ArrayField { c.key === 'fear' ? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear) : resources[c.key].isReversed - ? resources[c.key].max + ? resources[c.key].max - resources[c.key].value : resources[c.key].value; if (c.scalable) c.maxStep = Math.floor((c.max - c.value) / c.step); return c; }); } + /** + * Check if the current Actor currently has all needed resources. + * Must be called within Action context. + * @param {*} costs + * @returns {boolean} + */ static hasCost(costs) { const realCosts = CostField.getRealCosts.call(this, costs), hasFearCost = realCosts.findIndex(c => c.key === 'fear'); @@ -73,6 +150,12 @@ export default class CostField extends fields.ArrayField { ); } + /** + * Get all Actor resources + parent Item potential one. + * Must be called within Action context. + * @param {*} costs + * @returns + */ static getResources(costs) { const actorResources = foundry.utils.deepClone(this.actor.system.resources); if (this.actor.system.partner) @@ -93,6 +176,11 @@ export default class CostField extends fields.ArrayField { }; } + /** + * + * @param {*} costs + * @returns + */ static getRealCosts(costs) { const realCosts = costs?.length ? costs.filter(c => c.enabled) : []; let mergedCosts = []; @@ -104,6 +192,12 @@ export default class CostField extends fields.ArrayField { return mergedCosts; } + /** + * Format scalable max cost, inject Action datas if it's a formula. + * Must be called within Action context. + * @param {number|string} max Configured maximum for that resource. + * @returns {number} The max cost value. + */ static formatMax(max) { max ??= 0; if (isNaN(max)) { diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 4b4dee7d..db43cfb0 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -1,8 +1,15 @@ import FormulaField from '../formulaField.mjs'; +import { setsEqual } from '../../../helpers/utils.mjs'; const fields = foundry.data.fields; export default class DamageField extends fields.SchemaField { + /** + * Action Workflow order + */ + static order = 20; + + /** @inheritDoc */ constructor(options, context = {}) { const damageFields = { parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)), @@ -14,6 +21,141 @@ export default class DamageField extends fields.SchemaField { }; super(damageFields, options, context); } + + /** + * Roll Damage/Healing Action Workflow part. + * Must be called within Action context or similar. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @param {string} [messageId=null] ChatMessage Id where the clicked button belong. + * @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example. + */ + static async execute(config, messageId = null, force = false) { + if(!this.hasDamage && !this.hasHealing) return; + if((this.hasRoll && DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id) && !force) return; + + let formulas = this.damage.parts.map(p => ({ + formula: DamageField.getFormulaValue.call(this, p, config).getFormula(this.actor), + damageTypes: p.applyTo === 'hitPoints' && !p.type.size ? new Set(['physical']) : p.type, + applyTo: p.applyTo + })); + + if (!formulas.length) return false; + + formulas = DamageField.formatFormulas.call(this, formulas, config); + + const damageConfig = { + ...config, + roll: formulas, + dialog: {}, + data: this.getRollData() + }; + delete damageConfig.evaluate; + + if(DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) damageConfig.dialog.configure = false; + if (config.hasSave) config.onSave = damageConfig.onSave = this.save.damageMod; + + damageConfig.source.message = config.message?._id ?? messageId; + damageConfig.directDamage = !!damageConfig.source?.message; + + if(damageConfig.source?.message && game.modules.get('dice-so-nice')?.active) + await game.dice3d.waitFor3DAnimationByMessageID(damageConfig.source.message); + + const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig); + if(!damageResult) return false; + config.damage = damageResult.damage; + config.message ??= damageConfig.message; + } + + /** + * Apply Damage/Healing Action Worflow part. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @param {*[]} targets Arrays of targets to bypass pre-selected ones. + * @param {boolean} force If the method should be executed outside of Action workflow, for ChatMessage button for example. + */ + static async applyDamage(config, targets = null, force = false) { + targets ??= config.targets.filter(target => target.hit); + if(!config.damage || !targets?.length || (!DamageField.getApplyAutomation() && !force)) return; + for (let target of targets) { + const actor = fromUuidSync(target.actorId); + if(!actor) continue; + if ( + !config.hasHealing && + config.onSave && + target.saved?.success === true + ) { + const mod = CONFIG.DH.ACTIONS.damageOnSave[config.onSave]?.mod ?? 1; + Object.entries(config.damage).forEach(([k, v]) => { + v.total = 0; + v.parts.forEach(part => { + part.total = Math.ceil(part.total * mod); + v.total += part.total; + }); + }); + } + + if (config.hasHealing) actor.takeHealing(config.damage); + else actor.takeDamage(config.damage, config.isDirect); + } + } + + /** + * Return value or valueAlt from damage part + * Must be called within Action context or similar. + * @param {object} part Damage Part + * @param {object} data Action getRollData + * @returns Formula value object + */ + static getFormulaValue(part, data) { + let formulaValue = part.value; + + if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt; + + const isAdversary = this.actor.type === 'adversary'; + if (isAdversary && this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) { + const hasHordeDamage = this.actor.effects.find(x => x.type === 'horde'); + if (hasHordeDamage && !hasHordeDamage.disabled) return part.valueAlt; + } + + return formulaValue; + } + + /** + * Prepare formulas for Damage Roll + * Must be called within Action context or similar. + * @param {object[]} formulas Array of formatted formulas object + * @param {object} data Action getRollData + * @returns + */ + static formatFormulas(formulas, data) { + const formattedFormulas = []; + formulas.forEach(formula => { + if (isNaN(formula.formula)) + formula.formula = Roll.replaceFormulaData(formula.formula, this.getRollData(data)); + const same = formattedFormulas.find( + f => setsEqual(f.damageTypes, formula.damageTypes) && f.applyTo === formula.applyTo + ); + if (same) same.formula += ` + ${formula.formula}`; + else formattedFormulas.push(formula); + }); + return formattedFormulas; + } + + /** + * Return the automation setting for execute method for current user role + * @returns {string} Id from settingsConfig.mjs actionAutomationChoices + */ + static getAutomation() { + return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.players) + } + + + /** + * Return the automation setting for applyDamage method for current user role + * @returns {boolean} If applyDamage should be triggered automatically + */ + static getApplyAutomation() { + return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.players) + } } export class DHActionDiceData extends foundry.abstract.DataModel { diff --git a/module/data/fields/action/effectsField.mjs b/module/data/fields/action/effectsField.mjs index ddc69d2d..3b8c5e43 100644 --- a/module/data/fields/action/effectsField.mjs +++ b/module/data/fields/action/effectsField.mjs @@ -1,6 +1,14 @@ +import { emitAsGM, GMUpdateEvent } from "../../../systemRegistration/socket.mjs"; + const fields = foundry.data.fields; export default class EffectsField extends fields.ArrayField { + /** + * Action Workflow order + */ + static order = 100; + + /** @inheritDoc */ constructor(options = {}, context = {}) { const element = new fields.SchemaField({ _id: new fields.DocumentIdField(), @@ -8,4 +16,86 @@ export default class EffectsField extends fields.ArrayField { }); super(element, options, context); } + + /** + * Apply Effects Action Workflow part. + * Must be called within Action context or similar. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @param {object[]} [targets=null] Array of targets to override pre-selected ones. + * @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example. + */ + static async execute(config, targets = null, force = false) { + if(!config.hasEffect) return; + let message = config.message ?? ui.chat.collection.get(config.parent?._id); + if(!message) { + const roll = new CONFIG.Dice.daggerheart.DHRoll(''); + roll._evaluated = true; + message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config); + } + if(EffectsField.getAutomation() || force) { + targets ??= (message.system?.targets ?? config.targets).filter(t => !config.hasRoll || t.hit); + await emitAsGM( + GMUpdateEvent.UpdateEffect, + EffectsField.applyEffects.bind(this), + targets, + this.uuid + ); + // EffectsField.applyEffects.call(this, config.targets.filter(t => !config.hasRoll || t.hit)); + } + } + + /** + * Apply Action Effects to a list of Targets + * Must be called within Action context or similar. + * @param {object[]} targets Array of formatted targets + */ + static async applyEffects(targets) { + if (!this.effects?.length || !targets?.length) return; + let effects = this.effects; + targets.forEach(async token => { + if (this.hasSave && token.saved.success === true) + effects = this.effects.filter(e => e.onSave === true); + if (!effects.length) return; + effects.forEach(async e => { + const actor = canvas.tokens.get(token.id)?.actor, + effect = this.item.effects.get(e._id); + if (!actor || !effect) return; + await EffectsField.applyEffect(effect, actor); + }); + }); + } + + /** + * Apply an Effect to a target or enable it if already on it + * @param {object} effect Effect object containing ActiveEffect UUID + * @param {object} actor Actor Document + */ + static async applyEffect(effect, actor) { + const existingEffect = actor.effects.find(e => e.origin === effect.uuid); + if (existingEffect) { + return effect.update( + foundry.utils.mergeObject({ + ...effect.constructor.getInitialDuration(), + disabled: false + }) + ); + } + + // Otherwise, create a new effect on the target + const effectData = foundry.utils.mergeObject({ + ...effect.toObject(), + disabled: false, + transfer: false, + origin: effect.uuid + }); + await ActiveEffect.implementation.create(effectData, { parent: actor }); + } + + /** + * Return the automation setting for execute method for current user role + * @returns {boolean} If execute should be triggered automatically + */ + static getAutomation() { + return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.players) + } } diff --git a/module/data/fields/action/healingField.mjs b/module/data/fields/action/healingField.mjs deleted file mode 100644 index 98f4f5ea..00000000 --- a/module/data/fields/action/healingField.mjs +++ /dev/null @@ -1,12 +0,0 @@ -import { DHDamageData } from './damageField.mjs'; - -const fields = foundry.data.fields; - -export default class HealingField extends fields.SchemaField { - constructor(options, context = {}) { - const healingFields = { - parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)) - }; - super(healingFields, options, context); - } -} diff --git a/module/data/fields/action/macroField.mjs b/module/data/fields/action/macroField.mjs index 62da0da0..222feb2a 100644 --- a/module/data/fields/action/macroField.mjs +++ b/module/data/fields/action/macroField.mjs @@ -1,7 +1,29 @@ const fields = foundry.data.fields; export default class MacroField extends fields.DocumentUUIDField { + /** + * Action Workflow order + */ + static order = 70; + + /** @inheritDoc */ constructor(context = {}) { super({ type: "Macro" }, context); } + + /** + * Macro Action Workflow part. + * Must be called within Action context or similar or similar. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. Currently not used. + */ + static async execute(config) { + const fixUUID = !this.macro.includes('Macro.') ? `Macro.${this.macro}` : this.macro, + macro = await fromUuid(fixUUID); + try { + if (!macro) throw new Error(`No macro found for the UUID: ${this.macro}.`); + macro.execute(); + } catch (error) { + ui.notifications.error(error); + } + } } diff --git a/module/data/fields/action/rangeField.mjs b/module/data/fields/action/rangeField.mjs index 221f00af..1237e507 100644 --- a/module/data/fields/action/rangeField.mjs +++ b/module/data/fields/action/rangeField.mjs @@ -1,6 +1,8 @@ const fields = foundry.data.fields; export default class RangeField extends fields.StringField { + + /** @inheritDoc */ constructor(context = {}) { const options = { choices: CONFIG.DH.GENERAL.range, @@ -11,7 +13,12 @@ export default class RangeField extends fields.StringField { super(options, context); } - static prepareConfig(config) { - return true; + /** + * Update Action Workflow config object. + * NOT YET IMPLEMENTED. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + */ + prepareConfig(config) { + return; } } diff --git a/module/data/fields/action/rollField.mjs b/module/data/fields/action/rollField.mjs index a4df2a9e..cfcfb56b 100644 --- a/module/data/fields/action/rollField.mjs +++ b/module/data/fields/action/rollField.mjs @@ -71,29 +71,6 @@ export class DHActionRollData extends foundry.abstract.DataModel { const modifiers = []; if (!this.parent?.actor) return modifiers; switch (this.parent.actor.type) { - case 'character': - const spellcastingTrait = - this.type === 'spellcast' - ? (this.parent.actor?.system?.spellcastModifierTrait?.key ?? 'agility') - : null; - const trait = - this.useDefault || !this.trait - ? (spellcastingTrait ?? this.parent.item.system.attack?.roll?.trait ?? 'agility') - : this.trait; - if ( - this.type === CONFIG.DH.GENERAL.rollTypes.attack.id || - this.type === CONFIG.DH.GENERAL.rollTypes.trait.id - ) - modifiers.push({ - label: `DAGGERHEART.CONFIG.Traits.${trait}.name`, - value: this.parent.actor.system.traits[trait].value - }); - else if (this.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id) - modifiers.push({ - label: `DAGGERHEART.CONFIG.RollTypes.spellcast.name`, - value: this.parent.actor.system.spellcastModifier - }); - break; case 'companion': case 'adversary': if (this.type === CONFIG.DH.GENERAL.rollTypes.attack.id) @@ -107,10 +84,74 @@ export class DHActionRollData extends foundry.abstract.DataModel { } return modifiers; } + + get rollTrait() { + if(this.parent?.actor?.type !== "character") return null; + switch (this.type) { + case CONFIG.DH.GENERAL.rollTypes.spellcast.id: + return this.parent.actor?.system?.spellcastModifierTrait?.key ?? 'agility'; + case CONFIG.DH.GENERAL.rollTypes.attack.id: + case CONFIG.DH.GENERAL.rollTypes.trait.id: + return this.useDefault || !this.trait + ? this.parent.item.system.attack?.roll?.trait ?? 'agility' + : this.trait; + default: + return null; + } + } } export default class RollField extends fields.EmbeddedDataField { + /** + * Action Workflow order + */ + static order = 10; + + /** @inheritDoc */ constructor(options, context = {}) { super(DHActionRollData, options, context); } + + /** + * Roll Action Workflow part. + * Must be called within Action context or similar. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + */ + static async execute(config) { + if(!config.hasRoll) return; + config = await this.actor.diceRoll(config); + if(!config) return false; + } + + /** + * Update Action Workflow config object. + * Must be called within Action context. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + */ + prepareConfig(config) { + if(!config.hasRoll) return; + + config.dialog.configure = RollField.getAutomation() ? !config.dialog.configure : config.dialog.configure; + + const roll = { + baseModifiers: this.roll.getModifier(), + label: 'Attack', + type: this.roll?.type, + trait: this.roll?.rollTrait, + difficulty: this.roll?.difficulty, + formula: this.roll.getFormula(), + advantage: CONFIG.DH.ACTIONS.advantageState[this.roll.advState].value + }; + if (this.roll.type === 'diceSet' || !this.hasRoll) roll.lite = true; + + config.roll = roll; + } + + /** + * Return the automation setting for execute method for current user role + * @returns {boolean} If execute should be triggered automatically + */ + static getAutomation() { + return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.players) + } } diff --git a/module/data/fields/action/saveField.mjs b/module/data/fields/action/saveField.mjs index e93a82a9..0003a4d5 100644 --- a/module/data/fields/action/saveField.mjs +++ b/module/data/fields/action/saveField.mjs @@ -1,6 +1,14 @@ +import { abilities } from "../../../config/actorConfig.mjs"; + const fields = foundry.data.fields; export default class SaveField extends fields.SchemaField { + /** + * Action Workflow order + */ + static order = 50; + + /** @inheritDoc */ constructor(options = {}, context = {}) { const saveFields = { trait: new fields.StringField({ @@ -16,4 +24,151 @@ export default class SaveField extends fields.SchemaField { }; super(saveFields, options, context); } + + /** + * Reaction Roll Action Workflow part. + * Must be called within Action context or similar. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @param {object[]} [targets=null] Array of targets to override pre-selected ones. + * @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example. + */ + static async execute(config, targets = null, force = false) { + if(!config.hasSave) return; + let message = config.message ?? ui.chat.collection.get(config.parent?._id); + + if(!message) { + const roll = new CONFIG.Dice.daggerheart.DHRoll(''); + roll._evaluated = true; + message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config); + } + if(SaveField.getAutomation() !== CONFIG.DH.SETTINGS.actionAutomationChoices.never.id || force) { + targets ??= config.targets.filter(t => !config.hasRoll || t.hit); + await SaveField.rollAllSave.call(this, targets, config.event, message); + } else return false; + } + + /** + * Roll a Reaction Roll for all targets. Send a query to the owner if the User is not. + * Must be called within Action context. + * @param {object[]} targets Array of formatted targets. + * @param {Event} event Triggering event + * @param {ChatMessage} message The ChatMessage the triggered button comes from. + */ + static async rollAllSave(targets, event, message) { + if(!targets) return; + return new Promise(resolve => { + const aPromise = []; + targets.forEach(target => { + aPromise.push( + new Promise(async subResolve => { + const actor = fromUuidSync(target.actorId); + if(actor) { + const rollSave = game.user === actor.owner ? + SaveField.rollSave.call(this, actor, event) + : actor.owner + .query('reactionRoll', { + actionId: this.uuid, + actorId: actor.uuid, + event, + message + }); + const result = await rollSave; + await SaveField.updateSaveMessage.call(this, result, message, target.id); + subResolve(); + } else subResolve(); + }) + ) + }); + Promise.all(aPromise).then(result => resolve()); + }) + } + + /** + * Roll a Reaction Roll for the specified Actor against the Action difficulty. + * Must be called within Action context. + * @param {*} actor Actor document + * @param {Event} event Triggering event + * @returns {object} Actor diceRoll config result. + */ + static async rollSave(actor, event) { + if (!actor) return; + const title = actor.isNPC + ? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll') + : game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { + ability: game.i18n.localize(abilities[this.save.trait]?.label) + }), + rollConfig = { + event, + title, + roll: { + trait: this.save.trait, + difficulty: this.save.difficulty ?? this.actor?.baseSaveDifficulty, + type: 'trait' + }, + actionType: 'reaction', + hasRoll: true, + data: actor.getRollData() + }; + if(SaveField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) rollConfig.dialog = { configure: false }; + return actor.diceRoll(rollConfig); + } + + /** + * Update a Roll ChatMessage for a token according to his Reaction Roll result. + * @param {object} result Result from the Reaction Roll + * @param {object} message ChatMessage to update + * @param {string} targetId Token ID + */ + static async updateSaveMessage(result, message, targetId) { + if (!result) return; + const updateMsg = async function(message, targetId, result) { + // setTimeout(async () => { + const chatMessage = ui.chat.collection.get(message._id), + changes = { + flags: { + [game.system.id]: { + reactionRolls: { + [targetId]: + { + result: result.roll.total, + success: result.roll.success + } + } + } + } + }; + await chatMessage.update(changes); + // }, 100); + }; + if (game.modules.get('dice-so-nice')?.active) + game.dice3d.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id).then(async () => await updateMsg(message, targetId, result)); + else await updateMsg(message, targetId, result); + } + + /** + * Return the automation setting for execute method for current user role + * @returns {string} Id from settingsConfig.mjs actionAutomationChoices + */ + static getAutomation() { + return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.players) + } + + /** + * Send a query to an Actor owner to roll a Reaction Roll then send back the result. + * @param {object} param0 + * @param {string} param0.actionId Action ID + * @param {string} param0.actorId Actor ID + * @param {Event} param0.event Triggering event + * @param {ChatMessage} param0.message Chat Message to update + * @returns + */ + static rollSaveQuery({ actionId, actorId, event, message }) { + return new Promise(async (resolve, reject) => { + const actor = await fromUuid(actorId), + action = await fromUuid(actionId); + if (!actor || !actor?.isOwner) reject(); + SaveField.rollSave.call(action, actor, event, message) + .then(result => resolve(result)); + }); + } } diff --git a/module/data/fields/action/targetField.mjs b/module/data/fields/action/targetField.mjs index bfb01db9..4499dcc8 100644 --- a/module/data/fields/action/targetField.mjs +++ b/module/data/fields/action/targetField.mjs @@ -1,6 +1,8 @@ const fields = foundry.data.fields; export default class TargetField extends fields.SchemaField { + + /** @inheritDoc */ constructor(options = {}, context = {}) { const targetFields = { type: new fields.StringField({ @@ -13,44 +15,70 @@ export default class TargetField extends fields.SchemaField { super(targetFields, options, context); } - static prepareConfig(config) { - if (!this.target?.type) return []; + /** + * Update Action Workflow config object. + * Must be called within Action context. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + */ + prepareConfig(config) { + if (!this.target?.type) return config.targets = []; config.hasTarget = true; let targets; + // If the Action is configured as self-targeted, set targets as the owner. if (this.target?.type === CONFIG.DH.GENERAL.targetTypes.self.id) targets = [this.actor.token ?? this.actor.prototypeToken]; else { targets = Array.from(game.user.targets); if (this.target.type !== CONFIG.DH.GENERAL.targetTypes.any.id) { - targets = targets.filter(t => TargetField.isTargetFriendly.call(this, t)); + targets = targets.filter(target => TargetField.isTargetFriendly(this.actor, target, this.target.type)); if (this.target.amount && targets.length > this.target.amount) targets = []; } } config.targets = targets.map(t => TargetField.formatTarget.call(this, t)); const hasTargets = TargetField.checkTargets.call(this, this.target.amount, config.targets); - if (config.isFastForward && !hasTargets) - return ui.notifications.warn('Too many targets selected for that actions.'); - return hasTargets; + if (config.dialog.configure === false && !hasTargets) { + ui.notifications.warn('Too many targets selected for that actions.'); + return hasTargets; + } } + /** + * Check if the number of selected targets respect the amount set in the Action. + * NOT YET IMPLEMENTED. Will be with Target Picker. + * @param {number} amount Max amount of targets configured in the action. + * @param {*[]} targets Array of targeted tokens. + * @returns {boolean} If the amount of targeted tokens does not exceed action configured one. + */ static checkTargets(amount, targets) { return true; // return !amount || (targets.length > amount); } - static isTargetFriendly(target) { - const actorDisposition = this.actor.token - ? this.actor.token.disposition - : this.actor.prototypeToken.disposition, + /** + * Compare 2 Actors disposition between each other + * @param {*} actor First actor document. + * @param {*} target Second actor document. + * @param {string} type Disposition id to compare (friendly/hostile). + * @returns {boolean} If both actors respect the provided type. + */ + static isTargetFriendly(actor, target, type) { + const actorDisposition = actor.token + ? actor.token.disposition + : actor.prototypeToken.disposition, targetDisposition = target.document.disposition; return ( - (this.target.type === CONFIG.DH.GENERAL.targetTypes.friendly.id && + (type === CONFIG.DH.GENERAL.targetTypes.friendly.id && actorDisposition === targetDisposition) || - (this.target.type === CONFIG.DH.GENERAL.targetTypes.hostile.id && + (type === CONFIG.DH.GENERAL.targetTypes.hostile.id && actorDisposition + targetDisposition === 0) ); } + /** + * Format actor to useful datas for Action roll workflow. + * @param {*} actor Actor object to format. + * @returns {*} Formatted Actor. + */ static formatTarget(actor) { return { id: actor.id, diff --git a/module/data/fields/action/usesField.mjs b/module/data/fields/action/usesField.mjs index 5c2bfb61..d180ddf8 100644 --- a/module/data/fields/action/usesField.mjs +++ b/module/data/fields/action/usesField.mjs @@ -3,6 +3,12 @@ import FormulaField from '../formulaField.mjs'; const fields = foundry.data.fields; export default class UsesField extends fields.SchemaField { + /** + * Action Workflow order + */ + static order = 160; + + /** @inheritDoc */ constructor(options = {}, context = {}) { const usesFields = { value: new fields.NumberField({ nullable: true, initial: null }), @@ -20,15 +26,45 @@ export default class UsesField extends fields.SchemaField { super(usesFields, options, context); } - static prepareConfig(config) { + /** + * Uses Consumption Action Workflow part. + * Increment Action spent uses by 1. + * Must be called within Action context or similar or similar. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @param {boolean} [successCost=false] Consume only resources configured as "On Success only" if not already consumed. + */ + static async execute(config, successCost = false) { + if ( + config.uses?.enabled && + ((!successCost && (!config.uses?.consumeOnSuccess || config.roll?.success)) || + (successCost && config.uses?.consumeOnSuccess)) + ) + this.update({ 'uses.value': this.uses.value + 1 }); + } + + /** + * Update Action Workflow config object. + * Must be called within Action context. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @returns {boolean} Return false if fast-forwarded and no more uses. + */ + prepareConfig(config) { const uses = this.uses?.max ? foundry.utils.deepClone(this.uses) : null; if (uses && !uses.value) uses.value = 0; config.uses = uses; const hasUses = UsesField.hasUses.call(this, config.uses); - if (config.isFastForward && !hasUses) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.actionNoUsesRemaining')); - return hasUses; + if (config.dialog.configure === false && !hasUses) { + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.actionNoUsesRemaining')); + return hasUses; + } } + /** + * Prepare Uses object for Action Workflow + * Must be called within Action context. + * @param {object} uses + * @returns {object} + */ static calcUses(uses) { if (!uses) return null; return { @@ -38,6 +74,12 @@ export default class UsesField extends fields.SchemaField { }; } + /** + * Check if the Action still get atleast one unspent uses. + * Must be called within Action context. + * @param {*} uses + * @returns {boolean} + */ static hasUses(uses) { if (!uses) return true; let max = uses.max ?? 0; diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs index e1d63669..da71e899 100644 --- a/module/data/settings/Automation.mjs +++ b/module/data/settings/Automation.mjs @@ -80,6 +80,72 @@ export default class DhAutomation extends foundry.abstract.DataModel { initial: CONFIG.DH.GENERAL.defeatedConditions.defeated.id, label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.companionDefault.label' }) + }), + roll: new fields.SchemaField({ + roll: new fields.SchemaField({ + gm: new fields.BooleanField({ + required: true, + initial: false, + label: 'DAGGERHEART.GENERAL.gm' + }), + players: new fields.BooleanField({ + required: true, + initial: false, + label: 'DAGGERHEART.GENERAL.player.plurial' + }) + }), + damage: new fields.SchemaField({ + gm: new fields.StringField({ + required: true, + initial: "never", + choices: CONFIG.DH.SETTINGS.actionAutomationChoices, + label: 'DAGGERHEART.GENERAL.gm' + }), + players: new fields.StringField({ + required: true, + initial: "never", + choices: CONFIG.DH.SETTINGS.actionAutomationChoices, + label: 'DAGGERHEART.GENERAL.player.plurial' + }) + }), + save: new fields.SchemaField({ + gm: new fields.StringField({ + required: true, + initial: "never", + choices: CONFIG.DH.SETTINGS.actionAutomationChoices, + label: 'DAGGERHEART.GENERAL.gm' + }), + players: new fields.StringField({ + required: true, + initial: "never", + choices: CONFIG.DH.SETTINGS.actionAutomationChoices, + label: 'DAGGERHEART.GENERAL.player.plurial' + }) + }), + damageApply: new fields.SchemaField({ + gm: new fields.BooleanField({ + required: true, + initial: false, + label: 'DAGGERHEART.GENERAL.gm' + }), + players: new fields.BooleanField({ + required: true, + initial: false, + label: 'DAGGERHEART.GENERAL.player.plurial' + }) + }), + effect: new fields.SchemaField({ + gm: new fields.BooleanField({ + required: true, + initial: false, + label: 'DAGGERHEART.GENERAL.gm' + }), + players: new fields.BooleanField({ + required: true, + initial: false, + label: 'DAGGERHEART.GENERAL.player.plurial' + }) + }) }) }; } diff --git a/module/dice/d20Roll.mjs b/module/dice/d20Roll.mjs index 63d84744..f0660106 100644 --- a/module/dice/d20Roll.mjs +++ b/module/dice/d20Roll.mjs @@ -128,7 +128,9 @@ export default class D20Roll extends DHRoll { applyBaseBonus() { const modifiers = foundry.utils.deepClone(this.options.roll.baseModifiers) ?? []; - modifiers.push(...this.getBonus(`roll.${this.options.type}`, `${this.options.type?.capitalize()} Bonus`)); + modifiers.push( + ...this.getBonus(`roll.${this.options.actionType}`, `${this.options.actionType?.capitalize()} Bonus`) + ); modifiers.push( ...this.getBonus(`roll.${this.options.roll.type}`, `${this.options.roll.type?.capitalize()} Bonus`) ); @@ -138,7 +140,7 @@ export default class D20Roll extends DHRoll { static postEvaluate(roll, config = {}) { const data = super.postEvaluate(roll, config); - data.type = config.roll?.type; + data.type = config.actionType; data.difficulty = config.roll.difficulty; if (config.targets?.length) { config.targets.forEach(target => { @@ -147,6 +149,7 @@ export default class D20Roll extends DHRoll { }); data.success = config.targets.some(target => target.hit); } else if (config.roll.difficulty) data.success = roll.isCritical || roll.total >= config.roll.difficulty; + config.successConsumed = data.success; data.advantage = { type: config.roll.advantage, diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index aa9e1d94..4d293d9d 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -9,6 +9,7 @@ export default class DamageRoll extends DHRoll { static DefaultDialog = DamageDialog; static async buildEvaluate(roll, config = {}, message = {}) { + if (config.dialog.configure === false) roll.constructFormula(config); if (config.evaluate !== false) for (const roll of config.roll) await roll.roll.evaluate(); roll._evaluated = true; @@ -46,9 +47,8 @@ export default class DamageRoll extends DHRoll { ); } await super.buildPost(roll, config, message); - if (config.source?.message) { + if (config.source?.message) chatMessage.update({ 'system.damage': config.damage }); - } } static unifyDamageRoll(rolls) { diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index ac340c64..0dcdd316 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -28,6 +28,7 @@ export default class DHRoll extends Roll { static async buildConfigure(config = {}, message = {}) { config.hooks = [...this.getHooks(), '']; config.dialog ??= {}; + for (const hook of config.hooks) { if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null; } @@ -45,10 +46,7 @@ export default class DHRoll extends Roll { } for (const hook of config.hooks) { - if ( - Hooks.call(`${CONFIG.DH.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false - ) - return []; + if (Hooks.call(`${CONFIG.DH.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false) return []; } return roll; } @@ -88,11 +86,12 @@ export default class DHRoll extends Roll { type: this.messageType, user: game.user.id, title: roll.title, - speaker: cls.getSpeaker(), + speaker: cls.getSpeaker({ actor: roll.data?.parent }), sound: config.mute ? null : CONFIG.sounds.dice, system: config, rolls: [roll] }; + config.selectedRollMode ??= game.settings.get('core', 'rollMode'); if (roll._evaluated) { @@ -226,7 +225,7 @@ export const registerRollDiceHooks = () => { if ( !config.source?.actor || (game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) || - config.roll.type === 'reaction' + config.actionType === 'reaction' ) return; diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index ac1047ab..93ac231e 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -19,7 +19,7 @@ export default class DualityRoll extends D20Roll { get title() { return game.i18n.localize( - `DAGGERHEART.GENERAL.${this.options?.roll?.type === CONFIG.DH.ITEM.actionTypes.reaction.id ? 'reactionRoll' : 'dualityRoll'}` + `DAGGERHEART.GENERAL.${this.options?.actionType === CONFIG.DH.ITEM.actionTypes.reaction.id ? 'reactionRoll' : 'dualityRoll'}` ); } @@ -153,10 +153,10 @@ export default class DualityRoll extends D20Roll { applyBaseBonus() { const modifiers = super.applyBaseBonus(); - - if (this.options.roll.trait && this.data.traits[this.options.roll.trait]) + + if (this.options.roll.trait && this.data.traits?.[this.options.roll.trait]) modifiers.unshift({ - label: `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`, + label: this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id ? "DAGGERHEART.CONFIG.RollTypes.spellcast.name" : `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`, value: this.data.traits[this.options.roll.trait].value }); diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 08735a45..950ed621 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -14,7 +14,7 @@ export default class DhpActor extends Actor { get owner() { const user = this.hasPlayerOwner && game.users.players.find(u => this.testUserPermission(u, 'OWNER') && u.active); - if (!user) return game.user.isGM ? game.user : null; + if (!user) return game.users.activeGM; return user; } diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index c7f30e48..1a619a9c 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -1,3 +1,5 @@ +import { emitAsGM, GMUpdateEvent } from "../systemRegistration/socket.mjs"; + export default class DhpChatMessage extends foundry.documents.ChatMessage { targetHook = null; @@ -102,20 +104,30 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { }); if (itemDesc && autoExpandRoll.desc) itemDesc.setAttribute('open', ''); } - - if (!game.user.isGM) { - const applyButtons = html.querySelector('.apply-buttons'); + + if(!this.isAuthor && !this.speakerActor?.isOwner) { + const applyButtons = html.querySelector(".apply-buttons"); applyButtons?.remove(); - if (!this.isAuthor && !this.speakerActor?.isOwner) { - const buttons = html.querySelectorAll('.ability-card-footer > .ability-use-button'); - buttons.forEach(b => b.remove()); - } + const buttons = html.querySelectorAll(".ability-card-footer > .ability-use-button"); + buttons.forEach(b => b.remove()); } } addChatListeners(html) { + html.querySelectorAll('.duality-action-damage').forEach(element => + element.addEventListener('click', this.onRollDamage.bind(this)) + ); + html.querySelectorAll('.damage-button').forEach(element => - element.addEventListener('click', this.onDamage.bind(this)) + element.addEventListener('click', this.onApplyDamage.bind(this)) + ); + + html.querySelectorAll('.target-save').forEach(element => + element.addEventListener('click', this.onRollSave.bind(this)) + ); + + html.querySelectorAll('.roll-all-save-button').forEach(element => + element.addEventListener('click', this.onRollAllSave.bind(this)) ); html.querySelectorAll('.duality-action-effect').forEach(element => @@ -133,17 +145,21 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { }); } - getTargetList() { - const targets = this.system.hitTargets ?? []; - return targets.map(target => game.canvas.tokens.documentCollection.find(t => t.actor?.uuid === target.actorId)); + async onRollDamage(event) { + event.stopPropagation(); + const config = foundry.utils.deepClone(this.system); + config.event = event; + this.system.action?.workflow.get("damage")?.execute(config, this._id, true); } - async onDamage(event) { + async onApplyDamage(event) { event.stopPropagation(); - const targets = this.getTargetList(); + const targets = this.filterPermTargets(this.system.hitTargets), + config = foundry.utils.deepClone(this.system); + config.event = event; if (this.system.onSave) { - const pendingingSaves = this.system.hitTargets.filter(t => t.saved.success === null); + const pendingingSaves = targets.filter(t => t.saved.success === null); if (pendingingSaves.length) { const confirm = await foundry.applications.api.DialogV2.confirm({ window: { title: 'Pending Reaction Rolls found' }, @@ -154,62 +170,62 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { } if (targets.length === 0) - return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected')); + return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm')); + + this.consumeOnSuccess(); + this.system.action?.workflow.get("applyDamage")?.execute(config, targets, true); + } - for (let target of targets) { - let damages = foundry.utils.deepClone(this.system.damage); - if ( - !this.system.hasHealing && - this.system.onSave && - this.system.hitTargets.find(t => t.id === target.id)?.saved?.success === true - ) { - const mod = CONFIG.DH.ACTIONS.damageOnSave[this.system.onSave]?.mod ?? 1; - Object.entries(damages).forEach(([k, v]) => { - v.total = 0; - v.parts.forEach(part => { - part.total = Math.ceil(part.total * mod); - v.total += part.total; - }); - }); - } - - this.consumeOnSuccess(); - if (this.system.hasHealing) target.actor.takeHealing(damages); - else target.actor.takeDamage(damages, this.system.isDirect); + async onRollSave(event) { + event.stopPropagation(); + const tokenId = event.target.closest('[data-token]')?.dataset.token, + token = game.canvas.tokens.get(tokenId); + if (!token?.actor || !token.isOwner) return true; + if (this.system.source.item && this.system.source.action) { + const action = this.system.action; + if (!action || !action?.hasSave) return; + game.system.api.fields.ActionFields.SaveField.rollSave.call(action, token.actor, event).then(result => + emitAsGM( + GMUpdateEvent.UpdateSaveMessage, + game.system.api.fields.ActionFields.SaveField.updateSaveMessage.bind(action, result, this, token.id), + { + action: action.uuid, + message: this._id, + token: token.id, + result + } + ) + ); } } - getAction(actor, itemId, actionId) { - const item = actor.items.get(itemId), - action = - actor.system.attack?._id === actionId - ? actor.system.attack - : item.system.attack?._id === actionId - ? item.system.attack - : item?.system?.actions?.get(actionId); - return action; + async onRollAllSave(event) { + event.stopPropagation(); + if (!game.user.isGM) return; + const targets = this.system.hitTargets, + config = foundry.utils.deepClone(this.system); + config.event = event; + this.system.action?.workflow.get("save")?.execute(config, targets, true); } async onApplyEffect(event) { event.stopPropagation(); - const actor = await foundry.utils.fromUuid(this.system.source.actor); - if (!actor || !game.user.isGM) return true; - if (this.system.source.item && this.system.source.action) { - const action = this.getAction(actor, this.system.source.item, this.system.source.action); - if (!action || !action?.applyEffects) return; - const targets = this.getTargetList(); - if (targets.length === 0) - ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected')); - this.consumeOnSuccess(); - await action.applyEffects(event, this, targets); - } + const targets = this.filterPermTargets(this.system.hitTargets), + config = foundry.utils.deepClone(this.system); + config.event = event; + if (targets.length === 0) + ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm')); + this.consumeOnSuccess(); + this.system.action?.workflow.get("effects")?.execute(config, targets, true); + } + + filterPermTargets(targets) { + return targets.filter(t => fromUuidSync(t.actorId)?.canUserModify(game.user, "update")) } consumeOnSuccess() { - if (!this.system.successConsumed && !this.targetSelection) { - const action = this.system.action; - if (action) action.consume(this.system, true); - } + if (!this.system.successConsumed && !this.targetSelection) + this.system.action?.consume(this.system, true); } hoverTarget(event) { diff --git a/module/systemRegistration/socket.mjs b/module/systemRegistration/socket.mjs index b47fee85..f3f9629b 100644 --- a/module/systemRegistration/socket.mjs +++ b/module/systemRegistration/socket.mjs @@ -22,6 +22,7 @@ export const socketEvent = { export const GMUpdateEvent = { UpdateDocument: 'DhGMUpdateDocument', + UpdateEffect: 'DhGMUpdateEffect', UpdateSetting: 'DhGMUpdateSetting', UpdateFear: 'DhGMUpdateFear', UpdateSaveMessage: 'DhGMUpdateSaveMessage' @@ -37,9 +38,12 @@ export const registerSocketHooks = () => { const document = data.uuid ? await fromUuid(data.uuid) : null; switch (data.action) { case GMUpdateEvent.UpdateDocument: - if (document && data.update) { + if (document && data.update) await document.update(data.update); - } + break; + case GMUpdateEvent.UpdateEffect: + if (document && data.update) + await game.system.api.fields.ActionFields.EffectsField.applyEffects.call(document, data.update); break; case GMUpdateEvent.UpdateSetting: await game.settings.set(CONFIG.DH.id, data.uuid, data.update); @@ -78,7 +82,7 @@ export const registerSocketHooks = () => { export const registerUserQueries = () => { CONFIG.queries.armorSlot = DamageReductionDialog.armorSlotQuery; - CONFIG.queries.reactionRoll = game.system.api.models.actions.actionsTypes.base.rollSaveQuery; + CONFIG.queries.reactionRoll = game.system.api.fields.ActionFields.SaveField.rollSaveQuery; }; export const emitAsGM = async (eventName, callback, update, uuid = null) => { diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index 399715ca..e4a2128c 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -624,6 +624,7 @@ .form-group { justify-content: end; + flex-wrap: nowrap; } .hint { diff --git a/styles/less/global/global.less b/styles/less/global/global.less index cf8431b5..e135846f 100644 --- a/styles/less/global/global.less +++ b/styles/less/global/global.less @@ -18,7 +18,7 @@ color: var(--color-form-hint); } - &:hover { + .form-group:hover { .hint { color: var(--color-form-hint-hover); } diff --git a/styles/less/ui/settings/settings.less b/styles/less/ui/settings/settings.less index 2ac8bfb0..67fe7718 100644 --- a/styles/less/ui/settings/settings.less +++ b/styles/less/ui/settings/settings.less @@ -127,4 +127,4 @@ text-align: center; } } -} +} \ No newline at end of file diff --git a/templates/actionTypes/beastform.hbs b/templates/actionTypes/beastform.hbs index b2710208..b9bea445 100644 --- a/templates/actionTypes/beastform.hbs +++ b/templates/actionTypes/beastform.hbs @@ -1,9 +1,4 @@ -
    - -
    {{localize "DAGGERHEART.ACTIONS.Config.beastform.label"}}
    -
    - -
    - {{formGroup fields.tierAccess.fields.exact value=beastform.tierAccess.exact labelAttr="label" valueAttr="key" localize=true blank=""}} -
    +
    + {{localize "DAGGERHEART.ACTIONS.Config.beastform.label"}} + {{formGroup fields.tierAccess.fields.exact value=source.tierAccess.exact name="beastform.tierAccess.exact" labelAttr="label" valueAttr="key" localize=true blank=""}}
    \ No newline at end of file diff --git a/templates/actionTypes/damage.hbs b/templates/actionTypes/damage.hbs index 6a94752b..96bb361c 100644 --- a/templates/actionTypes/damage.hbs +++ b/templates/actionTypes/damage.hbs @@ -56,7 +56,7 @@
    {{formField ../fields.valueAlt.fields.flatMultiplier value=dmg.valueAlt.flatMultiplier name=(concat ../path "damage.parts." realIndex ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }} - {{formField ../fields.valueAlt.fields.dice value=dmg.valueAlt.dice name=(concat ../path "damage.parts." realIndex ".valueAlt.dice") classes="inline-child"}} + {{formField ../fields.valueAlt.fields.dice value=dmg.valueAlt.dice name=(concat ../path "damage.parts." realIndex ".valueAlt.dice") classes="inline-child" localize=true}} {{formField ../fields.valueAlt.fields.bonus value=dmg.valueAlt.bonus name=(concat ../path "damage.parts." realIndex ".valueAlt.bonus") localize=true classes="inline-child"}}
    @@ -70,7 +70,7 @@ {{#*inline "formula"}} {{#unless dmg.base}} - {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat path "damage.parts." realIndex "." target ".custom.enabled") classes="checkbox"}} + {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat path "damage.parts." realIndex "." target ".custom.enabled") classes="checkbox" localize=true}} {{/unless}} {{#if source.custom.enabled}} {{formField fields.custom.fields.formula value=source.custom.formula name=(concat path "damage.parts." realIndex "." target ".custom.formula") localize=true}} @@ -79,8 +79,8 @@ {{#unless @root.isNPC}} {{formField fields.multiplier value=source.multiplier name=(concat path "damage.parts." realIndex "." target ".multiplier") localize=true}} {{/unless}} - {{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat path "damage.parts." realIndex "." target ".flatMultiplier") }}{{/if}} - {{formField fields.dice value=source.dice name=(concat path "damage.parts." realIndex "." target ".dice")}} + {{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat path "damage.parts." realIndex "." target ".flatMultiplier") localize=true }}{{/if}} + {{formField fields.dice value=source.dice name=(concat path "damage.parts." realIndex "." target ".dice") localize=true}} {{formField fields.bonus value=source.bonus name=(concat path "damage.parts." realIndex "." target ".bonus") localize=true}}
    {{/if}} diff --git a/templates/actionTypes/save.hbs b/templates/actionTypes/save.hbs index 90bc0483..85536c87 100644 --- a/templates/actionTypes/save.hbs +++ b/templates/actionTypes/save.hbs @@ -1,6 +1,9 @@ -
    - {{localize "DAGGERHEART.GENERAL.save"}} - {{formField fields.trait label="Trait" name="save.trait" value=source.trait localize=true}} - {{formField fields.difficulty label="Difficulty" name="save.difficulty" value=source.difficulty disabled=(not source.trait) placeholder=@root.baseSaveDifficulty}} - {{formField fields.damageMod label="Damage on Save" name="save.damageMod" value=source.damageMod localize=true disabled=(not source.trait)}} +
    + {{localize "DAGGERHEART.GENERAL.Roll.reaction"}} +

    {{localize "DAGGERHEART.ACTIONS.Settings.saveHint"}}

    +
    + {{formField fields.trait label="Trait" name="save.trait" value=source.trait localize=true}} + {{formField fields.difficulty label="Difficulty" name="save.difficulty" value=source.difficulty disabled=(not source.trait) placeholder=@root.baseSaveDifficulty}} + {{formField fields.damageMod label="Damage on Save" name="save.damageMod" value=source.damageMod localize=true disabled=(not source.trait)}} +
    \ No newline at end of file diff --git a/templates/dialogs/dice-roll/rollSelection.hbs b/templates/dialogs/dice-roll/rollSelection.hbs index b2feaa0b..6a4655d6 100644 --- a/templates/dialogs/dice-roll/rollSelection.hbs +++ b/templates/dialogs/dice-roll/rollSelection.hbs @@ -75,16 +75,8 @@ {{#each experiences}} {{#if name}}
    - {{#if (includes ../selectedExperiences id)}} - - {{else}} - - {{/if}} - {{#if (eq @root.rollType 'D20Roll')}} - {{name}} +{{modifier}} - {{else}} - {{name}} +{{value}} - {{/if}} + + {{name}} +{{value}}
    {{/if}} {{/each}} @@ -126,6 +118,12 @@ {{selectOptions diceOptions selected=@root.roll.dAdvantage.denomination}}
    + {{#if abilities}} + {{localize "DAGGERHEART.GENERAL.traitModifier"}} + + {{/if}} {{/unless}} {{#if @root.rallyDie.length}} {{localize "DAGGERHEART.CLASS.Feature.rallyDice"}} diff --git a/templates/settings/automation-settings/roll.hbs b/templates/settings/automation-settings/roll.hbs new file mode 100644 index 00000000..5769bf61 --- /dev/null +++ b/templates/settings/automation-settings/roll.hbs @@ -0,0 +1,22 @@ +
    +
    + + {{localize "DAGGERHEART.SETTINGS.Automation.roll.title"}} + + {{#each settingFields.schema.fields.roll.fields as | field |}} + {{!-- {{formGroup field value=(lookup @root.settingFields.roll field.name) localize=true rootId="automation-roll"}} --}} +
    + + {{#with (lookup @root.settingFields.roll field.name) as | values |}} + {{formGroup field.fields.gm value=values.gm rootId=(concat "automation-roll-" field.name "-gm") localize=true}} + {{formGroup field.fields.players value=values.players rootId=(concat "automation-roll-" field.name "-players") localize=true}} + {{/with}} +

    {{localize (concat "DAGGERHEART.SETTINGS.Automation.FIELDS.roll." field.name ".hint")}}

    +
    + {{/each}} +
    +
    \ No newline at end of file diff --git a/templates/sheets-settings/action-settings/effect.hbs b/templates/sheets-settings/action-settings/effect.hbs index 26a097af..51c15aae 100644 --- a/templates/sheets-settings/action-settings/effect.hbs +++ b/templates/sheets-settings/action-settings/effect.hbs @@ -6,7 +6,6 @@ {{#if fields.roll}}{{> 'systems/daggerheart/templates/actionTypes/roll.hbs' fields=fields.roll.fields source=source.roll}}{{/if}} {{#if fields.save}}{{> 'systems/daggerheart/templates/actionTypes/save.hbs' fields=fields.save.fields source=source.save}}{{/if}} {{#if fields.damage}}{{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=fields.damage.fields.parts.element.fields source=source.damage directField=fields.damage.fields.direct }}{{/if}} - {{#if fields.resource}}{{> 'systems/daggerheart/templates/actionTypes/resource.hbs' fields=fields.resource.fields source=source.resource}}{{/if}} {{#if fields.macro}}{{> 'systems/daggerheart/templates/actionTypes/macro.hbs' fields=fields.macro source=source.macro}}{{/if}} {{#if fields.effects}}{{> 'systems/daggerheart/templates/actionTypes/effect.hbs' fields=fields.effects.element.fields source=source.effects}}{{/if}} {{#if fields.beastform}}{{> 'systems/daggerheart/templates/actionTypes/beastform.hbs' fields=fields.beastform.fields source=source.beastform}}{{/if}} diff --git a/templates/ui/chat/parts/button-part.hbs b/templates/ui/chat/parts/button-part.hbs index f83972f7..21317939 100644 --- a/templates/ui/chat/parts/button-part.hbs +++ b/templates/ui/chat/parts/button-part.hbs @@ -1,17 +1,17 @@
    {{#if hasDamage}} {{#unless (empty damage)}} - {{#if canButtonApply}}{{/if}} + {{else}} {{/unless}} {{/if}} {{#if hasHealing}} {{#unless (empty damage)}} - {{#if canButtonApply}}{{/if}} + {{else}} {{/unless}} {{/if}} - {{#if (and hasEffect canButtonApply)}}{{/if}} + {{#if (and hasEffect)}}{{/if}}
    \ No newline at end of file diff --git a/templates/ui/chat/parts/damage-part.hbs b/templates/ui/chat/parts/damage-part.hbs index 05b6b825..97828bd2 100644 --- a/templates/ui/chat/parts/damage-part.hbs +++ b/templates/ui/chat/parts/damage-part.hbs @@ -13,10 +13,10 @@ {{#each damage as | roll index | }}
    - {{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.inChatRoll')}}
    {{localize "DAGGERHEART.GENERAL.total"}}: {{roll.total}}
    {{#if (and (eq index "hitPoints")../isDirect)}}
    {{localize "DAGGERHEART.CONFIG.DamageType.direct.short"}}
    {{/if}} + {{#if ../hasHealing}}{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.name')}}{{else}}{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.inChatRoll')}}{{/if}}
    {{localize "DAGGERHEART.GENERAL.total"}}: {{roll.total}}
    {{#if (and (eq index "hitPoints") ../isDirect)}}
    {{localize "DAGGERHEART.CONFIG.DamageType.direct.short"}}
    {{/if}}
    {{#each roll.parts}} - {{#if damageTypes.length}} + {{#if (and (not @root.hasHealing) damageTypes.length)}}
    {{/if}}
    - {{#if (and ../hasSave (or hit (not @root.hasRoll)))}} + {{#if (and ../hasSave (or hit (not @root.hasRoll) (not @root.targetMode)))}}
    From 18687b61313531d6001c7d15c33ad5c90c220c23 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Wed, 27 Aug 2025 17:01:15 +1000 Subject: [PATCH 43/66] Fix Avalanche typo (#1098) Co-authored-by: Chris Ryan --- .../adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json | 8 ++++---- .../adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json | 8 ++++---- .../environment_Mountain_Pass_acMu9wJrMZZzLSTJ.json | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/packs/adversaries/adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json b/src/packs/adversaries/adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json index 760e1c74..0d5bd5eb 100644 --- a/src/packs/adversaries/adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json +++ b/src/packs/adversaries/adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json @@ -427,7 +427,7 @@ "_key": "!actors.items!3aAS2Qm3R6cgaYfE.tQgxiSS48TJ3X1Dl" }, { - "name": "Avalance Roar", + "name": "Avalanche Roar", "type": "feature", "system": { "description": "

    Spend a Fear to roar while within a cave and cause a cave-in. All targets within Close range must succeed on an Agility Reaction Roll (14) or take 2d10 physical damage. The rubble can be cleared with a Progress Countdown (8).

    @Template[type:emanation|range:c]

    ", @@ -532,12 +532,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754085059319, - "modifiedTime": 1754143365810, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1756256613353, + "lastModifiedBy": "CEZZA7TXd7uT8O2c" }, "_key": "!actors.items!3aAS2Qm3R6cgaYfE.9Z0i0uURfBMVIapJ" }, diff --git a/src/packs/adversaries/adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json b/src/packs/adversaries/adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json index 48b1cd14..251a1ab7 100644 --- a/src/packs/adversaries/adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json +++ b/src/packs/adversaries/adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json @@ -548,7 +548,7 @@ "_key": "!actors.items!UGPiPLJsPvMTSKEF.QV2ytK4b1VWF71OS" }, { - "name": "Avalance", + "name": "Avalanche", "type": "feature", "system": { "description": "

    Spend a Fear to have the Dragon unleash a huge downfall of snow and ice, covering all other creatures within Far range. All targets within this area must succeed on an Instinct Reaction Roll or be buried in snow and rocks, becoming Vulnerable until they dig themselves out from the debris. For each PC that fails the reaction roll, you gain a Fear.

    @Template[type:emanation|range:f]

    ", @@ -678,12 +678,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754131703390, - "modifiedTime": 1754131790034, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1756256581072, + "lastModifiedBy": "CEZZA7TXd7uT8O2c" }, "_key": "!actors.items!UGPiPLJsPvMTSKEF.CcRTxCDCJskiu3fI" }, diff --git a/src/packs/environments/environment_Mountain_Pass_acMu9wJrMZZzLSTJ.json b/src/packs/environments/environment_Mountain_Pass_acMu9wJrMZZzLSTJ.json index e2a31c41..8b962e9f 100644 --- a/src/packs/environments/environment_Mountain_Pass_acMu9wJrMZZzLSTJ.json +++ b/src/packs/environments/environment_Mountain_Pass_acMu9wJrMZZzLSTJ.json @@ -177,7 +177,7 @@ "_key": "!actors.items!acMu9wJrMZZzLSTJ.cIAMenvMXHPTpOFn" }, { - "name": "Avalance", + "name": "Avalanche", "type": "feature", "system": { "description": "

    Spend a Fear to carve the mountain with an icy torrent, causing an avalanche. All PCs in its path must succeed on an Agility or Strength Reaction Roll or be bowled over and carried down the mountain. A PC using rope, pitons, or other climbing gear gains advantage on this roll. Targets who fail are knocked down the mountain to Far range, take 2d20 physical damage, and must mark a Stress. Targets who succeed must mark a Stress.

    How do the PCs try to weather the avalanche? What approach do the characters take to fi nd one another when their companions go hurtling down the mountainside?

    ", @@ -252,12 +252,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754217019442, - "modifiedTime": 1754217102897, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1756256534443, + "lastModifiedBy": "CEZZA7TXd7uT8O2c" }, "_key": "!actors.items!acMu9wJrMZZzLSTJ.jkm03DXYYajsRk2j" }, From 9f2c2f1bedaa8bc957d37ae8262405f9eefd2fc2 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Wed, 27 Aug 2025 15:22:29 +0200 Subject: [PATCH 44/66] Changed so the default domaincard view is list (#1102) --- module/applications/sheets/actors/character.mjs | 6 +++--- module/config/flagsConfig.mjs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index c860e9e9..a140a7c9 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -210,7 +210,7 @@ export default class CharacterSheet extends DHBaseActorSheet { * @protected */ async _prepareLoadoutContext(context, _options) { - context.cardView = !game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.displayDomainCardsAsList); + context.cardView = game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.displayDomainCardsAsCard); } /** @@ -718,8 +718,8 @@ export default class CharacterSheet extends DHBaseActorSheet { * @type {ApplicationClickAction} */ static async #toggleLoadoutView(_, button) { - const newAbilityView = button.dataset.value !== 'true'; - await game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.displayDomainCardsAsList, newAbilityView); + const newAbilityView = button.dataset.value === 'true'; + await game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.displayDomainCardsAsCard, newAbilityView); this.render(); } diff --git a/module/config/flagsConfig.mjs b/module/config/flagsConfig.mjs index 91712288..c2a6dff2 100644 --- a/module/config/flagsConfig.mjs +++ b/module/config/flagsConfig.mjs @@ -1,4 +1,4 @@ -export const displayDomainCardsAsList = 'displayDomainCardsAsList'; +export const displayDomainCardsAsCard = 'displayDomainCardsAsCard'; export const narrativeCountdown = { simple: 'countdown-narrative-simple', position: 'countdown-narrative-position' From 9dd773001daf96db95a7f28a252fa671ec3ae199 Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Thu, 28 Aug 2025 03:29:40 +0200 Subject: [PATCH 45/66] Compendium browser per type (#1103) * Compendium Browser per type * Sort number column * Re-add subclass config * Sidebar buttons * Add Characters folder * Css * Done --- daggerheart.mjs | 6 + lang/en.json | 7 +- .../characterCreation/characterCreation.mjs | 10 +- module/applications/levelup/levelup.mjs | 11 +- .../applications/sheets/actors/character.mjs | 14 +- .../sheets/api/application-mixin.mjs | 28 +- module/applications/ui/_module.mjs | 1 + module/applications/ui/itemBrowser.mjs | 206 +++++++--- module/config/itemBrowserConfig.mjs | 353 ++++++++++++------ module/systemRegistration/handlebars.mjs | 3 +- styles/less/global/global.less | 25 ++ styles/less/ui/item-browser/item-browser.less | 19 +- .../sheets/actors/character/inventory.hbs | 3 - templates/ui/itemBrowser/filterContainer.hbs | 22 ++ templates/ui/itemBrowser/itemBrowser.hbs | 70 +--- templates/ui/itemBrowser/itemContainer.hbs | 16 + templates/ui/itemBrowser/sidebar.hbs | 48 +-- 17 files changed, 542 insertions(+), 300 deletions(-) create mode 100644 templates/ui/itemBrowser/filterContainer.hbs create mode 100644 templates/ui/itemBrowser/itemContainer.hbs diff --git a/daggerheart.mjs b/daggerheart.mjs index c9a6b54e..1c4c2a85 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -162,6 +162,9 @@ Hooks.on('ready', async () => { if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).displayFear !== 'hide') ui.resources.render({ force: true }); + if(!(ui.compendiumBrowser instanceof applications.ui.ItemBrowser)) + ui.compendiumBrowser = new applications.ui.ItemBrowser(); + registerCountdownHooks(); socketRegistration.registerSocketHooks(); registerRollDiceHooks(); @@ -305,3 +308,6 @@ Hooks.on('moveToken', async (movedToken, data) => { await effect.value.update({ disabled: effect.disabled }); } }); + +Hooks.on("renderCompendiumDirectory", (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html)); +Hooks.on("renderDocumentDirectory", (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html)); diff --git a/lang/en.json b/lang/en.json index d3d707d6..01221e9a 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2423,6 +2423,7 @@ "evasionMax": "Evasion (Max)", "subtype": "Subtype", "folders": { + "characters": "Characters", "adversaries": "Adversaries", "ancestries": "Ancestries", "equipment": "Equipment", @@ -2433,7 +2434,11 @@ "environments": "Environments", "beastforms": "Beastforms", "features": "Features", - "items": "Items" + "items": "Items", + "weapons": "Weapons", + "armors": "Armors", + "consumables": "Consumables", + "loots": "Loots" } }, "Notifications": { diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index ba98ef61..490294cd 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -1,6 +1,5 @@ import { abilities } from '../../config/actorConfig.mjs'; import { burden } from '../../config/generalConfig.mjs'; -import { ItemBrowser } from '../ui/itemBrowser.mjs'; import { createEmbeddedItemsWithEffects, createEmbeddedItemWithEffects } from '../../helpers/utils.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -46,8 +45,6 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl }; this._dragDrop = this._createDragDropHandlers(); - - this.itemBrowser = null; } get title() { @@ -425,8 +422,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl equipment = ['armor', 'weapon']; const presets = { - compendium: 'daggerheart', - folder: equipment.includes(type) ? 'equipments' : type, + folder: equipment.includes(type) ? `equipments.folders.${type}s` : type, render: { noFolder: true } @@ -449,7 +445,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl 'type': { key: 'type', value: type } }; - return (this.itemBrowser = await new ItemBrowser({ presets }).render({ force: true })); + ui.compendiumBrowser.open(presets); } static async viewItem(_, target) { @@ -567,7 +563,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl { overwrite: true } ); - if (this.itemBrowser) this.itemBrowser.close(); + if (ui.compendiumBrowser) ui.compendiumBrowser.close(); this.close(); } diff --git a/module/applications/levelup/levelup.mjs b/module/applications/levelup/levelup.mjs index 0b3f8970..99cc53f6 100644 --- a/module/applications/levelup/levelup.mjs +++ b/module/applications/levelup/levelup.mjs @@ -1,6 +1,5 @@ import { abilities, subclassFeatureLabels } from '../../config/actorConfig.mjs'; import { getDeleteKeys, tagifyElement } from '../../helpers/utils.mjs'; -import { ItemBrowser } from '../ui/itemBrowser.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -12,8 +11,6 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) this._dragDrop = this._createDragDropHandlers(); this.tabGroups.primary = 'advancements'; - - this.itemBrowser = null; } get title() { @@ -539,8 +536,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) static async viewCompendium(event, target) { const type = target.dataset.compendium ?? target.dataset.type; - const presets = { - compendium: 'daggerheart', + const presets = { folder: type, render: { noFolder: true @@ -559,7 +555,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) }; } - return (this.itemBrowser = await new ItemBrowser({ presets }).render({ force: true })); + ui.compendiumBrowser.open(presets); } static async selectPreview(_, button) { @@ -662,7 +658,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) }, {}); await this.actor.levelUp(levelupData); - if (this.itemBrowser) this.itemBrowser.close(); + + if (ui.compendiumBrowser) ui.compendiumBrowser.close(); this.close(); } } diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 308faee7..500141c1 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -5,7 +5,6 @@ import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs'; import DhCharacterCreation from '../../characterCreation/characterCreation.mjs'; import FilterMenu from '../../ux/filter-menu.mjs'; import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs'; -import { ItemBrowser } from '../../ui/itemBrowser.mjs'; /**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ @@ -29,8 +28,7 @@ export default class CharacterSheet extends DHBaseActorSheet { toggleEquipItem: CharacterSheet.#toggleEquipItem, toggleResourceDice: CharacterSheet.#toggleResourceDice, handleResourceDice: CharacterSheet.#handleResourceDice, - useDowntime: this.useDowntime, - tempBrowser: CharacterSheet.#tempBrowser + useDowntime: this.useDowntime }, window: { resizable: true, @@ -635,7 +633,6 @@ export default class CharacterSheet extends DHBaseActorSheet { const { key } = button.dataset; const presets = { - compendium: 'daggerheart', folder: key, filter: key === 'subclasses' @@ -651,7 +648,7 @@ export default class CharacterSheet extends DHBaseActorSheet { } }; - return new ItemBrowser({ presets }).render({ force: true }); + ui.compendiumBrowser.open(presets); } /** @@ -768,13 +765,6 @@ export default class CharacterSheet extends DHBaseActorSheet { }); } - /** - * Temp - */ - static async #tempBrowser(_, target) { - new ItemBrowser().render({ force: true }); - } - /** * Handle the roll values of resource dice. * @type {ApplicationClickAction} diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 7f338ac1..2158e48b 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -1,6 +1,5 @@ const { HandlebarsApplicationMixin } = foundry.applications.api; import { getDocFromElement, getDocFromElementSync, tagifyElement } from '../../../helpers/utils.mjs'; -import { ItemBrowser } from '../../ui/itemBrowser.mjs'; const typeSettingsMap = { character: 'extendCharacterDescriptions', @@ -589,28 +588,27 @@ export default function DHApplicationMixin(Base) { static async #browseItem(event, target) { const type = target.dataset.compendium ?? target.dataset.type; - const presets = {}; + const presets = { + render: { + noFolder: true + } + }; switch (type) { case 'loot': + presets.folder = 'equipments.folders.loots'; + break; case 'consumable': + presets.folder = 'equipments.folders.consumables'; + break; case 'armor': + presets.folder = 'equipments.folders.armors'; + break; case 'weapon': - presets.compendium = 'daggerheart'; - presets.folder = 'equipments'; - presets.render = { - noFolder: true - }; - presets.filter = { - type: { key: 'type', value: type, forced: true } - }; + presets.folder = 'equipments.folders.weapons'; break; case 'domainCard': - presets.compendium = 'daggerheart'; presets.folder = 'domains'; - presets.render = { - noFolder: true - }; presets.filter = { 'level.max': { key: 'level.max', value: this.document.system.levelData.level.current }, 'system.domain': { key: 'system.domain', value: this.document.system.domains } @@ -620,7 +618,7 @@ export default function DHApplicationMixin(Base) { return; } - return new ItemBrowser({ presets }).render({ force: true }); + ui.compendiumBrowser.open(presets); } /** diff --git a/module/applications/ui/_module.mjs b/module/applications/ui/_module.mjs index 6a17a61e..815fc4e7 100644 --- a/module/applications/ui/_module.mjs +++ b/module/applications/ui/_module.mjs @@ -3,3 +3,4 @@ export { default as DhCombatTracker } from './combatTracker.mjs'; export * as DhCountdowns from './countdowns.mjs'; export { default as DhFearTracker } from './fearTracker.mjs'; export { default as DhHotbar } from './hotbar.mjs'; +export { ItemBrowser } from './itemBrowser.mjs'; diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index 8eefd9cd..40884817 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -15,16 +15,13 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { this.fieldFilter = []; this.selectedMenu = { path: [], data: null }; this.config = CONFIG.DH.ITEMBROWSER.compendiumConfig; - this.presets = options.presets; - - if (this.presets?.compendium && this.presets?.folder) - ItemBrowser.selectFolder.call(this, null, null, this.presets.compendium, this.presets.folder); + this.presets = {}; } /** @inheritDoc */ static DEFAULT_OPTIONS = { id: 'itemBrowser', - classes: ['daggerheart', 'dh-style', 'dialog', 'compendium-browser'], + classes: ['daggerheart', 'dh-style', 'dialog', 'compendium-browser', 'loader'], tag: 'div', window: { frame: true, @@ -84,17 +81,15 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { } }; - /** @inheritDoc */ - async _preFirstRender(context, options) { - if (context.presets?.render?.noFolder || context.presets?.render?.lite) options.position.width = 600; - - await super._preFirstRender(context, options); - } - /** @inheritDoc */ async _preRender(context, options) { - if (context.presets?.render?.noFolder || context.presets?.render?.lite) - options.parts.splice(options.parts.indexOf('sidebar'), 1); + this.presets = options.presets ?? {}; + + const width = this.presets?.render?.noFolder === true || this.presets?.render?.lite === true ? 600 : 850; + if(this.rendered) + this.setPosition({ width }); + else + options.position.width = width; await super._preRender(context, options); } @@ -103,22 +98,18 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { async _onRender(context, options) { await super._onRender(context, options); + this.element + .querySelectorAll('[data-action="selectFolder"]') + .forEach(element => element.classList.toggle('is-selected', element.dataset.folderId === this.selectedMenu.path.join('.'))); + this._createSearchFilter(); - this._createFilterInputs(); - this._createDragProcess(); - - if (context.presets?.render?.lite) this.element.classList.add('lite'); - - if (context.presets?.render?.noFolder) this.element.classList.add('no-folder'); - - if (context.presets?.render?.noFilter) this.element.classList.add('no-filter'); - - if (this.presets?.filter) { - Object.entries(this.presets.filter).forEach( - ([k, v]) => (this.fieldFilter.find(c => c.name === k).value = v.value) - ); - await this._onInputFilterBrowser(); - } + + this.element.classList.toggle('lite', this.presets?.render?.lite === true); + this.element.classList.toggle('no-folder', this.presets?.render?.noFolder === true); + this.element.classList.toggle('no-filter', this.presets?.render?.noFilter === true); + this.element.querySelectorAll('.folder-list > [data-action="selectFolder"]').forEach(element => { + element.hidden = this.presets.render?.folders?.length && !this.presets.render.folders.includes(element.dataset.folderId); + }); } _attachPartListeners(partId, htmlElement, options) { @@ -139,19 +130,23 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { async _prepareContext(options) { const context = await super._prepareContext(options); context.compendiums = this.getCompendiumFolders(foundry.utils.deepClone(this.config)); - // context.pathTitle = this.pathTile; context.menu = this.selectedMenu; context.formatLabel = this.formatLabel; context.formatChoices = this.formatChoices; - context.fieldFilter = this.fieldFilter = this._createFieldFilter(); context.items = this.items; context.presets = this.presets; return context; } + open(presets = {}) { + this.presets = presets; + ItemBrowser.selectFolder.call(this); + } + getCompendiumFolders(config, parent = null, depth = 0) { let folders = []; Object.values(config).forEach(c => { + // if(this.presets.render?.folders?.length && !this.presets.render.folders.includes(c.id)) return; const folder = { id: c.id, label: game.i18n.localize(c.label), @@ -162,16 +157,14 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { : []; folders.push(folder); }); + folders.sort((a, b) => a.label.localeCompare(b.label)) return folders; } - static async selectFolder(_, target, compend, folder) { - const config = foundry.utils.deepClone(this.config), - compendium = compend ?? target.closest('[data-compendium-id]').dataset.compendiumId, - folderId = folder ?? target.dataset.folderId, - folderPath = `${compendium}.folders.${folderId}`, - folderData = foundry.utils.getProperty(config, folderPath); + static async selectFolder(_, target) { + const folderId = target?.dataset?.folderId ?? this.presets.folder, + folderData = foundry.utils.getProperty(this.config, folderId) ?? {}; const columns = ItemBrowser.getFolderConfig(folderData).map(col => ({ ...col, @@ -179,31 +172,17 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { })); this.selectedMenu = { - path: folderPath.split('.'), + path: folderId?.split('.') ?? [], data: { ...folderData, columns: columns } }; - let items = []; - for (const key of folderData.keys) { - const comp = game.packs.get(`${compendium}.${key}`); - if (!comp) return; - items = items.concat(await comp.getDocuments({ type__in: folderData.type })); - } + await this.render({ force: true, presets: this.presets }); - this.items = ItemBrowser.sortBy(items, 'name'); - - if (target) { - target - .closest('.compendium-sidebar') - .querySelectorAll('[data-action="selectFolder"]') - .forEach(element => element.classList.remove('is-selected')); - target.classList.add('is-selected'); - } - - this.render({ force: true }); + if(this.selectedMenu?.data?.type?.length) + this.loadItems(); } _replaceHTML(result, content, options) { @@ -211,6 +190,75 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { super._replaceHTML(result, content, options); } + loadItems() { + let loadTimeout = this.toggleLoader(true); + + const promises = []; + + game.packs.forEach(pack => { + promises.push( + new Promise(async resolve => { + const items = await pack.getDocuments({ type__in: this.selectedMenu?.data?.type }); + resolve(items); + }) + ) + }); + + Promise.all(promises).then(async result => { + this.items = ItemBrowser.sortBy(result.flatMap(r => r), 'name'); + this.fieldFilter = this._createFieldFilter(); + + if (this.presets?.filter) { + Object.entries(this.presets.filter).forEach( + ([k, v]) => { + const filter = this.fieldFilter.find(c => c.name === k) + if(filter) filter.value = v.value; + } + ); + // await this._onInputFilterBrowser(); + } + + const filterList = await foundry.applications.handlebars.renderTemplate('systems/daggerheart/templates/ui/itemBrowser/filterContainer.hbs', + { + fieldFilter: this.fieldFilter, + presets: this.presets, + formatChoices: this.formatChoices + } + ); + + this.element.querySelector('.filter-content .wrapper').innerHTML = filterList; + const filterContainer = this.element.querySelector('.filter-header > [data-action="expandContent"]'); + if(this.fieldFilter.length === 0) + filterContainer.setAttribute('disabled', ''); + else + filterContainer.removeAttribute('disabled'); + + const itemList = await foundry.applications.handlebars.renderTemplate('systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs', + { + items: this.items, + menu: this.selectedMenu, + formatLabel: this.formatLabel + } + ); + + this.element.querySelector('.item-list').innerHTML = itemList; + + this._createFilterInputs(); + await this._onInputFilterBrowser(); + this._createDragProcess(); + + clearTimeout(loadTimeout); + this.toggleLoader(false); + }); + } + + toggleLoader(state) { + const container = this.element.querySelector('.item-list'); + return setTimeout(() => { + container.classList.toggle("loader", state); + }, 100); + } + static expandContent(_, target) { const parent = target.parentElement; parent.classList.toggle('expanded'); @@ -328,6 +376,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { for (const li of html.querySelectorAll('.item-container')) { const itemUUID = li.dataset.itemUuid, item = this.items.find(i => i.uuid === itemUUID); + if(!item) continue; const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name); if (matchesSearch) this.#filteredItems.browser.search.add(item.id); const { input } = this.#filteredItems.browser; @@ -350,7 +399,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { item = this.items.find(i => i.uuid === itemUUID); if (!item) continue; - + const matchesMenu = this.fieldFilter.length === 0 || this.fieldFilter.every( @@ -419,11 +468,13 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { const newOrder = [...itemList].reverse().sort((a, b) => { const aProp = a.querySelector(`[data-item-key="${key}"]`), - bProp = b.querySelector(`[data-item-key="${key}"]`); + bProp = b.querySelector(`[data-item-key="${key}"]`), + aValue = isNaN(aProp.innerText) ? aProp.innerText : Number(aProp.innerText), + bValue = isNaN(bProp.innerText) ? bProp.innerText : Number(bProp.innerText); if (type === 'DESC') { - return aProp.innerText < bProp.innerText ? 1 : -1; + return aValue < bValue ? 1 : -1; } else { - return aProp.innerText > bProp.innerText ? 1 : -1; + return aValue > bValue ? 1 : -1; } }); @@ -452,4 +503,41 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { _canDragStart() { return true; } + + static injectSidebarButton(html) { + if(!game.user.isGM) return; + const sectionId = html.dataset.tab, + menus = { + actors: { + folder: "adversaries", + render: { + folders: ["adversaries", "characters", "environments"] + } + }, + items: { + folder: "equipments", + render: { + noFolder: true + } + }, + compendium: {} + }; + + if(Object.keys(menus).includes(sectionId)) { + const headerActions = html.querySelector(".header-actions"); + + const button = document.createElement("button"); + button.type = "button"; + button.classList.add("open-compendium-browser"); + button.innerHTML = ` + + ${game.i18n.localize("DAGGERHEART.UI.Tooltip.compendiumBrowser")} + `; + button.addEventListener("click", event => { + ui.compendiumBrowser.open(menus[sectionId]); + }); + + headerActions.append(button); + } + } } diff --git a/module/config/itemBrowserConfig.mjs b/module/config/itemBrowserConfig.mjs index d5afe72e..046efd8b 100644 --- a/module/config/itemBrowserConfig.mjs +++ b/module/config/itemBrowserConfig.mjs @@ -149,6 +149,104 @@ export const typeConfig = { } ] }, + weapons: { + columns: [ + { + key: 'system.secondary', + label: 'DAGGERHEART.UI.ItemBrowser.subtype', + format: isSecondary => (isSecondary ? 'secondary' : isSecondary === false ? 'primary' : '-') + }, + { + key: 'system.tier', + label: 'DAGGERHEART.GENERAL.Tiers.singular' + } + ], + filters: [ + { + key: 'system.secondary', + label: 'DAGGERHEART.UI.ItemBrowser.subtype', + choices: [ + { value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon' }, + { value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon' } + ] + }, + { + key: 'system.tier', + label: 'DAGGERHEART.GENERAL.Tiers.singular', + choices: [ + { value: '1', label: '1' }, + { value: '2', label: '2' }, + { value: '3', label: '3' }, + { value: '4', label: '4' } + ] + }, + { + key: 'system.burden', + label: 'DAGGERHEART.GENERAL.burden', + field: 'system.api.models.items.DHWeapon.schema.fields.burden' + }, + { + key: 'system.attack.roll.trait', + label: 'DAGGERHEART.GENERAL.Trait.single', + field: 'system.api.models.actions.actionsTypes.attack.schema.fields.roll.fields.trait' + }, + { + key: 'system.attack.range', + label: 'DAGGERHEART.GENERAL.range', + field: 'system.api.models.actions.actionsTypes.attack.schema.fields.range' + }, + { + key: 'system.itemFeatures', + label: 'DAGGERHEART.GENERAL.features', + choices: () => + Object.entries(CONFIG.DH.ITEM.weaponFeatures) + .map(([k, v]) => ({ value: k, label: v.label })), + operator: 'contains3' + } + ] + }, + armors: { + columns: [ + { + key: 'system.tier', + label: 'DAGGERHEART.GENERAL.Tiers.singular' + } + ], + filters: [ + { + key: 'system.tier', + label: 'DAGGERHEART.GENERAL.Tiers.singular', + choices: [ + { value: '1', label: '1' }, + { value: '2', label: '2' }, + { value: '3', label: '3' }, + { value: '4', label: '4' } + ] + }, + { + key: 'system.baseScore', + name: 'armor.min', + label: 'DAGGERHEART.UI.ItemBrowser.armorScoreMin', + field: 'system.api.models.items.DHArmor.schema.fields.baseScore', + operator: 'gte' + }, + { + key: 'system.baseScore', + name: 'armor.max', + label: 'DAGGERHEART.UI.ItemBrowser.armorScoreMax', + field: 'system.api.models.items.DHArmor.schema.fields.baseScore', + operator: 'lte' + }, + { + key: 'system.itemFeatures', + label: 'DAGGERHEART.GENERAL.features', + choices: () => + Object.entries(CONFIG.DH.ITEM.armorFeatures) + .map(([k, v]) => ({ value: k, label: v.label })), + operator: 'contains3' + } + ] + }, features: { columns: [], filters: [] @@ -257,7 +355,7 @@ export const typeConfig = { { key: 'system.domains', label: 'DAGGERHEART.GENERAL.Domain.plural', - choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label })), + choices: () => Object.values(CONFIG.DH.DOMAIN.allDomains()).map(d => ({ value: d.id, label: d.label })), operator: 'contains2' } ] @@ -265,18 +363,28 @@ export const typeConfig = { subclasses: { columns: [ { - key: 'id', - label: 'TYPES.Item.class', - format: id => { - return ''; - } + key: 'system.linkedClass', + label: 'Class', + format: linkedClass => linkedClass.name }, { key: 'system.spellcastingTrait', label: 'DAGGERHEART.ITEMS.Subclass.spellcastingTrait' } ], - filters: [] + filters: [ + { + key: 'system.linkedClass.uuid', + label: 'Class', + choices: (items) => { + const list = items.map(item => ({ value: item.system.linkedClass.uuid, label: item.system.linkedClass.name })); + return list.reduce((a,c) => { + if(!(a.find(i => i.value === c.value))) a.push(c); + return a; + }, []); + } + } + ] }, beastforms: { columns: [ @@ -305,109 +413,144 @@ export const typeConfig = { }; export const compendiumConfig = { - daggerheart: { - id: 'daggerheart', - label: 'DAGGERHEART', - folders: { - adversaries: { - id: 'adversaries', - keys: ['adversaries'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.adversaries', - type: ['adversary'], - listType: 'adversaries' - }, - ancestries: { - id: 'ancestries', + characters: { + id: 'characters', + keys: ['characters'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.characters', + type: ['character'], + // listType: 'characters' + }, + adversaries: { + id: 'adversaries', + keys: ['adversaries'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.adversaries', + type: ['adversary'], + listType: 'adversaries' + }, + ancestries: { + id: 'ancestries', + keys: ['ancestries'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.ancestries', + type: ['ancestry'], + /* folders: { + features: { + id: 'features', keys: ['ancestries'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.ancestries', - type: ['ancestry'], - folders: { - features: { - id: 'features', - keys: ['ancestries'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.features', - type: ['feature'] - } - } + label: 'DAGGERHEART.UI.ItemBrowser.folders.features', + type: ['feature'] + } + } */ + }, + equipments: { + id: 'equipments', + keys: ['armors', 'weapons', 'consumables', 'loot'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.equipment', + type: ['armor', 'weapon', 'consumable', 'loot'], + listType: 'items', + folders: { + weapons: { + id: 'weapons', + keys: ['weapons'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.weapons', + type: ['weapon'], + listType: 'weapons' }, - equipments: { - id: 'equipments', - keys: ['armors', 'weapons', 'consumables', 'loot'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.equipment', - type: ['armor', 'weapon', 'consumable', 'loot'], - listType: 'items' + armors: { + id: 'armors', + keys: ['armors'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.armors', + type: ['armor'], + listType: 'armors' }, - classes: { - id: 'classes', - keys: ['classes'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.classes', - type: ['class'], - folders: { - features: { - id: 'features', - keys: ['classes'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.features', - type: ['feature'] - }, - items: { - id: 'items', - keys: ['classes'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.items', - type: ['armor', 'weapon', 'consumable', 'loot'], - listType: 'items' - } - }, - listType: 'classes' + consumables: { + id: 'consumables', + keys: ['consumables'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.consumables', + type: ['consumable'] }, - subclasses: { - id: 'subclasses', - keys: ['subclasses'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.subclasses', - type: ['subclass'], - listType: 'subclasses' - }, - domains: { - id: 'domains', - keys: ['domains'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.domainCards', - type: ['domainCard'], - listType: 'cards' - }, - communities: { - id: 'communities', - keys: ['communities'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.communities', - type: ['community'], - folders: { - features: { - id: 'features', - keys: ['communities'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.features', - type: ['feature'] - } - } - }, - environments: { - id: 'environments', - keys: ['environments'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.environments', - type: ['environment'] - }, - beastforms: { - id: 'beastforms', - keys: ['beastforms'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.beastforms', - type: ['beastform'], - listType: 'beastforms', - folders: { - features: { - id: 'features', - keys: ['beastforms'], - label: 'DAGGERHEART.UI.ItemBrowser.folders.features', - type: ['feature'] - } - } + loots: { + id: 'loots', + keys: ['loots'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.loots', + type: ['loot'] } } + }, + classes: { + id: 'classes', + keys: ['classes'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.classes', + type: ['class'], + /* folders: { + features: { + id: 'features', + keys: ['classes'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.features', + type: ['feature'] + }, + items: { + id: 'items', + keys: ['classes'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.items', + type: ['armor', 'weapon', 'consumable', 'loot'], + listType: 'items' + } + }, */ + listType: 'classes' + }, + subclasses: { + id: 'subclasses', + keys: ['subclasses'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.subclasses', + type: ['subclass'], + listType: 'subclasses' + }, + domains: { + id: 'domains', + keys: ['domains'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.domainCards', + type: ['domainCard'], + listType: 'cards' + }, + communities: { + id: 'communities', + keys: ['communities'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.communities', + type: ['community'], + /* folders: { + features: { + id: 'features', + keys: ['communities'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.features', + type: ['feature'] + } + } */ + }, + environments: { + id: 'environments', + keys: ['environments'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.environments', + type: ['environment'] + }, + beastforms: { + id: 'beastforms', + keys: ['beastforms'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.beastforms', + type: ['beastform'], + listType: 'beastforms', + /* folders: { + features: { + id: 'features', + keys: ['beastforms'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.features', + type: ['feature'] + } + } */ + }, + features: { + id: 'features', + keys: ['features'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.features', + type: ['feature'] } }; diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs index cb7be42a..fd569499 100644 --- a/module/systemRegistration/handlebars.mjs +++ b/module/systemRegistration/handlebars.mjs @@ -30,12 +30,13 @@ export const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/dialogs/downtime/activities.hbs', 'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs', - 'systems/daggerheart/templates/ui/chat/parts/roll-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/damage-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/target-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/button-part.hbs', + 'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs', + 'systems/daggerheart/templates/scene/dh-config.hbs', ]); diff --git a/styles/less/global/global.less b/styles/less/global/global.less index e135846f..4c06d42b 100644 --- a/styles/less/global/global.less +++ b/styles/less/global/global.less @@ -23,4 +23,29 @@ color: var(--color-form-hint-hover); } } + + .loader { + position: relative; + overflow: hidden !important; + + div { + opacity: .5; + } + + &:before { + font-family: "Font Awesome 6 Pro"; + content: '\f110'; + position: absolute; + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + animation: spinner 1.5s linear infinite; + } + } + + @keyframes spinner { + to { transform: rotate(360deg); } + } } \ No newline at end of file diff --git a/styles/less/ui/item-browser/item-browser.less b/styles/less/ui/item-browser/item-browser.less index ab9db27c..70a64d89 100644 --- a/styles/less/ui/item-browser/item-browser.less +++ b/styles/less/ui/item-browser/item-browser.less @@ -71,6 +71,7 @@ } .compendium-results { + position: relative; padding: 16px; } @@ -101,10 +102,14 @@ .folder-list, .item-list-header, .item-header > div { - gap: 10px; cursor: pointer; } + .item-list-header, + .item-header > div { + gap: 10px; + } + .item-filter { display: flex; align-items: center; @@ -228,7 +233,8 @@ } .item-list-header, - .item-list { + .item-list, + .compendium-sidebar > .folder-list { overflow-y: auto; scrollbar-gutter: stable; scrollbar-width: thin; @@ -286,6 +292,7 @@ display: flex; flex-direction: column; gap: 5px; + flex: 1; .item-container { &:hover { @@ -385,8 +392,12 @@ margin: 0; .title { - margin: 0; text-align: center; + font-weight: bold; + } + + .hint { + flex: unset; } } @@ -398,7 +409,7 @@ &.lite, &.no-folder { - .menu-path { + .compendium-sidebar, .menu-path { display: none; } } diff --git a/templates/sheets/actors/character/inventory.hbs b/templates/sheets/actors/character/inventory.hbs index ee5b6034..017d37d9 100644 --- a/templates/sheets/actors/character/inventory.hbs +++ b/templates/sheets/actors/character/inventory.hbs @@ -10,9 +10,6 @@ - - -
    diff --git a/templates/ui/itemBrowser/filterContainer.hbs b/templates/ui/itemBrowser/filterContainer.hbs new file mode 100644 index 00000000..da8e8cfe --- /dev/null +++ b/templates/ui/itemBrowser/filterContainer.hbs @@ -0,0 +1,22 @@ +{{#each fieldFilter}} + {{#if choices }} +
    + +
    + +
    +
    + {{else}} + {{#if filtered }} + {{formField field localize=true blank="" name=name choices=(@root.formatChoices this) valueAttr="value" dataset=(object key=key) value=value}} + {{else}} + {{#if field.label}} + {{formField field localize=true blank="" name=name dataset=(object key=key) value=value}} + {{else}} + {{formField field localize=true blank="" name=name dataset=(object key=key) label=label value=value}} + {{/if}} + {{/if}} + {{/if}} +{{/each}} \ No newline at end of file diff --git a/templates/ui/itemBrowser/itemBrowser.hbs b/templates/ui/itemBrowser/itemBrowser.hbs index ca0def19..137693fc 100644 --- a/templates/ui/itemBrowser/itemBrowser.hbs +++ b/templates/ui/itemBrowser/itemBrowser.hbs @@ -1,5 +1,5 @@
    - {{#if menu.data }} + {{#if menu.path.length }}
    - {{#if fieldFilter.length}} - - {{/if}} +
    -
    - {{#each fieldFilter}} - {{#if choices }} -
    - -
    - -
    -
    - {{else}} - {{#if filtered }} - {{formField field localize=true blank="" name=name choices=(@root.formatChoices this) valueAttr="value" dataset=(object key=key) value=value}} - {{else}} - {{#if field.label}} - {{formField field localize=true blank="" name=name dataset=(object key=key) value=value}} - {{else}} - {{formField field localize=true blank="" name=name dataset=(object key=key) label=label value=value}} - {{/if}} - {{/if}} - {{/if}} - {{/each}} -
    +
    - {{!--
    --}} - {{#if menu.data.columns.length}} -
    -
    -
    {{localize 'DAGGERHEART.UI.ItemBrowser.columnName'}}
    - {{#each menu.data.columns}} - {{localize label}} - {{/each}} -
    - {{/if}} -
    - {{#each items}} -
    -
    -
    - - {{name}} - {{#each ../menu.data.columns}} - {{#with (@root.formatLabel ../this this) as | label |}}{{{label}}}{{/with}} - {{/each}} -
    -
    -
    - {{{system.description}}} -
    -
    - {{/each}} -
    - {{!--
    --}} + {{#if menu.data.columns.length}} +
    +
    +
    {{localize 'DAGGERHEART.UI.ItemBrowser.columnName'}}
    + {{#each menu.data.columns}} +
    {{localize label}}
    + {{/each}} +
    + {{/if}} +
    {{else}}

    {{localize "DAGGERHEART.UI.ItemBrowser.title"}}

    diff --git a/templates/ui/itemBrowser/itemContainer.hbs b/templates/ui/itemBrowser/itemContainer.hbs new file mode 100644 index 00000000..f6aefa6b --- /dev/null +++ b/templates/ui/itemBrowser/itemContainer.hbs @@ -0,0 +1,16 @@ +{{#each items}} +
    +
    +
    + + {{name}} + {{#each ../menu.data.columns}} + {{#with (@root.formatLabel ../this this) as | label |}}{{{label}}}{{/with}} + {{/each}} +
    +
    +
    + {{{system.description}}} +
    +
    +{{/each}} \ No newline at end of file diff --git a/templates/ui/itemBrowser/sidebar.hbs b/templates/ui/itemBrowser/sidebar.hbs index 8df0aed3..28a34a22 100644 --- a/templates/ui/itemBrowser/sidebar.hbs +++ b/templates/ui/itemBrowser/sidebar.hbs @@ -1,32 +1,22 @@
    - {{#each compendiums}} -
    - - {{label}} - - -
    - {{#each folders}} -
    {{label}}
    - {{!--
    {{label}}
    --}} - {{#if folders.length}} -
    -
    - {{#each folders}} -
    - • {{label}} -
    - {{/each}} -
    +
    + {{#each compendiums}} +
    {{label}}
    + {{#if folders.length}} +
    +
    + {{#each folders}} +
    + • {{label}} +
    + {{/each}}
    - {{/if}} - {{/each}} -
    - -
    - {{/each}} +
    + {{/if}} + {{/each}} +
    From 8fd63d5963aad66e95ee76e360f5a0424051723b Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Thu, 28 Aug 2025 18:29:17 +0200 Subject: [PATCH 46/66] [Feature] 1033 - Consume Quantity On Use (#1106) * Initial migration * Updated compendium YML * Added Quantity as a possible cost * Added quantity consumption to all Compendium Consumables * . * Added DestroyOnEmpty property --- lang/en.json | 4 +- module/applications/dialogs/d20RollDialog.mjs | 31 +++--- .../sheets-configs/action-config.mjs | 14 ++- .../applications/sheets/actors/character.mjs | 3 +- .../applications/sheets/actors/companion.mjs | 3 +- module/config/generalConfig.mjs | 16 +++- module/data/fields/action/costField.mjs | 96 +++++++++++++------ module/data/item/consumable.mjs | 5 +- module/documents/actor.mjs | 23 +++-- module/systemRegistration/migrations.mjs | 55 +++++++++++ ...ersary_Acid_Burrower_89yAh30vaNQOALlz.json | 1 - ...ary_Adult_Flickerfly_G7jiltRjgvVhZewm.json | 3 - ..._Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json | 1 - ...ary_Arch_Necromancer_WPEOIGfclNJxWb87.json | 4 - ...sary_Archer_Squadron_0ts6CGd93lLqGZI5.json | 1 - ...adversary_Battle_Box_dgH3fW9FTYLaIDvS.json | 2 - .../adversary_Bear_71qKDLKO3CsrNkdy.json | 1 - ...ersary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json | 1 - .../adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json | 2 - ...dversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json | 1 - .../adversary_Conscript_99TqczuQipBmaB8i.json | 1 - .../adversary_Construct_uOP5oT9QzXPlnf3p.json | 2 - .../adversary_Courtesan_ZxWaWPdzFIUPNC62.json | 1 - .../adversary_Courtier_CBBuEXAlLKFMJdjg.json | 2 - ...adversary_Cult_Adept_0NxCSugvKQ4W8OYZ.json | 3 - .../adversary_Cult_Fang_tyBOpLfigAhI9bU3.json | 2 - ...ersary_Cult_Initiate_zx99sOGTXicP4SSD.json | 1 - ...ary_Demon_of_Despair_kE4dfhqmIQpNd44e.json | 2 - ...sary_Demon_of_Hubris_2VN3BftageoTTIzu.json | 3 - ...ry_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json | 1 - ...rsary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json | 2 - .../adversary_Dire_Bat_tBWHW00epmMnkawe.json | 1 - .../adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json | 1 - .../adversary_Dryad_wR7cFKrHvRzbzhBT.json | 3 - ...ersary_Electric_Eels_TLzY1nDw0Bu9Ud40.json | 1 - ...sary_Elemental_Spark_P7h54ZePFPHpYwvB.json | 1 - ...ersary_Elite_Soldier_bfhVWMBUh61b9J6n.json | 2 - ...ry_Failed_Experiment_ChwwVqowFw8hJQwT.json | 1 - ...y_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json | 1 - ...sary_Fallen_Sorcerer_PELRry1vqjBzSAlr.json | 1 - ...rlord__Realm_Breaker_hxZ0sgoFJubh5aj6.json | 1 - ..._Undefeated_Champion_RXkZTwBRi4dJ3JE5.json | 1 - ...ersary_Giant_Brawler_YnObCleGjPT7yqEc.json | 1 - ...dversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json | 1 - ...ary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json | 1 - .../adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json | 1 - ...ersary_Giant_Recruit_5s8wSvpyC5rxY5aD.json | 1 - ...rsary_Giant_Scorpion_fmfntuJ8mHRCAktP.json | 1 - ...dversary_Glass_Snake_8KWVLWXFhlY2kYx0.json | 2 - .../adversary_Gorgon_8mJYMpbLTb8qIOrr.json | 1 - ...ater_Earth_Elemental_dsfB3YhoL5SudvS2.json | 1 - ...ater_Water_Elemental_xIICT6tEdnA7dKDV.json | 3 - ...adversary_Green_Ooze_SHXedd9zZPVfUgUa.json | 1 - ...sary_Hallowed_Archer_kabueAo6BALApWqp.json | 1 - ...ary_Hallowed_Soldier_VENwg7xEFcYObjmT.json | 2 - .../adversary_Harrier_uRtghKE9mHlII4rs.json | 1 - ...adversary_Head_Guard_mK3A5FTx6k8iPU3F.json | 1 - ...versary_Head_Vampire_i2UNbRvgyoSs07M6.json | 1 - ...dversary_High_Seraph_r1mbfSSwKWdcFdAU.json | 3 - ...sary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json | 1 - .../adversary_Hydra_MI126iMOOobQ1Obn.json | 1 - ..._Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json | 1 - ...ged_Knife_Lieutenant_aTljstqteGoLpCBq.json | 2 - ..._Knight_of_the_Realm_7ai2opemrclQe3VF.json | 1 - ...sary_Master_Assassin_dNta0cUzr96xcFhf.json | 2 - ...rsary_Merchant_Baron_Vy02IhGhkJLuezu4.json | 1 - ...inor_Chaos_Elemental_sRn4bqerfARvhgSV.json | 2 - ...dversary_Minor_Demon_3tqCjDwJAQ7JKqMb.json | 2 - ...Minor_Fire_Elemental_DscWkNVoHak6P4hh.json | 2 - ...versary_Minor_Treant_G62k4oSkhkoXEs2D.json | 1 - ...ary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json | 2 - .../adversary_Monarch_yx0vK2yfNVZKWUUi.json | 2 - ...ersary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json | 2 - ...rsary_Oracle_of_Doom_befIqd5IYKg6eUz2.json | 3 - ..._Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json | 1 - ...atchwork_Zombie_Hulk_EQTOAOUrkIvS2z88.json | 1 - ...ary_Perfected_Zombie_CP6iRfHdyFWniTHY.json | 1 - ...dversary_Petty_Noble_wycLpvebWdUqRhpP.json | 2 - ...rsary_Pirate_Captain_OROJbjsqagVh7ECV.json | 2 - .../adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json | 1 - ...ersary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json | 1 - ...ersary_Royal_Advisor_EtLJiTsilPPZvLUX.json | 2 - ...ersary_Secret_Keeper_sLAccjvCWfeedbpI.json | 3 - .../adversary_Sellsword_bgreCaQ6ap2DVpCr.json | 1 - .../adversary_Shark_YmVAkdNsyuXWTtYp.json | 1 - .../adversary_Siren_BK4jwyXSRx7IOQiO.json | 1 - ...sary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json | 1 - ...sary_Skeleton_Knight_Q9LaVTyXF9NF12C7.json | 1 - ...sary_Spectral_Archer_5tCkhnBByUIN5UdG.json | 1 - ...ary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json | 3 - ...ry_Spectral_Guardian_UFVGl1osOsJTneLf.json | 2 - ...adversary_Spellblade_ldbWEL7uZs84vyrR.json | 2 - .../adversary_Spy_8zlynOhnVA59KpKT.json | 2 - ...dversary_Stag_Knight_KGVwnLq85ywP9xvB.json | 2 - ...dversary_Stonewraith_3aAS2Qm3R6cgaYfE.json | 2 - ...rsary_Sylvan_Soldier_VtFBt9XBE0WrGGxP.json | 2 - ...Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json | 1 - ...rsary_Tangle_Bramble_XcAGOSmtCFLT1unN.json | 1 - ...rsary_Treant_Sapling_o63nS0k3wHu6EgKP.json | 1 - .../adversary_Vampire_WWyUp6Mxl1S3KYUG.json | 1 - ...ault_Guardian_Gaoler_JqYraOqNmmhHk4Yy.json | 1 - ...lt_Guardian_Sentinel_FVgYb28fhxlVcGwA.json | 2 - ...ault_Guardian_Turret_c5hGdvY5UnSjlHws.json | 2 - ...Dragon__Ashen_Tyrant_pMuXGCSOQaxpi5tb.json | 2 - ...agon__Molten_Scourge_eArAPuB38CNR0ZIM.json | 2 - ...adversary_War_Wizard_noDdT0tsN6FXSmC8.json | 4 - ...versary_Weaponmaster_ZNbQ2jg35LG4t9eH.json | 16 ++-- ...dversary_Young_Dryad_8yUj2Mzvnifhxegm.json | 2 - ...ary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json | 2 - ...ersary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json | 1 - ...dversary_Zombie_Pack_Nf0v43rtflV56V2T.json | 1 - ...feature_Adaptability_BNofV1UC4ZbdFTkb.json | 1 - .../feature_Charge_AA2CZlJSWW8GPhrR.json | 1 - ...feature_Danger_Sense_AXqcoxnRoWBbbKpK.json | 1 - ...ure_Death_Connection_WuwXH2r2uM9sDJtj.json | 1 - .../feature_Fearless_IlWvn5kCqCBMuUJn.json | 1 - ...ure_Feline_Instincts_lNgbbYnCKgrdvA85.json | 1 - ..._Increased_Fortitude_0RN0baBxh95GT1cm.json | 1 - .../feature_Long_Tongue_oWbdlh51ajn1Q5kL.json | 1 - .../feature_Luckbender_U6iFjZgLYawlOlQZ.json | 1 - ...ture_Quick_Reactions_0NSPSuB8KSEYTJIP.json | 1 - .../feature_Tusks_YhxD1ujZpftPu19w.json | 1 - .../feature_Wings_WquAjoOcso8lwySW.json | 1 - .../feature_Agile_xLS5YT1B6yeCiNTg.json | 1 - ...eature_Armored_Shell_nDQZdIF2epKlhauX.json | 1 - .../feature_Cannonball_jp5KpPRBFBOIs46Q.json | 1 - .../feature_Demolish_DfBXO8jTchwFG8dZ.json | 1 - ..._Devastating_Strikes_HJbQcKWcFZ9NoFxs.json | 1 - ...feature_Elusive_Prey_a7Qvmm14nx9BCysA.json | 1 - .../feature_Fleet_GhHsSHOa509cwCvr.json | 1 - ...ture_Hobbling_Strike_8u0HkK3WgtU9lWYs.json | 1 - .../feature_Rampage_8upqfcZvi7b5hRLE.json | 1 - ...ture_Snapping_Strike_Ky3rZD3sJMXYZOBC.json | 1 - .../feature_Takedown_0ey4kM9ssj2otHvb.json | 1 - .../feature_Trample_A0lgd6eVEfX6oqSB.json | 1 - ...feature_Vicious_Maul_jYUBi7yLHap5ljpa.json | 1 - ...feature_Warning_Hiss_cTlqpQZPy5TvdDAT.json | 1 - .../feature_Beastform_P1K0jcnH2RiS6TLd.json | 1 - ...re_Channel_Raw_Power_P02cbN50LIoD662z.json | 1 - .../feature_Evolution_6rlxhrRwFaVgq9fe.json | 1 - ...ature_Frontline_Tank_YS1g7YdWwOaS629x.json | 1 - ...eature_Hold_Them_Off_2Cyb9ZeuAesf5Sb3.json | 1 - ...feature_Life_Support_lSlvSUHbOoX36q2j.json | 1 - ...feature_Make_a_Scene_N9E5skDDK2VgvohR.json | 1 - .../feature_No_Mercy_njj2C3tMDeCHHOoh.json | 1 - ...eature_Not_This_Time_h3VE0jhcM5xHKBs4.json | 1 - ...ature_Ranger_s_Focus_ncLx2P8BOUtrAD38.json | 1 - ...eature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json | 1 - ...ature_Volatile_Magic_ieiQlD0joWSqt53D.json | 1 - ...feature_Nomadic_Pack_2RSrQouA2zEJ5Xee.json | 1 - ...nCard_Adjust_Reality_Zp2S2EnLS5Iv3XuT.json | 1 - ...nCard_Arcana_Touched_5PvMQKCjrgSxzstn.json | 12 +-- ...rd_Arcane_Reflection_JzSvxy9Mu3RJp1jV.json | 1 - ...rd_Astral_Projection_YNOCNmZ96sCp9NEr.json | 1 - ...nCard_Battle_Monster_P0ezScyQ5t8ruByf.json | 1 - ...domainCard_Blink_Out_Qu0iA4s3Xov10Erd.json | 1 - ...inCard_Bold_Presence_tdsL00yTSLNgZWs6.json | 1 - ...mainCard_Bolt_Beacon_BNevJyGk7hmN7XOY.json | 1 - ...ainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json | 1 - ...mainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json | 1 - ...inCard_Book_of_Exota_oVs2MSC6Uf5GbgEG.json | 1 - ...inCard_Book_of_Grynn_R0LNheiZycZlZzV3.json | 1 - ...nCard_Book_of_Illiat_df4iRqQzRntrF6Qw.json | 4 +- ...nCard_Book_of_Korvax_cWRFHJdxEZ0M1dAg.json | 2 - ...inCard_Book_of_Sitil_eq8VNqYMRHhF9xw9.json | 1 - ...nCard_Book_of_Vagras_aknDDYtN7EObv94t.json | 1 - ...inCard_Book_of_Vyola_VOIgm2j2Ijszwc5m.json | 1 - ...nCard_Book_of_Yarrow_J1ovx2FpNDvPq1o6.json | 1 - .../domainCard_Boost_VKAHS6eWz28ukcDs.json | 1 - ...inCard_Breaking_Blow_8UANBgSdhMZ0sqfO.json | 1 - ...Card_Chain_Lightning_0kAVO6rordCfZqYP.json | 1 - ...Card_Champion_s_Edge_rnejRbUQsNGX1GMC.json | 3 - ...domainCard_Chokehold_R5GYUalYXLLFRlNl.json | 2 - ...nCard_Cloaking_Blast_Zhw7PtK8nMPlsOqD.json | 1 - ...inCard_Codex_Touched_7Pu83ABdMukTxu3e.json | 1 - ...nCard_Confusing_Aura_R8NDiJXJWmC48WSr.json | 1 - ...inCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json | 3 - ...Card_Conjured_Steeds_Jkp6cMDiHHaBZQRS.json | 3 +- ...Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json | 3 +- ...inCard_Dark_Whispers_yL2qrSWmTwXVOySH.json | 1 - .../domainCard_Deathrun_xFOSn8IVVNizgHFq.json | 1 - ...inCard_Deft_Deceiver_38znCh6kHTkaPwYi.json | 1 - ...nCard_Deft_Maneuvers_dc4rAXlv95srZUct.json | 1 - ...omainCard_Divination_K8oFepK24UVsAX8B.json | 1 - ...omainCard_Earthquake_C0qLOwSSvZ6PG3Ws.json | 16 ++-- ...ard_Endless_Charisma_tNzFNlVHghloKsFi.json | 1 - ...domainCard_Enrapture_a8lFiKX1o8T924ze.json | 1 - ...mainCard_Falling_Sky_hZJp9mdkMnqKDROe.json | 3 +- .../domainCard_Flight_54GUjNuBEy7xdzMz.json | 12 +-- ...Card_Force_of_Nature_LzVpMkD5I4QeaIHf.json | 2 - ...inCard_Forceful_Push_z8FFPhDh2SdFkFfS.json | 1 - ...nCard_Forest_Sprites_JrkUMTzaFmQNBHVm.json | 3 +- ...omainCard_Full_Surge_SgvjJfMyubZowPxS.json | 1 - ...nCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json | 3 +- ...inCard_Glancing_Blow_nCNCqSH7UgW4O3To.json | 1 - ...d_Glyph_of_Nightfall_B5HXqYRJiL3xMNKT.json | 1 - ...inCard_Grace_Touched_KAuNb51AwhD8KEXk.json | 1 - ...ainCard_Ground_Pound_WnGldYhJPDhx8v9X.json | 1 - ...inCard_Healing_Field_GlRm1Dxlc0Z1b04o.json | 31 +++--- ...inCard_Healing_Hands_WTlhnQMajc1r8i50.json | 4 - ...nCard_Healing_Strike_XtSc0jIJLOoMTMYS.json | 1 - ...inCard_Hold_the_Line_kdFoLo3KXwn4LqTG.json | 1 - .../domainCard_Hush_gwmYasmfgXZ7tFS6.json | 1 - ...ard_I_Am_Your_Shield_KOf6LLpMRNwjezDx.json | 1 - ...Card_I_See_It_Coming_Kp6RejHGimnuoBom.json | 1 - ..._Inspirational_Words_cWu1o82ZF7GvnbXc.json | 31 +++--- ...ainCard_Invigoration_X8OfkEoI5gLTRf1B.json | 1 - ...ainCard_Invisibility_KHkzA4Zrw8EWN1CH.json | 1 - ...nCard_Know_Thy_Enemy_O38MQMhJWdZnXi6b.json | 2 - ...Card_Lead_by_Example_YWCRplmtwpCjpq5i.json | 1 - ...domainCard_Life_Ward_OszbCj0jTqq2ADx9.json | 1 - ...inCard_Manifest_Wall_TtGOtWkbr23VhHfH.json | 1 - ...nCard_Mass_Enrapture_ubpixIgZrJXKyM3b.json | 1 - ...inCard_Mending_Touch_TGjR4vJVNbQRV8zr.json | 28 +++--- ...Card_Midnight_Spirit_FXLsB3QbQvTtqX5B.json | 2 - ...ard_Midnight_Touched_uSyGKVxOJcnp28po.json | 1 - ...ard_Natural_Familiar_Tag303LoRNC5zGgl.json | 2 - ...Card_Nature_s_Tongue_atWLorlCOxcrq8WB.json | 1 - ...nCard_Never_Upstaged_McdncxmO9K1YNP7Y.json | 70 ++++++++------ ...ainCard_Night_Terror_zcldCuqOg3dphUVI.json | 13 +-- ...domainCard_Notorious_IqxzvvjZiYbgx21A.json | 1 - ...domainCard_Onslaught_I7pNsQ9Yx6mRJX4V.json | 1 - ...rd_Overwhelming_Aura_iEBLySZD9z8CLdz7.json | 1 - ...Card_Phantom_Retreat_0vdpIn06ifF3xxqZ.json | 2 - ...mainCard_Premonition_aC43NiFQLpOADyjO.json | 12 +-- .../domainCard_Rage_Up_GRL0cvs96vrTDckZ.json | 2 - ...nCard_Rain_of_Blades_Ucenef6JpjQxwXni.json | 1 - ...inCard_Rapid_Riposte_tceJDcCUefrMS2Ov.json | 1 - ...Card_Reaper_s_Strike_MCgNRlh0s5XUPCfl.json | 1 - .../domainCard_Reckless_2ooUo2yoilGifY81.json | 1 - .../domainCard_Redirect_faU0XkJCbar69PiN.json | 1 - ...mainCard_Restoration_wUQFsRtww18naYaq.json | 24 ++--- ...domainCard_Rune_Ward_GEhBUmv9Bj7oJfHk.json | 1 - ...omainCard_Safe_Haven_lmBLMPuR8qLbuzNf.json | 1 - ...ainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json | 24 ++--- ...nCard_Salvation_Beam_4uAFGp3LxiC07woC.json | 3 +- ...mainCard_Second_Wind_ffPbSEvLuFrFsMxl.json | 18 ++-- ...nCard_Shape_Material_db4xV3YErHRslbVE.json | 1 - ...mainCard_Shield_Aura_rfIv6lln40Fh6EIl.json | 1 - ...ainCard_Shrug_It_Off_JwfhtgmmuRxg4zhI.json | 1 - .../domainCard_Smite_U1uWJE94HZVudujz.json | 1 - ..._Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json | 1 - ...mainCard_Spellcharge_ewhIzXQ2h9fS9I8c.json | 15 +-- ...d_Splintering_Strike_TYKfM3H9vBXyWiH4.json | 1 - ...rd_Stealth_Expertise_NIUhmuQGwbb3UClZ.json | 1 - ...d_Strategic_Approach_5b1awkgTmMp3FVrm.json | 12 +-- ...rd_Stunning_Sunlight_lRHo6ZkK1zybeEoG.json | 3 +- ...ainCard_Support_Tank_stId5syX7YpP2JGz.json | 1 - ...omainCard_Thorn_Skin_oUipGK84E2KjoKqh.json | 14 +-- ...nCard_Thought_Delver_B4choj481tqajWb9.json | 1 - ...nCard_Towering_Stalk_n0P3VS1WfxvmXbB6.json | 23 ++--- ...d_Transcendent_Union_kVkoCLBXLAIifqpz.json | 1 - ...inCard_Twilight_Toll_SDjjV61TC1NceV1m.json | 13 +-- ...ard_Uncanny_Disguise_TV56wSysbU5xAlOa.json | 15 +-- ...inCard_Unleash_Chaos_o62i0QdbUDIiAhSq.json | 13 ++- ...Card_Vanishing_Dodge_GBMIElIpk4cvk1Bd.json | 1 - ...rd_Versatile_Fighter_wQ53ImDswEHv5SGQ.json | 1 - ...ard_Vicious_Entangle_qvpvTnkAoRn9vYO4.json | 1 - ...domainCard_Wall_Walk_1ROT08E1UVBwHLAS.json | 1 - ...domainCard_Whirlwind_anO0arioUy7I5zBg.json | 1 - ...inCard_Wild_Fortress_9dFvcM1i3bxG3BSA.json | 1 - ...omainCard_Wild_Surge_DjnKlZQYaWdQGKcK.json | 1 - ...nvironment_Ambushers_uXZpebPR77YQ1oXI.json | 1 - ...ironment_Chaos_Realm_2Z1mKc65LxNk2PqR.json | 2 - ...ent_Cliffside_Ascent_LPpfdlNKqiZIl04w.json | 1 - ...nt_Divine_Usurpation_4DLYez7VbMCFDAuZ.json | 2 - ...ment_Hallowed_Temple_dsA6j69AnaJhUyqH.json | 1 - ...nment_Imperial_Court_jr1xAoXzVwVblzxI.json | 2 - ...ecromancer_s_Ossuary_h3KyRL7AshhLAmcH.json | 1 - ...ronment_Outpost_Town_YezryR32uo39xRxW.json | 1 - ...nment_Pitched_Battle_EWD3ZsLoK6VMVOf7.json | 1 - ...ronment_Raging_River_t4cdqTfzcqP3H1vJ.json | 2 - ...or_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json | 1 - ...or_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json | 13 +-- ...nes_of_Fortification_P4qAEDJUoNLgVRsA.json | 1 - ...netan_Floating_Armor_tHlBUDQC24YMZqd6.json | 1 - ...consumable_Acidpaste_cfVFmS8vT9dbq9s1.json | 18 +++- ...mable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json | 18 +++- ...umable_Attune_Potion_JGD3M9hBHtVAA8XP.json | 18 +++- ...sumable_Blinding_Orb_eAXHdzA5qNPldOpn.json | 18 +++- ...e_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json | 18 +++- ...mable_Bolster_Potion_FOPQNqXbiVO0ilYL.json | 18 +++- ...umable_Bonding_Honey_PfQvqopXgvroBklL.json | 18 +++- ...nsumable_Bridge_Seed_RrIasiMCt6mqVTps.json | 18 +++- ...sumable_Channelstone_IKMVQ6VwtapwoUim.json | 18 +++- ...sumable_Charm_Potion_CVBbFfOY75YwyQsp.json | 18 +++- ...e_Circle_of_the_Void_elsyP6VhHw1JjGSl.json | 14 +-- ...mable_Control_Potion_eeBhZSGLjuNZuJuI.json | 18 +++- ...consumable_Death_Tea_xDnJeF1grkmKck8Q.json | 18 +++- ...able_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json | 18 +++- ...able_Dripfang_Poison_eU8VpbWB2NHIL47n.json | 18 +++- ...ble_Enlighten_Potion_aWHSO2AqDufi7nL4.json | 18 +++- ...mable_Feast_of_Xuria_aX6NyxkNzu0LcJpt.json | 18 +++- ...nsumable_Featherbone_DpxEMpwfasEBpORU.json | 18 +++- ...onsumable_Gill_Salve_Nvbb9mze6o5D0AEg.json | 18 +++- ...e_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json | 18 +++- ...mable_Growing_Potion_fl2f3ees8RFMze9t.json | 18 +++- ...umable_Health_Potion_Aruc2NLutWuVIjP1.json | 18 +++- ...omet_s_Secret_Potion_VSwa1LpQ9PjZKsWF.json | 18 +++- ...mable_Hopehold_Flare_EhaQCPJ8oiqpRIwB.json | 18 +++- ...mproved_Arcane_Shard_nQTo6mNoPTEVBtkm.json | 18 +++- ...d_Grindletooth_Venom_BqBWXXe9T07AMV4u.json | 18 +++- ...e_Jar_of_Lost_Voices_yUol6M5b8jsbk9za.json | 18 +++- ...sumable_Jumping_Root_c2putn9apuurJhWX.json | 18 +++- ...able_Knowledge_Stone_nL9IALzm9BNi5oSt.json | 18 +++- ...e_Major_Arcane_Shard_AA7bmiwv00lshPrC.json | 18 +++- ..._Major_Attune_Potion_CCPFm5iXXwvyYYwR.json | 18 +++- ...Major_Bolster_Potion_mnyQDRtngWWQeRXF.json | 18 +++- ...e_Major_Charm_Potion_IJLAUlQymbSjzsri.json | 18 +++- ...Major_Control_Potion_80s1FLmTLtohZ5GH.json | 18 +++- ...jor_Enlighten_Potion_SDdv1G2veMLKrxcJ.json | 18 +++- ..._Major_Health_Potion_cM7pHe8bBAxSZ2xR.json | 18 +++- ...Major_Stamina_Potion_I4cQ03xbxnc81EGa.json | 18 +++- ..._Major_Stride_Potion_yK6eEDUrsPbZA8G0.json | 18 +++- ..._Minor_Health_Potion_tPfKtKRRjv8qdSqy.json | 18 +++- ...Minor_Stamina_Potion_b6vGSPFWOlzZZDLO.json | 18 +++- ...e_Mirror_of_Marigold_UFQVwgYOUZ88UxcH.json | 16 ++-- ...umable_Morphing_Clay_f1NHVSIHJJCIOaBl.json | 16 ++-- ...nsumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json | 18 +++- ...consumable_Ogre_Musk_qr1bosjFcUfuwq4B.json | 18 +++- ..._Potion_of_Stability_dvL8oaxpEF6jKvYN.json | 18 +++- ...able_Redthorn_Saliva_s2Exl2XFuoOhtIov.json | 18 +++- ...eplication_Parchment_yJkwz4AP6yhGo8Vj.json | 18 +++- ...ble_Shrinking_Potion_HGixKenQwhyRAYNk.json | 18 +++- ...sumable_Sleeping_Sap_XZavUVlHEvE2srEt.json | 18 +++- ...nsumable_Snap_Powder_cg6VtQ0eVZjDdcK0.json | 14 +-- ...mable_Stamina_Potion_hf3k1POoVSooJyN2.json | 18 +++- .../consumable_Stardrop_y4c1jrlHrf0wBWOq.json | 18 +++- ...umable_Stride_Potion_lNtcrkgFGOJNaroE.json | 18 +++- ...sumable_Sun_Tree_Sap_kwexUzdM9wm1Qums.json | 18 +++- ...onsumable_Sweet_Moss_GrDrRqWgv7gvl9vn.json | 29 +++++- ...nstable_Arcane_Shard_mUepnLbkvFk0ha4Z.json | 18 +++- ...sumable_Varik_Leaves_hvy5BkG3F6iOIXTx.json | 18 +++- ...le_Vial_of_Darksmoke_Nwv5ydGf0MWnzq1n.json | 18 +++- ...ble_Vial_of_Moondrip_VqEX5YwK5oL3r1t6.json | 18 +++- ...onsumable_Wingsprout_n10vozlmosVR6lo4.json | 18 +++- .../loot_Belt_of_Unity_gFzkUGCjkRJtyoe9.json | 1 - .../loot_Glamour_Stone_Pj17cvdJ1XG1jv6I.json | 1 - .../loot/loot_Glider_CiXwelozmBDcPY48.json | 1 - ...ot_Hopekeeper_Locket_9DcFR75tsnBYIp6Z.json | 1 - ...loot_Paragon_s_Chain_F4hoRfvVdZq5bhhI.json | 1 - ...loot_Ring_of_Silence_K1ysGnTpNyxPu5Au.json | 1 - ..._Unbreakable_Resolve_kn71qCQY0DnjmQBJ.json | 1 - ...loot_Shard_of_Memory_2ULPgNyqCrxea0v0.json | 1 - ...ght_Frame_Wheelchair_BuMfupnCzHbziQ8o.json | 1 - ...apon_Advanced_Rapier_KxFne76d7cak15dO.json | 1 - ...weapon_Advanced_Whip_01izMUSJcAUo79IX.json | 1 - ...apon_Axe_of_Fortunis_YcS1rHgfnSlla8Xf.json | 1 - .../weapon_Bladed_Whip_5faflfNz20cFW1EM.json | 1 - .../weapon_Bloodstaff_IoMVDz92WVvfGGdc.json | 1 - .../weapon_Buckler_EmFTp9wzT6MHSaNz.json | 13 +-- ...pon_Dual_Ended_Sword_nXjuBa215H1sTUK3.json | 1 - ...apon_Hammer_of_Wrath_1R4uzOpWD8bkYRUm.json | 1 - ...apon_Impact_Gauntlet_Zg6IutksQVOqAg8K.json | 1 - ...ght_Frame_Wheelchair_ZJsetdHKV77ygtCE.json | 1 - ...apon_Improved_Rapier_LFPH8nD2f4Blv3AM.json | 1 - ...weapon_Improved_Whip_ftTp8VlsBQ1r4LFD.json | 1 - ...ght_Frame_Wheelchair_Xt8tVSn5Fu6ly6LF.json | 1 - ...pon_Legendary_Rapier_BakN97v4jTePcXiZ.json | 1 - ...eapon_Legendary_Whip_Wcdbf6yS3LEt7nsg.json | 1 - ...ght_Frame_Wheelchair_iaGnlUkShBgdeMo0.json | 1 - ...pon_Powered_Gauntlet_bW3xw5S9DbaLCN3E.json | 1 - .../weapon_Rapier_zkAgEW6zMkRZalEm.json | 1 - ...weapon_Ricochet_Axes_E9QDh5o9eQ1Qx0kH.json | 3 +- ...n_Runes_of_Ruination_EG6mZhr3ib56r974.json | 1 - ...n_Swinging_Ropeblade_1jOJHHKdtk3s2jaY.json | 1 - ...Wand_of_Enthrallment_tP6vmnrmTq2h5sj7.json | 1 - .../weapons/weapon_Whip_CmtWqw6DwoePnX7W.json | 1 - .../feature_Adept_v511C6GMShsBblah.json | 1 - ...eature_Apex_Predator_lwH3E0Zyf4gbVOd0.json | 1 - ...re_Clarity_of_Nature_etaQ01yGJhBLDUqZ.json | 15 +-- ..._Contacts_Everywhere_cXbRm744mW6UXGam.json | 1 - .../feature_Defender_Jdktv5p1K2PfgxrT.json | 1 - ...e_Elemental_Dominion_EFUJHrkTuyv8uA9l.json | 1 - ...lemental_Incarnation_f37TTgCc0Q3Ih1A1.json | 4 - ...feature_Elementalist_dPcqKN5NeDkjB1HW.json | 2 - ...ture_Heart_of_a_Poet_Ce0sn0kqAw3PFe0k.json | 1 - ...ture_Loyal_Protector_hd7UeBPr86Mz21Pe.json | 1 - ...ure_Manipulate_Magic_UNg4eyNfEQrMdD7G.json | 1 - ...ture_Natural_Evasion_TnuLBtHQGbqyzn82.json | 1 - .../feature_Nemesis_DPKmipNRlSAMs2Cg.json | 1 - ...ture_Partner_in_Arms_G54qY96XK62hgoK9.json | 1 - ...feature_Regeneration_KRyrbSLVGreIOTZe.json | 1 - .../feature_Revenge_oNfA5F9cKwNR7joq.json | 1 - ...ature_Rousing_Speech_PCmYTX02JLzBpgml.json | 1 - ...re_Ruthless_Predator_Qny2J3R35bvC0Cey.json | 1 - ...ature_Shadow_Stepper_hAwTXjhyphiE3aeW.json | 1 - ...eature_Sparing_Touch_GfOSgVJW8bS1OjNq.json | 18 ++-- ...eature_Spirit_Weapon_McoS0RxNLOg3SfSt.json | 1 - ...ture_Thrive_in_Chaos_1nmFmkNXY6OYyyju.json | 1 - ...eature_Transcendence_th6HZwEFnVBjUtqm.json | 1 - ...eature_Vanishing_Act_iyIg1VLwO8C6jvFZ.json | 1 - ...re_Weapon_Specialist_HAqtoKUTrk8Mip1n.json | 1 - ...ature_Wings_of_Light_KkQH0tYhagIqe2MT.json | 1 - system.json | 2 +- .../global/partials/inventory-item-V2.hbs | 2 +- .../sheets/items/consumable/settings.hbs | 3 + 397 files changed, 1276 insertions(+), 930 deletions(-) diff --git a/lang/en.json b/lang/en.json index 01221e9a..6068d48e 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1983,6 +1983,7 @@ "inactiveEffects": "Inactive Effects", "inventory": "Inventory", "itemResource": "Item Resource", + "itemQuantity": "Item Quantity", "items": "Items", "label": "Label", "level": "Level", @@ -2124,7 +2125,8 @@ } }, "Consumable": { - "consumeOnUse": "Consume On Use" + "consumeOnUse": "Consume On Use", + "destroyOnEmpty": "Destroy On Empty" }, "DomainCard": { "type": "Type", diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index ed8fd6a1..6c227152 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -1,4 +1,4 @@ -import { abilities } from "../../config/actorConfig.mjs"; +import { abilities } from '../../config/actorConfig.mjs'; const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; @@ -83,7 +83,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio ); context.costs = updatedCosts.map(x => ({ ...x, - label: x.keyIsID + label: x.itemId ? this.action.parent.parent.name : game.i18n.localize(CONFIG.DH.GENERAL.abilityCosts[x.key].label) })); @@ -115,7 +115,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio context.isLite = this.config.roll?.lite; context.extraFormula = this.config.extraFormula; context.formula = this.roll.constructFormula(this.config); - if(this.actor.system.traits) context.abilities = this.getTraitModifiers(); + if (this.actor.system.traits) context.abilities = this.getTraitModifiers(); context.showReaction = !this.config.roll?.type && context.rollType === 'DualityRoll'; context.reactionOverride = this.reactionOverride; @@ -124,12 +124,15 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio } getTraitModifiers() { - return Object.values(abilities).map(a => ({ id: a.id, label: `${game.i18n.localize(a.label)} (${this.actor.system.traits[a.id]?.value.signedString() ?? 0})` })) + return Object.values(abilities).map(a => ({ + id: a.id, + label: `${game.i18n.localize(a.label)} (${this.actor.system.traits[a.id]?.value.signedString() ?? 0})` + })); } static updateRollConfiguration(event, _, formData) { const { ...rest } = foundry.utils.expandObject(formData.object); - + this.config.selectedRollMode = rest.selectedRollMode; if (this.config.costs) { @@ -141,7 +144,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.roll[key] = value; }); } - if(rest.hasOwnProperty("trait")) { + if (rest.hasOwnProperty('trait')) { this.config.roll.trait = rest.trait; this.config.title = game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { ability: game.i18n.localize(abilities[this.config.roll.trait]?.label) @@ -169,14 +172,14 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.config.costs.indexOf(this.config.costs.find(c => c.extKey === button.dataset.key)) > -1 ? this.config.costs.filter(x => x.extKey !== button.dataset.key) : [ - ...this.config.costs, - { - extKey: button.dataset.key, - key: this.config?.data?.parent?.isNPC ? 'fear' : 'hope', - value: 1, - name: this.config.data?.experiences?.[button.dataset.key]?.name - } - ]; + ...this.config.costs, + { + extKey: button.dataset.key, + key: this.config?.data?.parent?.isNPC ? 'fear' : 'hope', + value: 1, + name: this.config.data?.experiences?.[button.dataset.key]?.name + } + ]; this.render(); } diff --git a/module/applications/sheets-configs/action-config.mjs b/module/applications/sheets-configs/action-config.mjs index ea3c6f31..96b6cc48 100644 --- a/module/applications/sheets-configs/action-config.mjs +++ b/module/applications/sheets-configs/action-config.mjs @@ -132,12 +132,19 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { const options = foundry.utils.deepClone(CONFIG.DH.GENERAL.abilityCosts); const resource = this.action.parent.resource; if (resource) { - options[this.action.parent.parent.id] = { + options.resource = { label: 'DAGGERHEART.GENERAL.itemResource', group: 'Global' }; } + if (this.action.parent.metadata.isQuantifiable) { + options.quantity = { + label: 'DAGGERHEART.GENERAL.itemQuantity', + group: 'Global' + }; + } + return options; } @@ -164,13 +171,14 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { _prepareSubmitData(_event, formData) { const submitData = foundry.utils.expandObject(formData.object); + + const itemAbilityCostKeys = Object.keys(CONFIG.DH.GENERAL.itemAbilityCosts); for (const keyPath of this.constructor.CLEAN_ARRAYS) { const data = foundry.utils.getProperty(submitData, keyPath); const dataValues = data ? Object.values(data) : []; if (keyPath === 'cost') { for (var value of dataValues) { - const item = this.action.parent.parent.id === value.key; - value.keyIsID = Boolean(item); + value.itemId = itemAbilityCostKeys.includes(value.key) ? this.action.parent.parent.id : null; } } diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 500141c1..5bc6828b 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -132,6 +132,7 @@ export default class CharacterSheet extends DHBaseActorSheet { }); htmlElement.querySelectorAll('.inventory-item-quantity').forEach(element => { element.addEventListener('change', this.updateItemQuantity.bind(this)); + element.addEventListener('click', e => e.stopPropagation()); }); // Add listener for armor marks input @@ -676,7 +677,7 @@ export default class CharacterSheet extends DHBaseActorSheet { }) }); - if(result) game.system.api.fields.ActionFields.CostField.execute.call(this, result); + if (result) game.system.api.fields.ActionFields.CostField.execute.call(this, result); } //TODO: redo toggleEquipItem method diff --git a/module/applications/sheets/actors/companion.mjs b/module/applications/sheets/actors/companion.mjs index 82aee312..b353adbb 100644 --- a/module/applications/sheets/actors/companion.mjs +++ b/module/applications/sheets/actors/companion.mjs @@ -84,8 +84,7 @@ export default class DhCompanionSheet extends DHBaseActorSheet { return { key: c.key, value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1), - target: resource.target, - keyIsID: resource.keyIsID + target: resource.target }; }); diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index e99c6ff1..d4e6318e 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -561,6 +561,19 @@ export const refreshTypes = { } }; +export const itemAbilityCosts = { + resource: { + id: 'resource', + label: 'DAGGERHEART.GENERAL.resource', + group: 'Global' + }, + quantity: { + id: 'quantity', + label: 'DAGGERHEART.GENERAL.quantity', + group: 'Global' + } +}; + export const abilityCosts = { hitPoints: { id: 'hitPoints', @@ -586,7 +599,8 @@ export const abilityCosts = { id: 'fear', label: 'Fear', group: 'TYPES.Actor.adversary' - } + }, + resource: itemAbilityCosts.resource }; export const countdownTypes = { diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index ca942909..e8ecf79b 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -5,7 +5,7 @@ export default class CostField extends fields.ArrayField { * Action Workflow order */ static order = 150; - + /** @inheritDoc */ constructor(options = {}, context = {}) { const element = new fields.SchemaField({ @@ -14,7 +14,7 @@ export default class CostField extends fields.ArrayField { required: true, initial: 'hope' }), - keyIsID: new fields.BooleanField(), + itemId: new fields.StringField({ nullable: true, initial: null }), value: new fields.NumberField({ nullable: true, initial: 1, min: 0 }), scalable: new fields.BooleanField({ initial: false }), step: new fields.NumberField({ nullable: true, initial: null }), @@ -34,23 +34,23 @@ export default class CostField extends fields.ArrayField { * @param {boolean} [successCost=false] Consume only resources configured as "On Success only" if not already consumed. */ static async execute(config, successCost = false) { - const actor= this.actor.system.partner ?? this.actor, + const actor = this.actor.system.partner ?? this.actor, usefulResources = { - ...foundry.utils.deepClone(actor.system.resources), - fear: { - value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), - max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, - reversed: false - } - }; + ...foundry.utils.deepClone(actor.system.resources), + fear: { + value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), + max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, + reversed: false + } + }; - if(this.parent?.parent) { + if (this.parent?.parent) { for (var cost of config.costs) { - if (cost.keyIsID) { + if (cost.itemId) { usefulResources[cost.key] = { value: cost.value, target: this.parent.parent, - keyIsID: true + itemId: cost.itemId }; } } @@ -69,12 +69,12 @@ export default class CostField extends fields.ArrayField { key: c.key, value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1), target: resource.target, - keyIsID: resource.keyIsID + itemId: resource.itemId }); return a; } }, []); - + await actor.modifyResource(resources); } @@ -95,14 +95,19 @@ export default class CostField extends fields.ArrayField { } /** - * + * * Must be called within Action context. - * @param {*} costs - * @returns + * @param {*} costs + * @returns */ static calcCosts(costs) { const resources = CostField.getResources.call(this, costs); - return costs.map(c => { + let filteredCosts = costs; + if (this.parent.metadata.isQuantifiable && this.parent.consumeOnUse === false) { + filteredCosts = filteredCosts.filter(c => c.key !== 'quantity'); + } + + return filteredCosts.map(c => { c.scale = c.scale ?? 0; c.step = c.step ?? 1; c.total = c.value + c.scale * c.step; @@ -121,7 +126,7 @@ export default class CostField extends fields.ArrayField { /** * Check if the current Actor currently has all needed resources. * Must be called within Action context. - * @param {*} costs + * @param {*} costs * @returns {boolean} */ static hasCost(costs) { @@ -153,8 +158,8 @@ export default class CostField extends fields.ArrayField { /** * Get all Actor resources + parent Item potential one. * Must be called within Action context. - * @param {*} costs - * @returns + * @param {*} costs + * @returns */ static getResources(costs) { const actorResources = foundry.utils.deepClone(this.actor.system.resources); @@ -162,11 +167,8 @@ export default class CostField extends fields.ArrayField { actorResources.hope = foundry.utils.deepClone(this.actor.system.partner.system.resources.hope); const itemResources = {}; for (let itemResource of costs) { - if (itemResource.keyIsID) { - itemResources[itemResource.key] = { - value: this.parent.resource.value ?? 0, - max: CostField.formatMax.call(this, this.parent?.resource?.max) - }; + if (itemResource.itemId) { + itemResources[itemResource.key] = CostField.getItemIdCostResource.bind(this)(itemResource); } } @@ -176,10 +178,44 @@ export default class CostField extends fields.ArrayField { }; } + static getItemIdCostResource(itemResource) { + switch (itemResource.key) { + case CONFIG.DH.GENERAL.itemAbilityCosts.resource.id: + return { + value: this.parent.resource.value ?? 0, + max: CostField.formatMax.call(this, this.parent?.resource?.max) + }; + case CONFIG.DH.GENERAL.itemAbilityCosts.quantity.id: + return { + value: this.parent.quantity ?? 0, + max: this.parent.quantity ?? 0 + }; + default: + return { value: 0, max: 0 }; + } + } + + static getItemIdCostUpdate(r) { + switch (r.key) { + case CONFIG.DH.GENERAL.itemAbilityCosts.resource.id: + return { + path: 'system.resource.value', + value: r.target.system.resource.value + r.value + }; + case CONFIG.DH.GENERAL.itemAbilityCosts.quantity.id: + return { + path: 'system.quantity', + value: r.target.system.quantity + r.value + }; + default: + return { path: '', value: undefined }; + } + } + /** - * - * @param {*} costs - * @returns + * + * @param {*} costs + * @returns */ static getRealCosts(costs) { const realCosts = costs?.length ? costs.filter(c => c.enabled) : []; diff --git a/module/data/item/consumable.mjs b/module/data/item/consumable.mjs index dad6a95c..5a50525a 100644 --- a/module/data/item/consumable.mjs +++ b/module/data/item/consumable.mjs @@ -1,5 +1,4 @@ import BaseDataItem from './base.mjs'; -import { ActionField } from '../fields/actionField.mjs'; export default class DHConsumable extends BaseDataItem { /** @inheritDoc */ @@ -19,7 +18,8 @@ export default class DHConsumable extends BaseDataItem { const fields = foundry.data.fields; return { ...super.defineSchema(), - consumeOnUse: new fields.BooleanField({ initial: false }) + consumeOnUse: new fields.BooleanField({ initial: true }), + destroyOnEmpty: new fields.BooleanField({ initial: true }) }; } @@ -27,5 +27,4 @@ export default class DHConsumable extends BaseDataItem { /**@override */ static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/items/round-potion.svg'; - } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 950ed621..79e71549 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -659,13 +659,22 @@ export default class DhpActor extends Actor { }; resources.forEach(r => { - if (r.keyIsID) { - updates.items[r.key] = { - target: r.target, - resources: { - 'system.resource.value': r.target.system.resource.value + r.value - } - }; + if (r.itemId) { + const { path, value } = game.system.api.fields.ActionFields.CostField.getItemIdCostUpdate(r); + + if ( + r.key === 'quantity' && + r.target.type === 'consumable' && + value === 0 && + r.target.system.destroyOnEmpty + ) { + r.target.delete(); + } else { + updates.items[r.key] = { + target: r.target, + resources: { [path]: value } + }; + } } else { switch (r.key) { case 'fear': diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index 134c8714..015c19cb 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -96,5 +96,60 @@ export async function runMigrations() { lastMigrationVersion = '1.1.1'; } + if (foundry.utils.isNewerVersion('1.2.0', lastMigrationVersion)) { + const lockedPacks = []; + const compendiumItems = []; + for (let pack of game.packs) { + if (pack.locked) { + lockedPacks.push(pack.collection); + await pack.configure({ locked: false }); + } + const documents = await pack.getDocuments(); + + compendiumItems.push(...documents.filter(x => x.system?.metadata?.hasActions)); + compendiumItems.push( + ...documents + .filter(x => x.items) + .flatMap(actor => actor.items.filter(x => x.system?.metadata?.hasActions)) + ); + } + + const worldItems = game.items.filter(x => x.system.metadata.hasActions); + const worldActorItems = Array.from(game.actors).flatMap(actor => + actor.items.filter(x => x.system.metadata.hasActions) + ); + + const validCostKeys = Object.keys(CONFIG.DH.GENERAL.abilityCosts); + for (let item of [...worldItems, ...worldActorItems, ...compendiumItems]) { + for (let action of item.system.actions) { + const resourceCostIndexes = Object.keys(action.cost).reduce( + (acc, index) => (!validCostKeys.includes(action.cost[index].key) ? [...acc, Number(index)] : acc), + [] + ); + if (resourceCostIndexes.length === 0) continue; + + await action.update({ + cost: action.cost.map((cost, index) => { + const { keyIsID, ...rest } = cost; + if (!resourceCostIndexes.includes(index)) return { ...rest }; + + return { + ...rest, + key: 'resource', + itemId: cost.key + }; + }) + }); + } + } + + for (let packId of lockedPacks) { + const pack = game.packs.get(packId); + await pack.configure({ locked: true }); + } + + lastMigrationVersion = '1.2.0'; + } + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion); } diff --git a/src/packs/adversaries/adversary_Acid_Burrower_89yAh30vaNQOALlz.json b/src/packs/adversaries/adversary_Acid_Burrower_89yAh30vaNQOALlz.json index 0bc8d39d..d5ae1c67 100644 --- a/src/packs/adversaries/adversary_Acid_Burrower_89yAh30vaNQOALlz.json +++ b/src/packs/adversaries/adversary_Acid_Burrower_89yAh30vaNQOALlz.json @@ -316,7 +316,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json b/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json index c8079e49..2dde1b60 100644 --- a/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json +++ b/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json @@ -392,7 +392,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -507,7 +506,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -727,7 +725,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json b/src/packs/adversaries/adversary_Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json index ad630902..663aa8a3 100644 --- a/src/packs/adversaries/adversary_Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json +++ b/src/packs/adversaries/adversary_Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json @@ -278,7 +278,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Arch_Necromancer_WPEOIGfclNJxWb87.json b/src/packs/adversaries/adversary_Arch_Necromancer_WPEOIGfclNJxWb87.json index 720dd90b..c8f0b0d1 100644 --- a/src/packs/adversaries/adversary_Arch_Necromancer_WPEOIGfclNJxWb87.json +++ b/src/packs/adversaries/adversary_Arch_Necromancer_WPEOIGfclNJxWb87.json @@ -257,7 +257,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -345,7 +344,6 @@ "scalable": false, "key": "stress", "value": 2, - "keyIsID": false, "step": null } ], @@ -579,7 +577,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -643,7 +640,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Archer_Squadron_0ts6CGd93lLqGZI5.json b/src/packs/adversaries/adversary_Archer_Squadron_0ts6CGd93lLqGZI5.json index 6f43714d..18906dbd 100644 --- a/src/packs/adversaries/adversary_Archer_Squadron_0ts6CGd93lLqGZI5.json +++ b/src/packs/adversaries/adversary_Archer_Squadron_0ts6CGd93lLqGZI5.json @@ -279,7 +279,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Battle_Box_dgH3fW9FTYLaIDvS.json b/src/packs/adversaries/adversary_Battle_Box_dgH3fW9FTYLaIDvS.json index 5765ca72..acb1b601 100644 --- a/src/packs/adversaries/adversary_Battle_Box_dgH3fW9FTYLaIDvS.json +++ b/src/packs/adversaries/adversary_Battle_Box_dgH3fW9FTYLaIDvS.json @@ -285,7 +285,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -1091,7 +1090,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Bear_71qKDLKO3CsrNkdy.json b/src/packs/adversaries/adversary_Bear_71qKDLKO3CsrNkdy.json index a1c06c52..e9f2c499 100644 --- a/src/packs/adversaries/adversary_Bear_71qKDLKO3CsrNkdy.json +++ b/src/packs/adversaries/adversary_Bear_71qKDLKO3CsrNkdy.json @@ -293,7 +293,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json b/src/packs/adversaries/adversary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json index 052f5c1e..9d59323c 100644 --- a/src/packs/adversaries/adversary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json +++ b/src/packs/adversaries/adversary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json @@ -404,7 +404,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json b/src/packs/adversaries/adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json index ca46acc2..e64e5c6f 100644 --- a/src/packs/adversaries/adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json +++ b/src/packs/adversaries/adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json @@ -253,7 +253,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -352,7 +351,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json b/src/packs/adversaries/adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json index ff9b1215..9be9c300 100644 --- a/src/packs/adversaries/adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json +++ b/src/packs/adversaries/adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json @@ -364,7 +364,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json b/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json index 51d3f73d..b30bdc84 100644 --- a/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json +++ b/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json @@ -272,7 +272,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Construct_uOP5oT9QzXPlnf3p.json b/src/packs/adversaries/adversary_Construct_uOP5oT9QzXPlnf3p.json index ed0d9abd..2f292c8f 100644 --- a/src/packs/adversaries/adversary_Construct_uOP5oT9QzXPlnf3p.json +++ b/src/packs/adversaries/adversary_Construct_uOP5oT9QzXPlnf3p.json @@ -316,7 +316,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -432,7 +431,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Courtesan_ZxWaWPdzFIUPNC62.json b/src/packs/adversaries/adversary_Courtesan_ZxWaWPdzFIUPNC62.json index 6432afab..95a29d0b 100644 --- a/src/packs/adversaries/adversary_Courtesan_ZxWaWPdzFIUPNC62.json +++ b/src/packs/adversaries/adversary_Courtesan_ZxWaWPdzFIUPNC62.json @@ -257,7 +257,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Courtier_CBBuEXAlLKFMJdjg.json b/src/packs/adversaries/adversary_Courtier_CBBuEXAlLKFMJdjg.json index 90f7bd6c..75bff568 100644 --- a/src/packs/adversaries/adversary_Courtier_CBBuEXAlLKFMJdjg.json +++ b/src/packs/adversaries/adversary_Courtier_CBBuEXAlLKFMJdjg.json @@ -254,7 +254,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -421,7 +420,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Cult_Adept_0NxCSugvKQ4W8OYZ.json b/src/packs/adversaries/adversary_Cult_Adept_0NxCSugvKQ4W8OYZ.json index d83ce510..091b862c 100644 --- a/src/packs/adversaries/adversary_Cult_Adept_0NxCSugvKQ4W8OYZ.json +++ b/src/packs/adversaries/adversary_Cult_Adept_0NxCSugvKQ4W8OYZ.json @@ -257,7 +257,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -397,7 +396,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -524,7 +522,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Cult_Fang_tyBOpLfigAhI9bU3.json b/src/packs/adversaries/adversary_Cult_Fang_tyBOpLfigAhI9bU3.json index 236402ea..47626209 100644 --- a/src/packs/adversaries/adversary_Cult_Fang_tyBOpLfigAhI9bU3.json +++ b/src/packs/adversaries/adversary_Cult_Fang_tyBOpLfigAhI9bU3.json @@ -246,7 +246,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -310,7 +309,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json b/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json index 6fc8ac9b..12f715c5 100644 --- a/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json +++ b/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json @@ -272,7 +272,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json b/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json index b5313df2..34f27f93 100644 --- a/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json +++ b/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json @@ -285,7 +285,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -399,7 +398,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Demon_of_Hubris_2VN3BftageoTTIzu.json b/src/packs/adversaries/adversary_Demon_of_Hubris_2VN3BftageoTTIzu.json index 61427aab..4144b8c3 100644 --- a/src/packs/adversaries/adversary_Demon_of_Hubris_2VN3BftageoTTIzu.json +++ b/src/packs/adversaries/adversary_Demon_of_Hubris_2VN3BftageoTTIzu.json @@ -252,7 +252,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -432,7 +431,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -527,7 +525,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json b/src/packs/adversaries/adversary_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json index 718f4a9a..ba8d1592 100644 --- a/src/packs/adversaries/adversary_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json +++ b/src/packs/adversaries/adversary_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json @@ -318,7 +318,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json b/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json index 5bdeddf9..027383e2 100644 --- a/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json +++ b/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json @@ -285,7 +285,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -349,7 +348,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Dire_Bat_tBWHW00epmMnkawe.json b/src/packs/adversaries/adversary_Dire_Bat_tBWHW00epmMnkawe.json index fde75123..22408c0e 100644 --- a/src/packs/adversaries/adversary_Dire_Bat_tBWHW00epmMnkawe.json +++ b/src/packs/adversaries/adversary_Dire_Bat_tBWHW00epmMnkawe.json @@ -421,7 +421,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json b/src/packs/adversaries/adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json index 7104b09f..0e501248 100644 --- a/src/packs/adversaries/adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json +++ b/src/packs/adversaries/adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json @@ -360,7 +360,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Dryad_wR7cFKrHvRzbzhBT.json b/src/packs/adversaries/adversary_Dryad_wR7cFKrHvRzbzhBT.json index 7fb7a97d..7bc91293 100644 --- a/src/packs/adversaries/adversary_Dryad_wR7cFKrHvRzbzhBT.json +++ b/src/packs/adversaries/adversary_Dryad_wR7cFKrHvRzbzhBT.json @@ -252,7 +252,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -393,7 +392,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -457,7 +455,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Electric_Eels_TLzY1nDw0Bu9Ud40.json b/src/packs/adversaries/adversary_Electric_Eels_TLzY1nDw0Bu9Ud40.json index 77a84dcf..0412ba4b 100644 --- a/src/packs/adversaries/adversary_Electric_Eels_TLzY1nDw0Bu9Ud40.json +++ b/src/packs/adversaries/adversary_Electric_Eels_TLzY1nDw0Bu9Ud40.json @@ -279,7 +279,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json b/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json index 2e08edfd..618323ce 100644 --- a/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json +++ b/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json @@ -272,7 +272,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Elite_Soldier_bfhVWMBUh61b9J6n.json b/src/packs/adversaries/adversary_Elite_Soldier_bfhVWMBUh61b9J6n.json index a9e26e65..145b2534 100644 --- a/src/packs/adversaries/adversary_Elite_Soldier_bfhVWMBUh61b9J6n.json +++ b/src/packs/adversaries/adversary_Elite_Soldier_bfhVWMBUh61b9J6n.json @@ -277,7 +277,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -391,7 +390,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json b/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json index 6546153e..96efe6c6 100644 --- a/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json +++ b/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json @@ -370,7 +370,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json b/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json index b87fb26a..f1652c33 100644 --- a/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json +++ b/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json @@ -358,7 +358,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Fallen_Sorcerer_PELRry1vqjBzSAlr.json b/src/packs/adversaries/adversary_Fallen_Sorcerer_PELRry1vqjBzSAlr.json index 91530e2e..a49ac9e1 100644 --- a/src/packs/adversaries/adversary_Fallen_Sorcerer_PELRry1vqjBzSAlr.json +++ b/src/packs/adversaries/adversary_Fallen_Sorcerer_PELRry1vqjBzSAlr.json @@ -252,7 +252,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Fallen_Warlord__Realm_Breaker_hxZ0sgoFJubh5aj6.json b/src/packs/adversaries/adversary_Fallen_Warlord__Realm_Breaker_hxZ0sgoFJubh5aj6.json index 73ae6429..4d4e9847 100644 --- a/src/packs/adversaries/adversary_Fallen_Warlord__Realm_Breaker_hxZ0sgoFJubh5aj6.json +++ b/src/packs/adversaries/adversary_Fallen_Warlord__Realm_Breaker_hxZ0sgoFJubh5aj6.json @@ -389,7 +389,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Fallen_Warlord__Undefeated_Champion_RXkZTwBRi4dJ3JE5.json b/src/packs/adversaries/adversary_Fallen_Warlord__Undefeated_Champion_RXkZTwBRi4dJ3JE5.json index d49889b8..d0a50e2c 100644 --- a/src/packs/adversaries/adversary_Fallen_Warlord__Undefeated_Champion_RXkZTwBRi4dJ3JE5.json +++ b/src/packs/adversaries/adversary_Fallen_Warlord__Undefeated_Champion_RXkZTwBRi4dJ3JE5.json @@ -497,7 +497,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Giant_Brawler_YnObCleGjPT7yqEc.json b/src/packs/adversaries/adversary_Giant_Brawler_YnObCleGjPT7yqEc.json index bfcbbba2..81f7ad37 100644 --- a/src/packs/adversaries/adversary_Giant_Brawler_YnObCleGjPT7yqEc.json +++ b/src/packs/adversaries/adversary_Giant_Brawler_YnObCleGjPT7yqEc.json @@ -252,7 +252,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json b/src/packs/adversaries/adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json index b342864e..f400054b 100644 --- a/src/packs/adversaries/adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json +++ b/src/packs/adversaries/adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json @@ -359,7 +359,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json b/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json index a16fab7d..8de3035a 100644 --- a/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json +++ b/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json @@ -374,7 +374,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json b/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json index 9046d679..4234430c 100644 --- a/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json +++ b/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json @@ -281,7 +281,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json b/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json index 3365533c..2e697015 100644 --- a/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json +++ b/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json @@ -272,7 +272,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Giant_Scorpion_fmfntuJ8mHRCAktP.json b/src/packs/adversaries/adversary_Giant_Scorpion_fmfntuJ8mHRCAktP.json index 32dee4b9..caebb002 100644 --- a/src/packs/adversaries/adversary_Giant_Scorpion_fmfntuJ8mHRCAktP.json +++ b/src/packs/adversaries/adversary_Giant_Scorpion_fmfntuJ8mHRCAktP.json @@ -254,7 +254,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Glass_Snake_8KWVLWXFhlY2kYx0.json b/src/packs/adversaries/adversary_Glass_Snake_8KWVLWXFhlY2kYx0.json index a79154e5..31b3721c 100644 --- a/src/packs/adversaries/adversary_Glass_Snake_8KWVLWXFhlY2kYx0.json +++ b/src/packs/adversaries/adversary_Glass_Snake_8KWVLWXFhlY2kYx0.json @@ -335,7 +335,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -449,7 +448,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Gorgon_8mJYMpbLTb8qIOrr.json b/src/packs/adversaries/adversary_Gorgon_8mJYMpbLTb8qIOrr.json index e68bc6f4..740fb7c9 100644 --- a/src/packs/adversaries/adversary_Gorgon_8mJYMpbLTb8qIOrr.json +++ b/src/packs/adversaries/adversary_Gorgon_8mJYMpbLTb8qIOrr.json @@ -523,7 +523,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json b/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json index 59b08577..3325eee8 100644 --- a/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json +++ b/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json @@ -455,7 +455,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Greater_Water_Elemental_xIICT6tEdnA7dKDV.json b/src/packs/adversaries/adversary_Greater_Water_Elemental_xIICT6tEdnA7dKDV.json index b1da3ec3..e8fcd0ce 100644 --- a/src/packs/adversaries/adversary_Greater_Water_Elemental_xIICT6tEdnA7dKDV.json +++ b/src/packs/adversaries/adversary_Greater_Water_Elemental_xIICT6tEdnA7dKDV.json @@ -246,7 +246,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -418,7 +417,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -559,7 +557,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Green_Ooze_SHXedd9zZPVfUgUa.json b/src/packs/adversaries/adversary_Green_Ooze_SHXedd9zZPVfUgUa.json index ca663abf..c359782a 100644 --- a/src/packs/adversaries/adversary_Green_Ooze_SHXedd9zZPVfUgUa.json +++ b/src/packs/adversaries/adversary_Green_Ooze_SHXedd9zZPVfUgUa.json @@ -563,7 +563,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json b/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json index aea1a4eb..4cf17ff5 100644 --- a/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json +++ b/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json @@ -279,7 +279,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json b/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json index 69b1d11d..82d74b93 100644 --- a/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json +++ b/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json @@ -272,7 +272,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -336,7 +335,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Harrier_uRtghKE9mHlII4rs.json b/src/packs/adversaries/adversary_Harrier_uRtghKE9mHlII4rs.json index 52289d7b..c4f94d8e 100644 --- a/src/packs/adversaries/adversary_Harrier_uRtghKE9mHlII4rs.json +++ b/src/packs/adversaries/adversary_Harrier_uRtghKE9mHlII4rs.json @@ -287,7 +287,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Head_Guard_mK3A5FTx6k8iPU3F.json b/src/packs/adversaries/adversary_Head_Guard_mK3A5FTx6k8iPU3F.json index 467ce106..59e35d06 100644 --- a/src/packs/adversaries/adversary_Head_Guard_mK3A5FTx6k8iPU3F.json +++ b/src/packs/adversaries/adversary_Head_Guard_mK3A5FTx6k8iPU3F.json @@ -259,7 +259,6 @@ "scalable": false, "key": "fear", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Head_Vampire_i2UNbRvgyoSs07M6.json b/src/packs/adversaries/adversary_Head_Vampire_i2UNbRvgyoSs07M6.json index 8ac5ecdb..c6a3c141 100644 --- a/src/packs/adversaries/adversary_Head_Vampire_i2UNbRvgyoSs07M6.json +++ b/src/packs/adversaries/adversary_Head_Vampire_i2UNbRvgyoSs07M6.json @@ -519,7 +519,6 @@ "scalable": false, "key": "fear", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_High_Seraph_r1mbfSSwKWdcFdAU.json b/src/packs/adversaries/adversary_High_Seraph_r1mbfSSwKWdcFdAU.json index e2cdfbef..ca62b98f 100644 --- a/src/packs/adversaries/adversary_High_Seraph_r1mbfSSwKWdcFdAU.json +++ b/src/packs/adversaries/adversary_High_Seraph_r1mbfSSwKWdcFdAU.json @@ -285,7 +285,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -455,7 +454,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -570,7 +568,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json b/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json index 5982e496..d9efc9fb 100644 --- a/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json +++ b/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json @@ -532,7 +532,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Hydra_MI126iMOOobQ1Obn.json b/src/packs/adversaries/adversary_Hydra_MI126iMOOobQ1Obn.json index d2d4fd83..002efede 100644 --- a/src/packs/adversaries/adversary_Hydra_MI126iMOOobQ1Obn.json +++ b/src/packs/adversaries/adversary_Hydra_MI126iMOOobQ1Obn.json @@ -317,7 +317,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json b/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json index fbfd0a66..edf8f3ea 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json @@ -281,7 +281,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Lieutenant_aTljstqteGoLpCBq.json b/src/packs/adversaries/adversary_Jagged_Knife_Lieutenant_aTljstqteGoLpCBq.json index 22d89d60..48430ae0 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Lieutenant_aTljstqteGoLpCBq.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Lieutenant_aTljstqteGoLpCBq.json @@ -253,7 +253,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -352,7 +351,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Knight_of_the_Realm_7ai2opemrclQe3VF.json b/src/packs/adversaries/adversary_Knight_of_the_Realm_7ai2opemrclQe3VF.json index 3fe4bac0..31fa44f3 100644 --- a/src/packs/adversaries/adversary_Knight_of_the_Realm_7ai2opemrclQe3VF.json +++ b/src/packs/adversaries/adversary_Knight_of_the_Realm_7ai2opemrclQe3VF.json @@ -562,7 +562,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Master_Assassin_dNta0cUzr96xcFhf.json b/src/packs/adversaries/adversary_Master_Assassin_dNta0cUzr96xcFhf.json index 2b16ec95..15766df4 100644 --- a/src/packs/adversaries/adversary_Master_Assassin_dNta0cUzr96xcFhf.json +++ b/src/packs/adversaries/adversary_Master_Assassin_dNta0cUzr96xcFhf.json @@ -290,7 +290,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -354,7 +353,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Merchant_Baron_Vy02IhGhkJLuezu4.json b/src/packs/adversaries/adversary_Merchant_Baron_Vy02IhGhkJLuezu4.json index 5b06271d..315f6039 100644 --- a/src/packs/adversaries/adversary_Merchant_Baron_Vy02IhGhkJLuezu4.json +++ b/src/packs/adversaries/adversary_Merchant_Baron_Vy02IhGhkJLuezu4.json @@ -363,7 +363,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json b/src/packs/adversaries/adversary_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json index 9761b071..8344cc1e 100644 --- a/src/packs/adversaries/adversary_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json +++ b/src/packs/adversaries/adversary_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json @@ -334,7 +334,6 @@ "scalable": false, "key": "hitPoints", "value": 1, - "keyIsID": false, "step": null } ], @@ -481,7 +480,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Minor_Demon_3tqCjDwJAQ7JKqMb.json b/src/packs/adversaries/adversary_Minor_Demon_3tqCjDwJAQ7JKqMb.json index da22947a..58b0b8e4 100644 --- a/src/packs/adversaries/adversary_Minor_Demon_3tqCjDwJAQ7JKqMb.json +++ b/src/packs/adversaries/adversary_Minor_Demon_3tqCjDwJAQ7JKqMb.json @@ -368,7 +368,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -484,7 +483,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Minor_Fire_Elemental_DscWkNVoHak6P4hh.json b/src/packs/adversaries/adversary_Minor_Fire_Elemental_DscWkNVoHak6P4hh.json index 34727ef0..b57a7ddc 100644 --- a/src/packs/adversaries/adversary_Minor_Fire_Elemental_DscWkNVoHak6P4hh.json +++ b/src/packs/adversaries/adversary_Minor_Fire_Elemental_DscWkNVoHak6P4hh.json @@ -282,7 +282,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -398,7 +397,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json b/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json index fd0e6a51..b5ca05c9 100644 --- a/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json +++ b/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json @@ -275,7 +275,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json b/src/packs/adversaries/adversary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json index b98e3640..ac3766d3 100644 --- a/src/packs/adversaries/adversary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json +++ b/src/packs/adversaries/adversary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json @@ -246,7 +246,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -310,7 +309,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Monarch_yx0vK2yfNVZKWUUi.json b/src/packs/adversaries/adversary_Monarch_yx0vK2yfNVZKWUUi.json index 4f88edeb..b4868595 100644 --- a/src/packs/adversaries/adversary_Monarch_yx0vK2yfNVZKWUUi.json +++ b/src/packs/adversaries/adversary_Monarch_yx0vK2yfNVZKWUUi.json @@ -290,7 +290,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -354,7 +353,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json b/src/packs/adversaries/adversary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json index 6681a8f2..8c2332aa 100644 --- a/src/packs/adversaries/adversary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json +++ b/src/packs/adversaries/adversary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json @@ -338,7 +338,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -452,7 +451,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Oracle_of_Doom_befIqd5IYKg6eUz2.json b/src/packs/adversaries/adversary_Oracle_of_Doom_befIqd5IYKg6eUz2.json index 3546a587..4ada4bf1 100644 --- a/src/packs/adversaries/adversary_Oracle_of_Doom_befIqd5IYKg6eUz2.json +++ b/src/packs/adversaries/adversary_Oracle_of_Doom_befIqd5IYKg6eUz2.json @@ -424,7 +424,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -539,7 +538,6 @@ "scalable": false, "key": "fear", "value": 2, - "keyIsID": false, "step": null } ], @@ -660,7 +658,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json b/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json index 94634439..4eb8ba4b 100644 --- a/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json +++ b/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json @@ -272,7 +272,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Patchwork_Zombie_Hulk_EQTOAOUrkIvS2z88.json b/src/packs/adversaries/adversary_Patchwork_Zombie_Hulk_EQTOAOUrkIvS2z88.json index 5509e2ff..75c438d2 100644 --- a/src/packs/adversaries/adversary_Patchwork_Zombie_Hulk_EQTOAOUrkIvS2z88.json +++ b/src/packs/adversaries/adversary_Patchwork_Zombie_Hulk_EQTOAOUrkIvS2z88.json @@ -452,7 +452,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Perfected_Zombie_CP6iRfHdyFWniTHY.json b/src/packs/adversaries/adversary_Perfected_Zombie_CP6iRfHdyFWniTHY.json index e428a33f..97fd5789 100644 --- a/src/packs/adversaries/adversary_Perfected_Zombie_CP6iRfHdyFWniTHY.json +++ b/src/packs/adversaries/adversary_Perfected_Zombie_CP6iRfHdyFWniTHY.json @@ -365,7 +365,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Petty_Noble_wycLpvebWdUqRhpP.json b/src/packs/adversaries/adversary_Petty_Noble_wycLpvebWdUqRhpP.json index 0f7b07a2..d0f8ec58 100644 --- a/src/packs/adversaries/adversary_Petty_Noble_wycLpvebWdUqRhpP.json +++ b/src/packs/adversaries/adversary_Petty_Noble_wycLpvebWdUqRhpP.json @@ -288,7 +288,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -377,7 +376,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Pirate_Captain_OROJbjsqagVh7ECV.json b/src/packs/adversaries/adversary_Pirate_Captain_OROJbjsqagVh7ECV.json index 343eed99..8893201c 100644 --- a/src/packs/adversaries/adversary_Pirate_Captain_OROJbjsqagVh7ECV.json +++ b/src/packs/adversaries/adversary_Pirate_Captain_OROJbjsqagVh7ECV.json @@ -346,7 +346,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -411,7 +410,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json b/src/packs/adversaries/adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json index 55dccaa4..54340160 100644 --- a/src/packs/adversaries/adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json +++ b/src/packs/adversaries/adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json @@ -499,7 +499,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json b/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json index 4a168cac..f9f0c5d0 100644 --- a/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json +++ b/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json @@ -275,7 +275,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Royal_Advisor_EtLJiTsilPPZvLUX.json b/src/packs/adversaries/adversary_Royal_Advisor_EtLJiTsilPPZvLUX.json index 58e52937..93453c35 100644 --- a/src/packs/adversaries/adversary_Royal_Advisor_EtLJiTsilPPZvLUX.json +++ b/src/packs/adversaries/adversary_Royal_Advisor_EtLJiTsilPPZvLUX.json @@ -343,7 +343,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -407,7 +406,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Secret_Keeper_sLAccjvCWfeedbpI.json b/src/packs/adversaries/adversary_Secret_Keeper_sLAccjvCWfeedbpI.json index ffba4d54..ac2dc316 100644 --- a/src/packs/adversaries/adversary_Secret_Keeper_sLAccjvCWfeedbpI.json +++ b/src/packs/adversaries/adversary_Secret_Keeper_sLAccjvCWfeedbpI.json @@ -257,7 +257,6 @@ "scalable": false, "key": "fear", "value": 2, - "keyIsID": false, "step": null } ], @@ -345,7 +344,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -532,7 +530,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json b/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json index 50bfa399..d281b974 100644 --- a/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json +++ b/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json @@ -275,7 +275,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Shark_YmVAkdNsyuXWTtYp.json b/src/packs/adversaries/adversary_Shark_YmVAkdNsyuXWTtYp.json index d8437156..1e1b422a 100644 --- a/src/packs/adversaries/adversary_Shark_YmVAkdNsyuXWTtYp.json +++ b/src/packs/adversaries/adversary_Shark_YmVAkdNsyuXWTtYp.json @@ -464,7 +464,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Siren_BK4jwyXSRx7IOQiO.json b/src/packs/adversaries/adversary_Siren_BK4jwyXSRx7IOQiO.json index cfa42eac..06f3c4a8 100644 --- a/src/packs/adversaries/adversary_Siren_BK4jwyXSRx7IOQiO.json +++ b/src/packs/adversaries/adversary_Siren_BK4jwyXSRx7IOQiO.json @@ -359,7 +359,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json b/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json index f444fb51..5f32df25 100644 --- a/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json +++ b/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json @@ -275,7 +275,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Skeleton_Knight_Q9LaVTyXF9NF12C7.json b/src/packs/adversaries/adversary_Skeleton_Knight_Q9LaVTyXF9NF12C7.json index 4696285a..eb13eed0 100644 --- a/src/packs/adversaries/adversary_Skeleton_Knight_Q9LaVTyXF9NF12C7.json +++ b/src/packs/adversaries/adversary_Skeleton_Knight_Q9LaVTyXF9NF12C7.json @@ -335,7 +335,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Spectral_Archer_5tCkhnBByUIN5UdG.json b/src/packs/adversaries/adversary_Spectral_Archer_5tCkhnBByUIN5UdG.json index 9a0e8aaf..a4ce7e26 100644 --- a/src/packs/adversaries/adversary_Spectral_Archer_5tCkhnBByUIN5UdG.json +++ b/src/packs/adversaries/adversary_Spectral_Archer_5tCkhnBByUIN5UdG.json @@ -252,7 +252,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json b/src/packs/adversaries/adversary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json index bd13b3e1..86d197b2 100644 --- a/src/packs/adversaries/adversary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json +++ b/src/packs/adversaries/adversary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json @@ -252,7 +252,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -368,7 +367,6 @@ "scalable": false, "key": "fear", "value": 2, - "keyIsID": false, "step": null } ], @@ -432,7 +430,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Spectral_Guardian_UFVGl1osOsJTneLf.json b/src/packs/adversaries/adversary_Spectral_Guardian_UFVGl1osOsJTneLf.json index 84a4a49a..38545446 100644 --- a/src/packs/adversaries/adversary_Spectral_Guardian_UFVGl1osOsJTneLf.json +++ b/src/packs/adversaries/adversary_Spectral_Guardian_UFVGl1osOsJTneLf.json @@ -252,7 +252,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -368,7 +367,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Spellblade_ldbWEL7uZs84vyrR.json b/src/packs/adversaries/adversary_Spellblade_ldbWEL7uZs84vyrR.json index 80130503..fcce5c20 100644 --- a/src/packs/adversaries/adversary_Spellblade_ldbWEL7uZs84vyrR.json +++ b/src/packs/adversaries/adversary_Spellblade_ldbWEL7uZs84vyrR.json @@ -289,7 +289,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -405,7 +404,6 @@ "scalable": false, "key": "fear", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Spy_8zlynOhnVA59KpKT.json b/src/packs/adversaries/adversary_Spy_8zlynOhnVA59KpKT.json index 9349713e..b4a5fbb7 100644 --- a/src/packs/adversaries/adversary_Spy_8zlynOhnVA59KpKT.json +++ b/src/packs/adversaries/adversary_Spy_8zlynOhnVA59KpKT.json @@ -252,7 +252,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -316,7 +315,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Stag_Knight_KGVwnLq85ywP9xvB.json b/src/packs/adversaries/adversary_Stag_Knight_KGVwnLq85ywP9xvB.json index f448bdf0..91b83239 100644 --- a/src/packs/adversaries/adversary_Stag_Knight_KGVwnLq85ywP9xvB.json +++ b/src/packs/adversaries/adversary_Stag_Knight_KGVwnLq85ywP9xvB.json @@ -339,7 +339,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -427,7 +426,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json b/src/packs/adversaries/adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json index e60fe4cc..f6957805 100644 --- a/src/packs/adversaries/adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json +++ b/src/packs/adversaries/adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json @@ -285,7 +285,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -450,7 +449,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Sylvan_Soldier_VtFBt9XBE0WrGGxP.json b/src/packs/adversaries/adversary_Sylvan_Soldier_VtFBt9XBE0WrGGxP.json index dc35d8b9..12b26a15 100644 --- a/src/packs/adversaries/adversary_Sylvan_Soldier_VtFBt9XBE0WrGGxP.json +++ b/src/packs/adversaries/adversary_Sylvan_Soldier_VtFBt9XBE0WrGGxP.json @@ -362,7 +362,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -478,7 +477,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json b/src/packs/adversaries/adversary_Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json index 1a8f1087..c54afb36 100644 --- a/src/packs/adversaries/adversary_Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json +++ b/src/packs/adversaries/adversary_Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json @@ -316,7 +316,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json b/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json index 6a5b19d5..f3fde38b 100644 --- a/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json +++ b/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json @@ -312,7 +312,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json b/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json index b081ca1f..139212b5 100644 --- a/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json +++ b/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json @@ -271,7 +271,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Vampire_WWyUp6Mxl1S3KYUG.json b/src/packs/adversaries/adversary_Vampire_WWyUp6Mxl1S3KYUG.json index 5617570b..285feb7b 100644 --- a/src/packs/adversaries/adversary_Vampire_WWyUp6Mxl1S3KYUG.json +++ b/src/packs/adversaries/adversary_Vampire_WWyUp6Mxl1S3KYUG.json @@ -407,7 +407,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Vault_Guardian_Gaoler_JqYraOqNmmhHk4Yy.json b/src/packs/adversaries/adversary_Vault_Guardian_Gaoler_JqYraOqNmmhHk4Yy.json index 5402a6cd..e28a4227 100644 --- a/src/packs/adversaries/adversary_Vault_Guardian_Gaoler_JqYraOqNmmhHk4Yy.json +++ b/src/packs/adversaries/adversary_Vault_Guardian_Gaoler_JqYraOqNmmhHk4Yy.json @@ -279,7 +279,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json b/src/packs/adversaries/adversary_Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json index 34e698e5..3afcd1f7 100644 --- a/src/packs/adversaries/adversary_Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json +++ b/src/packs/adversaries/adversary_Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json @@ -279,7 +279,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -400,7 +399,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json b/src/packs/adversaries/adversary_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json index b16a2f85..05b0908b 100644 --- a/src/packs/adversaries/adversary_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json +++ b/src/packs/adversaries/adversary_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json @@ -284,7 +284,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -405,7 +404,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Volcanic_Dragon__Ashen_Tyrant_pMuXGCSOQaxpi5tb.json b/src/packs/adversaries/adversary_Volcanic_Dragon__Ashen_Tyrant_pMuXGCSOQaxpi5tb.json index 9b65ed36..f3e4f280 100644 --- a/src/packs/adversaries/adversary_Volcanic_Dragon__Ashen_Tyrant_pMuXGCSOQaxpi5tb.json +++ b/src/packs/adversaries/adversary_Volcanic_Dragon__Ashen_Tyrant_pMuXGCSOQaxpi5tb.json @@ -299,7 +299,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -666,7 +665,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Volcanic_Dragon__Molten_Scourge_eArAPuB38CNR0ZIM.json b/src/packs/adversaries/adversary_Volcanic_Dragon__Molten_Scourge_eArAPuB38CNR0ZIM.json index 8ea0f54e..350da61a 100644 --- a/src/packs/adversaries/adversary_Volcanic_Dragon__Molten_Scourge_eArAPuB38CNR0ZIM.json +++ b/src/packs/adversaries/adversary_Volcanic_Dragon__Molten_Scourge_eArAPuB38CNR0ZIM.json @@ -332,7 +332,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -554,7 +553,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_War_Wizard_noDdT0tsN6FXSmC8.json b/src/packs/adversaries/adversary_War_Wizard_noDdT0tsN6FXSmC8.json index b77c51a9..37d0328a 100644 --- a/src/packs/adversaries/adversary_War_Wizard_noDdT0tsN6FXSmC8.json +++ b/src/packs/adversaries/adversary_War_Wizard_noDdT0tsN6FXSmC8.json @@ -257,7 +257,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -321,7 +320,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -385,7 +383,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -500,7 +497,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Weaponmaster_ZNbQ2jg35LG4t9eH.json b/src/packs/adversaries/adversary_Weaponmaster_ZNbQ2jg35LG4t9eH.json index c35018ba..e82fe2f6 100644 --- a/src/packs/adversaries/adversary_Weaponmaster_ZNbQ2jg35LG4t9eH.json +++ b/src/packs/adversaries/adversary_Weaponmaster_ZNbQ2jg35LG4t9eH.json @@ -408,18 +408,18 @@ "actionType": "action", "cost": [ { - "scalable": false, "key": "fear", + "itemId": null, "value": 1, - "keyIsID": false, + "scalable": false, "step": null, "consumeOnSuccess": false }, { - "scalable": false, - "key": "UsC0vtOBbf9Kut4v", + "key": "resource", + "itemId": "UsC0vtOBbf9Kut4v", "value": 1, - "keyIsID": true, + "scalable": false, "step": null, "consumeOnSuccess": false } @@ -527,10 +527,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.4", + "systemVersion": "1.2.0", "createdTime": 1754055703816, - "modifiedTime": 1755264604190, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325576007, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_key": "!actors.items!ZNbQ2jg35LG4t9eH.UsC0vtOBbf9Kut4v" }, diff --git a/src/packs/adversaries/adversary_Young_Dryad_8yUj2Mzvnifhxegm.json b/src/packs/adversaries/adversary_Young_Dryad_8yUj2Mzvnifhxegm.json index 3fa1cb51..bda226f0 100644 --- a/src/packs/adversaries/adversary_Young_Dryad_8yUj2Mzvnifhxegm.json +++ b/src/packs/adversaries/adversary_Young_Dryad_8yUj2Mzvnifhxegm.json @@ -254,7 +254,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -343,7 +342,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json b/src/packs/adversaries/adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json index b30f12c6..388301f3 100644 --- a/src/packs/adversaries/adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json +++ b/src/packs/adversaries/adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json @@ -404,7 +404,6 @@ "scalable": false, "key": "fear", "value": 2, - "keyIsID": false, "step": null } ], @@ -571,7 +570,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json b/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json index 4d63e66a..c97721a2 100644 --- a/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json +++ b/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json @@ -397,7 +397,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/adversaries/adversary_Zombie_Pack_Nf0v43rtflV56V2T.json b/src/packs/adversaries/adversary_Zombie_Pack_Nf0v43rtflV56V2T.json index a2f6c836..a51ce6a0 100644 --- a/src/packs/adversaries/adversary_Zombie_Pack_Nf0v43rtflV56V2T.json +++ b/src/packs/adversaries/adversary_Zombie_Pack_Nf0v43rtflV56V2T.json @@ -282,7 +282,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/ancestries/feature_Adaptability_BNofV1UC4ZbdFTkb.json b/src/packs/ancestries/feature_Adaptability_BNofV1UC4ZbdFTkb.json index 854e377c..8f3353d0 100644 --- a/src/packs/ancestries/feature_Adaptability_BNofV1UC4ZbdFTkb.json +++ b/src/packs/ancestries/feature_Adaptability_BNofV1UC4ZbdFTkb.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/ancestries/feature_Charge_AA2CZlJSWW8GPhrR.json b/src/packs/ancestries/feature_Charge_AA2CZlJSWW8GPhrR.json index 291785f2..7cba9e0c 100644 --- a/src/packs/ancestries/feature_Charge_AA2CZlJSWW8GPhrR.json +++ b/src/packs/ancestries/feature_Charge_AA2CZlJSWW8GPhrR.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/ancestries/feature_Danger_Sense_AXqcoxnRoWBbbKpK.json b/src/packs/ancestries/feature_Danger_Sense_AXqcoxnRoWBbbKpK.json index 595a2a6b..93cdabd5 100644 --- a/src/packs/ancestries/feature_Danger_Sense_AXqcoxnRoWBbbKpK.json +++ b/src/packs/ancestries/feature_Danger_Sense_AXqcoxnRoWBbbKpK.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/ancestries/feature_Death_Connection_WuwXH2r2uM9sDJtj.json b/src/packs/ancestries/feature_Death_Connection_WuwXH2r2uM9sDJtj.json index 3fffc763..b0d9cc7b 100644 --- a/src/packs/ancestries/feature_Death_Connection_WuwXH2r2uM9sDJtj.json +++ b/src/packs/ancestries/feature_Death_Connection_WuwXH2r2uM9sDJtj.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/ancestries/feature_Fearless_IlWvn5kCqCBMuUJn.json b/src/packs/ancestries/feature_Fearless_IlWvn5kCqCBMuUJn.json index 13cf7f84..ca954014 100644 --- a/src/packs/ancestries/feature_Fearless_IlWvn5kCqCBMuUJn.json +++ b/src/packs/ancestries/feature_Fearless_IlWvn5kCqCBMuUJn.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/ancestries/feature_Feline_Instincts_lNgbbYnCKgrdvA85.json b/src/packs/ancestries/feature_Feline_Instincts_lNgbbYnCKgrdvA85.json index 09d5dbbe..27e03f8b 100644 --- a/src/packs/ancestries/feature_Feline_Instincts_lNgbbYnCKgrdvA85.json +++ b/src/packs/ancestries/feature_Feline_Instincts_lNgbbYnCKgrdvA85.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hope", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/ancestries/feature_Increased_Fortitude_0RN0baBxh95GT1cm.json b/src/packs/ancestries/feature_Increased_Fortitude_0RN0baBxh95GT1cm.json index 8ecbfb61..2de5bbaf 100644 --- a/src/packs/ancestries/feature_Increased_Fortitude_0RN0baBxh95GT1cm.json +++ b/src/packs/ancestries/feature_Increased_Fortitude_0RN0baBxh95GT1cm.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null } ], diff --git a/src/packs/ancestries/feature_Long_Tongue_oWbdlh51ajn1Q5kL.json b/src/packs/ancestries/feature_Long_Tongue_oWbdlh51ajn1Q5kL.json index d474f7ca..0a569130 100644 --- a/src/packs/ancestries/feature_Long_Tongue_oWbdlh51ajn1Q5kL.json +++ b/src/packs/ancestries/feature_Long_Tongue_oWbdlh51ajn1Q5kL.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/ancestries/feature_Luckbender_U6iFjZgLYawlOlQZ.json b/src/packs/ancestries/feature_Luckbender_U6iFjZgLYawlOlQZ.json index 6059aa88..70dcaa51 100644 --- a/src/packs/ancestries/feature_Luckbender_U6iFjZgLYawlOlQZ.json +++ b/src/packs/ancestries/feature_Luckbender_U6iFjZgLYawlOlQZ.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null } ], diff --git a/src/packs/ancestries/feature_Quick_Reactions_0NSPSuB8KSEYTJIP.json b/src/packs/ancestries/feature_Quick_Reactions_0NSPSuB8KSEYTJIP.json index 522faf75..4384e554 100644 --- a/src/packs/ancestries/feature_Quick_Reactions_0NSPSuB8KSEYTJIP.json +++ b/src/packs/ancestries/feature_Quick_Reactions_0NSPSuB8KSEYTJIP.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json b/src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json index 746be465..09e749a3 100644 --- a/src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json +++ b/src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json @@ -17,7 +17,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/ancestries/feature_Wings_WquAjoOcso8lwySW.json b/src/packs/ancestries/feature_Wings_WquAjoOcso8lwySW.json index 87aa36a9..ee14bca6 100644 --- a/src/packs/ancestries/feature_Wings_WquAjoOcso8lwySW.json +++ b/src/packs/ancestries/feature_Wings_WquAjoOcso8lwySW.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/beastforms/feature_Agile_xLS5YT1B6yeCiNTg.json b/src/packs/beastforms/feature_Agile_xLS5YT1B6yeCiNTg.json index 4c54bebb..bbc22d34 100644 --- a/src/packs/beastforms/feature_Agile_xLS5YT1B6yeCiNTg.json +++ b/src/packs/beastforms/feature_Agile_xLS5YT1B6yeCiNTg.json @@ -15,7 +15,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json b/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json index 26534190..4d6a5627 100644 --- a/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json +++ b/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json @@ -18,7 +18,6 @@ "scalable": false, "key": "armor", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/beastforms/feature_Cannonball_jp5KpPRBFBOIs46Q.json b/src/packs/beastforms/feature_Cannonball_jp5KpPRBFBOIs46Q.json index 49f5f88d..c73cc803 100644 --- a/src/packs/beastforms/feature_Cannonball_jp5KpPRBFBOIs46Q.json +++ b/src/packs/beastforms/feature_Cannonball_jp5KpPRBFBOIs46Q.json @@ -37,7 +37,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/beastforms/feature_Demolish_DfBXO8jTchwFG8dZ.json b/src/packs/beastforms/feature_Demolish_DfBXO8jTchwFG8dZ.json index 45ceee81..026db6c4 100644 --- a/src/packs/beastforms/feature_Demolish_DfBXO8jTchwFG8dZ.json +++ b/src/packs/beastforms/feature_Demolish_DfBXO8jTchwFG8dZ.json @@ -15,7 +15,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/beastforms/feature_Devastating_Strikes_HJbQcKWcFZ9NoFxs.json b/src/packs/beastforms/feature_Devastating_Strikes_HJbQcKWcFZ9NoFxs.json index 9b24e84e..a2ffb88d 100644 --- a/src/packs/beastforms/feature_Devastating_Strikes_HJbQcKWcFZ9NoFxs.json +++ b/src/packs/beastforms/feature_Devastating_Strikes_HJbQcKWcFZ9NoFxs.json @@ -18,7 +18,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/beastforms/feature_Elusive_Prey_a7Qvmm14nx9BCysA.json b/src/packs/beastforms/feature_Elusive_Prey_a7Qvmm14nx9BCysA.json index 27b3eb9c..582d3c4e 100644 --- a/src/packs/beastforms/feature_Elusive_Prey_a7Qvmm14nx9BCysA.json +++ b/src/packs/beastforms/feature_Elusive_Prey_a7Qvmm14nx9BCysA.json @@ -18,7 +18,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/beastforms/feature_Fleet_GhHsSHOa509cwCvr.json b/src/packs/beastforms/feature_Fleet_GhHsSHOa509cwCvr.json index 554cba68..dd13a8cc 100644 --- a/src/packs/beastforms/feature_Fleet_GhHsSHOa509cwCvr.json +++ b/src/packs/beastforms/feature_Fleet_GhHsSHOa509cwCvr.json @@ -15,7 +15,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/beastforms/feature_Hobbling_Strike_8u0HkK3WgtU9lWYs.json b/src/packs/beastforms/feature_Hobbling_Strike_8u0HkK3WgtU9lWYs.json index 95477418..fa90cf04 100644 --- a/src/packs/beastforms/feature_Hobbling_Strike_8u0HkK3WgtU9lWYs.json +++ b/src/packs/beastforms/feature_Hobbling_Strike_8u0HkK3WgtU9lWYs.json @@ -18,7 +18,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/beastforms/feature_Rampage_8upqfcZvi7b5hRLE.json b/src/packs/beastforms/feature_Rampage_8upqfcZvi7b5hRLE.json index e9b157ba..eedab4d6 100644 --- a/src/packs/beastforms/feature_Rampage_8upqfcZvi7b5hRLE.json +++ b/src/packs/beastforms/feature_Rampage_8upqfcZvi7b5hRLE.json @@ -18,7 +18,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/beastforms/feature_Snapping_Strike_Ky3rZD3sJMXYZOBC.json b/src/packs/beastforms/feature_Snapping_Strike_Ky3rZD3sJMXYZOBC.json index e49f6b47..0ab50abe 100644 --- a/src/packs/beastforms/feature_Snapping_Strike_Ky3rZD3sJMXYZOBC.json +++ b/src/packs/beastforms/feature_Snapping_Strike_Ky3rZD3sJMXYZOBC.json @@ -15,7 +15,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/beastforms/feature_Takedown_0ey4kM9ssj2otHvb.json b/src/packs/beastforms/feature_Takedown_0ey4kM9ssj2otHvb.json index f31ed781..5392665b 100644 --- a/src/packs/beastforms/feature_Takedown_0ey4kM9ssj2otHvb.json +++ b/src/packs/beastforms/feature_Takedown_0ey4kM9ssj2otHvb.json @@ -18,7 +18,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/beastforms/feature_Trample_A0lgd6eVEfX6oqSB.json b/src/packs/beastforms/feature_Trample_A0lgd6eVEfX6oqSB.json index b55d8f6e..0db60d14 100644 --- a/src/packs/beastforms/feature_Trample_A0lgd6eVEfX6oqSB.json +++ b/src/packs/beastforms/feature_Trample_A0lgd6eVEfX6oqSB.json @@ -18,7 +18,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/beastforms/feature_Vicious_Maul_jYUBi7yLHap5ljpa.json b/src/packs/beastforms/feature_Vicious_Maul_jYUBi7yLHap5ljpa.json index 06706094..8083ec69 100644 --- a/src/packs/beastforms/feature_Vicious_Maul_jYUBi7yLHap5ljpa.json +++ b/src/packs/beastforms/feature_Vicious_Maul_jYUBi7yLHap5ljpa.json @@ -15,7 +15,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/beastforms/feature_Warning_Hiss_cTlqpQZPy5TvdDAT.json b/src/packs/beastforms/feature_Warning_Hiss_cTlqpQZPy5TvdDAT.json index 39b4a777..639966e6 100644 --- a/src/packs/beastforms/feature_Warning_Hiss_cTlqpQZPy5TvdDAT.json +++ b/src/packs/beastforms/feature_Warning_Hiss_cTlqpQZPy5TvdDAT.json @@ -18,7 +18,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/classes/feature_Beastform_P1K0jcnH2RiS6TLd.json b/src/packs/classes/feature_Beastform_P1K0jcnH2RiS6TLd.json index a0aab233..327e611f 100644 --- a/src/packs/classes/feature_Beastform_P1K0jcnH2RiS6TLd.json +++ b/src/packs/classes/feature_Beastform_P1K0jcnH2RiS6TLd.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/classes/feature_Channel_Raw_Power_P02cbN50LIoD662z.json b/src/packs/classes/feature_Channel_Raw_Power_P02cbN50LIoD662z.json index d0b792cc..a371bfea 100644 --- a/src/packs/classes/feature_Channel_Raw_Power_P02cbN50LIoD662z.json +++ b/src/packs/classes/feature_Channel_Raw_Power_P02cbN50LIoD662z.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hitPoints", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/classes/feature_Evolution_6rlxhrRwFaVgq9fe.json b/src/packs/classes/feature_Evolution_6rlxhrRwFaVgq9fe.json index 8194c296..cd011361 100644 --- a/src/packs/classes/feature_Evolution_6rlxhrRwFaVgq9fe.json +++ b/src/packs/classes/feature_Evolution_6rlxhrRwFaVgq9fe.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null } ], diff --git a/src/packs/classes/feature_Frontline_Tank_YS1g7YdWwOaS629x.json b/src/packs/classes/feature_Frontline_Tank_YS1g7YdWwOaS629x.json index 701f8eb9..b2e30707 100644 --- a/src/packs/classes/feature_Frontline_Tank_YS1g7YdWwOaS629x.json +++ b/src/packs/classes/feature_Frontline_Tank_YS1g7YdWwOaS629x.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null } ], diff --git a/src/packs/classes/feature_Hold_Them_Off_2Cyb9ZeuAesf5Sb3.json b/src/packs/classes/feature_Hold_Them_Off_2Cyb9ZeuAesf5Sb3.json index 3a640f48..0c831a14 100644 --- a/src/packs/classes/feature_Hold_Them_Off_2Cyb9ZeuAesf5Sb3.json +++ b/src/packs/classes/feature_Hold_Them_Off_2Cyb9ZeuAesf5Sb3.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null } ], diff --git a/src/packs/classes/feature_Life_Support_lSlvSUHbOoX36q2j.json b/src/packs/classes/feature_Life_Support_lSlvSUHbOoX36q2j.json index 64256829..2ee5cba8 100644 --- a/src/packs/classes/feature_Life_Support_lSlvSUHbOoX36q2j.json +++ b/src/packs/classes/feature_Life_Support_lSlvSUHbOoX36q2j.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/classes/feature_Make_a_Scene_N9E5skDDK2VgvohR.json b/src/packs/classes/feature_Make_a_Scene_N9E5skDDK2VgvohR.json index 3d92768d..1627a77a 100644 --- a/src/packs/classes/feature_Make_a_Scene_N9E5skDDK2VgvohR.json +++ b/src/packs/classes/feature_Make_a_Scene_N9E5skDDK2VgvohR.json @@ -19,7 +19,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null } ], diff --git a/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json b/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json index 7d689fe3..caa2462c 100644 --- a/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json +++ b/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/classes/feature_Not_This_Time_h3VE0jhcM5xHKBs4.json b/src/packs/classes/feature_Not_This_Time_h3VE0jhcM5xHKBs4.json index fe59bd63..b8053ed2 100644 --- a/src/packs/classes/feature_Not_This_Time_h3VE0jhcM5xHKBs4.json +++ b/src/packs/classes/feature_Not_This_Time_h3VE0jhcM5xHKBs4.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/classes/feature_Ranger_s_Focus_ncLx2P8BOUtrAD38.json b/src/packs/classes/feature_Ranger_s_Focus_ncLx2P8BOUtrAD38.json index d3d10bc0..2060a2e1 100644 --- a/src/packs/classes/feature_Ranger_s_Focus_ncLx2P8BOUtrAD38.json +++ b/src/packs/classes/feature_Ranger_s_Focus_ncLx2P8BOUtrAD38.json @@ -17,7 +17,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json b/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json index fcc178d0..c96fe2fc 100644 --- a/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json +++ b/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null } ], diff --git a/src/packs/classes/feature_Volatile_Magic_ieiQlD0joWSqt53D.json b/src/packs/classes/feature_Volatile_Magic_ieiQlD0joWSqt53D.json index 08c89b32..65e4c0c8 100644 --- a/src/packs/classes/feature_Volatile_Magic_ieiQlD0joWSqt53D.json +++ b/src/packs/classes/feature_Volatile_Magic_ieiQlD0joWSqt53D.json @@ -19,7 +19,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/communities/feature_Nomadic_Pack_2RSrQouA2zEJ5Xee.json b/src/packs/communities/feature_Nomadic_Pack_2RSrQouA2zEJ5Xee.json index 2e4c3507..b2f8eeb3 100644 --- a/src/packs/communities/feature_Nomadic_Pack_2RSrQouA2zEJ5Xee.json +++ b/src/packs/communities/feature_Nomadic_Pack_2RSrQouA2zEJ5Xee.json @@ -17,7 +17,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Adjust_Reality_Zp2S2EnLS5Iv3XuT.json b/src/packs/domains/domainCard_Adjust_Reality_Zp2S2EnLS5Iv3XuT.json index e816526d..051e27f0 100644 --- a/src/packs/domains/domainCard_Adjust_Reality_Zp2S2EnLS5Iv3XuT.json +++ b/src/packs/domains/domainCard_Adjust_Reality_Zp2S2EnLS5Iv3XuT.json @@ -22,7 +22,6 @@ "scalable": false, "key": "hope", "value": 5, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json b/src/packs/domains/domainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json index 1f7f2aad..da201cbe 100644 --- a/src/packs/domains/domainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json +++ b/src/packs/domains/domainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json @@ -26,10 +26,10 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "5PvMQKCjrgSxzstn", + "key": "resource", + "itemId": "5PvMQKCjrgSxzstn", "value": 1, - "keyIsID": true, + "scalable": false, "step": null, "consumeOnSuccess": false } @@ -63,10 +63,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784408, - "modifiedTime": 1755428012661, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575261, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "5PvMQKCjrgSxzstn", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Arcane_Reflection_JzSvxy9Mu3RJp1jV.json b/src/packs/domains/domainCard_Arcane_Reflection_JzSvxy9Mu3RJp1jV.json index 2a6789be..d9f887c6 100644 --- a/src/packs/domains/domainCard_Arcane_Reflection_JzSvxy9Mu3RJp1jV.json +++ b/src/packs/domains/domainCard_Arcane_Reflection_JzSvxy9Mu3RJp1jV.json @@ -23,7 +23,6 @@ "key": "hope", "value": 1, "step": 1, - "keyIsID": false, "consumeOnSuccess": false } ], diff --git a/src/packs/domains/domainCard_Astral_Projection_YNOCNmZ96sCp9NEr.json b/src/packs/domains/domainCard_Astral_Projection_YNOCNmZ96sCp9NEr.json index 1927078e..619ab731 100644 --- a/src/packs/domains/domainCard_Astral_Projection_YNOCNmZ96sCp9NEr.json +++ b/src/packs/domains/domainCard_Astral_Projection_YNOCNmZ96sCp9NEr.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Battle_Monster_P0ezScyQ5t8ruByf.json b/src/packs/domains/domainCard_Battle_Monster_P0ezScyQ5t8ruByf.json index 7c2f5e54..13178c37 100644 --- a/src/packs/domains/domainCard_Battle_Monster_P0ezScyQ5t8ruByf.json +++ b/src/packs/domains/domainCard_Battle_Monster_P0ezScyQ5t8ruByf.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 4, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Blink_Out_Qu0iA4s3Xov10Erd.json b/src/packs/domains/domainCard_Blink_Out_Qu0iA4s3Xov10Erd.json index d211ab8f..c069362d 100644 --- a/src/packs/domains/domainCard_Blink_Out_Qu0iA4s3Xov10Erd.json +++ b/src/packs/domains/domainCard_Blink_Out_Qu0iA4s3Xov10Erd.json @@ -23,7 +23,6 @@ "scalable": true, "key": "hope", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Bold_Presence_tdsL00yTSLNgZWs6.json b/src/packs/domains/domainCard_Bold_Presence_tdsL00yTSLNgZWs6.json index c83c1fde..2c80d1d5 100644 --- a/src/packs/domains/domainCard_Bold_Presence_tdsL00yTSLNgZWs6.json +++ b/src/packs/domains/domainCard_Bold_Presence_tdsL00yTSLNgZWs6.json @@ -22,7 +22,6 @@ "scalable": false, "key": "hope", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Bolt_Beacon_BNevJyGk7hmN7XOY.json b/src/packs/domains/domainCard_Bolt_Beacon_BNevJyGk7hmN7XOY.json index 89fac249..5363b3d8 100644 --- a/src/packs/domains/domainCard_Bolt_Beacon_BNevJyGk7hmN7XOY.json +++ b/src/packs/domains/domainCard_Bolt_Beacon_BNevJyGk7hmN7XOY.json @@ -23,7 +23,6 @@ "scalable": false, "key": "hope", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json b/src/packs/domains/domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json index d398e708..922a77d9 100644 --- a/src/packs/domains/domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json +++ b/src/packs/domains/domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json @@ -22,7 +22,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json b/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json index 3fa7e50c..0c460a1d 100644 --- a/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json +++ b/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json @@ -92,7 +92,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Book_of_Exota_oVs2MSC6Uf5GbgEG.json b/src/packs/domains/domainCard_Book_of_Exota_oVs2MSC6Uf5GbgEG.json index 1073b715..611c71cb 100644 --- a/src/packs/domains/domainCard_Book_of_Exota_oVs2MSC6Uf5GbgEG.json +++ b/src/packs/domains/domainCard_Book_of_Exota_oVs2MSC6Uf5GbgEG.json @@ -66,7 +66,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Book_of_Grynn_R0LNheiZycZlZzV3.json b/src/packs/domains/domainCard_Book_of_Grynn_R0LNheiZycZlZzV3.json index c7f334db..83974d28 100644 --- a/src/packs/domains/domainCard_Book_of_Grynn_R0LNheiZycZlZzV3.json +++ b/src/packs/domains/domainCard_Book_of_Grynn_R0LNheiZycZlZzV3.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Book_of_Illiat_df4iRqQzRntrF6Qw.json b/src/packs/domains/domainCard_Book_of_Illiat_df4iRqQzRntrF6Qw.json index 35bfe6b9..1d4084d6 100644 --- a/src/packs/domains/domainCard_Book_of_Illiat_df4iRqQzRntrF6Qw.json +++ b/src/packs/domains/domainCard_Book_of_Illiat_df4iRqQzRntrF6Qw.json @@ -73,8 +73,7 @@ "scalable": true, "key": "hope", "value": 1, - "step": 1, - "keyIsID": false + "step": 1 } ], "uses": { @@ -129,7 +128,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Book_of_Korvax_cWRFHJdxEZ0M1dAg.json b/src/packs/domains/domainCard_Book_of_Korvax_cWRFHJdxEZ0M1dAg.json index be949650..d2e80c21 100644 --- a/src/packs/domains/domainCard_Book_of_Korvax_cWRFHJdxEZ0M1dAg.json +++ b/src/packs/domains/domainCard_Book_of_Korvax_cWRFHJdxEZ0M1dAg.json @@ -65,7 +65,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, @@ -122,7 +121,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Book_of_Sitil_eq8VNqYMRHhF9xw9.json b/src/packs/domains/domainCard_Book_of_Sitil_eq8VNqYMRHhF9xw9.json index 3c60a08d..1002b85c 100644 --- a/src/packs/domains/domainCard_Book_of_Sitil_eq8VNqYMRHhF9xw9.json +++ b/src/packs/domains/domainCard_Book_of_Sitil_eq8VNqYMRHhF9xw9.json @@ -49,7 +49,6 @@ "scalable": false, "key": "hope", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Book_of_Vagras_aknDDYtN7EObv94t.json b/src/packs/domains/domainCard_Book_of_Vagras_aknDDYtN7EObv94t.json index 28e768a2..1e3b3bf4 100644 --- a/src/packs/domains/domainCard_Book_of_Vagras_aknDDYtN7EObv94t.json +++ b/src/packs/domains/domainCard_Book_of_Vagras_aknDDYtN7EObv94t.json @@ -70,7 +70,6 @@ "scalable": false, "key": "hope", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Book_of_Vyola_VOIgm2j2Ijszwc5m.json b/src/packs/domains/domainCard_Book_of_Vyola_VOIgm2j2Ijszwc5m.json index b001346c..9ecf339a 100644 --- a/src/packs/domains/domainCard_Book_of_Vyola_VOIgm2j2Ijszwc5m.json +++ b/src/packs/domains/domainCard_Book_of_Vyola_VOIgm2j2Ijszwc5m.json @@ -65,7 +65,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Book_of_Yarrow_J1ovx2FpNDvPq1o6.json b/src/packs/domains/domainCard_Book_of_Yarrow_J1ovx2FpNDvPq1o6.json index 8d91c1ee..2896a467 100644 --- a/src/packs/domains/domainCard_Book_of_Yarrow_J1ovx2FpNDvPq1o6.json +++ b/src/packs/domains/domainCard_Book_of_Yarrow_J1ovx2FpNDvPq1o6.json @@ -68,7 +68,6 @@ "scalable": false, "key": "hope", "value": 5, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Boost_VKAHS6eWz28ukcDs.json b/src/packs/domains/domainCard_Boost_VKAHS6eWz28ukcDs.json index ebc99d4e..f4d69fcd 100644 --- a/src/packs/domains/domainCard_Boost_VKAHS6eWz28ukcDs.json +++ b/src/packs/domains/domainCard_Boost_VKAHS6eWz28ukcDs.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Breaking_Blow_8UANBgSdhMZ0sqfO.json b/src/packs/domains/domainCard_Breaking_Blow_8UANBgSdhMZ0sqfO.json index 3781989d..a660200b 100644 --- a/src/packs/domains/domainCard_Breaking_Blow_8UANBgSdhMZ0sqfO.json +++ b/src/packs/domains/domainCard_Breaking_Blow_8UANBgSdhMZ0sqfO.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Chain_Lightning_0kAVO6rordCfZqYP.json b/src/packs/domains/domainCard_Chain_Lightning_0kAVO6rordCfZqYP.json index 5c1132f9..9193247f 100644 --- a/src/packs/domains/domainCard_Chain_Lightning_0kAVO6rordCfZqYP.json +++ b/src/packs/domains/domainCard_Chain_Lightning_0kAVO6rordCfZqYP.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 2, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Champion_s_Edge_rnejRbUQsNGX1GMC.json b/src/packs/domains/domainCard_Champion_s_Edge_rnejRbUQsNGX1GMC.json index 80176175..48c35a57 100644 --- a/src/packs/domains/domainCard_Champion_s_Edge_rnejRbUQsNGX1GMC.json +++ b/src/packs/domains/domainCard_Champion_s_Edge_rnejRbUQsNGX1GMC.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, @@ -96,7 +95,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, @@ -173,7 +171,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Chokehold_R5GYUalYXLLFRlNl.json b/src/packs/domains/domainCard_Chokehold_R5GYUalYXLLFRlNl.json index 1785da8e..0036cad9 100644 --- a/src/packs/domains/domainCard_Chokehold_R5GYUalYXLLFRlNl.json +++ b/src/packs/domains/domainCard_Chokehold_R5GYUalYXLLFRlNl.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -57,7 +56,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Cloaking_Blast_Zhw7PtK8nMPlsOqD.json b/src/packs/domains/domainCard_Cloaking_Blast_Zhw7PtK8nMPlsOqD.json index 5314de9e..a853cbcf 100644 --- a/src/packs/domains/domainCard_Cloaking_Blast_Zhw7PtK8nMPlsOqD.json +++ b/src/packs/domains/domainCard_Cloaking_Blast_Zhw7PtK8nMPlsOqD.json @@ -22,7 +22,6 @@ "scalable": true, "key": "hope", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Codex_Touched_7Pu83ABdMukTxu3e.json b/src/packs/domains/domainCard_Codex_Touched_7Pu83ABdMukTxu3e.json index 69c40e15..e06b2cff 100644 --- a/src/packs/domains/domainCard_Codex_Touched_7Pu83ABdMukTxu3e.json +++ b/src/packs/domains/domainCard_Codex_Touched_7Pu83ABdMukTxu3e.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Confusing_Aura_R8NDiJXJWmC48WSr.json b/src/packs/domains/domainCard_Confusing_Aura_R8NDiJXJWmC48WSr.json index b4f0d1b5..47ab3b32 100644 --- a/src/packs/domains/domainCard_Confusing_Aura_R8NDiJXJWmC48WSr.json +++ b/src/packs/domains/domainCard_Confusing_Aura_R8NDiJXJWmC48WSr.json @@ -70,7 +70,6 @@ "key": "stress", "value": 1, "step": 1, - "keyIsID": false, "consumeOnSuccess": false } ], diff --git a/src/packs/domains/domainCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json b/src/packs/domains/domainCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json index 82d68f9c..43bc16e1 100644 --- a/src/packs/domains/domainCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json +++ b/src/packs/domains/domainCard_Conjure_Swarm_rZPH0BY8Sznc9sFG.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -78,7 +77,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, @@ -112,7 +110,6 @@ "scalable": false, "key": "hope", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Conjured_Steeds_Jkp6cMDiHHaBZQRS.json b/src/packs/domains/domainCard_Conjured_Steeds_Jkp6cMDiHHaBZQRS.json index a4361133..28cb6834 100644 --- a/src/packs/domains/domainCard_Conjured_Steeds_Jkp6cMDiHHaBZQRS.json +++ b/src/packs/domains/domainCard_Conjured_Steeds_Jkp6cMDiHHaBZQRS.json @@ -22,8 +22,7 @@ "scalable": true, "key": "hope", "value": 1, - "step": 1, - "keyIsID": false + "step": 1 } ], "uses": { diff --git a/src/packs/domains/domainCard_Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json b/src/packs/domains/domainCard_Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json index ef1cd258..5bd95b01 100644 --- a/src/packs/domains/domainCard_Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json +++ b/src/packs/domains/domainCard_Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json @@ -93,8 +93,7 @@ "scalable": true, "key": "stress", "value": 0, - "step": 2, - "keyIsID": false + "step": 2 } ], "uses": { diff --git a/src/packs/domains/domainCard_Dark_Whispers_yL2qrSWmTwXVOySH.json b/src/packs/domains/domainCard_Dark_Whispers_yL2qrSWmTwXVOySH.json index b92f15cd..019e4d51 100644 --- a/src/packs/domains/domainCard_Dark_Whispers_yL2qrSWmTwXVOySH.json +++ b/src/packs/domains/domainCard_Dark_Whispers_yL2qrSWmTwXVOySH.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Deathrun_xFOSn8IVVNizgHFq.json b/src/packs/domains/domainCard_Deathrun_xFOSn8IVVNizgHFq.json index a4e50ac7..8d6ea214 100644 --- a/src/packs/domains/domainCard_Deathrun_xFOSn8IVVNizgHFq.json +++ b/src/packs/domains/domainCard_Deathrun_xFOSn8IVVNizgHFq.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Deft_Deceiver_38znCh6kHTkaPwYi.json b/src/packs/domains/domainCard_Deft_Deceiver_38znCh6kHTkaPwYi.json index c55fc136..3daad187 100644 --- a/src/packs/domains/domainCard_Deft_Deceiver_38znCh6kHTkaPwYi.json +++ b/src/packs/domains/domainCard_Deft_Deceiver_38znCh6kHTkaPwYi.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Deft_Maneuvers_dc4rAXlv95srZUct.json b/src/packs/domains/domainCard_Deft_Maneuvers_dc4rAXlv95srZUct.json index 1880da2e..ae4932bf 100644 --- a/src/packs/domains/domainCard_Deft_Maneuvers_dc4rAXlv95srZUct.json +++ b/src/packs/domains/domainCard_Deft_Maneuvers_dc4rAXlv95srZUct.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Divination_K8oFepK24UVsAX8B.json b/src/packs/domains/domainCard_Divination_K8oFepK24UVsAX8B.json index 5c97f2dc..4c0a599d 100644 --- a/src/packs/domains/domainCard_Divination_K8oFepK24UVsAX8B.json +++ b/src/packs/domains/domainCard_Divination_K8oFepK24UVsAX8B.json @@ -22,7 +22,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Earthquake_C0qLOwSSvZ6PG3Ws.json b/src/packs/domains/domainCard_Earthquake_C0qLOwSSvZ6PG3Ws.json index 83ebfad4..e69e928c 100644 --- a/src/packs/domains/domainCard_Earthquake_C0qLOwSSvZ6PG3Ws.json +++ b/src/packs/domains/domainCard_Earthquake_C0qLOwSSvZ6PG3Ws.json @@ -19,12 +19,12 @@ "actionType": "action", "cost": [ { - "consumeOnSuccess": true, - "scalable": false, - "key": "C0qLOwSSvZ6PG3Ws", + "key": "resource", + "itemId": "C0qLOwSSvZ6PG3Ws", "value": 1, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": true } ], "uses": { @@ -113,10 +113,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784449, - "modifiedTime": 1755428045130, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575299, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "C0qLOwSSvZ6PG3Ws", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Endless_Charisma_tNzFNlVHghloKsFi.json b/src/packs/domains/domainCard_Endless_Charisma_tNzFNlVHghloKsFi.json index 6c11b1b2..0277175a 100644 --- a/src/packs/domains/domainCard_Endless_Charisma_tNzFNlVHghloKsFi.json +++ b/src/packs/domains/domainCard_Endless_Charisma_tNzFNlVHghloKsFi.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Enrapture_a8lFiKX1o8T924ze.json b/src/packs/domains/domainCard_Enrapture_a8lFiKX1o8T924ze.json index 5aca5a59..a933e609 100644 --- a/src/packs/domains/domainCard_Enrapture_a8lFiKX1o8T924ze.json +++ b/src/packs/domains/domainCard_Enrapture_a8lFiKX1o8T924ze.json @@ -73,7 +73,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Falling_Sky_hZJp9mdkMnqKDROe.json b/src/packs/domains/domainCard_Falling_Sky_hZJp9mdkMnqKDROe.json index 60018023..f827cd09 100644 --- a/src/packs/domains/domainCard_Falling_Sky_hZJp9mdkMnqKDROe.json +++ b/src/packs/domains/domainCard_Falling_Sky_hZJp9mdkMnqKDROe.json @@ -23,8 +23,7 @@ "scalable": true, "key": "stress", "value": 1, - "step": 1, - "keyIsID": false + "step": 1 } ], "uses": { diff --git a/src/packs/domains/domainCard_Flight_54GUjNuBEy7xdzMz.json b/src/packs/domains/domainCard_Flight_54GUjNuBEy7xdzMz.json index 97a3fcf7..d3ba4c1d 100644 --- a/src/packs/domains/domainCard_Flight_54GUjNuBEy7xdzMz.json +++ b/src/packs/domains/domainCard_Flight_54GUjNuBEy7xdzMz.json @@ -71,10 +71,10 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "54GUjNuBEy7xdzMz", + "key": "resource", + "itemId": "54GUjNuBEy7xdzMz", "value": 1, - "keyIsID": true, + "scalable": false, "step": null, "consumeOnSuccess": false } @@ -114,10 +114,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784454, - "modifiedTime": 1755427958620, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575249, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "54GUjNuBEy7xdzMz", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json b/src/packs/domains/domainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json index 9aefa1bf..517c42f6 100644 --- a/src/packs/domains/domainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json +++ b/src/packs/domains/domainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -54,7 +53,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Forceful_Push_z8FFPhDh2SdFkFfS.json b/src/packs/domains/domainCard_Forceful_Push_z8FFPhDh2SdFkFfS.json index 3212a11c..7f7cb2b5 100644 --- a/src/packs/domains/domainCard_Forceful_Push_z8FFPhDh2SdFkFfS.json +++ b/src/packs/domains/domainCard_Forceful_Push_z8FFPhDh2SdFkFfS.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Forest_Sprites_JrkUMTzaFmQNBHVm.json b/src/packs/domains/domainCard_Forest_Sprites_JrkUMTzaFmQNBHVm.json index a881ec6c..ecfaba75 100644 --- a/src/packs/domains/domainCard_Forest_Sprites_JrkUMTzaFmQNBHVm.json +++ b/src/packs/domains/domainCard_Forest_Sprites_JrkUMTzaFmQNBHVm.json @@ -23,8 +23,7 @@ "scalable": true, "key": "hope", "value": 1, - "step": 1, - "keyIsID": false + "step": 1 } ], "uses": { diff --git a/src/packs/domains/domainCard_Full_Surge_SgvjJfMyubZowPxS.json b/src/packs/domains/domainCard_Full_Surge_SgvjJfMyubZowPxS.json index b8ac3547..61ee12e0 100644 --- a/src/packs/domains/domainCard_Full_Surge_SgvjJfMyubZowPxS.json +++ b/src/packs/domains/domainCard_Full_Surge_SgvjJfMyubZowPxS.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 3, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json b/src/packs/domains/domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json index f4cc79de..de38e408 100644 --- a/src/packs/domains/domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json +++ b/src/packs/domains/domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json @@ -22,8 +22,7 @@ "scalable": true, "key": "hope", "value": 1, - "step": 1, - "keyIsID": false + "step": 1 } ], "uses": { diff --git a/src/packs/domains/domainCard_Glancing_Blow_nCNCqSH7UgW4O3To.json b/src/packs/domains/domainCard_Glancing_Blow_nCNCqSH7UgW4O3To.json index 71e1a34a..cde0c6f0 100644 --- a/src/packs/domains/domainCard_Glancing_Blow_nCNCqSH7UgW4O3To.json +++ b/src/packs/domains/domainCard_Glancing_Blow_nCNCqSH7UgW4O3To.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Glyph_of_Nightfall_B5HXqYRJiL3xMNKT.json b/src/packs/domains/domainCard_Glyph_of_Nightfall_B5HXqYRJiL3xMNKT.json index d630e5f7..d24686a8 100644 --- a/src/packs/domains/domainCard_Glyph_of_Nightfall_B5HXqYRJiL3xMNKT.json +++ b/src/packs/domains/domainCard_Glyph_of_Nightfall_B5HXqYRJiL3xMNKT.json @@ -23,7 +23,6 @@ "scalable": false, "key": "hope", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Grace_Touched_KAuNb51AwhD8KEXk.json b/src/packs/domains/domainCard_Grace_Touched_KAuNb51AwhD8KEXk.json index 2a59fb30..11a9c47a 100644 --- a/src/packs/domains/domainCard_Grace_Touched_KAuNb51AwhD8KEXk.json +++ b/src/packs/domains/domainCard_Grace_Touched_KAuNb51AwhD8KEXk.json @@ -22,7 +22,6 @@ "scalable": false, "key": "armor", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Ground_Pound_WnGldYhJPDhx8v9X.json b/src/packs/domains/domainCard_Ground_Pound_WnGldYhJPDhx8v9X.json index 02091c6c..2b27f9d6 100644 --- a/src/packs/domains/domainCard_Ground_Pound_WnGldYhJPDhx8v9X.json +++ b/src/packs/domains/domainCard_Ground_Pound_WnGldYhJPDhx8v9X.json @@ -22,7 +22,6 @@ "scalable": false, "key": "hope", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Healing_Field_GlRm1Dxlc0Z1b04o.json b/src/packs/domains/domainCard_Healing_Field_GlRm1Dxlc0Z1b04o.json index d97dea73..49001e24 100644 --- a/src/packs/domains/domainCard_Healing_Field_GlRm1Dxlc0Z1b04o.json +++ b/src/packs/domains/domainCard_Healing_Field_GlRm1Dxlc0Z1b04o.json @@ -19,11 +19,12 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "GlRm1Dxlc0Z1b04o", + "key": "resource", + "itemId": "GlRm1Dxlc0Z1b04o", "value": 1, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -94,18 +95,20 @@ "actionType": "action", "cost": [ { - "scalable": false, "key": "hope", + "itemId": null, "value": 2, - "keyIsID": false, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false }, { - "scalable": false, - "key": "GlRm1Dxlc0Z1b04o", + "key": "resource", + "itemId": "GlRm1Dxlc0Z1b04o", "value": 1, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -189,10 +192,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784466, - "modifiedTime": 1755429518847, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575341, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "GlRm1Dxlc0Z1b04o", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Healing_Hands_WTlhnQMajc1r8i50.json b/src/packs/domains/domainCard_Healing_Hands_WTlhnQMajc1r8i50.json index 2f626025..5fbe8470 100644 --- a/src/packs/domains/domainCard_Healing_Hands_WTlhnQMajc1r8i50.json +++ b/src/packs/domains/domainCard_Healing_Hands_WTlhnQMajc1r8i50.json @@ -75,7 +75,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -151,7 +150,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } @@ -228,7 +226,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } @@ -305,7 +302,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Healing_Strike_XtSc0jIJLOoMTMYS.json b/src/packs/domains/domainCard_Healing_Strike_XtSc0jIJLOoMTMYS.json index 76af7794..68c2143b 100644 --- a/src/packs/domains/domainCard_Healing_Strike_XtSc0jIJLOoMTMYS.json +++ b/src/packs/domains/domainCard_Healing_Strike_XtSc0jIJLOoMTMYS.json @@ -22,7 +22,6 @@ "scalable": false, "key": "hope", "value": 2, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Hold_the_Line_kdFoLo3KXwn4LqTG.json b/src/packs/domains/domainCard_Hold_the_Line_kdFoLo3KXwn4LqTG.json index 6ea5b5c2..cbca693c 100644 --- a/src/packs/domains/domainCard_Hold_the_Line_kdFoLo3KXwn4LqTG.json +++ b/src/packs/domains/domainCard_Hold_the_Line_kdFoLo3KXwn4LqTG.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Hush_gwmYasmfgXZ7tFS6.json b/src/packs/domains/domainCard_Hush_gwmYasmfgXZ7tFS6.json index f60aaf26..c82cb6a1 100644 --- a/src/packs/domains/domainCard_Hush_gwmYasmfgXZ7tFS6.json +++ b/src/packs/domains/domainCard_Hush_gwmYasmfgXZ7tFS6.json @@ -23,7 +23,6 @@ "scalable": false, "key": "hope", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_I_Am_Your_Shield_KOf6LLpMRNwjezDx.json b/src/packs/domains/domainCard_I_Am_Your_Shield_KOf6LLpMRNwjezDx.json index 866589a1..258499a7 100644 --- a/src/packs/domains/domainCard_I_Am_Your_Shield_KOf6LLpMRNwjezDx.json +++ b/src/packs/domains/domainCard_I_Am_Your_Shield_KOf6LLpMRNwjezDx.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_I_See_It_Coming_Kp6RejHGimnuoBom.json b/src/packs/domains/domainCard_I_See_It_Coming_Kp6RejHGimnuoBom.json index 0633f0e3..d44ae867 100644 --- a/src/packs/domains/domainCard_I_See_It_Coming_Kp6RejHGimnuoBom.json +++ b/src/packs/domains/domainCard_I_See_It_Coming_Kp6RejHGimnuoBom.json @@ -23,7 +23,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Inspirational_Words_cWu1o82ZF7GvnbXc.json b/src/packs/domains/domainCard_Inspirational_Words_cWu1o82ZF7GvnbXc.json index ceb386d3..5b24a20e 100644 --- a/src/packs/domains/domainCard_Inspirational_Words_cWu1o82ZF7GvnbXc.json +++ b/src/packs/domains/domainCard_Inspirational_Words_cWu1o82ZF7GvnbXc.json @@ -27,11 +27,12 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "cWu1o82ZF7GvnbXc", + "key": "resource", + "itemId": "cWu1o82ZF7GvnbXc", "value": 1, + "scalable": false, "step": null, - "keyIsID": true + "consumeOnSuccess": false } ], "uses": { @@ -102,11 +103,12 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "cWu1o82ZF7GvnbXc", + "key": "resource", + "itemId": "cWu1o82ZF7GvnbXc", "value": 1, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -177,11 +179,12 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "cWu1o82ZF7GvnbXc", + "key": "resource", + "itemId": "cWu1o82ZF7GvnbXc", "value": 1, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -257,10 +260,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784473, - "modifiedTime": 1755429148644, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575659, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "cWu1o82ZF7GvnbXc", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Invigoration_X8OfkEoI5gLTRf1B.json b/src/packs/domains/domainCard_Invigoration_X8OfkEoI5gLTRf1B.json index e655d21b..fe5da256 100644 --- a/src/packs/domains/domainCard_Invigoration_X8OfkEoI5gLTRf1B.json +++ b/src/packs/domains/domainCard_Invigoration_X8OfkEoI5gLTRf1B.json @@ -23,7 +23,6 @@ "key": "hope", "value": 1, "step": 1, - "keyIsID": false, "consumeOnSuccess": false } ], diff --git a/src/packs/domains/domainCard_Invisibility_KHkzA4Zrw8EWN1CH.json b/src/packs/domains/domainCard_Invisibility_KHkzA4Zrw8EWN1CH.json index ac5337f1..000711a8 100644 --- a/src/packs/domains/domainCard_Invisibility_KHkzA4Zrw8EWN1CH.json +++ b/src/packs/domains/domainCard_Invisibility_KHkzA4Zrw8EWN1CH.json @@ -23,7 +23,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Know_Thy_Enemy_O38MQMhJWdZnXi6b.json b/src/packs/domains/domainCard_Know_Thy_Enemy_O38MQMhJWdZnXi6b.json index 2a86115c..75b94c59 100644 --- a/src/packs/domains/domainCard_Know_Thy_Enemy_O38MQMhJWdZnXi6b.json +++ b/src/packs/domains/domainCard_Know_Thy_Enemy_O38MQMhJWdZnXi6b.json @@ -23,7 +23,6 @@ "scalable": false, "key": "hope", "value": 1, - "keyIsID": false, "step": null } ], @@ -78,7 +77,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Lead_by_Example_YWCRplmtwpCjpq5i.json b/src/packs/domains/domainCard_Lead_by_Example_YWCRplmtwpCjpq5i.json index 2566febc..86e8b1ca 100644 --- a/src/packs/domains/domainCard_Lead_by_Example_YWCRplmtwpCjpq5i.json +++ b/src/packs/domains/domainCard_Lead_by_Example_YWCRplmtwpCjpq5i.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Life_Ward_OszbCj0jTqq2ADx9.json b/src/packs/domains/domainCard_Life_Ward_OszbCj0jTqq2ADx9.json index 1c680cd4..12884992 100644 --- a/src/packs/domains/domainCard_Life_Ward_OszbCj0jTqq2ADx9.json +++ b/src/packs/domains/domainCard_Life_Ward_OszbCj0jTqq2ADx9.json @@ -22,7 +22,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Manifest_Wall_TtGOtWkbr23VhHfH.json b/src/packs/domains/domainCard_Manifest_Wall_TtGOtWkbr23VhHfH.json index 077ea5d0..c938aca6 100644 --- a/src/packs/domains/domainCard_Manifest_Wall_TtGOtWkbr23VhHfH.json +++ b/src/packs/domains/domainCard_Manifest_Wall_TtGOtWkbr23VhHfH.json @@ -23,7 +23,6 @@ "scalable": false, "key": "hope", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Mass_Enrapture_ubpixIgZrJXKyM3b.json b/src/packs/domains/domainCard_Mass_Enrapture_ubpixIgZrJXKyM3b.json index 29da4453..60f93510 100644 --- a/src/packs/domains/domainCard_Mass_Enrapture_ubpixIgZrJXKyM3b.json +++ b/src/packs/domains/domainCard_Mass_Enrapture_ubpixIgZrJXKyM3b.json @@ -73,7 +73,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Mending_Touch_TGjR4vJVNbQRV8zr.json b/src/packs/domains/domainCard_Mending_Touch_TGjR4vJVNbQRV8zr.json index 12940038..84278082 100644 --- a/src/packs/domains/domainCard_Mending_Touch_TGjR4vJVNbQRV8zr.json +++ b/src/packs/domains/domainCard_Mending_Touch_TGjR4vJVNbQRV8zr.json @@ -22,7 +22,6 @@ "scalable": false, "key": "hope", "value": 2, - "keyIsID": false, "step": null, "consumeOnSuccess": false } @@ -99,7 +98,6 @@ "scalable": false, "key": "hope", "value": 2, - "keyIsID": false, "step": null, "consumeOnSuccess": false } @@ -173,18 +171,18 @@ "actionType": "action", "cost": [ { - "scalable": false, "key": "hope", + "itemId": null, "value": 2, - "keyIsID": false, + "scalable": false, "step": null, "consumeOnSuccess": false }, { - "scalable": false, - "key": "TGjR4vJVNbQRV8zr", + "key": "resource", + "itemId": "TGjR4vJVNbQRV8zr", "value": 1, - "keyIsID": true, + "scalable": false, "step": null, "consumeOnSuccess": false } @@ -258,18 +256,18 @@ "actionType": "action", "cost": [ { - "scalable": false, "key": "hope", + "itemId": null, "value": 2, - "keyIsID": false, + "scalable": false, "step": null, "consumeOnSuccess": false }, { - "scalable": false, - "key": "TGjR4vJVNbQRV8zr", + "key": "resource", + "itemId": "TGjR4vJVNbQRV8zr", "value": 1, - "keyIsID": true, + "scalable": false, "step": null, "consumeOnSuccess": false } @@ -356,10 +354,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784482, - "modifiedTime": 1755429662900, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575507, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "TGjR4vJVNbQRV8zr", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Midnight_Spirit_FXLsB3QbQvTtqX5B.json b/src/packs/domains/domainCard_Midnight_Spirit_FXLsB3QbQvTtqX5B.json index 7809506f..8cb485e4 100644 --- a/src/packs/domains/domainCard_Midnight_Spirit_FXLsB3QbQvTtqX5B.json +++ b/src/packs/domains/domainCard_Midnight_Spirit_FXLsB3QbQvTtqX5B.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, @@ -49,7 +48,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Midnight_Touched_uSyGKVxOJcnp28po.json b/src/packs/domains/domainCard_Midnight_Touched_uSyGKVxOJcnp28po.json index ac120ef6..39c82f3a 100644 --- a/src/packs/domains/domainCard_Midnight_Touched_uSyGKVxOJcnp28po.json +++ b/src/packs/domains/domainCard_Midnight_Touched_uSyGKVxOJcnp28po.json @@ -89,7 +89,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Natural_Familiar_Tag303LoRNC5zGgl.json b/src/packs/domains/domainCard_Natural_Familiar_Tag303LoRNC5zGgl.json index 974ea647..1e2c783d 100644 --- a/src/packs/domains/domainCard_Natural_Familiar_Tag303LoRNC5zGgl.json +++ b/src/packs/domains/domainCard_Natural_Familiar_Tag303LoRNC5zGgl.json @@ -22,7 +22,6 @@ "scalable": true, "key": "hope", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } @@ -104,7 +103,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Nature_s_Tongue_atWLorlCOxcrq8WB.json b/src/packs/domains/domainCard_Nature_s_Tongue_atWLorlCOxcrq8WB.json index d290126d..4ede0a27 100644 --- a/src/packs/domains/domainCard_Nature_s_Tongue_atWLorlCOxcrq8WB.json +++ b/src/packs/domains/domainCard_Nature_s_Tongue_atWLorlCOxcrq8WB.json @@ -65,7 +65,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Never_Upstaged_McdncxmO9K1YNP7Y.json b/src/packs/domains/domainCard_Never_Upstaged_McdncxmO9K1YNP7Y.json index 5549707f..7d8ec8bc 100644 --- a/src/packs/domains/domainCard_Never_Upstaged_McdncxmO9K1YNP7Y.json +++ b/src/packs/domains/domainCard_Never_Upstaged_McdncxmO9K1YNP7Y.json @@ -25,18 +25,20 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "McdncxmO9K1YNP7Y", + "key": "resource", + "itemId": "McdncxmO9K1YNP7Y", "value": 1, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false }, { - "scalable": false, "key": "stress", + "itemId": null, "value": 1, - "keyIsID": false, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -67,18 +69,20 @@ "actionType": "action", "cost": [ { - "scalable": false, "key": "stress", + "itemId": null, "value": 1, - "keyIsID": false, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false }, { - "scalable": false, - "key": "McdncxmO9K1YNP7Y", + "key": "resource", + "itemId": "McdncxmO9K1YNP7Y", "value": 2, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -109,18 +113,20 @@ "actionType": "action", "cost": [ { - "scalable": false, "key": "stress", + "itemId": null, "value": 1, - "keyIsID": false, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false }, { - "scalable": false, - "key": "McdncxmO9K1YNP7Y", + "key": "resource", + "itemId": "McdncxmO9K1YNP7Y", "value": 3, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -151,18 +157,20 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "McdncxmO9K1YNP7Y", + "key": "resource", + "itemId": "McdncxmO9K1YNP7Y", "value": 4, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false }, { - "scalable": false, "key": "stress", + "itemId": null, "value": 1, - "keyIsID": false, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -198,10 +206,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784486, - "modifiedTime": 1755429215057, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575431, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "McdncxmO9K1YNP7Y", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Night_Terror_zcldCuqOg3dphUVI.json b/src/packs/domains/domainCard_Night_Terror_zcldCuqOg3dphUVI.json index c2abcab4..f2f9a556 100644 --- a/src/packs/domains/domainCard_Night_Terror_zcldCuqOg3dphUVI.json +++ b/src/packs/domains/domainCard_Night_Terror_zcldCuqOg3dphUVI.json @@ -70,11 +70,12 @@ "actionType": "action", "cost": [ { - "scalable": true, - "key": "zcldCuqOg3dphUVI", + "key": "resource", + "itemId": "zcldCuqOg3dphUVI", "value": 1, + "scalable": true, "step": 1, - "keyIsID": true + "consumeOnSuccess": false } ], "uses": { @@ -135,10 +136,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784487, - "modifiedTime": 1755429413469, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575872, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "zcldCuqOg3dphUVI", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Notorious_IqxzvvjZiYbgx21A.json b/src/packs/domains/domainCard_Notorious_IqxzvvjZiYbgx21A.json index 7f9244dc..4f9e09e2 100644 --- a/src/packs/domains/domainCard_Notorious_IqxzvvjZiYbgx21A.json +++ b/src/packs/domains/domainCard_Notorious_IqxzvvjZiYbgx21A.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Onslaught_I7pNsQ9Yx6mRJX4V.json b/src/packs/domains/domainCard_Onslaught_I7pNsQ9Yx6mRJX4V.json index 79fe6211..20a677d0 100644 --- a/src/packs/domains/domainCard_Onslaught_I7pNsQ9Yx6mRJX4V.json +++ b/src/packs/domains/domainCard_Onslaught_I7pNsQ9Yx6mRJX4V.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Overwhelming_Aura_iEBLySZD9z8CLdz7.json b/src/packs/domains/domainCard_Overwhelming_Aura_iEBLySZD9z8CLdz7.json index e3dee02a..20bf8aeb 100644 --- a/src/packs/domains/domainCard_Overwhelming_Aura_iEBLySZD9z8CLdz7.json +++ b/src/packs/domains/domainCard_Overwhelming_Aura_iEBLySZD9z8CLdz7.json @@ -23,7 +23,6 @@ "scalable": false, "key": "hope", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Phantom_Retreat_0vdpIn06ifF3xxqZ.json b/src/packs/domains/domainCard_Phantom_Retreat_0vdpIn06ifF3xxqZ.json index 43c2d220..3ee2198a 100644 --- a/src/packs/domains/domainCard_Phantom_Retreat_0vdpIn06ifF3xxqZ.json +++ b/src/packs/domains/domainCard_Phantom_Retreat_0vdpIn06ifF3xxqZ.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, @@ -49,7 +48,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Premonition_aC43NiFQLpOADyjO.json b/src/packs/domains/domainCard_Premonition_aC43NiFQLpOADyjO.json index 5ec9f9b8..8678a0f8 100644 --- a/src/packs/domains/domainCard_Premonition_aC43NiFQLpOADyjO.json +++ b/src/packs/domains/domainCard_Premonition_aC43NiFQLpOADyjO.json @@ -19,10 +19,10 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "aC43NiFQLpOADyjO", + "key": "resource", + "itemId": "aC43NiFQLpOADyjO", "value": 1, - "keyIsID": true, + "scalable": false, "step": null, "consumeOnSuccess": false } @@ -63,10 +63,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784494, - "modifiedTime": 1755427986895, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575616, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "aC43NiFQLpOADyjO", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json b/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json index 140e570d..f7e72b9f 100644 --- a/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json +++ b/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } @@ -59,7 +58,6 @@ "scalable": false, "key": "stress", "value": 2, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Rain_of_Blades_Ucenef6JpjQxwXni.json b/src/packs/domains/domainCard_Rain_of_Blades_Ucenef6JpjQxwXni.json index 03337fa2..a15778c3 100644 --- a/src/packs/domains/domainCard_Rain_of_Blades_Ucenef6JpjQxwXni.json +++ b/src/packs/domains/domainCard_Rain_of_Blades_Ucenef6JpjQxwXni.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Rapid_Riposte_tceJDcCUefrMS2Ov.json b/src/packs/domains/domainCard_Rapid_Riposte_tceJDcCUefrMS2Ov.json index 6da00db8..2f3bb3a0 100644 --- a/src/packs/domains/domainCard_Rapid_Riposte_tceJDcCUefrMS2Ov.json +++ b/src/packs/domains/domainCard_Rapid_Riposte_tceJDcCUefrMS2Ov.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Reaper_s_Strike_MCgNRlh0s5XUPCfl.json b/src/packs/domains/domainCard_Reaper_s_Strike_MCgNRlh0s5XUPCfl.json index 0bc016be..df98b899 100644 --- a/src/packs/domains/domainCard_Reaper_s_Strike_MCgNRlh0s5XUPCfl.json +++ b/src/packs/domains/domainCard_Reaper_s_Strike_MCgNRlh0s5XUPCfl.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Reckless_2ooUo2yoilGifY81.json b/src/packs/domains/domainCard_Reckless_2ooUo2yoilGifY81.json index 28847ad5..3784874b 100644 --- a/src/packs/domains/domainCard_Reckless_2ooUo2yoilGifY81.json +++ b/src/packs/domains/domainCard_Reckless_2ooUo2yoilGifY81.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Redirect_faU0XkJCbar69PiN.json b/src/packs/domains/domainCard_Redirect_faU0XkJCbar69PiN.json index c30bfb3c..86c765a2 100644 --- a/src/packs/domains/domainCard_Redirect_faU0XkJCbar69PiN.json +++ b/src/packs/domains/domainCard_Redirect_faU0XkJCbar69PiN.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Restoration_wUQFsRtww18naYaq.json b/src/packs/domains/domainCard_Restoration_wUQFsRtww18naYaq.json index 89ebb736..1d64e639 100644 --- a/src/packs/domains/domainCard_Restoration_wUQFsRtww18naYaq.json +++ b/src/packs/domains/domainCard_Restoration_wUQFsRtww18naYaq.json @@ -27,11 +27,11 @@ "actionType": "action", "cost": [ { - "scalable": true, - "key": "wUQFsRtww18naYaq", + "key": "resource", + "itemId": "wUQFsRtww18naYaq", "value": 1, + "scalable": true, "step": null, - "keyIsID": true, "consumeOnSuccess": false } ], @@ -104,11 +104,11 @@ "actionType": "action", "cost": [ { - "scalable": true, - "key": "wUQFsRtww18naYaq", + "key": "resource", + "itemId": "wUQFsRtww18naYaq", "value": 1, + "scalable": true, "step": 1, - "keyIsID": true, "consumeOnSuccess": false } ], @@ -181,11 +181,11 @@ "actionType": "action", "cost": [ { - "scalable": true, - "key": "wUQFsRtww18naYaq", + "key": "resource", + "itemId": "wUQFsRtww18naYaq", "value": 1, + "scalable": true, "step": null, - "keyIsID": true, "consumeOnSuccess": false } ], @@ -218,10 +218,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784502, - "modifiedTime": 1755429742185, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575854, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "wUQFsRtww18naYaq", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Rune_Ward_GEhBUmv9Bj7oJfHk.json b/src/packs/domains/domainCard_Rune_Ward_GEhBUmv9Bj7oJfHk.json index 5e45f832..8a1d15d4 100644 --- a/src/packs/domains/domainCard_Rune_Ward_GEhBUmv9Bj7oJfHk.json +++ b/src/packs/domains/domainCard_Rune_Ward_GEhBUmv9Bj7oJfHk.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Safe_Haven_lmBLMPuR8qLbuzNf.json b/src/packs/domains/domainCard_Safe_Haven_lmBLMPuR8qLbuzNf.json index 2ab645bd..7f542794 100644 --- a/src/packs/domains/domainCard_Safe_Haven_lmBLMPuR8qLbuzNf.json +++ b/src/packs/domains/domainCard_Safe_Haven_lmBLMPuR8qLbuzNf.json @@ -22,7 +22,6 @@ "scalable": false, "key": "hope", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json b/src/packs/domains/domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json index 625ed0d0..9d2ccec7 100644 --- a/src/packs/domains/domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json +++ b/src/packs/domains/domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json @@ -19,11 +19,12 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "VOSFaQHZbmhMyXwi", + "key": "resource", + "itemId": "VOSFaQHZbmhMyXwi", "value": 1, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -54,11 +55,12 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "VOSFaQHZbmhMyXwi", + "key": "resource", + "itemId": "VOSFaQHZbmhMyXwi", "value": 1, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -101,10 +103,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784507, - "modifiedTime": 1755429585605, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575562, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "VOSFaQHZbmhMyXwi", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Salvation_Beam_4uAFGp3LxiC07woC.json b/src/packs/domains/domainCard_Salvation_Beam_4uAFGp3LxiC07woC.json index c8efc258..7009df65 100644 --- a/src/packs/domains/domainCard_Salvation_Beam_4uAFGp3LxiC07woC.json +++ b/src/packs/domains/domainCard_Salvation_Beam_4uAFGp3LxiC07woC.json @@ -23,8 +23,7 @@ "scalable": true, "key": "stress", "value": 1, - "step": null, - "keyIsID": false + "step": null } ], "uses": { diff --git a/src/packs/domains/domainCard_Second_Wind_ffPbSEvLuFrFsMxl.json b/src/packs/domains/domainCard_Second_Wind_ffPbSEvLuFrFsMxl.json index 90756d98..671ffcfb 100644 --- a/src/packs/domains/domainCard_Second_Wind_ffPbSEvLuFrFsMxl.json +++ b/src/packs/domains/domainCard_Second_Wind_ffPbSEvLuFrFsMxl.json @@ -19,10 +19,10 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "ffPbSEvLuFrFsMxl", + "key": "resource", + "itemId": "ffPbSEvLuFrFsMxl", "value": 1, - "keyIsID": true, + "scalable": false, "step": null, "consumeOnSuccess": false } @@ -96,10 +96,10 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "ffPbSEvLuFrFsMxl", + "key": "resource", + "itemId": "ffPbSEvLuFrFsMxl", "value": 1, - "keyIsID": true, + "scalable": false, "step": null, "consumeOnSuccess": false } @@ -185,10 +185,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784509, - "modifiedTime": 1755429692907, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575706, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "ffPbSEvLuFrFsMxl", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Shape_Material_db4xV3YErHRslbVE.json b/src/packs/domains/domainCard_Shape_Material_db4xV3YErHRslbVE.json index 87f8bb7a..e3b3569c 100644 --- a/src/packs/domains/domainCard_Shape_Material_db4xV3YErHRslbVE.json +++ b/src/packs/domains/domainCard_Shape_Material_db4xV3YErHRslbVE.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Shield_Aura_rfIv6lln40Fh6EIl.json b/src/packs/domains/domainCard_Shield_Aura_rfIv6lln40Fh6EIl.json index e03eaf12..8abd548c 100644 --- a/src/packs/domains/domainCard_Shield_Aura_rfIv6lln40Fh6EIl.json +++ b/src/packs/domains/domainCard_Shield_Aura_rfIv6lln40Fh6EIl.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Shrug_It_Off_JwfhtgmmuRxg4zhI.json b/src/packs/domains/domainCard_Shrug_It_Off_JwfhtgmmuRxg4zhI.json index d61473c9..cded950d 100644 --- a/src/packs/domains/domainCard_Shrug_It_Off_JwfhtgmmuRxg4zhI.json +++ b/src/packs/domains/domainCard_Shrug_It_Off_JwfhtgmmuRxg4zhI.json @@ -23,7 +23,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Smite_U1uWJE94HZVudujz.json b/src/packs/domains/domainCard_Smite_U1uWJE94HZVudujz.json index 7b966db2..1b707341 100644 --- a/src/packs/domains/domainCard_Smite_U1uWJE94HZVudujz.json +++ b/src/packs/domains/domainCard_Smite_U1uWJE94HZVudujz.json @@ -22,7 +22,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json b/src/packs/domains/domainCard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json index a7af630e..94720b86 100644 --- a/src/packs/domains/domainCard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json +++ b/src/packs/domains/domainCard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Spellcharge_ewhIzXQ2h9fS9I8c.json b/src/packs/domains/domainCard_Spellcharge_ewhIzXQ2h9fS9I8c.json index c0b78538..64382b12 100644 --- a/src/packs/domains/domainCard_Spellcharge_ewhIzXQ2h9fS9I8c.json +++ b/src/packs/domains/domainCard_Spellcharge_ewhIzXQ2h9fS9I8c.json @@ -25,11 +25,12 @@ "actionType": "action", "cost": [ { - "scalable": true, - "key": "ewhIzXQ2h9fS9I8c", + "key": "resource", + "itemId": "ewhIzXQ2h9fS9I8c", "value": 1, - "keyIsID": true, - "step": null + "scalable": true, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -60,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784521, - "modifiedTime": 1755429406322, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575684, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "ewhIzXQ2h9fS9I8c", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Splintering_Strike_TYKfM3H9vBXyWiH4.json b/src/packs/domains/domainCard_Splintering_Strike_TYKfM3H9vBXyWiH4.json index 20f8deca..24a0433f 100644 --- a/src/packs/domains/domainCard_Splintering_Strike_TYKfM3H9vBXyWiH4.json +++ b/src/packs/domains/domainCard_Splintering_Strike_TYKfM3H9vBXyWiH4.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Stealth_Expertise_NIUhmuQGwbb3UClZ.json b/src/packs/domains/domainCard_Stealth_Expertise_NIUhmuQGwbb3UClZ.json index f5a140d6..bb2d4eaa 100644 --- a/src/packs/domains/domainCard_Stealth_Expertise_NIUhmuQGwbb3UClZ.json +++ b/src/packs/domains/domainCard_Stealth_Expertise_NIUhmuQGwbb3UClZ.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Strategic_Approach_5b1awkgTmMp3FVrm.json b/src/packs/domains/domainCard_Strategic_Approach_5b1awkgTmMp3FVrm.json index b984da10..697f29cd 100644 --- a/src/packs/domains/domainCard_Strategic_Approach_5b1awkgTmMp3FVrm.json +++ b/src/packs/domains/domainCard_Strategic_Approach_5b1awkgTmMp3FVrm.json @@ -27,10 +27,10 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "5b1awkgTmMp3FVrm", + "key": "resource", + "itemId": "5b1awkgTmMp3FVrm", "value": 1, - "keyIsID": true, + "scalable": false, "step": null, "consumeOnSuccess": false } @@ -64,10 +64,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784523, - "modifiedTime": 1755428265451, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575271, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "5b1awkgTmMp3FVrm", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Stunning_Sunlight_lRHo6ZkK1zybeEoG.json b/src/packs/domains/domainCard_Stunning_Sunlight_lRHo6ZkK1zybeEoG.json index 103765e3..df23f8c0 100644 --- a/src/packs/domains/domainCard_Stunning_Sunlight_lRHo6ZkK1zybeEoG.json +++ b/src/packs/domains/domainCard_Stunning_Sunlight_lRHo6ZkK1zybeEoG.json @@ -23,8 +23,7 @@ "scalable": true, "key": "hope", "value": 1, - "step": null, - "keyIsID": false + "step": null } ], "uses": { diff --git a/src/packs/domains/domainCard_Support_Tank_stId5syX7YpP2JGz.json b/src/packs/domains/domainCard_Support_Tank_stId5syX7YpP2JGz.json index 1d5f2cf7..7e0dcf37 100644 --- a/src/packs/domains/domainCard_Support_Tank_stId5syX7YpP2JGz.json +++ b/src/packs/domains/domainCard_Support_Tank_stId5syX7YpP2JGz.json @@ -22,7 +22,6 @@ "scalable": false, "key": "hope", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Thorn_Skin_oUipGK84E2KjoKqh.json b/src/packs/domains/domainCard_Thorn_Skin_oUipGK84E2KjoKqh.json index 41103c18..eba9736d 100644 --- a/src/packs/domains/domainCard_Thorn_Skin_oUipGK84E2KjoKqh.json +++ b/src/packs/domains/domainCard_Thorn_Skin_oUipGK84E2KjoKqh.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, @@ -49,11 +48,12 @@ "actionType": "action", "cost": [ { - "scalable": true, - "key": "oUipGK84E2KjoKqh", + "key": "resource", + "itemId": "oUipGK84E2KjoKqh", "value": 1, + "scalable": true, "step": 1, - "keyIsID": true + "consumeOnSuccess": false } ], "uses": { @@ -92,10 +92,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.2.0", "createdTime": 1753922784531, - "modifiedTime": 1756038664007, - "lastModifiedBy": "vUIbuan0U50nfKBE" + "modifiedTime": 1756325575772, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "oUipGK84E2KjoKqh", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Thought_Delver_B4choj481tqajWb9.json b/src/packs/domains/domainCard_Thought_Delver_B4choj481tqajWb9.json index ed0e7184..9dbbd376 100644 --- a/src/packs/domains/domainCard_Thought_Delver_B4choj481tqajWb9.json +++ b/src/packs/domains/domainCard_Thought_Delver_B4choj481tqajWb9.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Towering_Stalk_n0P3VS1WfxvmXbB6.json b/src/packs/domains/domainCard_Towering_Stalk_n0P3VS1WfxvmXbB6.json index a4ab4541..3b5a5f97 100644 --- a/src/packs/domains/domainCard_Towering_Stalk_n0P3VS1WfxvmXbB6.json +++ b/src/packs/domains/domainCard_Towering_Stalk_n0P3VS1WfxvmXbB6.json @@ -19,18 +19,20 @@ "actionType": "action", "cost": [ { - "scalable": false, "key": "stress", + "itemId": null, "value": 1, - "keyIsID": false, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false }, { - "scalable": false, - "key": "n0P3VS1WfxvmXbB6", + "key": "resource", + "itemId": "n0P3VS1WfxvmXbB6", "value": 1, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -110,7 +112,6 @@ "scalable": false, "key": "hitPoints", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } @@ -144,10 +145,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784534, - "modifiedTime": 1755429505289, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575741, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "n0P3VS1WfxvmXbB6", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Transcendent_Union_kVkoCLBXLAIifqpz.json b/src/packs/domains/domainCard_Transcendent_Union_kVkoCLBXLAIifqpz.json index 91539c93..ecee943e 100644 --- a/src/packs/domains/domainCard_Transcendent_Union_kVkoCLBXLAIifqpz.json +++ b/src/packs/domains/domainCard_Transcendent_Union_kVkoCLBXLAIifqpz.json @@ -22,7 +22,6 @@ "scalable": false, "key": "hope", "value": 5, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Twilight_Toll_SDjjV61TC1NceV1m.json b/src/packs/domains/domainCard_Twilight_Toll_SDjjV61TC1NceV1m.json index 620e0558..0f64514f 100644 --- a/src/packs/domains/domainCard_Twilight_Toll_SDjjV61TC1NceV1m.json +++ b/src/packs/domains/domainCard_Twilight_Toll_SDjjV61TC1NceV1m.json @@ -26,11 +26,12 @@ "actionType": "action", "cost": [ { - "scalable": true, - "key": "SDjjV61TC1NceV1m", + "key": "resource", + "itemId": "SDjjV61TC1NceV1m", "value": 1, + "scalable": true, "step": 1, - "keyIsID": true + "consumeOnSuccess": false } ], "uses": { @@ -90,10 +91,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753922784535, - "modifiedTime": 1755429419135, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575478, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "SDjjV61TC1NceV1m", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json b/src/packs/domains/domainCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json index be34704d..687a4312 100644 --- a/src/packs/domains/domainCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json +++ b/src/packs/domains/domainCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json @@ -50,11 +50,12 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "TV56wSysbU5xAlOa", + "key": "resource", + "itemId": "TV56wSysbU5xAlOa", "value": 1, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -91,10 +92,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.2.0", "createdTime": 1753922784537, - "modifiedTime": 1756038348939, - "lastModifiedBy": "vUIbuan0U50nfKBE" + "modifiedTime": 1756325575516, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "TV56wSysbU5xAlOa", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Unleash_Chaos_o62i0QdbUDIiAhSq.json b/src/packs/domains/domainCard_Unleash_Chaos_o62i0QdbUDIiAhSq.json index 757aca62..066501dc 100644 --- a/src/packs/domains/domainCard_Unleash_Chaos_o62i0QdbUDIiAhSq.json +++ b/src/packs/domains/domainCard_Unleash_Chaos_o62i0QdbUDIiAhSq.json @@ -27,11 +27,11 @@ "actionType": "action", "cost": [ { - "scalable": true, - "key": "o62i0QdbUDIiAhSq", + "key": "resource", + "itemId": "o62i0QdbUDIiAhSq", "value": 1, + "scalable": true, "step": 1, - "keyIsID": true, "consumeOnSuccess": false } ], @@ -113,7 +113,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } @@ -147,10 +146,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.2.0", "createdTime": 1753922784537, - "modifiedTime": 1756037237479, - "lastModifiedBy": "vUIbuan0U50nfKBE" + "modifiedTime": 1756325575757, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_id": "o62i0QdbUDIiAhSq", "sort": 3400000, diff --git a/src/packs/domains/domainCard_Vanishing_Dodge_GBMIElIpk4cvk1Bd.json b/src/packs/domains/domainCard_Vanishing_Dodge_GBMIElIpk4cvk1Bd.json index d5284f5a..660e3c7a 100644 --- a/src/packs/domains/domainCard_Vanishing_Dodge_GBMIElIpk4cvk1Bd.json +++ b/src/packs/domains/domainCard_Vanishing_Dodge_GBMIElIpk4cvk1Bd.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Versatile_Fighter_wQ53ImDswEHv5SGQ.json b/src/packs/domains/domainCard_Versatile_Fighter_wQ53ImDswEHv5SGQ.json index 6d4db3d1..21f92663 100644 --- a/src/packs/domains/domainCard_Versatile_Fighter_wQ53ImDswEHv5SGQ.json +++ b/src/packs/domains/domainCard_Versatile_Fighter_wQ53ImDswEHv5SGQ.json @@ -22,7 +22,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/domains/domainCard_Vicious_Entangle_qvpvTnkAoRn9vYO4.json b/src/packs/domains/domainCard_Vicious_Entangle_qvpvTnkAoRn9vYO4.json index 042cb330..c78bebca 100644 --- a/src/packs/domains/domainCard_Vicious_Entangle_qvpvTnkAoRn9vYO4.json +++ b/src/packs/domains/domainCard_Vicious_Entangle_qvpvTnkAoRn9vYO4.json @@ -97,7 +97,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Wall_Walk_1ROT08E1UVBwHLAS.json b/src/packs/domains/domainCard_Wall_Walk_1ROT08E1UVBwHLAS.json index 0b32e34d..f57206be 100644 --- a/src/packs/domains/domainCard_Wall_Walk_1ROT08E1UVBwHLAS.json +++ b/src/packs/domains/domainCard_Wall_Walk_1ROT08E1UVBwHLAS.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Whirlwind_anO0arioUy7I5zBg.json b/src/packs/domains/domainCard_Whirlwind_anO0arioUy7I5zBg.json index a4614046..14dc5fc9 100644 --- a/src/packs/domains/domainCard_Whirlwind_anO0arioUy7I5zBg.json +++ b/src/packs/domains/domainCard_Whirlwind_anO0arioUy7I5zBg.json @@ -19,7 +19,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/domains/domainCard_Wild_Fortress_9dFvcM1i3bxG3BSA.json b/src/packs/domains/domainCard_Wild_Fortress_9dFvcM1i3bxG3BSA.json index 57c0fd94..cfe14983 100644 --- a/src/packs/domains/domainCard_Wild_Fortress_9dFvcM1i3bxG3BSA.json +++ b/src/packs/domains/domainCard_Wild_Fortress_9dFvcM1i3bxG3BSA.json @@ -23,7 +23,6 @@ "scalable": false, "key": "hope", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/domains/domainCard_Wild_Surge_DjnKlZQYaWdQGKcK.json b/src/packs/domains/domainCard_Wild_Surge_DjnKlZQYaWdQGKcK.json index 06eb5b84..11fcf48e 100644 --- a/src/packs/domains/domainCard_Wild_Surge_DjnKlZQYaWdQGKcK.json +++ b/src/packs/domains/domainCard_Wild_Surge_DjnKlZQYaWdQGKcK.json @@ -28,7 +28,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/environments/environment_Ambushers_uXZpebPR77YQ1oXI.json b/src/packs/environments/environment_Ambushers_uXZpebPR77YQ1oXI.json index 6510bc5c..4efb6b52 100644 --- a/src/packs/environments/environment_Ambushers_uXZpebPR77YQ1oXI.json +++ b/src/packs/environments/environment_Ambushers_uXZpebPR77YQ1oXI.json @@ -183,7 +183,6 @@ "scalable": false, "key": "fear", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/environments/environment_Chaos_Realm_2Z1mKc65LxNk2PqR.json b/src/packs/environments/environment_Chaos_Realm_2Z1mKc65LxNk2PqR.json index d31300b5..0c566991 100644 --- a/src/packs/environments/environment_Chaos_Realm_2Z1mKc65LxNk2PqR.json +++ b/src/packs/environments/environment_Chaos_Realm_2Z1mKc65LxNk2PqR.json @@ -371,7 +371,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -486,7 +485,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/environments/environment_Cliffside_Ascent_LPpfdlNKqiZIl04w.json b/src/packs/environments/environment_Cliffside_Ascent_LPpfdlNKqiZIl04w.json index 43a0f61f..ab8daf7d 100644 --- a/src/packs/environments/environment_Cliffside_Ascent_LPpfdlNKqiZIl04w.json +++ b/src/packs/environments/environment_Cliffside_Ascent_LPpfdlNKqiZIl04w.json @@ -455,7 +455,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json b/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json index b924328f..38821a59 100644 --- a/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json +++ b/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json @@ -272,7 +272,6 @@ "scalable": false, "key": "fear", "value": 2, - "keyIsID": false, "step": null } ], @@ -336,7 +335,6 @@ "scalable": false, "key": "fear", "value": 3, - "keyIsID": false, "step": null } ], diff --git a/src/packs/environments/environment_Hallowed_Temple_dsA6j69AnaJhUyqH.json b/src/packs/environments/environment_Hallowed_Temple_dsA6j69AnaJhUyqH.json index 8eed0247..8ae4134d 100644 --- a/src/packs/environments/environment_Hallowed_Temple_dsA6j69AnaJhUyqH.json +++ b/src/packs/environments/environment_Hallowed_Temple_dsA6j69AnaJhUyqH.json @@ -379,7 +379,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/environments/environment_Imperial_Court_jr1xAoXzVwVblzxI.json b/src/packs/environments/environment_Imperial_Court_jr1xAoXzVwVblzxI.json index 1a9888f1..bbf23666 100644 --- a/src/packs/environments/environment_Imperial_Court_jr1xAoXzVwVblzxI.json +++ b/src/packs/environments/environment_Imperial_Court_jr1xAoXzVwVblzxI.json @@ -227,7 +227,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -420,7 +419,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/environments/environment_Necromancer_s_Ossuary_h3KyRL7AshhLAmcH.json b/src/packs/environments/environment_Necromancer_s_Ossuary_h3KyRL7AshhLAmcH.json index dc76c382..fc14d738 100644 --- a/src/packs/environments/environment_Necromancer_s_Ossuary_h3KyRL7AshhLAmcH.json +++ b/src/packs/environments/environment_Necromancer_s_Ossuary_h3KyRL7AshhLAmcH.json @@ -462,7 +462,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/environments/environment_Outpost_Town_YezryR32uo39xRxW.json b/src/packs/environments/environment_Outpost_Town_YezryR32uo39xRxW.json index ef961283..82bf42e3 100644 --- a/src/packs/environments/environment_Outpost_Town_YezryR32uo39xRxW.json +++ b/src/packs/environments/environment_Outpost_Town_YezryR32uo39xRxW.json @@ -301,7 +301,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/environments/environment_Pitched_Battle_EWD3ZsLoK6VMVOf7.json b/src/packs/environments/environment_Pitched_Battle_EWD3ZsLoK6VMVOf7.json index abd838dd..9c86d63d 100644 --- a/src/packs/environments/environment_Pitched_Battle_EWD3ZsLoK6VMVOf7.json +++ b/src/packs/environments/environment_Pitched_Battle_EWD3ZsLoK6VMVOf7.json @@ -235,7 +235,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/environments/environment_Raging_River_t4cdqTfzcqP3H1vJ.json b/src/packs/environments/environment_Raging_River_t4cdqTfzcqP3H1vJ.json index 47ec74e2..79c89611 100644 --- a/src/packs/environments/environment_Raging_River_t4cdqTfzcqP3H1vJ.json +++ b/src/packs/environments/environment_Raging_River_t4cdqTfzcqP3H1vJ.json @@ -225,7 +225,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], @@ -392,7 +391,6 @@ "scalable": false, "key": "fear", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json b/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json index 33924bf9..1e690af7 100644 --- a/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json +++ b/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json @@ -23,7 +23,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json b/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json index 4b08aa31..c90ff961 100644 --- a/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json +++ b/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json @@ -16,11 +16,12 @@ "img": "icons/magic/time/hourglass-brown-orange.webp", "cost": [ { - "key": "armorSlot", + "key": "resource", + "itemId": "armorSlot", "value": 1, - "keyIsID": false, "scalable": false, - "step": null + "step": null, + "consumeOnSuccess": false } ], "roll": { @@ -100,10 +101,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753809264956, - "modifiedTime": 1755431812730, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575974, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_key": "!items!hAY6UgdGT7dj22Pr" } diff --git a/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json b/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json index ab892847..0de3a7f9 100644 --- a/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json +++ b/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json b/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json index 1edf8517..52aa1978 100644 --- a/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json +++ b/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/consumables/consumable_Acidpaste_cfVFmS8vT9dbq9s1.json b/src/packs/items/consumables/consumable_Acidpaste_cfVFmS8vT9dbq9s1.json index e092929a..9bbca941 100644 --- a/src/packs/items/consumables/consumable_Acidpaste_cfVFmS8vT9dbq9s1.json +++ b/src/packs/items/consumables/consumable_Acidpaste_cfVFmS8vT9dbq9s1.json @@ -14,7 +14,16 @@ "description": "

    This paste eats away walls and other surfaces in bright flashes.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "cfVFmS8vT9dbq9s1", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -31,6 +40,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -51,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753590344700, - "modifiedTime": 1755432861888, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336051101, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!cfVFmS8vT9dbq9s1" } diff --git a/src/packs/items/consumables/consumable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json b/src/packs/items/consumables/consumable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json index 4f68e0d7..b06f1634 100644 --- a/src/packs/items/consumables/consumable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json +++ b/src/packs/items/consumables/consumable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json @@ -20,7 +20,16 @@ "key": "hope", "value": 1, "step": 1, - "keyIsID": false + "itemId": null, + "consumeOnSuccess": false + }, + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "VlbsCjvvLNfTzNXb", + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -39,6 +48,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -59,10 +69,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.2.0", "createdTime": 1753588973384, - "modifiedTime": 1756041701211, - "lastModifiedBy": "vUIbuan0U50nfKBE" + "modifiedTime": 1756336337676, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!VlbsCjvvLNfTzNXb" } diff --git a/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json b/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json index 64db5be0..ad6dea85 100644 --- a/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json +++ b/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json @@ -14,7 +14,16 @@ "description": "

    You gain a +1 bonus to your next Instinct Roll.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "JGD3M9hBHtVAA8XP", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.2.0", "createdTime": 1753587033468, - "modifiedTime": 1756041711386, - "lastModifiedBy": "vUIbuan0U50nfKBE" + "modifiedTime": 1756336352911, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!JGD3M9hBHtVAA8XP" } diff --git a/src/packs/items/consumables/consumable_Blinding_Orb_eAXHdzA5qNPldOpn.json b/src/packs/items/consumables/consumable_Blinding_Orb_eAXHdzA5qNPldOpn.json index e0e2eb55..1e875f98 100644 --- a/src/packs/items/consumables/consumable_Blinding_Orb_eAXHdzA5qNPldOpn.json +++ b/src/packs/items/consumables/consumable_Blinding_Orb_eAXHdzA5qNPldOpn.json @@ -14,7 +14,16 @@ "description": "

    You can activate this orb to create a flash of bright light. All targets within Close range become Vulnerable until they mark HP.

    @Template[type:emanation|range:c]

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "eAXHdzA5qNPldOpn", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, @@ -96,10 +106,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753592512731, - "modifiedTime": 1755433077981, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336362549, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!eAXHdzA5qNPldOpn" } diff --git a/src/packs/items/consumables/consumable_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json b/src/packs/items/consumables/consumable_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json index 7d7596eb..545ce424 100644 --- a/src/packs/items/consumables/consumable_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json +++ b/src/packs/items/consumables/consumable_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json @@ -14,7 +14,16 @@ "description": "

    You can drink this blood to disappear from where you are and immediately reappear at a point you can see within Very Far range.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "pDGzmczoTlKGmKgd", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -31,6 +40,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -51,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.2.0", "createdTime": 1753589954973, - "modifiedTime": 1756041739719, - "lastModifiedBy": "vUIbuan0U50nfKBE" + "modifiedTime": 1756336370750, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!pDGzmczoTlKGmKgd" } diff --git a/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json b/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json index 70d93553..56bd6696 100644 --- a/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json +++ b/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json @@ -14,7 +14,16 @@ "description": "

    You gain a +1 bonus to your next Strength Roll.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "FOPQNqXbiVO0ilYL", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.2.0", "createdTime": 1753586850134, - "modifiedTime": 1756041748451, - "lastModifiedBy": "vUIbuan0U50nfKBE" + "modifiedTime": 1756336378610, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!FOPQNqXbiVO0ilYL" } diff --git a/src/packs/items/consumables/consumable_Bonding_Honey_PfQvqopXgvroBklL.json b/src/packs/items/consumables/consumable_Bonding_Honey_PfQvqopXgvroBklL.json index 64984603..73dcfc1f 100644 --- a/src/packs/items/consumables/consumable_Bonding_Honey_PfQvqopXgvroBklL.json +++ b/src/packs/items/consumables/consumable_Bonding_Honey_PfQvqopXgvroBklL.json @@ -14,7 +14,16 @@ "description": "

    This honey can be used to glue two objects together permanently.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "PfQvqopXgvroBklL", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -31,6 +40,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, @@ -51,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753592033119, - "modifiedTime": 1755433017227, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336387437, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!PfQvqopXgvroBklL" } diff --git a/src/packs/items/consumables/consumable_Bridge_Seed_RrIasiMCt6mqVTps.json b/src/packs/items/consumables/consumable_Bridge_Seed_RrIasiMCt6mqVTps.json index 6a199792..777adc08 100644 --- a/src/packs/items/consumables/consumable_Bridge_Seed_RrIasiMCt6mqVTps.json +++ b/src/packs/items/consumables/consumable_Bridge_Seed_RrIasiMCt6mqVTps.json @@ -14,7 +14,16 @@ "description": "

    Thick vines grow from your location to a point of your choice within Far range, allowing you to climb up or across them. The vines dissipate on your next short rest.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "RrIasiMCt6mqVTps", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -31,6 +40,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -51,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753591685990, - "modifiedTime": 1755432982976, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336396404, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!RrIasiMCt6mqVTps" } diff --git a/src/packs/items/consumables/consumable_Channelstone_IKMVQ6VwtapwoUim.json b/src/packs/items/consumables/consumable_Channelstone_IKMVQ6VwtapwoUim.json index 76e8833a..85e8ed29 100644 --- a/src/packs/items/consumables/consumable_Channelstone_IKMVQ6VwtapwoUim.json +++ b/src/packs/items/consumables/consumable_Channelstone_IKMVQ6VwtapwoUim.json @@ -14,7 +14,16 @@ "description": "

    You can use this stone to take a spell or grimoire from your vault, use it once, and return it to your vault.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "IKMVQ6VwtapwoUim", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -31,6 +40,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -51,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753590192533, - "modifiedTime": 1755432844735, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336406347, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!IKMVQ6VwtapwoUim" } diff --git a/src/packs/items/consumables/consumable_Charm_Potion_CVBbFfOY75YwyQsp.json b/src/packs/items/consumables/consumable_Charm_Potion_CVBbFfOY75YwyQsp.json index 46e8f8e6..ae60104a 100644 --- a/src/packs/items/consumables/consumable_Charm_Potion_CVBbFfOY75YwyQsp.json +++ b/src/packs/items/consumables/consumable_Charm_Potion_CVBbFfOY75YwyQsp.json @@ -14,7 +14,16 @@ "description": "

    You gain a +1 bonus to your next Presence Roll.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "CVBbFfOY75YwyQsp", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753587097370, - "modifiedTime": 1755432545153, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336420032, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!CVBbFfOY75YwyQsp" } diff --git a/src/packs/items/consumables/consumable_Circle_of_the_Void_elsyP6VhHw1JjGSl.json b/src/packs/items/consumables/consumable_Circle_of_the_Void_elsyP6VhHw1JjGSl.json index aa4917eb..a6ca40ba 100644 --- a/src/packs/items/consumables/consumable_Circle_of_the_Void_elsyP6VhHw1JjGSl.json +++ b/src/packs/items/consumables/consumable_Circle_of_the_Void_elsyP6VhHw1JjGSl.json @@ -17,10 +17,11 @@ "cost": [ { "scalable": false, - "key": "stress", + "key": "quantity", "value": 1, - "keyIsID": false, - "step": null + "itemId": "elsyP6VhHw1JjGSl", + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -39,6 +40,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -59,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753590692159, - "modifiedTime": 1755432898488, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336427253, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!elsyP6VhHw1JjGSl" } diff --git a/src/packs/items/consumables/consumable_Control_Potion_eeBhZSGLjuNZuJuI.json b/src/packs/items/consumables/consumable_Control_Potion_eeBhZSGLjuNZuJuI.json index 17fc697d..3c58799a 100644 --- a/src/packs/items/consumables/consumable_Control_Potion_eeBhZSGLjuNZuJuI.json +++ b/src/packs/items/consumables/consumable_Control_Potion_eeBhZSGLjuNZuJuI.json @@ -14,7 +14,16 @@ "description": "

    You gain a +1 bonus to your next Finesse Roll.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "eeBhZSGLjuNZuJuI", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753586944889, - "modifiedTime": 1755432529169, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336435661, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!eeBhZSGLjuNZuJuI" } diff --git a/src/packs/items/consumables/consumable_Death_Tea_xDnJeF1grkmKck8Q.json b/src/packs/items/consumables/consumable_Death_Tea_xDnJeF1grkmKck8Q.json index 25f7a45d..fe22618c 100644 --- a/src/packs/items/consumables/consumable_Death_Tea_xDnJeF1grkmKck8Q.json +++ b/src/packs/items/consumables/consumable_Death_Tea_xDnJeF1grkmKck8Q.json @@ -14,7 +14,16 @@ "description": "

    After you drink this tea, you instantly kill your target when you critically succeed on an attack. If you don’t critically succeed on an attack before your next long rest, you die.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "xDnJeF1grkmKck8Q", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.2.0", "createdTime": 1753592717630, - "modifiedTime": 1756041829971, - "lastModifiedBy": "vUIbuan0U50nfKBE" + "modifiedTime": 1756336445925, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!xDnJeF1grkmKck8Q" } diff --git a/src/packs/items/consumables/consumable_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json b/src/packs/items/consumables/consumable_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json index ee696b5d..3bb5766f 100644 --- a/src/packs/items/consumables/consumable_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json +++ b/src/packs/items/consumables/consumable_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json @@ -14,7 +14,16 @@ "description": "

    You can drink this tea to unleash a fiery breath attack. Make an Instinct Roll against all adversaries in front of you within Close range. Targets you succeed against take d20 physical damage using your Proficiency.

    @Template[type:emanation|range:c]

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "consumeOnSuccess": false, + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "wM18PWWW2Ami4fBG", + "step": null + } + ], "uses": { "value": null, "max": "", @@ -80,6 +89,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -100,10 +110,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.2.0", "createdTime": 1753591525829, - "modifiedTime": 1756041882255, - "lastModifiedBy": "vUIbuan0U50nfKBE" + "modifiedTime": 1756336455635, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!wM18PWWW2Ami4fBG" } diff --git a/src/packs/items/consumables/consumable_Dripfang_Poison_eU8VpbWB2NHIL47n.json b/src/packs/items/consumables/consumable_Dripfang_Poison_eU8VpbWB2NHIL47n.json index 81c57691..73f1b3c2 100644 --- a/src/packs/items/consumables/consumable_Dripfang_Poison_eU8VpbWB2NHIL47n.json +++ b/src/packs/items/consumables/consumable_Dripfang_Poison_eU8VpbWB2NHIL47n.json @@ -14,7 +14,16 @@ "description": "

    A creature who consumes this poison takes 8d10 direct magic damage.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "eU8VpbWB2NHIL47n", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -62,6 +71,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -82,10 +92,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753590938047, - "modifiedTime": 1755432918239, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336465398, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!eU8VpbWB2NHIL47n" } diff --git a/src/packs/items/consumables/consumable_Enlighten_Potion_aWHSO2AqDufi7nL4.json b/src/packs/items/consumables/consumable_Enlighten_Potion_aWHSO2AqDufi7nL4.json index 87be77e4..dc8260d0 100644 --- a/src/packs/items/consumables/consumable_Enlighten_Potion_aWHSO2AqDufi7nL4.json +++ b/src/packs/items/consumables/consumable_Enlighten_Potion_aWHSO2AqDufi7nL4.json @@ -14,7 +14,16 @@ "description": "

    You gain a +1 bonus to your next Knowledge Roll.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "aWHSO2AqDufi7nL4", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753587185754, - "modifiedTime": 1755432555520, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336472979, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!aWHSO2AqDufi7nL4" } diff --git a/src/packs/items/consumables/consumable_Feast_of_Xuria_aX6NyxkNzu0LcJpt.json b/src/packs/items/consumables/consumable_Feast_of_Xuria_aX6NyxkNzu0LcJpt.json index e463d5c3..4b4c8236 100644 --- a/src/packs/items/consumables/consumable_Feast_of_Xuria_aX6NyxkNzu0LcJpt.json +++ b/src/packs/items/consumables/consumable_Feast_of_Xuria_aX6NyxkNzu0LcJpt.json @@ -14,7 +14,16 @@ "description": "

    You can eat this meal to clear all HP and Stress and gain 1d4 Hope.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "aX6NyxkNzu0LcJpt", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -125,6 +134,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, @@ -145,10 +155,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753591925502, - "modifiedTime": 1755433009779, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336480162, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!aX6NyxkNzu0LcJpt" } diff --git a/src/packs/items/consumables/consumable_Featherbone_DpxEMpwfasEBpORU.json b/src/packs/items/consumables/consumable_Featherbone_DpxEMpwfasEBpORU.json index fbdb9c17..023edb88 100644 --- a/src/packs/items/consumables/consumable_Featherbone_DpxEMpwfasEBpORU.json +++ b/src/packs/items/consumables/consumable_Featherbone_DpxEMpwfasEBpORU.json @@ -14,7 +14,16 @@ "description": "

    You can use this bone to control your falling speed for a number of minutes equal to your level.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "DpxEMpwfasEBpORU", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.2.0", "createdTime": 1753590624634, - "modifiedTime": 1756042004101, - "lastModifiedBy": "vUIbuan0U50nfKBE" + "modifiedTime": 1756336487822, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!DpxEMpwfasEBpORU" } diff --git a/src/packs/items/consumables/consumable_Gill_Salve_Nvbb9mze6o5D0AEg.json b/src/packs/items/consumables/consumable_Gill_Salve_Nvbb9mze6o5D0AEg.json index dd7ce092..b76ff1f9 100644 --- a/src/packs/items/consumables/consumable_Gill_Salve_Nvbb9mze6o5D0AEg.json +++ b/src/packs/items/consumables/consumable_Gill_Salve_Nvbb9mze6o5D0AEg.json @@ -14,7 +14,16 @@ "description": "

    You can apply this salve to your neck to breathe underwater for a number of minutes equal to your level.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "Nvbb9mze6o5D0AEg", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.2.0", "createdTime": 1753589241094, - "modifiedTime": 1756041983578, - "lastModifiedBy": "vUIbuan0U50nfKBE" + "modifiedTime": 1756336496390, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!Nvbb9mze6o5D0AEg" } diff --git a/src/packs/items/consumables/consumable_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json b/src/packs/items/consumables/consumable_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json index e2150bed..b208c3ac 100644 --- a/src/packs/items/consumables/consumable_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json +++ b/src/packs/items/consumables/consumable_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json @@ -14,7 +14,16 @@ "description": "

    You can apply this venom to a weapon that deals physical damage to add a d6 to your next damage roll with that weapon.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "8WkhvSzeOmLdnoLJ", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -108,10 +118,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753587386639, - "modifiedTime": 1755432584688, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336506199, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!8WkhvSzeOmLdnoLJ" } diff --git a/src/packs/items/consumables/consumable_Growing_Potion_fl2f3ees8RFMze9t.json b/src/packs/items/consumables/consumable_Growing_Potion_fl2f3ees8RFMze9t.json index 055fd275..5563b561 100644 --- a/src/packs/items/consumables/consumable_Growing_Potion_fl2f3ees8RFMze9t.json +++ b/src/packs/items/consumables/consumable_Growing_Potion_fl2f3ees8RFMze9t.json @@ -14,7 +14,16 @@ "description": "

    You can drink this potion to double your size until you choose to drop this form or your next rest. While in this form, you have a +2 bonus to Strength and a +1 bonus to your Proficiency.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "fl2f3ees8RFMze9t", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, @@ -107,10 +117,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753592174440, - "modifiedTime": 1755433036930, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336513568, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!fl2f3ees8RFMze9t" } diff --git a/src/packs/items/consumables/consumable_Health_Potion_Aruc2NLutWuVIjP1.json b/src/packs/items/consumables/consumable_Health_Potion_Aruc2NLutWuVIjP1.json index 22afde4d..c600d102 100644 --- a/src/packs/items/consumables/consumable_Health_Potion_Aruc2NLutWuVIjP1.json +++ b/src/packs/items/consumables/consumable_Health_Potion_Aruc2NLutWuVIjP1.json @@ -14,7 +14,16 @@ "description": "

    Clear 1d4+1 HP.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "Aruc2NLutWuVIjP1", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -75,6 +84,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -95,10 +105,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753588839527, - "modifiedTime": 1755432692443, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336520901, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!Aruc2NLutWuVIjP1" } diff --git a/src/packs/items/consumables/consumable_Homet_s_Secret_Potion_VSwa1LpQ9PjZKsWF.json b/src/packs/items/consumables/consumable_Homet_s_Secret_Potion_VSwa1LpQ9PjZKsWF.json index 9f814705..8488df03 100644 --- a/src/packs/items/consumables/consumable_Homet_s_Secret_Potion_VSwa1LpQ9PjZKsWF.json +++ b/src/packs/items/consumables/consumable_Homet_s_Secret_Potion_VSwa1LpQ9PjZKsWF.json @@ -14,7 +14,16 @@ "description": "

    After drinking this potion, the next successful attack you make critically succeeds.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "VSwa1LpQ9PjZKsWF", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753589998065, - "modifiedTime": 1755432817234, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336528677, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!VSwa1LpQ9PjZKsWF" } diff --git a/src/packs/items/consumables/consumable_Hopehold_Flare_EhaQCPJ8oiqpRIwB.json b/src/packs/items/consumables/consumable_Hopehold_Flare_EhaQCPJ8oiqpRIwB.json index 75c96d1b..6cb963a8 100644 --- a/src/packs/items/consumables/consumable_Hopehold_Flare_EhaQCPJ8oiqpRIwB.json +++ b/src/packs/items/consumables/consumable_Hopehold_Flare_EhaQCPJ8oiqpRIwB.json @@ -14,7 +14,16 @@ "description": "

    When you use this flare, allies within Close range roll a d6 when they spend a Hope. On a result of 6, they gain the effect of that Hope without spending it. The flare lasts until the end of the scene.

    @Template[type:emanation|range:c]

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "EhaQCPJ8oiqpRIwB", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -31,6 +40,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -51,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753590388618, - "modifiedTime": 1755432869271, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336535700, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!EhaQCPJ8oiqpRIwB" } diff --git a/src/packs/items/consumables/consumable_Improved_Arcane_Shard_nQTo6mNoPTEVBtkm.json b/src/packs/items/consumables/consumable_Improved_Arcane_Shard_nQTo6mNoPTEVBtkm.json index 2319ae44..09ba52dc 100644 --- a/src/packs/items/consumables/consumable_Improved_Arcane_Shard_nQTo6mNoPTEVBtkm.json +++ b/src/packs/items/consumables/consumable_Improved_Arcane_Shard_nQTo6mNoPTEVBtkm.json @@ -14,7 +14,16 @@ "description": "

    You can make a Finesse Roll to throw this shard at a group of adversaries within Far range. Targets you succeed against take 2d20 magic damage.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "consumeOnSuccess": false, + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "nQTo6mNoPTEVBtkm", + "step": null + } + ], "uses": { "value": null, "max": "", @@ -82,6 +91,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -102,10 +112,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753589403341, - "modifiedTime": 1755432739380, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336543336, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!nQTo6mNoPTEVBtkm" } diff --git a/src/packs/items/consumables/consumable_Improved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json b/src/packs/items/consumables/consumable_Improved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json index 1ad9bdb4..240e2bd3 100644 --- a/src/packs/items/consumables/consumable_Improved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json +++ b/src/packs/items/consumables/consumable_Improved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json @@ -14,7 +14,16 @@ "description": "

    You can apply this venom to a weapon that deals physical damage to add a d8 to your next damage roll with that weapon.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "BqBWXXe9T07AMV4u", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -108,10 +118,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753588170670, - "modifiedTime": 1755432643893, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336563205, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!BqBWXXe9T07AMV4u" } diff --git a/src/packs/items/consumables/consumable_Jar_of_Lost_Voices_yUol6M5b8jsbk9za.json b/src/packs/items/consumables/consumable_Jar_of_Lost_Voices_yUol6M5b8jsbk9za.json index cff7849b..7a9a8193 100644 --- a/src/packs/items/consumables/consumable_Jar_of_Lost_Voices_yUol6M5b8jsbk9za.json +++ b/src/packs/items/consumables/consumable_Jar_of_Lost_Voices_yUol6M5b8jsbk9za.json @@ -14,7 +14,16 @@ "description": "

    You can open this jar to release a deafening echo of voices for a number of minutes equal to your Instinct. Creatures within Far range unprepared for the sound take 6d8 magic damage.

    @Template[type:emanation|range:f]

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "yUol6M5b8jsbk9za", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -62,6 +71,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -82,10 +92,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753591372075, - "modifiedTime": 1755432964594, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336570894, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!yUol6M5b8jsbk9za" } diff --git a/src/packs/items/consumables/consumable_Jumping_Root_c2putn9apuurJhWX.json b/src/packs/items/consumables/consumable_Jumping_Root_c2putn9apuurJhWX.json index 352c229d..9f9939a8 100644 --- a/src/packs/items/consumables/consumable_Jumping_Root_c2putn9apuurJhWX.json +++ b/src/packs/items/consumables/consumable_Jumping_Root_c2putn9apuurJhWX.json @@ -14,7 +14,16 @@ "description": "

    Eat this root to leap up to Far range once without needing to roll.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "c2putn9apuurJhWX", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -31,6 +40,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -51,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753588566489, - "modifiedTime": 1755432674260, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336580501, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!c2putn9apuurJhWX" } diff --git a/src/packs/items/consumables/consumable_Knowledge_Stone_nL9IALzm9BNi5oSt.json b/src/packs/items/consumables/consumable_Knowledge_Stone_nL9IALzm9BNi5oSt.json index 17a2992e..398d12f5 100644 --- a/src/packs/items/consumables/consumable_Knowledge_Stone_nL9IALzm9BNi5oSt.json +++ b/src/packs/items/consumables/consumable_Knowledge_Stone_nL9IALzm9BNi5oSt.json @@ -14,7 +14,16 @@ "description": "

    If you die while holding this stone, an ally can take a card from your loadout to place in their loadout or vault. After they take this knowledge, the stone crumbles.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "nL9IALzm9BNi5oSt", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -31,6 +40,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, @@ -51,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753592240392, - "modifiedTime": 1755433061047, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336587702, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!nL9IALzm9BNi5oSt" } diff --git a/src/packs/items/consumables/consumable_Major_Arcane_Shard_AA7bmiwv00lshPrC.json b/src/packs/items/consumables/consumable_Major_Arcane_Shard_AA7bmiwv00lshPrC.json index 002bd46d..1cc164a2 100644 --- a/src/packs/items/consumables/consumable_Major_Arcane_Shard_AA7bmiwv00lshPrC.json +++ b/src/packs/items/consumables/consumable_Major_Arcane_Shard_AA7bmiwv00lshPrC.json @@ -14,7 +14,16 @@ "description": "

    You can make a Finesse Roll to throw this shard at a group of adversaries within Far range. Targets you succeed against take 4d20 magic damage.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "consumeOnSuccess": false, + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "AA7bmiwv00lshPrC", + "step": null + } + ], "uses": { "value": null, "max": "", @@ -80,6 +89,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -100,10 +110,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753590515261, - "modifiedTime": 1755432877237, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336596900, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!AA7bmiwv00lshPrC" } diff --git a/src/packs/items/consumables/consumable_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json b/src/packs/items/consumables/consumable_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json index 127bcd72..05e4fe5d 100644 --- a/src/packs/items/consumables/consumable_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json +++ b/src/packs/items/consumables/consumable_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json @@ -14,7 +14,16 @@ "description": "

    You gain a +1 bonus to your Instinct until your next rest.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "CCPFm5iXXwvyYYwR", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753589747286, - "modifiedTime": 1755432778983, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336603508, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!CCPFm5iXXwvyYYwR" } diff --git a/src/packs/items/consumables/consumable_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json b/src/packs/items/consumables/consumable_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json index 90d28bc8..12f5bf1d 100644 --- a/src/packs/items/consumables/consumable_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json +++ b/src/packs/items/consumables/consumable_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json @@ -14,7 +14,16 @@ "description": "

    You gain a +1 bonus to your Strength until your next rest.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "mnyQDRtngWWQeRXF", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753589623872, - "modifiedTime": 1755432764001, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336610323, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!mnyQDRtngWWQeRXF" } diff --git a/src/packs/items/consumables/consumable_Major_Charm_Potion_IJLAUlQymbSjzsri.json b/src/packs/items/consumables/consumable_Major_Charm_Potion_IJLAUlQymbSjzsri.json index 8d5d83c6..84a18028 100644 --- a/src/packs/items/consumables/consumable_Major_Charm_Potion_IJLAUlQymbSjzsri.json +++ b/src/packs/items/consumables/consumable_Major_Charm_Potion_IJLAUlQymbSjzsri.json @@ -14,7 +14,16 @@ "description": "

    You gain a +1 bonus to your Presence until your next rest.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "IJLAUlQymbSjzsri", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753589816684, - "modifiedTime": 1755432785816, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336617341, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!IJLAUlQymbSjzsri" } diff --git a/src/packs/items/consumables/consumable_Major_Control_Potion_80s1FLmTLtohZ5GH.json b/src/packs/items/consumables/consumable_Major_Control_Potion_80s1FLmTLtohZ5GH.json index dfab73ea..782e543c 100644 --- a/src/packs/items/consumables/consumable_Major_Control_Potion_80s1FLmTLtohZ5GH.json +++ b/src/packs/items/consumables/consumable_Major_Control_Potion_80s1FLmTLtohZ5GH.json @@ -14,7 +14,16 @@ "description": "

    You gain a +1 bonus to your Finesse until your next rest.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "80s1FLmTLtohZ5GH", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753589675185, - "modifiedTime": 1755432772014, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336639693, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!80s1FLmTLtohZ5GH" } diff --git a/src/packs/items/consumables/consumable_Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json b/src/packs/items/consumables/consumable_Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json index c51237fc..a827a738 100644 --- a/src/packs/items/consumables/consumable_Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json +++ b/src/packs/items/consumables/consumable_Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json @@ -14,7 +14,16 @@ "description": "

    You gain a +1 bonus to your Knowledge until your next rest.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "SDdv1G2veMLKrxcJ", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753589874661, - "modifiedTime": 1755432794986, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336647833, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!SDdv1G2veMLKrxcJ" } diff --git a/src/packs/items/consumables/consumable_Major_Health_Potion_cM7pHe8bBAxSZ2xR.json b/src/packs/items/consumables/consumable_Major_Health_Potion_cM7pHe8bBAxSZ2xR.json index 49d0f6f7..3e83a73c 100644 --- a/src/packs/items/consumables/consumable_Major_Health_Potion_cM7pHe8bBAxSZ2xR.json +++ b/src/packs/items/consumables/consumable_Major_Health_Potion_cM7pHe8bBAxSZ2xR.json @@ -14,7 +14,16 @@ "description": "

    Clear 1d4+2 HP.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "cM7pHe8bBAxSZ2xR", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -75,6 +84,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -95,10 +105,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753591046168, - "modifiedTime": 1755432927907, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336654766, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!cM7pHe8bBAxSZ2xR" } diff --git a/src/packs/items/consumables/consumable_Major_Stamina_Potion_I4cQ03xbxnc81EGa.json b/src/packs/items/consumables/consumable_Major_Stamina_Potion_I4cQ03xbxnc81EGa.json index f28faa25..a7cd987e 100644 --- a/src/packs/items/consumables/consumable_Major_Stamina_Potion_I4cQ03xbxnc81EGa.json +++ b/src/packs/items/consumables/consumable_Major_Stamina_Potion_I4cQ03xbxnc81EGa.json @@ -14,7 +14,16 @@ "description": "

    Clear 1d4+2 Stress.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "I4cQ03xbxnc81EGa", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -75,6 +84,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -95,10 +105,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753591113317, - "modifiedTime": 1755432935058, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336661213, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!I4cQ03xbxnc81EGa" } diff --git a/src/packs/items/consumables/consumable_Major_Stride_Potion_yK6eEDUrsPbZA8G0.json b/src/packs/items/consumables/consumable_Major_Stride_Potion_yK6eEDUrsPbZA8G0.json index 80dcead1..60cd96b2 100644 --- a/src/packs/items/consumables/consumable_Major_Stride_Potion_yK6eEDUrsPbZA8G0.json +++ b/src/packs/items/consumables/consumable_Major_Stride_Potion_yK6eEDUrsPbZA8G0.json @@ -14,7 +14,16 @@ "description": "

    You gain a +1 bonus to your Agility until your next rest.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "yK6eEDUrsPbZA8G0", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753589545730, - "modifiedTime": 1755432751630, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336667559, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!yK6eEDUrsPbZA8G0" } diff --git a/src/packs/items/consumables/consumable_Minor_Health_Potion_tPfKtKRRjv8qdSqy.json b/src/packs/items/consumables/consumable_Minor_Health_Potion_tPfKtKRRjv8qdSqy.json index bc7bb074..5dc8ca28 100644 --- a/src/packs/items/consumables/consumable_Minor_Health_Potion_tPfKtKRRjv8qdSqy.json +++ b/src/packs/items/consumables/consumable_Minor_Health_Potion_tPfKtKRRjv8qdSqy.json @@ -14,7 +14,16 @@ "description": "

    Clear 1d4 HP.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "tPfKtKRRjv8qdSqy", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -75,6 +84,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -95,10 +105,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753587253431, - "modifiedTime": 1755432566654, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336675059, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!tPfKtKRRjv8qdSqy" } diff --git a/src/packs/items/consumables/consumable_Minor_Stamina_Potion_b6vGSPFWOlzZZDLO.json b/src/packs/items/consumables/consumable_Minor_Stamina_Potion_b6vGSPFWOlzZZDLO.json index b4bd2a2c..81b000f7 100644 --- a/src/packs/items/consumables/consumable_Minor_Stamina_Potion_b6vGSPFWOlzZZDLO.json +++ b/src/packs/items/consumables/consumable_Minor_Stamina_Potion_b6vGSPFWOlzZZDLO.json @@ -14,7 +14,16 @@ "description": "

    Clear 1d4 Stress.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "b6vGSPFWOlzZZDLO", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -75,6 +84,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -95,10 +105,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753587324465, - "modifiedTime": 1755432574155, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336685650, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!b6vGSPFWOlzZZDLO" } diff --git a/src/packs/items/consumables/consumable_Mirror_of_Marigold_UFQVwgYOUZ88UxcH.json b/src/packs/items/consumables/consumable_Mirror_of_Marigold_UFQVwgYOUZ88UxcH.json index 1415d195..7c80bc78 100644 --- a/src/packs/items/consumables/consumable_Mirror_of_Marigold_UFQVwgYOUZ88UxcH.json +++ b/src/packs/items/consumables/consumable_Mirror_of_Marigold_UFQVwgYOUZ88UxcH.json @@ -16,11 +16,12 @@ "actionType": "action", "cost": [ { - "keyIsID": false, - "key": "hope", - "value": 1, "scalable": false, - "step": null + "key": "quantity", + "value": 1, + "itemId": "UFQVwgYOUZ88UxcH", + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -39,6 +40,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, @@ -59,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753592792213, - "modifiedTime": 1755433095534, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336692719, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!UFQVwgYOUZ88UxcH" } diff --git a/src/packs/items/consumables/consumable_Morphing_Clay_f1NHVSIHJJCIOaBl.json b/src/packs/items/consumables/consumable_Morphing_Clay_f1NHVSIHJJCIOaBl.json index ce16c984..4afc72ba 100644 --- a/src/packs/items/consumables/consumable_Morphing_Clay_f1NHVSIHJJCIOaBl.json +++ b/src/packs/items/consumables/consumable_Morphing_Clay_f1NHVSIHJJCIOaBl.json @@ -16,11 +16,12 @@ "actionType": "action", "cost": [ { - "keyIsID": false, - "key": "hope", - "value": 1, "scalable": false, - "step": null + "key": "quantity", + "value": 1, + "itemId": "f1NHVSIHJJCIOaBl", + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -44,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -109,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753588254032, - "modifiedTime": 1755432657276, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336699132, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!f1NHVSIHJJCIOaBl" } diff --git a/src/packs/items/consumables/consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json b/src/packs/items/consumables/consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json index f720881e..5c5f4325 100644 --- a/src/packs/items/consumables/consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json +++ b/src/packs/items/consumables/consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json @@ -14,7 +14,16 @@ "description": "

    You can apply this dust to a weapon that deals magic damage to add a d12 to your next damage roll with that weapon.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "Zsh2AvZr8EkGtLyw", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -108,10 +118,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753590241722, - "modifiedTime": 1755432854453, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336705787, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!Zsh2AvZr8EkGtLyw" } diff --git a/src/packs/items/consumables/consumable_Ogre_Musk_qr1bosjFcUfuwq4B.json b/src/packs/items/consumables/consumable_Ogre_Musk_qr1bosjFcUfuwq4B.json index 38ef8357..be940eab 100644 --- a/src/packs/items/consumables/consumable_Ogre_Musk_qr1bosjFcUfuwq4B.json +++ b/src/packs/items/consumables/consumable_Ogre_Musk_qr1bosjFcUfuwq4B.json @@ -14,7 +14,16 @@ "description": "

    You can use this musk to prevent anyone from tracking you by mundane or magical means until your next rest.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "qr1bosjFcUfuwq4B", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753591168468, - "modifiedTime": 1755432944044, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336713230, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!qr1bosjFcUfuwq4B" } diff --git a/src/packs/items/consumables/consumable_Potion_of_Stability_dvL8oaxpEF6jKvYN.json b/src/packs/items/consumables/consumable_Potion_of_Stability_dvL8oaxpEF6jKvYN.json index 41b29bdf..c7fa2bbb 100644 --- a/src/packs/items/consumables/consumable_Potion_of_Stability_dvL8oaxpEF6jKvYN.json +++ b/src/packs/items/consumables/consumable_Potion_of_Stability_dvL8oaxpEF6jKvYN.json @@ -14,7 +14,16 @@ "description": "

    You can drink this potion to choose one additional downtime move.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "dvL8oaxpEF6jKvYN", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -107,10 +117,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753588038000, - "modifiedTime": 1755432626825, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336720970, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!dvL8oaxpEF6jKvYN" } diff --git a/src/packs/items/consumables/consumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json b/src/packs/items/consumables/consumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json index e14b5524..82e4d4b9 100644 --- a/src/packs/items/consumables/consumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json +++ b/src/packs/items/consumables/consumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json @@ -14,7 +14,16 @@ "description": "

    You can apply this saliva to a weapon that deals physical damage to add a d12 to your next damage roll with that weapon.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "s2Exl2XFuoOhtIov", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -108,10 +118,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753590061810, - "modifiedTime": 1755432828838, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336727954, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!s2Exl2XFuoOhtIov" } diff --git a/src/packs/items/consumables/consumable_Replication_Parchment_yJkwz4AP6yhGo8Vj.json b/src/packs/items/consumables/consumable_Replication_Parchment_yJkwz4AP6yhGo8Vj.json index 68681ddd..7e07b702 100644 --- a/src/packs/items/consumables/consumable_Replication_Parchment_yJkwz4AP6yhGo8Vj.json +++ b/src/packs/items/consumables/consumable_Replication_Parchment_yJkwz4AP6yhGo8Vj.json @@ -14,7 +14,16 @@ "description": "

    By touching this piece of parchment to another, you can perfectly copy the second parchment’s contents. Once used, this parchment becomes mundane paper.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "yJkwz4AP6yhGo8Vj", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -31,6 +40,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -51,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753589306667, - "modifiedTime": 1755432726696, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336734771, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!yJkwz4AP6yhGo8Vj" } diff --git a/src/packs/items/consumables/consumable_Shrinking_Potion_HGixKenQwhyRAYNk.json b/src/packs/items/consumables/consumable_Shrinking_Potion_HGixKenQwhyRAYNk.json index f2336545..08af29cc 100644 --- a/src/packs/items/consumables/consumable_Shrinking_Potion_HGixKenQwhyRAYNk.json +++ b/src/packs/items/consumables/consumable_Shrinking_Potion_HGixKenQwhyRAYNk.json @@ -14,7 +14,16 @@ "description": "

    You can drink this potion to halve your size until you choose to drop this form or your next rest. While in this form, you have a +2 bonus to Agility and a −1 penalty to your Proficiency.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "HGixKenQwhyRAYNk", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, @@ -107,10 +117,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753592077792, - "modifiedTime": 1755433027028, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336741086, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!HGixKenQwhyRAYNk" } diff --git a/src/packs/items/consumables/consumable_Sleeping_Sap_XZavUVlHEvE2srEt.json b/src/packs/items/consumables/consumable_Sleeping_Sap_XZavUVlHEvE2srEt.json index a397074b..b627695a 100644 --- a/src/packs/items/consumables/consumable_Sleeping_Sap_XZavUVlHEvE2srEt.json +++ b/src/packs/items/consumables/consumable_Sleeping_Sap_XZavUVlHEvE2srEt.json @@ -14,7 +14,16 @@ "description": "

    You can drink this potion to fall asleep for a full night’s rest. You clear all Stress upon waking.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "XZavUVlHEvE2srEt", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -76,6 +85,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, @@ -96,10 +106,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753591837472, - "modifiedTime": 1755432997515, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336748248, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!XZavUVlHEvE2srEt" } diff --git a/src/packs/items/consumables/consumable_Snap_Powder_cg6VtQ0eVZjDdcK0.json b/src/packs/items/consumables/consumable_Snap_Powder_cg6VtQ0eVZjDdcK0.json index 86879588..cdc760ff 100644 --- a/src/packs/items/consumables/consumable_Snap_Powder_cg6VtQ0eVZjDdcK0.json +++ b/src/packs/items/consumables/consumable_Snap_Powder_cg6VtQ0eVZjDdcK0.json @@ -17,10 +17,11 @@ "cost": [ { "scalable": false, - "key": "stress", + "key": "quantity", "value": 1, - "keyIsID": false, - "step": null + "itemId": "cg6VtQ0eVZjDdcK0", + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -84,6 +85,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -104,10 +106,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753588752841, - "modifiedTime": 1755432682259, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336754402, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!cg6VtQ0eVZjDdcK0" } diff --git a/src/packs/items/consumables/consumable_Stamina_Potion_hf3k1POoVSooJyN2.json b/src/packs/items/consumables/consumable_Stamina_Potion_hf3k1POoVSooJyN2.json index 8837f815..d278c6fe 100644 --- a/src/packs/items/consumables/consumable_Stamina_Potion_hf3k1POoVSooJyN2.json +++ b/src/packs/items/consumables/consumable_Stamina_Potion_hf3k1POoVSooJyN2.json @@ -14,7 +14,16 @@ "description": "

    Clear 1d4+1 Stress.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "hf3k1POoVSooJyN2", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -75,6 +84,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -95,10 +105,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753588904835, - "modifiedTime": 1755432700497, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336761163, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!hf3k1POoVSooJyN2" } diff --git a/src/packs/items/consumables/consumable_Stardrop_y4c1jrlHrf0wBWOq.json b/src/packs/items/consumables/consumable_Stardrop_y4c1jrlHrf0wBWOq.json index 7d6bf0cd..b4c16c8d 100644 --- a/src/packs/items/consumables/consumable_Stardrop_y4c1jrlHrf0wBWOq.json +++ b/src/packs/items/consumables/consumable_Stardrop_y4c1jrlHrf0wBWOq.json @@ -14,7 +14,16 @@ "description": "

    You can use this stardrop to summon a hailstorm of comets that deals 8d20 physical damage to all targets within Very Far range.

    @Template[type:emanation|range:vf]

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "y4c1jrlHrf0wBWOq", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -62,6 +71,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, @@ -82,10 +92,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753592933782, - "modifiedTime": 1755433102382, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336777144, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!y4c1jrlHrf0wBWOq" } diff --git a/src/packs/items/consumables/consumable_Stride_Potion_lNtcrkgFGOJNaroE.json b/src/packs/items/consumables/consumable_Stride_Potion_lNtcrkgFGOJNaroE.json index 3849ac0d..dd8a354b 100644 --- a/src/packs/items/consumables/consumable_Stride_Potion_lNtcrkgFGOJNaroE.json +++ b/src/packs/items/consumables/consumable_Stride_Potion_lNtcrkgFGOJNaroE.json @@ -14,7 +14,16 @@ "description": "

    You gain a +1 bonus to your next Agility Roll.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "lNtcrkgFGOJNaroE", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753585993187, - "modifiedTime": 1755432512018, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336784139, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!lNtcrkgFGOJNaroE" } diff --git a/src/packs/items/consumables/consumable_Sun_Tree_Sap_kwexUzdM9wm1Qums.json b/src/packs/items/consumables/consumable_Sun_Tree_Sap_kwexUzdM9wm1Qums.json index 3ee1ec60..f79704f4 100644 --- a/src/packs/items/consumables/consumable_Sun_Tree_Sap_kwexUzdM9wm1Qums.json +++ b/src/packs/items/consumables/consumable_Sun_Tree_Sap_kwexUzdM9wm1Qums.json @@ -14,7 +14,16 @@ "description": "

    Consume this sap to roll a [[/r d6]]. On a result of 5–6, clear 2 HP. On a result of 2–4, clear 3 Stress. On a result of 1, see through the veil of death and return changed, gaining one scar.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "consumeOnSuccess": false, + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "kwexUzdM9wm1Qums", + "step": null + } + ], "uses": { "value": null, "max": "", @@ -55,6 +64,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -75,10 +85,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753590791260, - "modifiedTime": 1755432908523, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336791229, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!kwexUzdM9wm1Qums" } diff --git a/src/packs/items/consumables/consumable_Sweet_Moss_GrDrRqWgv7gvl9vn.json b/src/packs/items/consumables/consumable_Sweet_Moss_GrDrRqWgv7gvl9vn.json index 8dac2f79..e366ddbe 100644 --- a/src/packs/items/consumables/consumable_Sweet_Moss_GrDrRqWgv7gvl9vn.json +++ b/src/packs/items/consumables/consumable_Sweet_Moss_GrDrRqWgv7gvl9vn.json @@ -14,7 +14,16 @@ "description": "

    You can consume this moss during a rest to clear 1d10 HP.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "GrDrRqWgv7gvl9vn", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -80,7 +89,16 @@ "description": "

    You can consume this moss during a rest to clear 1d10 Stress.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "GrDrRqWgv7gvl9vn", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -141,6 +159,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, @@ -161,10 +180,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753592391195, - "modifiedTime": 1755433069335, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336804065, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!GrDrRqWgv7gvl9vn" } diff --git a/src/packs/items/consumables/consumable_Unstable_Arcane_Shard_mUepnLbkvFk0ha4Z.json b/src/packs/items/consumables/consumable_Unstable_Arcane_Shard_mUepnLbkvFk0ha4Z.json index ab941d10..8194d7b2 100644 --- a/src/packs/items/consumables/consumable_Unstable_Arcane_Shard_mUepnLbkvFk0ha4Z.json +++ b/src/packs/items/consumables/consumable_Unstable_Arcane_Shard_mUepnLbkvFk0ha4Z.json @@ -14,7 +14,16 @@ "description": "

    You can make a Finesse Roll to throw this shard at a group of adversaries within Far range. Targets you succeed against take 1d20 magic damage.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "consumeOnSuccess": false, + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "mUepnLbkvFk0ha4Z", + "step": null + } + ], "uses": { "value": null, "max": "", @@ -82,6 +91,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -102,10 +112,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753587732694, - "modifiedTime": 1755432611972, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336811267, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!mUepnLbkvFk0ha4Z" } diff --git a/src/packs/items/consumables/consumable_Varik_Leaves_hvy5BkG3F6iOIXTx.json b/src/packs/items/consumables/consumable_Varik_Leaves_hvy5BkG3F6iOIXTx.json index aef91404..cec9395e 100644 --- a/src/packs/items/consumables/consumable_Varik_Leaves_hvy5BkG3F6iOIXTx.json +++ b/src/packs/items/consumables/consumable_Varik_Leaves_hvy5BkG3F6iOIXTx.json @@ -14,7 +14,16 @@ "description": "

    You can eat these paired leaves to immediately gain 2 Hope.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "hvy5BkG3F6iOIXTx", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -76,6 +85,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -96,10 +106,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753587484164, - "modifiedTime": 1755432594538, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336817973, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!hvy5BkG3F6iOIXTx" } diff --git a/src/packs/items/consumables/consumable_Vial_of_Darksmoke_Nwv5ydGf0MWnzq1n.json b/src/packs/items/consumables/consumable_Vial_of_Darksmoke_Nwv5ydGf0MWnzq1n.json index 56957a44..4959dc10 100644 --- a/src/packs/items/consumables/consumable_Vial_of_Darksmoke_Nwv5ydGf0MWnzq1n.json +++ b/src/packs/items/consumables/consumable_Vial_of_Darksmoke_Nwv5ydGf0MWnzq1n.json @@ -14,7 +14,16 @@ "description": "

    When an adversary attacks you, use this vial and roll a number of d6s equal to your Agility. Add the highest result to your Evasion against the attack.

    ", "chatDisplay": true, "actionType": "reaction", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "Nwv5ydGf0MWnzq1n", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -31,6 +40,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -51,10 +61,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753588345314, - "modifiedTime": 1755432665859, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336825474, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!Nwv5ydGf0MWnzq1n" } diff --git a/src/packs/items/consumables/consumable_Vial_of_Moondrip_VqEX5YwK5oL3r1t6.json b/src/packs/items/consumables/consumable_Vial_of_Moondrip_VqEX5YwK5oL3r1t6.json index 26dc7ede..b849d989 100644 --- a/src/packs/items/consumables/consumable_Vial_of_Moondrip_VqEX5YwK5oL3r1t6.json +++ b/src/packs/items/consumables/consumable_Vial_of_Moondrip_VqEX5YwK5oL3r1t6.json @@ -14,7 +14,16 @@ "description": "

    When you drink the contents of this vial, you can see in total darkness until your next rest.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "VqEX5YwK5oL3r1t6", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753587590537, - "modifiedTime": 1755432602190, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336832961, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!VqEX5YwK5oL3r1t6" } diff --git a/src/packs/items/consumables/consumable_Wingsprout_n10vozlmosVR6lo4.json b/src/packs/items/consumables/consumable_Wingsprout_n10vozlmosVR6lo4.json index 32552f86..eb3aeb5c 100644 --- a/src/packs/items/consumables/consumable_Wingsprout_n10vozlmosVR6lo4.json +++ b/src/packs/items/consumables/consumable_Wingsprout_n10vozlmosVR6lo4.json @@ -14,7 +14,16 @@ "description": "

    You gain magic wings that allow you to fly for a number of minutes equal to your level.

    ", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "quantity", + "value": 1, + "itemId": "n10vozlmosVR6lo4", + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -36,6 +45,7 @@ } }, "consumeOnUse": true, + "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, @@ -101,10 +111,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753591283853, - "modifiedTime": 1755432952411, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756336840548, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!n10vozlmosVR6lo4" } diff --git a/src/packs/items/loot/loot_Belt_of_Unity_gFzkUGCjkRJtyoe9.json b/src/packs/items/loot/loot_Belt_of_Unity_gFzkUGCjkRJtyoe9.json index acd9ffee..eaa9def5 100644 --- a/src/packs/items/loot/loot_Belt_of_Unity_gFzkUGCjkRJtyoe9.json +++ b/src/packs/items/loot/loot_Belt_of_Unity_gFzkUGCjkRJtyoe9.json @@ -19,7 +19,6 @@ "scalable": false, "key": "hope", "value": 5, - "keyIsID": false, "step": null } ], diff --git a/src/packs/items/loot/loot_Glamour_Stone_Pj17cvdJ1XG1jv6I.json b/src/packs/items/loot/loot_Glamour_Stone_Pj17cvdJ1XG1jv6I.json index fa76826b..98714741 100644 --- a/src/packs/items/loot/loot_Glamour_Stone_Pj17cvdJ1XG1jv6I.json +++ b/src/packs/items/loot/loot_Glamour_Stone_Pj17cvdJ1XG1jv6I.json @@ -16,7 +16,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/items/loot/loot_Glider_CiXwelozmBDcPY48.json b/src/packs/items/loot/loot_Glider_CiXwelozmBDcPY48.json index 2457d2e2..2dfce26c 100644 --- a/src/packs/items/loot/loot_Glider_CiXwelozmBDcPY48.json +++ b/src/packs/items/loot/loot_Glider_CiXwelozmBDcPY48.json @@ -19,7 +19,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/items/loot/loot_Hopekeeper_Locket_9DcFR75tsnBYIp6Z.json b/src/packs/items/loot/loot_Hopekeeper_Locket_9DcFR75tsnBYIp6Z.json index 83356cb6..f1eac926 100644 --- a/src/packs/items/loot/loot_Hopekeeper_Locket_9DcFR75tsnBYIp6Z.json +++ b/src/packs/items/loot/loot_Hopekeeper_Locket_9DcFR75tsnBYIp6Z.json @@ -16,7 +16,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/items/loot/loot_Paragon_s_Chain_F4hoRfvVdZq5bhhI.json b/src/packs/items/loot/loot_Paragon_s_Chain_F4hoRfvVdZq5bhhI.json index ab2f2cc3..59260e5d 100644 --- a/src/packs/items/loot/loot_Paragon_s_Chain_F4hoRfvVdZq5bhhI.json +++ b/src/packs/items/loot/loot_Paragon_s_Chain_F4hoRfvVdZq5bhhI.json @@ -16,7 +16,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/items/loot/loot_Ring_of_Silence_K1ysGnTpNyxPu5Au.json b/src/packs/items/loot/loot_Ring_of_Silence_K1ysGnTpNyxPu5Au.json index 7d38f5a2..d6e15140 100644 --- a/src/packs/items/loot/loot_Ring_of_Silence_K1ysGnTpNyxPu5Au.json +++ b/src/packs/items/loot/loot_Ring_of_Silence_K1ysGnTpNyxPu5Au.json @@ -16,7 +16,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/items/loot/loot_Ring_of_Unbreakable_Resolve_kn71qCQY0DnjmQBJ.json b/src/packs/items/loot/loot_Ring_of_Unbreakable_Resolve_kn71qCQY0DnjmQBJ.json index e4a4511f..99f856da 100644 --- a/src/packs/items/loot/loot_Ring_of_Unbreakable_Resolve_kn71qCQY0DnjmQBJ.json +++ b/src/packs/items/loot/loot_Ring_of_Unbreakable_Resolve_kn71qCQY0DnjmQBJ.json @@ -19,7 +19,6 @@ "scalable": false, "key": "hope", "value": 4, - "keyIsID": false, "step": null } ], diff --git a/src/packs/items/loot/loot_Shard_of_Memory_2ULPgNyqCrxea0v0.json b/src/packs/items/loot/loot_Shard_of_Memory_2ULPgNyqCrxea0v0.json index f6d004e4..efc12e63 100644 --- a/src/packs/items/loot/loot_Shard_of_Memory_2ULPgNyqCrxea0v0.json +++ b/src/packs/items/loot/loot_Shard_of_Memory_2ULPgNyqCrxea0v0.json @@ -19,7 +19,6 @@ "scalable": false, "key": "hope", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/items/weapons/weapon_Advanced_Light_Frame_Wheelchair_BuMfupnCzHbziQ8o.json b/src/packs/items/weapons/weapon_Advanced_Light_Frame_Wheelchair_BuMfupnCzHbziQ8o.json index 9321abf8..03b69488 100644 --- a/src/packs/items/weapons/weapon_Advanced_Light_Frame_Wheelchair_BuMfupnCzHbziQ8o.json +++ b/src/packs/items/weapons/weapon_Advanced_Light_Frame_Wheelchair_BuMfupnCzHbziQ8o.json @@ -17,7 +17,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Advanced_Rapier_KxFne76d7cak15dO.json b/src/packs/items/weapons/weapon_Advanced_Rapier_KxFne76d7cak15dO.json index 9352e5de..7e7226d2 100644 --- a/src/packs/items/weapons/weapon_Advanced_Rapier_KxFne76d7cak15dO.json +++ b/src/packs/items/weapons/weapon_Advanced_Rapier_KxFne76d7cak15dO.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Advanced_Whip_01izMUSJcAUo79IX.json b/src/packs/items/weapons/weapon_Advanced_Whip_01izMUSJcAUo79IX.json index 99cae67f..dfbb4906 100644 --- a/src/packs/items/weapons/weapon_Advanced_Whip_01izMUSJcAUo79IX.json +++ b/src/packs/items/weapons/weapon_Advanced_Whip_01izMUSJcAUo79IX.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Axe_of_Fortunis_YcS1rHgfnSlla8Xf.json b/src/packs/items/weapons/weapon_Axe_of_Fortunis_YcS1rHgfnSlla8Xf.json index eb5c8e2b..f7f748ae 100644 --- a/src/packs/items/weapons/weapon_Axe_of_Fortunis_YcS1rHgfnSlla8Xf.json +++ b/src/packs/items/weapons/weapon_Axe_of_Fortunis_YcS1rHgfnSlla8Xf.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Bladed_Whip_5faflfNz20cFW1EM.json b/src/packs/items/weapons/weapon_Bladed_Whip_5faflfNz20cFW1EM.json index c7a74b26..dba962aa 100644 --- a/src/packs/items/weapons/weapon_Bladed_Whip_5faflfNz20cFW1EM.json +++ b/src/packs/items/weapons/weapon_Bladed_Whip_5faflfNz20cFW1EM.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Bloodstaff_IoMVDz92WVvfGGdc.json b/src/packs/items/weapons/weapon_Bloodstaff_IoMVDz92WVvfGGdc.json index 1273d9a8..fb41b73f 100644 --- a/src/packs/items/weapons/weapon_Bloodstaff_IoMVDz92WVvfGGdc.json +++ b/src/packs/items/weapons/weapon_Bloodstaff_IoMVDz92WVvfGGdc.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json b/src/packs/items/weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json index 8e6ef6ca..2a7e0db6 100644 --- a/src/packs/items/weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json +++ b/src/packs/items/weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json @@ -20,11 +20,12 @@ }, "cost": [ { - "key": "armorSlot", + "key": "resource", + "itemId": "armorSlot", "value": 1, - "keyIsID": false, "scalable": false, - "step": null + "step": null, + "consumeOnSuccess": false } ], "effects": [ @@ -196,10 +197,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1753795181779, - "modifiedTime": 1755430450631, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575921, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_key": "!items!EmFTp9wzT6MHSaNz" } diff --git a/src/packs/items/weapons/weapon_Dual_Ended_Sword_nXjuBa215H1sTUK3.json b/src/packs/items/weapons/weapon_Dual_Ended_Sword_nXjuBa215H1sTUK3.json index 1c4f75ce..9b43770c 100644 --- a/src/packs/items/weapons/weapon_Dual_Ended_Sword_nXjuBa215H1sTUK3.json +++ b/src/packs/items/weapons/weapon_Dual_Ended_Sword_nXjuBa215H1sTUK3.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Hammer_of_Wrath_1R4uzOpWD8bkYRUm.json b/src/packs/items/weapons/weapon_Hammer_of_Wrath_1R4uzOpWD8bkYRUm.json index 0a1d9dab..bf29dc34 100644 --- a/src/packs/items/weapons/weapon_Hammer_of_Wrath_1R4uzOpWD8bkYRUm.json +++ b/src/packs/items/weapons/weapon_Hammer_of_Wrath_1R4uzOpWD8bkYRUm.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Impact_Gauntlet_Zg6IutksQVOqAg8K.json b/src/packs/items/weapons/weapon_Impact_Gauntlet_Zg6IutksQVOqAg8K.json index b817a332..ca842d59 100644 --- a/src/packs/items/weapons/weapon_Impact_Gauntlet_Zg6IutksQVOqAg8K.json +++ b/src/packs/items/weapons/weapon_Impact_Gauntlet_Zg6IutksQVOqAg8K.json @@ -22,7 +22,6 @@ { "key": "hope", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Improved_Light_Frame_Wheelchair_ZJsetdHKV77ygtCE.json b/src/packs/items/weapons/weapon_Improved_Light_Frame_Wheelchair_ZJsetdHKV77ygtCE.json index e774d93d..8922d621 100644 --- a/src/packs/items/weapons/weapon_Improved_Light_Frame_Wheelchair_ZJsetdHKV77ygtCE.json +++ b/src/packs/items/weapons/weapon_Improved_Light_Frame_Wheelchair_ZJsetdHKV77ygtCE.json @@ -17,7 +17,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Improved_Rapier_LFPH8nD2f4Blv3AM.json b/src/packs/items/weapons/weapon_Improved_Rapier_LFPH8nD2f4Blv3AM.json index 7c79855b..229d1682 100644 --- a/src/packs/items/weapons/weapon_Improved_Rapier_LFPH8nD2f4Blv3AM.json +++ b/src/packs/items/weapons/weapon_Improved_Rapier_LFPH8nD2f4Blv3AM.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Improved_Whip_ftTp8VlsBQ1r4LFD.json b/src/packs/items/weapons/weapon_Improved_Whip_ftTp8VlsBQ1r4LFD.json index 9f44f5bc..b52bf465 100644 --- a/src/packs/items/weapons/weapon_Improved_Whip_ftTp8VlsBQ1r4LFD.json +++ b/src/packs/items/weapons/weapon_Improved_Whip_ftTp8VlsBQ1r4LFD.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Legendary_Light_Frame_Wheelchair_Xt8tVSn5Fu6ly6LF.json b/src/packs/items/weapons/weapon_Legendary_Light_Frame_Wheelchair_Xt8tVSn5Fu6ly6LF.json index 7d9fc299..a9b35948 100644 --- a/src/packs/items/weapons/weapon_Legendary_Light_Frame_Wheelchair_Xt8tVSn5Fu6ly6LF.json +++ b/src/packs/items/weapons/weapon_Legendary_Light_Frame_Wheelchair_Xt8tVSn5Fu6ly6LF.json @@ -17,7 +17,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Legendary_Rapier_BakN97v4jTePcXiZ.json b/src/packs/items/weapons/weapon_Legendary_Rapier_BakN97v4jTePcXiZ.json index eaa6768c..b4df7c8c 100644 --- a/src/packs/items/weapons/weapon_Legendary_Rapier_BakN97v4jTePcXiZ.json +++ b/src/packs/items/weapons/weapon_Legendary_Rapier_BakN97v4jTePcXiZ.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Legendary_Whip_Wcdbf6yS3LEt7nsg.json b/src/packs/items/weapons/weapon_Legendary_Whip_Wcdbf6yS3LEt7nsg.json index 603f8fae..736db796 100644 --- a/src/packs/items/weapons/weapon_Legendary_Whip_Wcdbf6yS3LEt7nsg.json +++ b/src/packs/items/weapons/weapon_Legendary_Whip_Wcdbf6yS3LEt7nsg.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Light_Frame_Wheelchair_iaGnlUkShBgdeMo0.json b/src/packs/items/weapons/weapon_Light_Frame_Wheelchair_iaGnlUkShBgdeMo0.json index 62090883..72aa2931 100644 --- a/src/packs/items/weapons/weapon_Light_Frame_Wheelchair_iaGnlUkShBgdeMo0.json +++ b/src/packs/items/weapons/weapon_Light_Frame_Wheelchair_iaGnlUkShBgdeMo0.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Powered_Gauntlet_bW3xw5S9DbaLCN3E.json b/src/packs/items/weapons/weapon_Powered_Gauntlet_bW3xw5S9DbaLCN3E.json index 2e88d91c..f75537d3 100644 --- a/src/packs/items/weapons/weapon_Powered_Gauntlet_bW3xw5S9DbaLCN3E.json +++ b/src/packs/items/weapons/weapon_Powered_Gauntlet_bW3xw5S9DbaLCN3E.json @@ -22,7 +22,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Rapier_zkAgEW6zMkRZalEm.json b/src/packs/items/weapons/weapon_Rapier_zkAgEW6zMkRZalEm.json index 45f5d8e7..7b6129ce 100644 --- a/src/packs/items/weapons/weapon_Rapier_zkAgEW6zMkRZalEm.json +++ b/src/packs/items/weapons/weapon_Rapier_zkAgEW6zMkRZalEm.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Ricochet_Axes_E9QDh5o9eQ1Qx0kH.json b/src/packs/items/weapons/weapon_Ricochet_Axes_E9QDh5o9eQ1Qx0kH.json index 9c0f0eae..c00a90b6 100644 --- a/src/packs/items/weapons/weapon_Ricochet_Axes_E9QDh5o9eQ1Qx0kH.json +++ b/src/packs/items/weapons/weapon_Ricochet_Axes_E9QDh5o9eQ1Qx0kH.json @@ -19,8 +19,7 @@ "key": "stress", "value": 1, "scalable": true, - "step": 1, - "keyIsID": false + "step": 1 } ], "_id": "x9cz8u1utQ6DtoKA", diff --git a/src/packs/items/weapons/weapon_Runes_of_Ruination_EG6mZhr3ib56r974.json b/src/packs/items/weapons/weapon_Runes_of_Ruination_EG6mZhr3ib56r974.json index b062a33a..533f7ca9 100644 --- a/src/packs/items/weapons/weapon_Runes_of_Ruination_EG6mZhr3ib56r974.json +++ b/src/packs/items/weapons/weapon_Runes_of_Ruination_EG6mZhr3ib56r974.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Swinging_Ropeblade_1jOJHHKdtk3s2jaY.json b/src/packs/items/weapons/weapon_Swinging_Ropeblade_1jOJHHKdtk3s2jaY.json index 03e4053f..141d92c1 100644 --- a/src/packs/items/weapons/weapon_Swinging_Ropeblade_1jOJHHKdtk3s2jaY.json +++ b/src/packs/items/weapons/weapon_Swinging_Ropeblade_1jOJHHKdtk3s2jaY.json @@ -18,7 +18,6 @@ { "key": "hope", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Wand_of_Enthrallment_tP6vmnrmTq2h5sj7.json b/src/packs/items/weapons/weapon_Wand_of_Enthrallment_tP6vmnrmTq2h5sj7.json index 88522c0b..425c9ea1 100644 --- a/src/packs/items/weapons/weapon_Wand_of_Enthrallment_tP6vmnrmTq2h5sj7.json +++ b/src/packs/items/weapons/weapon_Wand_of_Enthrallment_tP6vmnrmTq2h5sj7.json @@ -22,7 +22,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/items/weapons/weapon_Whip_CmtWqw6DwoePnX7W.json b/src/packs/items/weapons/weapon_Whip_CmtWqw6DwoePnX7W.json index dd75905f..36490a5d 100644 --- a/src/packs/items/weapons/weapon_Whip_CmtWqw6DwoePnX7W.json +++ b/src/packs/items/weapons/weapon_Whip_CmtWqw6DwoePnX7W.json @@ -18,7 +18,6 @@ { "key": "stress", "value": 1, - "keyIsID": false, "scalable": false, "step": null } diff --git a/src/packs/subclasses/feature_Adept_v511C6GMShsBblah.json b/src/packs/subclasses/feature_Adept_v511C6GMShsBblah.json index 4e5b9d0b..3cfe1052 100644 --- a/src/packs/subclasses/feature_Adept_v511C6GMShsBblah.json +++ b/src/packs/subclasses/feature_Adept_v511C6GMShsBblah.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/subclasses/feature_Apex_Predator_lwH3E0Zyf4gbVOd0.json b/src/packs/subclasses/feature_Apex_Predator_lwH3E0Zyf4gbVOd0.json index 31d79e90..545f2116 100644 --- a/src/packs/subclasses/feature_Apex_Predator_lwH3E0Zyf4gbVOd0.json +++ b/src/packs/subclasses/feature_Apex_Predator_lwH3E0Zyf4gbVOd0.json @@ -17,7 +17,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/subclasses/feature_Clarity_of_Nature_etaQ01yGJhBLDUqZ.json b/src/packs/subclasses/feature_Clarity_of_Nature_etaQ01yGJhBLDUqZ.json index a0c614a5..ff56a435 100644 --- a/src/packs/subclasses/feature_Clarity_of_Nature_etaQ01yGJhBLDUqZ.json +++ b/src/packs/subclasses/feature_Clarity_of_Nature_etaQ01yGJhBLDUqZ.json @@ -17,11 +17,12 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "etaQ01yGJhBLDUqZ", + "key": "resource", + "itemId": "etaQ01yGJhBLDUqZ", "value": 1, - "keyIsID": true, - "step": null + "scalable": false, + "step": null, + "consumeOnSuccess": false } ], "uses": { @@ -105,10 +106,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1754179740310, - "modifiedTime": 1755391695859, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575203, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_key": "!items!etaQ01yGJhBLDUqZ" } diff --git a/src/packs/subclasses/feature_Contacts_Everywhere_cXbRm744mW6UXGam.json b/src/packs/subclasses/feature_Contacts_Everywhere_cXbRm744mW6UXGam.json index 2b69d05e..75a5f905 100644 --- a/src/packs/subclasses/feature_Contacts_Everywhere_cXbRm744mW6UXGam.json +++ b/src/packs/subclasses/feature_Contacts_Everywhere_cXbRm744mW6UXGam.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hitPoints", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/subclasses/feature_Defender_Jdktv5p1K2PfgxrT.json b/src/packs/subclasses/feature_Defender_Jdktv5p1K2PfgxrT.json index 515f12da..017a0752 100644 --- a/src/packs/subclasses/feature_Defender_Jdktv5p1K2PfgxrT.json +++ b/src/packs/subclasses/feature_Defender_Jdktv5p1K2PfgxrT.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/subclasses/feature_Elemental_Dominion_EFUJHrkTuyv8uA9l.json b/src/packs/subclasses/feature_Elemental_Dominion_EFUJHrkTuyv8uA9l.json index 11317c54..5cad10d1 100644 --- a/src/packs/subclasses/feature_Elemental_Dominion_EFUJHrkTuyv8uA9l.json +++ b/src/packs/subclasses/feature_Elemental_Dominion_EFUJHrkTuyv8uA9l.json @@ -74,7 +74,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/subclasses/feature_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json b/src/packs/subclasses/feature_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json index 56387284..02af8c17 100644 --- a/src/packs/subclasses/feature_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json +++ b/src/packs/subclasses/feature_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -106,7 +105,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -141,7 +139,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], @@ -206,7 +203,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/subclasses/feature_Elementalist_dPcqKN5NeDkjB1HW.json b/src/packs/subclasses/feature_Elementalist_dPcqKN5NeDkjB1HW.json index ac661a74..bfb4df2c 100644 --- a/src/packs/subclasses/feature_Elementalist_dPcqKN5NeDkjB1HW.json +++ b/src/packs/subclasses/feature_Elementalist_dPcqKN5NeDkjB1HW.json @@ -17,7 +17,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, @@ -54,7 +53,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/subclasses/feature_Heart_of_a_Poet_Ce0sn0kqAw3PFe0k.json b/src/packs/subclasses/feature_Heart_of_a_Poet_Ce0sn0kqAw3PFe0k.json index f0088f38..0b5b61f5 100644 --- a/src/packs/subclasses/feature_Heart_of_a_Poet_Ce0sn0kqAw3PFe0k.json +++ b/src/packs/subclasses/feature_Heart_of_a_Poet_Ce0sn0kqAw3PFe0k.json @@ -16,7 +16,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/subclasses/feature_Loyal_Protector_hd7UeBPr86Mz21Pe.json b/src/packs/subclasses/feature_Loyal_Protector_hd7UeBPr86Mz21Pe.json index 667fefd8..e7f060d5 100644 --- a/src/packs/subclasses/feature_Loyal_Protector_hd7UeBPr86Mz21Pe.json +++ b/src/packs/subclasses/feature_Loyal_Protector_hd7UeBPr86Mz21Pe.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/subclasses/feature_Manipulate_Magic_UNg4eyNfEQrMdD7G.json b/src/packs/subclasses/feature_Manipulate_Magic_UNg4eyNfEQrMdD7G.json index 6981cd57..46ac1a9d 100644 --- a/src/packs/subclasses/feature_Manipulate_Magic_UNg4eyNfEQrMdD7G.json +++ b/src/packs/subclasses/feature_Manipulate_Magic_UNg4eyNfEQrMdD7G.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/subclasses/feature_Natural_Evasion_TnuLBtHQGbqyzn82.json b/src/packs/subclasses/feature_Natural_Evasion_TnuLBtHQGbqyzn82.json index 41c12fe9..33804ae4 100644 --- a/src/packs/subclasses/feature_Natural_Evasion_TnuLBtHQGbqyzn82.json +++ b/src/packs/subclasses/feature_Natural_Evasion_TnuLBtHQGbqyzn82.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/subclasses/feature_Nemesis_DPKmipNRlSAMs2Cg.json b/src/packs/subclasses/feature_Nemesis_DPKmipNRlSAMs2Cg.json index aa7a3458..1d8f3b8f 100644 --- a/src/packs/subclasses/feature_Nemesis_DPKmipNRlSAMs2Cg.json +++ b/src/packs/subclasses/feature_Nemesis_DPKmipNRlSAMs2Cg.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hope", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/subclasses/feature_Partner_in_Arms_G54qY96XK62hgoK9.json b/src/packs/subclasses/feature_Partner_in_Arms_G54qY96XK62hgoK9.json index 1dab1e94..b0b37c6d 100644 --- a/src/packs/subclasses/feature_Partner_in_Arms_G54qY96XK62hgoK9.json +++ b/src/packs/subclasses/feature_Partner_in_Arms_G54qY96XK62hgoK9.json @@ -20,7 +20,6 @@ "scalable": false, "key": "armor", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/subclasses/feature_Regeneration_KRyrbSLVGreIOTZe.json b/src/packs/subclasses/feature_Regeneration_KRyrbSLVGreIOTZe.json index a0349e7c..9947cc38 100644 --- a/src/packs/subclasses/feature_Regeneration_KRyrbSLVGreIOTZe.json +++ b/src/packs/subclasses/feature_Regeneration_KRyrbSLVGreIOTZe.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hope", "value": 3, - "keyIsID": false, "step": null } ], diff --git a/src/packs/subclasses/feature_Revenge_oNfA5F9cKwNR7joq.json b/src/packs/subclasses/feature_Revenge_oNfA5F9cKwNR7joq.json index ad653f1e..83113e14 100644 --- a/src/packs/subclasses/feature_Revenge_oNfA5F9cKwNR7joq.json +++ b/src/packs/subclasses/feature_Revenge_oNfA5F9cKwNR7joq.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 2, - "keyIsID": false, "step": null } ], diff --git a/src/packs/subclasses/feature_Rousing_Speech_PCmYTX02JLzBpgml.json b/src/packs/subclasses/feature_Rousing_Speech_PCmYTX02JLzBpgml.json index be204300..a2708a8c 100644 --- a/src/packs/subclasses/feature_Rousing_Speech_PCmYTX02JLzBpgml.json +++ b/src/packs/subclasses/feature_Rousing_Speech_PCmYTX02JLzBpgml.json @@ -19,7 +19,6 @@ "scalable": false, "key": "hitPoints", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/subclasses/feature_Ruthless_Predator_Qny2J3R35bvC0Cey.json b/src/packs/subclasses/feature_Ruthless_Predator_Qny2J3R35bvC0Cey.json index c58ad931..f53d7b4a 100644 --- a/src/packs/subclasses/feature_Ruthless_Predator_Qny2J3R35bvC0Cey.json +++ b/src/packs/subclasses/feature_Ruthless_Predator_Qny2J3R35bvC0Cey.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/subclasses/feature_Shadow_Stepper_hAwTXjhyphiE3aeW.json b/src/packs/subclasses/feature_Shadow_Stepper_hAwTXjhyphiE3aeW.json index de0f54b7..fe624444 100644 --- a/src/packs/subclasses/feature_Shadow_Stepper_hAwTXjhyphiE3aeW.json +++ b/src/packs/subclasses/feature_Shadow_Stepper_hAwTXjhyphiE3aeW.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/subclasses/feature_Sparing_Touch_GfOSgVJW8bS1OjNq.json b/src/packs/subclasses/feature_Sparing_Touch_GfOSgVJW8bS1OjNq.json index 311cb456..6b1bc11a 100644 --- a/src/packs/subclasses/feature_Sparing_Touch_GfOSgVJW8bS1OjNq.json +++ b/src/packs/subclasses/feature_Sparing_Touch_GfOSgVJW8bS1OjNq.json @@ -24,10 +24,10 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "GfOSgVJW8bS1OjNq", + "key": "resource", + "itemId": "GfOSgVJW8bS1OjNq", "value": 1, - "keyIsID": true, + "scalable": false, "step": null, "consumeOnSuccess": false } @@ -101,10 +101,10 @@ "actionType": "action", "cost": [ { - "scalable": false, - "key": "GfOSgVJW8bS1OjNq", + "key": "resource", + "itemId": "GfOSgVJW8bS1OjNq", "value": 1, - "keyIsID": true, + "scalable": false, "step": null, "consumeOnSuccess": false } @@ -191,10 +191,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.0.5", + "systemVersion": "1.2.0", "createdTime": 1754353243691, - "modifiedTime": 1755392184339, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1756325575182, + "lastModifiedBy": "bjJtdJOhqWr47GhC" }, "_key": "!items!GfOSgVJW8bS1OjNq" } diff --git a/src/packs/subclasses/feature_Spirit_Weapon_McoS0RxNLOg3SfSt.json b/src/packs/subclasses/feature_Spirit_Weapon_McoS0RxNLOg3SfSt.json index 54a8303a..d7b2c877 100644 --- a/src/packs/subclasses/feature_Spirit_Weapon_McoS0RxNLOg3SfSt.json +++ b/src/packs/subclasses/feature_Spirit_Weapon_McoS0RxNLOg3SfSt.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/subclasses/feature_Thrive_in_Chaos_1nmFmkNXY6OYyyju.json b/src/packs/subclasses/feature_Thrive_in_Chaos_1nmFmkNXY6OYyyju.json index a8952599..953802de 100644 --- a/src/packs/subclasses/feature_Thrive_in_Chaos_1nmFmkNXY6OYyyju.json +++ b/src/packs/subclasses/feature_Thrive_in_Chaos_1nmFmkNXY6OYyyju.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/subclasses/feature_Transcendence_th6HZwEFnVBjUtqm.json b/src/packs/subclasses/feature_Transcendence_th6HZwEFnVBjUtqm.json index b0cc2c4f..da7da282 100644 --- a/src/packs/subclasses/feature_Transcendence_th6HZwEFnVBjUtqm.json +++ b/src/packs/subclasses/feature_Transcendence_th6HZwEFnVBjUtqm.json @@ -20,7 +20,6 @@ "scalable": false, "key": "hitPoints", "value": 1, - "keyIsID": false, "step": null, "consumeOnSuccess": false } diff --git a/src/packs/subclasses/feature_Vanishing_Act_iyIg1VLwO8C6jvFZ.json b/src/packs/subclasses/feature_Vanishing_Act_iyIg1VLwO8C6jvFZ.json index 57ae1815..c8065dbb 100644 --- a/src/packs/subclasses/feature_Vanishing_Act_iyIg1VLwO8C6jvFZ.json +++ b/src/packs/subclasses/feature_Vanishing_Act_iyIg1VLwO8C6jvFZ.json @@ -20,7 +20,6 @@ "scalable": false, "key": "stress", "value": 1, - "keyIsID": false, "step": null } ], diff --git a/src/packs/subclasses/feature_Weapon_Specialist_HAqtoKUTrk8Mip1n.json b/src/packs/subclasses/feature_Weapon_Specialist_HAqtoKUTrk8Mip1n.json index 886f2e21..4773af61 100644 --- a/src/packs/subclasses/feature_Weapon_Specialist_HAqtoKUTrk8Mip1n.json +++ b/src/packs/subclasses/feature_Weapon_Specialist_HAqtoKUTrk8Mip1n.json @@ -17,7 +17,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/src/packs/subclasses/feature_Wings_of_Light_KkQH0tYhagIqe2MT.json b/src/packs/subclasses/feature_Wings_of_Light_KkQH0tYhagIqe2MT.json index 9580889b..c46a1ecf 100644 --- a/src/packs/subclasses/feature_Wings_of_Light_KkQH0tYhagIqe2MT.json +++ b/src/packs/subclasses/feature_Wings_of_Light_KkQH0tYhagIqe2MT.json @@ -40,7 +40,6 @@ "actionType": "action", "cost": [ { - "keyIsID": false, "key": "hope", "value": 1, "scalable": false, diff --git a/system.json b/system.json index 8e5a7610..4eeeff1a 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "1.1.2", + "version": "1.2.0", "compatibility": { "minimum": "13", "verified": "13.347", diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs index b456c17d..a4ecec3a 100644 --- a/templates/sheets/global/partials/inventory-item-V2.hbs +++ b/templates/sheets/global/partials/inventory-item-V2.hbs @@ -65,7 +65,7 @@ Parameters: {{#if (and (not hideResources) (eq item.system.resource.type 'simple'))}} {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} {{/if}} - {{#if (and (not hideResources) item.system.quantity)}} + {{#if (and (not hideResources) (gte item.system.quantity 0))}}
    diff --git a/templates/sheets/items/consumable/settings.hbs b/templates/sheets/items/consumable/settings.hbs index 71bb2a83..7cc75f6f 100644 --- a/templates/sheets/items/consumable/settings.hbs +++ b/templates/sheets/items/consumable/settings.hbs @@ -10,5 +10,8 @@ {{localize "DAGGERHEART.ITEMS.Consumable.consumeOnUse"}} {{formField systemFields.consumeOnUse value=source.system.consumeOnUse}} + + {{localize "DAGGERHEART.ITEMS.Consumable.destroyOnEmpty"}} + {{formField systemFields.destroyOnEmpty value=source.system.destroyOnEmpty}}
    \ No newline at end of file From c6741b1c7a17b2730363921e9ab4a851e71bf242 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Fri, 29 Aug 2025 01:26:22 +0200 Subject: [PATCH 47/66] Added background and connection questions (#1109) --- lang/en.json | 9 +++++-- module/applications/sheets/items/class.mjs | 12 ++++++++- module/data/item/class.mjs | 16 +++++++++++ .../classes/class_Bard_vegl3bFOq3pcFTWT.json | 18 ++++++++++--- .../classes/class_Druid_ZNwUTCyGCEcidZFv.json | 18 ++++++++++--- .../class_Guardian_nRAyoC0fOzXPDa4z.json | 18 ++++++++++--- .../class_Ranger_BTyfve69LKqoOi9S.json | 18 ++++++++++--- .../classes/class_Rogue_CvHlkHZfpMiCz5uT.json | 18 ++++++++++--- .../class_Seraph_5ZnlJ5bEoyOTkUJv.json | 18 ++++++++++--- .../class_Sorcerer_DchOzHcWIJE9FKcR.json | 18 ++++++++++--- .../class_Warrior_xCUWwJz4WSthvLfy.json | 18 ++++++++++--- .../class_Wizard_5LwX4m8ziY3F1ZGC.json | 18 ++++++++++--- styles/less/sheets/items/class.less | 14 ++++++++++ templates/sheets/items/class/questions.hbs | 27 +++++++++++++++++++ 14 files changed, 201 insertions(+), 39 deletions(-) create mode 100644 templates/sheets/items/class/questions.hbs diff --git a/lang/en.json b/lang/en.json index 6068d48e..82ffac7a 100755 --- a/lang/en.json +++ b/lang/en.json @@ -170,7 +170,9 @@ "hint": "Add single words or short text as reminders and hints of what a character has advantage on." }, "age": "Age", + "backgroundQuestions": "Backgrounds", "companionFeatures": "Companion Features", + "connections": "Connections", "contextMenu": { "consume": "Consume Item", "equip": "Equip", @@ -267,7 +269,8 @@ "experience": "Experience", "traits": "Traits", "domainCards": "Domain Cards", - "equipment": "Equipment" + "equipment": "Equipment", + "story": "Story" }, "ancestryNamePlaceholder": "Your ancestry's name", "buttonTitle": "Character Setup", @@ -288,6 +291,7 @@ "selectSubclass": "Select Subclass", "startingItems": "Starting Items", "story": "Story", + "storyExplanation": "Select which background and connection prompts you want to copy into your character's background.", "suggestedArmor": "Suggested Armor", "suggestedPrimaryWeapon": "Suggested Primary Weapon", "suggestedSecondaryWeapon": "Suggested Secondary Weapon", @@ -1912,7 +1916,8 @@ "downtime": "Downtime", "roll": "Roll", "rules": "Rules", - "types": "Types" + "types": "Types", + "questions": "Questions" }, "Tiers": { "singular": "Tier", diff --git a/module/applications/sheets/items/class.mjs b/module/applications/sheets/items/class.mjs index 033c63e6..b88e6ca3 100644 --- a/module/applications/sheets/items/class.mjs +++ b/module/applications/sheets/items/class.mjs @@ -46,6 +46,10 @@ export default class ClassSheet extends DHBaseItemSheet { template: 'systems/daggerheart/templates/sheets/items/class/settings.hbs', scrollable: ['.settings'] }, + questions: { + template: 'systems/daggerheart/templates/sheets/items/class/questions.hbs', + scrollable: ['.questions'] + }, effects: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs', scrollable: ['.effects'] @@ -55,7 +59,13 @@ export default class ClassSheet extends DHBaseItemSheet { /** @inheritdoc */ static TABS = { primary: { - tabs: [{ id: 'description' }, { id: 'features' }, { id: 'settings' }, { id: 'effects' }], + tabs: [ + { id: 'description' }, + { id: 'features' }, + { id: 'settings' }, + { id: 'questions' }, + { id: 'effects' } + ], initial: 'description', labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } diff --git a/module/data/item/class.mjs b/module/data/item/class.mjs index 5e92d2fc..c233a31b 100644 --- a/module/data/item/class.mjs +++ b/module/data/item/class.mjs @@ -49,6 +49,8 @@ export default class DHClass extends BaseDataItem { suggestedSecondaryWeapon: new ForeignDocumentUUIDField({ type: 'Item' }), suggestedArmor: new ForeignDocumentUUIDField({ type: 'Item' }) }), + backgroundQuestions: new fields.ArrayField(new fields.StringField(), { initial: ['', '', ''] }), + connections: new fields.ArrayField(new fields.StringField(), { initial: ['', '', ''] }), isMulticlass: new fields.BooleanField({ initial: false }) }; } @@ -96,6 +98,20 @@ export default class DHClass extends BaseDataItem { } } } + + if (!data.system.isMulticlass) { + const addQuestions = (base, questions) => { + return `${base}${questions.map(q => `

    ${q}

    `).join('
    ')}`; + }; + const backgroundQuestions = data.system.backgroundQuestions.filter(x => x); + const connections = data.system.connections.filter(x => x); + await this.actor.update({ + 'system.biography': { + background: addQuestions(this.actor.system.biography.background, backgroundQuestions), + connections: addQuestions(this.actor.system.biography.connections, connections) + } + }); + } } const allowed = await super._preCreate(data, options, user); diff --git a/src/packs/classes/class_Bard_vegl3bFOq3pcFTWT.json b/src/packs/classes/class_Bard_vegl3bFOq3pcFTWT.json index 4a7a697e..dfa5f29c 100644 --- a/src/packs/classes/class_Bard_vegl3bFOq3pcFTWT.json +++ b/src/packs/classes/class_Bard_vegl3bFOq3pcFTWT.json @@ -63,7 +63,17 @@ "source": "Daggerheart SRD", "page": 9, "artist": "" - } + }, + "backgroundQuestions": [ + "Who from your community taught you to have such confidence in yourself?", + "You were in love once. Who did you adore, and how did they hurt you?", + "You’ve always looked up to another bard. Who are they, and why do you idolize them?" + ], + "connections": [ + "What made you realize we were going to be such good friends?", + "What do I do that annoys you?", + "Why do you grab my hand at night?" + ] }, "effects": [], "ownership": { @@ -77,10 +87,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.1.2", "createdTime": 1754174600538, - "modifiedTime": 1755943467705, - "lastModifiedBy": "tt3PwMBXcTLCtIQU" + "modifiedTime": 1756399046200, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_id": "vegl3bFOq3pcFTWT", "sort": 300000, diff --git a/src/packs/classes/class_Druid_ZNwUTCyGCEcidZFv.json b/src/packs/classes/class_Druid_ZNwUTCyGCEcidZFv.json index 6fae5522..5e30b889 100644 --- a/src/packs/classes/class_Druid_ZNwUTCyGCEcidZFv.json +++ b/src/packs/classes/class_Druid_ZNwUTCyGCEcidZFv.json @@ -63,7 +63,17 @@ "source": "Daggerheart SRD", "page": 10, "artist": "" - } + }, + "backgroundQuestions": [ + "Why was the community you grew up in so reliant on nature and its creatures?", + "Who was the first wild animal you bonded with? Why did your bond end?", + "Who has been trying to hunt you down? What do they want from you?" + ], + "connections": [ + "What did you confide in me that makes me leap into danger for you every time?", + "What animal do I say you remind me of?", + "What affectionate nickname have you given me?" + ] }, "effects": [], "folder": null, @@ -79,10 +89,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.1.2", "createdTime": 1754222247012, - "modifiedTime": 1755943479440, - "lastModifiedBy": "tt3PwMBXcTLCtIQU" + "modifiedTime": 1756399003725, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!ZNwUTCyGCEcidZFv" } diff --git a/src/packs/classes/class_Guardian_nRAyoC0fOzXPDa4z.json b/src/packs/classes/class_Guardian_nRAyoC0fOzXPDa4z.json index 81f9c18f..c412abba 100644 --- a/src/packs/classes/class_Guardian_nRAyoC0fOzXPDa4z.json +++ b/src/packs/classes/class_Guardian_nRAyoC0fOzXPDa4z.json @@ -59,7 +59,17 @@ "source": "Daggerheart SRD", "page": 15, "artist": "" - } + }, + "backgroundQuestions": [ + "Who from your community did you fail to protect, and why do you still think of them?", + "You’ve been tasked with protecting something important and delivering\nit somewhere dangerous. What is it, and where does it need to go?", + "You consider an aspect of yourself to be a weakness. What is it, and how has it affected you?" + ], + "connections": [ + "How did I save your life the first time we met?", + "What small gift did you give me that you notice I always carry with me?", + "What lie have you told me about yourself that I absolutely believe?" + ] }, "effects": [], "folder": null, @@ -75,10 +85,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.1.2", "createdTime": 1754246931974, - "modifiedTime": 1755943488697, - "lastModifiedBy": "tt3PwMBXcTLCtIQU" + "modifiedTime": 1756398951257, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!nRAyoC0fOzXPDa4z" } diff --git a/src/packs/classes/class_Ranger_BTyfve69LKqoOi9S.json b/src/packs/classes/class_Ranger_BTyfve69LKqoOi9S.json index 3c15afec..f85f6d59 100644 --- a/src/packs/classes/class_Ranger_BTyfve69LKqoOi9S.json +++ b/src/packs/classes/class_Ranger_BTyfve69LKqoOi9S.json @@ -59,7 +59,17 @@ "source": "Daggerheart SRD", "page": 16, "artist": "" - } + }, + "backgroundQuestions": [ + "A terrible creature hurt your community, and you’ve vowed to hunt them down. What are they, and what unique trail or sign do they leave behind?", + "Your first kill almost killed you, too. What was it, and what part of you was never the same after that event?", + "You’ve traveled many dangerous lands, but what is the one place you refuse to go?" + ], + "connections": [ + "What friendly competition do we have?", + "Why do you act differently when we’re alone than when others are around?", + "What threat have you asked me to watch for, and why are you worried about it?" + ] }, "effects": [], "folder": null, @@ -75,10 +85,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.1.2", "createdTime": 1754268869310, - "modifiedTime": 1755943505024, - "lastModifiedBy": "tt3PwMBXcTLCtIQU" + "modifiedTime": 1756398897309, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!BTyfve69LKqoOi9S" } diff --git a/src/packs/classes/class_Rogue_CvHlkHZfpMiCz5uT.json b/src/packs/classes/class_Rogue_CvHlkHZfpMiCz5uT.json index e2d1728d..a0a59613 100644 --- a/src/packs/classes/class_Rogue_CvHlkHZfpMiCz5uT.json +++ b/src/packs/classes/class_Rogue_CvHlkHZfpMiCz5uT.json @@ -63,7 +63,17 @@ "source": "Daggerheart SRD", "page": 19, "artist": "" - } + }, + "backgroundQuestions": [ + "What did you get caught doing that got you exiled from your home community?", + "You used to have a different life, but you’ve tried to leave it behind. Who from your past is still chasing you?", + "Who from your past were you most sad to say goodbye to?" + ], + "connections": [ + "What did I recently convince you to do that got us both in trouble?", + "What have I discovered about your past that I hold secret from the others?", + "Who do you know from my past, and how have they influenced your feelings about me?" + ] }, "effects": [], "folder": null, @@ -79,10 +89,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.1.2", "createdTime": 1754325275832, - "modifiedTime": 1755943515533, - "lastModifiedBy": "tt3PwMBXcTLCtIQU" + "modifiedTime": 1756398839983, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!CvHlkHZfpMiCz5uT" } diff --git a/src/packs/classes/class_Seraph_5ZnlJ5bEoyOTkUJv.json b/src/packs/classes/class_Seraph_5ZnlJ5bEoyOTkUJv.json index 4dbf7efb..fa11b261 100644 --- a/src/packs/classes/class_Seraph_5ZnlJ5bEoyOTkUJv.json +++ b/src/packs/classes/class_Seraph_5ZnlJ5bEoyOTkUJv.json @@ -59,7 +59,17 @@ "source": "Daggerheart SRD", "page": 20, "artist": "" - } + }, + "backgroundQuestions": [ + "Which god did you devote yourself to? What incredible feat did they perform for you in a moment of desperation?", + "How did your appearance change after taking your oath?", + "In what strange or unique way do you communicate with your god?" + ], + "connections": [ + "What promise did you make me agree to, should you die on the battlefield?", + "Why do you ask me so many questions about my god?", + "You’ve told me to protect one member of our party above all others, even yourself. Who are they and why?" + ] }, "effects": [], "folder": null, @@ -75,10 +85,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.1.2", "createdTime": 1754351482530, - "modifiedTime": 1755943523935, - "lastModifiedBy": "tt3PwMBXcTLCtIQU" + "modifiedTime": 1756398795596, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!5ZnlJ5bEoyOTkUJv" } diff --git a/src/packs/classes/class_Sorcerer_DchOzHcWIJE9FKcR.json b/src/packs/classes/class_Sorcerer_DchOzHcWIJE9FKcR.json index 10f4a31c..a096f177 100644 --- a/src/packs/classes/class_Sorcerer_DchOzHcWIJE9FKcR.json +++ b/src/packs/classes/class_Sorcerer_DchOzHcWIJE9FKcR.json @@ -67,7 +67,17 @@ "source": "Daggerheart SRD", "page": 22, "artist": "" - } + }, + "backgroundQuestions": [ + "What did you do that made the people in your community wary of you?", + "What mentor taught you to control your untamed magic, and why are they no longer able to guide you?", + "You have a deep fear you hide from everyone. What is it, and why does it scare you?" + ], + "connections": [ + "Why do you trust me so deeply?", + "What did I do that makes you cautious around me?", + "Why do we keep our shared past a secret?" + ] }, "effects": [], "folder": null, @@ -83,10 +93,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.1.2", "createdTime": 1754349743129, - "modifiedTime": 1755943536635, - "lastModifiedBy": "tt3PwMBXcTLCtIQU" + "modifiedTime": 1756398741027, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!DchOzHcWIJE9FKcR" } diff --git a/src/packs/classes/class_Warrior_xCUWwJz4WSthvLfy.json b/src/packs/classes/class_Warrior_xCUWwJz4WSthvLfy.json index fd068f16..3ecb2b72 100644 --- a/src/packs/classes/class_Warrior_xCUWwJz4WSthvLfy.json +++ b/src/packs/classes/class_Warrior_xCUWwJz4WSthvLfy.json @@ -63,7 +63,17 @@ "source": "Daggerheart SRD", "page": 23, "artist": "" - } + }, + "backgroundQuestions": [ + "Who taught you to fight, and why did they stay behind when you left home?", + "Somebody defeated you in battle years ago and left you to die. Who was it, and how did they betray you?", + "What legendary place have you always wanted to visit, and why is it so special?" + ], + "connections": [ + "We knew each other long before this party came together. How?", + "What mundane task do you usually help me with off the battlefield?", + "What fear am I helping you overcome?" + ] }, "effects": [], "folder": null, @@ -79,10 +89,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.1.2", "createdTime": 1754255776706, - "modifiedTime": 1755943545980, - "lastModifiedBy": "tt3PwMBXcTLCtIQU" + "modifiedTime": 1756398696324, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!xCUWwJz4WSthvLfy" } diff --git a/src/packs/classes/class_Wizard_5LwX4m8ziY3F1ZGC.json b/src/packs/classes/class_Wizard_5LwX4m8ziY3F1ZGC.json index 56383931..d5cc53ca 100644 --- a/src/packs/classes/class_Wizard_5LwX4m8ziY3F1ZGC.json +++ b/src/packs/classes/class_Wizard_5LwX4m8ziY3F1ZGC.json @@ -63,7 +63,17 @@ "source": "Daggerheart SRD", "page": 25, "artist": "" - } + }, + "backgroundQuestions": [ + "What responsibilities did your community once count on you for?\nHow did you let them down?", + "You’ve spent your life searching for a book or object of great\nsignificance. What is it, and why is it so important to you?", + "You have a powerful rival. Who are they, and why are you so determined to defeat them?" + ], + "connections": [ + "What favor have I asked of you that you’re not sure you can fulfill?", + "What weird hobby or strange fascination do we both share?", + "What secret about yourself have you entrusted only to me?" + ] }, "effects": [], "folder": null, @@ -79,10 +89,10 @@ "exportSource": null, "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "1.1.0", + "systemVersion": "1.1.2", "createdTime": 1754253505323, - "modifiedTime": 1755943555087, - "lastModifiedBy": "tt3PwMBXcTLCtIQU" + "modifiedTime": 1756391897762, + "lastModifiedBy": "gbAAZWyczKwejDNh" }, "_key": "!items!5LwX4m8ziY3F1ZGC" } diff --git a/styles/less/sheets/items/class.less b/styles/less/sheets/items/class.less index 686715c6..526aa77f 100644 --- a/styles/less/sheets/items/class.less +++ b/styles/less/sheets/items/class.less @@ -43,4 +43,18 @@ } } } + + .tab.questions { + .questions-container { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + + .questions-section { + display: flex; + flex-direction: column; + gap: 4px; + } + } + } } diff --git a/templates/sheets/items/class/questions.hbs b/templates/sheets/items/class/questions.hbs new file mode 100644 index 00000000..590881b3 --- /dev/null +++ b/templates/sheets/items/class/questions.hbs @@ -0,0 +1,27 @@ +
    +
    +
    + {{localize "DAGGERHEART.ACTORS.Character.backgroundQuestions"}} + +
    + {{#each source.system.backgroundQuestions as | question index |}} + + {{/each}} +
    +
    + +
    + {{localize "DAGGERHEART.ACTORS.Character.connections"}} + +
    + {{#each source.system.connections as | connection index |}} + + {{/each}} +
    +
    +
    +
    \ No newline at end of file From 3f1e7f4f4ab5af28e1e6c9eca2c5b71c15213b1c Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Fri, 29 Aug 2025 23:49:33 +1000 Subject: [PATCH 48/66] Added some protection for no data supplied (#1115) Co-authored-by: Chris Ryan --- module/enrichers/DualityRollEnricher.mjs | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/module/enrichers/DualityRollEnricher.mjs b/module/enrichers/DualityRollEnricher.mjs index 73d4f25a..1d6404ff 100644 --- a/module/enrichers/DualityRollEnricher.mjs +++ b/module/enrichers/DualityRollEnricher.mjs @@ -9,12 +9,12 @@ export default function DhDualityRollEnricher(match, _options) { } function getDualityMessage(roll, flavor) { - const trait = roll.trait && abilities[roll.trait] ? game.i18n.localize(abilities[roll.trait].label) : null; + const trait = roll?.trait && abilities[roll.trait] ? game.i18n.localize(abilities[roll.trait].label) : null; const label = flavor ?? - (roll.trait + (roll?.trait ? game.i18n.format('DAGGERHEART.GENERAL.rollWith', { roll: trait }) - : roll.reaction + : roll?.reaction ? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll') : game.i18n.localize('DAGGERHEART.GENERAL.duality')); @@ -22,9 +22,9 @@ function getDualityMessage(roll, flavor) { ? game.i18n.localize(abilities[roll.trait].label) : game.i18n.localize('DAGGERHEART.GENERAL.duality'); - const advantage = roll.advantage + const advantage = roll?.advantage ? CONFIG.DH.ACTIONS.advantageState.advantage.value - : roll.disadvantage + : roll?.disadvantage ? CONFIG.DH.ACTIONS.advantageState.disadvantage.value : undefined; const advantageLabel = @@ -36,21 +36,21 @@ function getDualityMessage(roll, flavor) { const dualityElement = document.createElement('span'); dualityElement.innerHTML = ` - `; From ef4d37d72508ecb9e0f66f23882becfdb42401cf Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Sat, 30 Aug 2025 11:47:08 +1000 Subject: [PATCH 49/66] typo fixes (#1121) Co-authored-by: Chris Ryan --- .../adversary_Acid_Burrower_89yAh30vaNQOALlz.json | 12 ++++++------ ...ary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json | 12 ++++++------ .../adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json | 12 ++++++------ .../adversary_Tiny_Green_Ooze_aLkLFuVoKz2NLoBK.json | 12 ++++++------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/packs/adversaries/adversary_Acid_Burrower_89yAh30vaNQOALlz.json b/src/packs/adversaries/adversary_Acid_Burrower_89yAh30vaNQOALlz.json index e226f013..90c5950e 100644 --- a/src/packs/adversaries/adversary_Acid_Burrower_89yAh30vaNQOALlz.json +++ b/src/packs/adversaries/adversary_Acid_Burrower_89yAh30vaNQOALlz.json @@ -434,14 +434,14 @@ "_id": "UpFsnlbZkyvM2Ftv", "img": "icons/magic/acid/projectile-smoke-glowing.webp", "system": { - "description": "

    Make an attack against all targets in front of the Burrower within Close range. Targets the Burrower succeeds against take 2d6 physical damage and must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP and you gain a Fear.

    @Template[type:inFront|range:c]

    ", + "description": "

    Make an attack against all targets in front of the Burrower within Close range. Targets the Burrower succeeds against take 2d6 physical damage and must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP and you gain a Fear.

    @Template[type:inFront|range:c]

    ", "resource": null, "actions": { "yd10HwK6Wa3OEvv2": { "type": "attack", "_id": "yd10HwK6Wa3OEvv2", "systemPath": "actions", - "description": "

    Make an attack against all targets in front of the Burrower within Close range. Targets the Burrower succeeds against take 2d6 physical damage and must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP and you gain a Fear.

    @Template[type:inFront|range:c]

    ", + "description": "

    Make an attack against all targets in front of the Burrower within Close range. Targets the Burrower succeeds against take 2d6 physical damage and must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP and you gain a Fear.

    @Template[type:inFront|range:c]

    ", "chatDisplay": true, "actionType": "action", "cost": [], @@ -553,11 +553,11 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.348", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "lastModifiedBy": "MQSznptE5yLT7kj8", - "modifiedTime": 1754143653876 + "systemVersion": "1.1.2", + "lastModifiedBy": "mdk78Q6pOyHh6aBg", + "modifiedTime": 1756510879809 }, "_key": "!actors.items!89yAh30vaNQOALlz.UpFsnlbZkyvM2Ftv" }, diff --git a/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json b/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json index e519b193..d4554bf9 100644 --- a/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json +++ b/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json @@ -264,14 +264,14 @@ "name": "Crushing Blows", "type": "feature", "system": { - "description": "

    When the Elemental makes a successful attack, the target must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.

    ", + "description": "

    When the Elemental makes a successful attack, the target must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.

    ", "resource": null, "actions": { "0sXciTiPc30v8czv": { "type": "damage", "_id": "0sXciTiPc30v8czv", "systemPath": "actions", - "description": "

    When the Elemental makes a successful attack, the target must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.

    ", + "description": "

    When the Elemental makes a successful attack, the target must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.

    ", "chatDisplay": true, "actionType": "action", "cost": [], @@ -337,12 +337,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.348", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.2", "createdTime": 1754127683751, - "modifiedTime": 1754127795809, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1756511006257, + "lastModifiedBy": "mdk78Q6pOyHh6aBg" }, "_key": "!actors.items!dsfB3YhoL5SudvS2.NnCkXIuATO0s3tSR" }, diff --git a/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json b/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json index af630cdd..6a986acd 100644 --- a/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json +++ b/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json @@ -270,14 +270,14 @@ "name": "Acidic Form", "type": "feature", "system": { - "description": "

    When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.

    ", + "description": "

    When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.

    ", "resource": null, "actions": { "gtT2oHSyZg9OHHJD": { "type": "damage", "_id": "gtT2oHSyZg9OHHJD", "systemPath": "actions", - "description": "

    When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.

    ", + "description": "

    When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.

    ", "chatDisplay": true, "actionType": "action", "cost": [], @@ -343,12 +343,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.348", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.2", "createdTime": 1754129153649, - "modifiedTime": 1754129204931, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1756510982337, + "lastModifiedBy": "mdk78Q6pOyHh6aBg" }, "_key": "!actors.items!6hbqmxDXFOzZJDk4.BQsVuuwFYByKwesR" }, diff --git a/src/packs/adversaries/adversary_Tiny_Green_Ooze_aLkLFuVoKz2NLoBK.json b/src/packs/adversaries/adversary_Tiny_Green_Ooze_aLkLFuVoKz2NLoBK.json index be94d1bd..06227cd5 100644 --- a/src/packs/adversaries/adversary_Tiny_Green_Ooze_aLkLFuVoKz2NLoBK.json +++ b/src/packs/adversaries/adversary_Tiny_Green_Ooze_aLkLFuVoKz2NLoBK.json @@ -224,14 +224,14 @@ "_id": "WpOh5kHHx7lcTvEY", "img": "icons/magic/acid/dissolve-drip-droplet-smoke.webp", "system": { - "description": "

    When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.

    ", + "description": "

    When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.

    ", "resource": null, "actions": { "HfK0u0c7NRppuF1Q": { "type": "damage", "_id": "HfK0u0c7NRppuF1Q", "systemPath": "actions", - "description": "

    When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.

    ", + "description": "

    When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.

    ", "chatDisplay": true, "actionType": "action", "cost": [], @@ -296,12 +296,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.348", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.2", "createdTime": 1754055148507, - "modifiedTime": 1754145130460, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1756510967769, + "lastModifiedBy": "mdk78Q6pOyHh6aBg" }, "_key": "!actors.items!aLkLFuVoKz2NLoBK.WpOh5kHHx7lcTvEY" } From 31238113c994c5c0aa81af25aa8de6333db3ad6b Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Sat, 30 Aug 2025 17:44:16 +0200 Subject: [PATCH 50/66] Fix/1116 fix action nullable fields (#1117) * Temp ActionField attack type missing * Move missing attack type to getModel * Damage/Healing fields nullable fix * Fix TargetField null value * Other fixes * Fix Action type to be not nullable --- module/data/action/baseAction.mjs | 3 ++- module/data/fields/action/damageField.mjs | 12 ++++++++++-- module/data/fields/action/rollField.mjs | 12 +++++++++--- module/data/fields/action/saveField.mjs | 4 +++- module/data/fields/action/targetField.mjs | 3 ++- 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 2f5935da..7ea7331a 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -32,7 +32,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel actionType: new fields.StringField({ choices: CONFIG.DH.ITEM.actionTypes, initial: 'action', - nullable: true + nullable: false, + required: true }) }; diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 810b6e76..e870e229 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -22,10 +22,18 @@ export class DHActionDiceData extends foundry.abstract.DataModel { multiplier: new fields.StringField({ choices: CONFIG.DH.GENERAL.multiplierTypes, initial: 'prof', - label: 'Multiplier' + label: 'Multiplier', + nullable: false, + required: true }), flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }), - dice: new fields.StringField({ choices: CONFIG.DH.GENERAL.diceTypes, initial: 'd6', label: 'Dice' }), + dice: new fields.StringField({ + choices: CONFIG.DH.GENERAL.diceTypes, + initial: 'd6', + label: 'Dice', + nullable: false, + required: true + }), bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }), custom: new fields.SchemaField({ enabled: new fields.BooleanField({ label: 'Custom Formula' }), diff --git a/module/data/fields/action/rollField.mjs b/module/data/fields/action/rollField.mjs index a4df2a9e..07a113d3 100644 --- a/module/data/fields/action/rollField.mjs +++ b/module/data/fields/action/rollField.mjs @@ -10,13 +10,17 @@ export class DHActionRollData extends foundry.abstract.DataModel { bonus: new fields.NumberField({ nullable: true, initial: null, integer: true }), advState: new fields.StringField({ choices: CONFIG.DH.ACTIONS.advantageState, - initial: 'neutral' + initial: 'neutral', + nullable: false, + required: true }), diceRolling: new fields.SchemaField({ multiplier: new fields.StringField({ choices: CONFIG.DH.GENERAL.diceSetNumbers, initial: 'prof', - label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.multiplier' + label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.multiplier', + nullable: false, + required: true }), flatMultiplier: new fields.NumberField({ nullable: true, @@ -26,7 +30,9 @@ export class DHActionRollData extends foundry.abstract.DataModel { dice: new fields.StringField({ choices: CONFIG.DH.GENERAL.diceTypes, initial: CONFIG.DH.GENERAL.diceTypes.d6, - label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.dice' + label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.dice', + nullable: false, + required: true }), compare: new fields.StringField({ choices: CONFIG.DH.ACTIONS.diceCompare, diff --git a/module/data/fields/action/saveField.mjs b/module/data/fields/action/saveField.mjs index e93a82a9..23378e05 100644 --- a/module/data/fields/action/saveField.mjs +++ b/module/data/fields/action/saveField.mjs @@ -11,7 +11,9 @@ export default class SaveField extends fields.SchemaField { difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }), damageMod: new fields.StringField({ initial: CONFIG.DH.ACTIONS.damageOnSave.none.id, - choices: CONFIG.DH.ACTIONS.damageOnSave + choices: CONFIG.DH.ACTIONS.damageOnSave, + nullable: false, + required: true }) }; super(saveFields, options, context); diff --git a/module/data/fields/action/targetField.mjs b/module/data/fields/action/targetField.mjs index bfb01db9..9dbf1ed9 100644 --- a/module/data/fields/action/targetField.mjs +++ b/module/data/fields/action/targetField.mjs @@ -6,7 +6,8 @@ export default class TargetField extends fields.SchemaField { type: new fields.StringField({ choices: CONFIG.DH.GENERAL.targetTypes, initial: CONFIG.DH.GENERAL.targetTypes.any.id, - nullable: true + nullable: true, + blank: true }), amount: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }) }; From 179dd3e6c32452049d284837aefa9aa92b418f6c Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Tue, 2 Sep 2025 10:50:48 +1000 Subject: [PATCH 51/66] Fix the typos related to the Protective feature on Round Shields (#1131) Co-authored-by: Chris Ryan --- ...Advanced_Round_Shield_hiEOGF2reabGLUoi.json | 18 ++++++++++++------ ...Improved_Round_Shield_DlinEBGZfIlvreO3.json | 18 ++++++++++++------ ...egendary_Round_Shield_A28WL9E2lJ3iLZHW.json | 18 ++++++++++++------ .../weapon_Round_Shield_mxwWKDujgsRcZWPT.json | 18 ++++++++++++------ 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/packs/items/weapons/weapon_Advanced_Round_Shield_hiEOGF2reabGLUoi.json b/src/packs/items/weapons/weapon_Advanced_Round_Shield_hiEOGF2reabGLUoi.json index 9240fbaf..efcd890b 100644 --- a/src/packs/items/weapons/weapon_Advanced_Round_Shield_hiEOGF2reabGLUoi.json +++ b/src/packs/items/weapons/weapon_Advanced_Round_Shield_hiEOGF2reabGLUoi.json @@ -105,13 +105,14 @@ "effects": [ { "name": "Protective", - "description": "Add your character's Tier to your Armor Score", + "description": "

    Add the item's Tier to your Armor Score

    ", "img": "icons/skills/melee/shield-block-gray-orange.webp", "changes": [ { "key": "system.armorScore", "mode": 2, - "value": "ITEM.@system.tier" + "value": "ITEM.@system.tier", + "priority": null } ], "_id": "i5HfkF5aKQuUCTEG", @@ -120,7 +121,12 @@ "disabled": false, "duration": { "startTime": null, - "combat": null + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null }, "origin": null, "tint": "#ffffff", @@ -132,12 +138,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.348", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1753794875150, - "modifiedTime": 1753794875150, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1756682958806, + "lastModifiedBy": "mdk78Q6pOyHh6aBg" }, "_key": "!items.effects!hiEOGF2reabGLUoi.i5HfkF5aKQuUCTEG" } diff --git a/src/packs/items/weapons/weapon_Improved_Round_Shield_DlinEBGZfIlvreO3.json b/src/packs/items/weapons/weapon_Improved_Round_Shield_DlinEBGZfIlvreO3.json index 49be7813..41a85d9f 100644 --- a/src/packs/items/weapons/weapon_Improved_Round_Shield_DlinEBGZfIlvreO3.json +++ b/src/packs/items/weapons/weapon_Improved_Round_Shield_DlinEBGZfIlvreO3.json @@ -105,13 +105,14 @@ "effects": [ { "name": "Protective", - "description": "Add your character's Tier to your Armor Score", + "description": "

    Add the item's Tier to your Armor Score

    ", "img": "icons/skills/melee/shield-block-gray-orange.webp", "changes": [ { "key": "system.armorScore", "mode": 2, - "value": "ITEM.@system.tier" + "value": "ITEM.@system.tier", + "priority": null } ], "_id": "cXWSV50apzaNQkdA", @@ -120,7 +121,12 @@ "disabled": false, "duration": { "startTime": null, - "combat": null + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null }, "origin": null, "tint": "#ffffff", @@ -132,12 +138,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.348", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1753794098464, - "modifiedTime": 1753794098464, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1756682973559, + "lastModifiedBy": "mdk78Q6pOyHh6aBg" }, "_key": "!items.effects!DlinEBGZfIlvreO3.cXWSV50apzaNQkdA" } diff --git a/src/packs/items/weapons/weapon_Legendary_Round_Shield_A28WL9E2lJ3iLZHW.json b/src/packs/items/weapons/weapon_Legendary_Round_Shield_A28WL9E2lJ3iLZHW.json index 421df201..5b768985 100644 --- a/src/packs/items/weapons/weapon_Legendary_Round_Shield_A28WL9E2lJ3iLZHW.json +++ b/src/packs/items/weapons/weapon_Legendary_Round_Shield_A28WL9E2lJ3iLZHW.json @@ -105,13 +105,14 @@ "effects": [ { "name": "Protective", - "description": "Add your character's Tier to your Armor Score", + "description": "

    Add the item's Tier to your Armor Score

    ", "img": "icons/skills/melee/shield-block-gray-orange.webp", "changes": [ { "key": "system.armorScore", "mode": 2, - "value": "ITEM.@system.tier" + "value": "ITEM.@system.tier", + "priority": null } ], "_id": "Z2p00q5h6x6seXys", @@ -120,7 +121,12 @@ "disabled": false, "duration": { "startTime": null, - "combat": null + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null }, "origin": null, "tint": "#ffffff", @@ -132,12 +138,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.348", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1753796983285, - "modifiedTime": 1753796983285, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1756682777682, + "lastModifiedBy": "mdk78Q6pOyHh6aBg" }, "_key": "!items.effects!A28WL9E2lJ3iLZHW.Z2p00q5h6x6seXys" } diff --git a/src/packs/items/weapons/weapon_Round_Shield_mxwWKDujgsRcZWPT.json b/src/packs/items/weapons/weapon_Round_Shield_mxwWKDujgsRcZWPT.json index c49aa7dd..5ba5de2b 100644 --- a/src/packs/items/weapons/weapon_Round_Shield_mxwWKDujgsRcZWPT.json +++ b/src/packs/items/weapons/weapon_Round_Shield_mxwWKDujgsRcZWPT.json @@ -105,13 +105,14 @@ "effects": [ { "name": "Protective", - "description": "Add your character's Tier to your Armor Score", + "description": "

    Add the item's Tier to your Armor Score.

    ", "img": "icons/skills/melee/shield-block-gray-orange.webp", "changes": [ { "key": "system.armorScore", "mode": 2, - "value": "ITEM.@system.tier" + "value": "ITEM.@system.tier", + "priority": null } ], "_id": "M70a81e0Mg66jHRL", @@ -120,7 +121,12 @@ "disabled": false, "duration": { "startTime": null, - "combat": null + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null }, "origin": null, "tint": "#ffffff", @@ -132,12 +138,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.348", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1753794114980, - "modifiedTime": 1753794114980, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1756682994216, + "lastModifiedBy": "mdk78Q6pOyHh6aBg" }, "_key": "!items.effects!mxwWKDujgsRcZWPT.M70a81e0Mg66jHRL" } From 1b9defe4ad960afef9d38695df8de1d0ad8c2a1e Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Tue, 2 Sep 2025 23:00:46 +1000 Subject: [PATCH 52/66] The uuid is expected, not id (#1132) Co-authored-by: Chris Ryan --- module/applications/sheets/api/application-mixin.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index bdcee27a..fc158ace 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -401,7 +401,7 @@ export default function DHApplicationMixin(Base) { options.push({ name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat', icon: 'fa-solid fa-message', - callback: async target => (await getDocFromElement(target)).toChat(this.document.id) + callback: async target => (await getDocFromElement(target)).toChat(this.document.uuid) }); if (deletable) From 3c893df175e463538a5d6bc079fede4ef7ea8d61 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Tue, 2 Sep 2025 23:26:32 +1000 Subject: [PATCH 53/66] Fix the missing translation keys (#1133) Co-authored-by: Chris Ryan --- module/config/generalConfig.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 34ca6009..60b3a2f1 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -574,17 +574,17 @@ export const abilityCosts = { }, hope: { id: 'hope', - label: 'Hope', + label: 'DAGGERHEART.CONFIG.HealingType.hope.name', group: 'TYPES.Actor.character' }, armor: { id: 'armor', - label: 'Armor Slot', + label: 'DAGGERHEART.CONFIG.HealingType.armor.name', group: 'TYPES.Actor.character' }, fear: { id: 'fear', - label: 'Fear', + label: 'DAGGERHEART.CONFIG.HealingType.fear.name', group: 'TYPES.Actor.adversary' } }; From 3f08741a34a0c065657067e7e9bebe2e69efcfd2 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Wed, 3 Sep 2025 13:54:41 +1000 Subject: [PATCH 54/66] Use less ambiguous sort direction symbols (#1108) * Use less ambiguous sort direction symbols * Up arrow for ascending, small bar at top, large bar at bottom; Down arrow for descending, large bar at top, small bar at bottom * Using matching icons * Switch the order --------- Co-authored-by: Chris Ryan --- styles/less/ui/item-browser/item-browser.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/styles/less/ui/item-browser/item-browser.less b/styles/less/ui/item-browser/item-browser.less index 70a64d89..5df0482a 100644 --- a/styles/less/ui/item-browser/item-browser.less +++ b/styles/less/ui/item-browser/item-browser.less @@ -279,11 +279,11 @@ } &[data-sort-type='ASC']:after { - content: '\f0d7'; + content: '\f884'; } &[data-sort-type='DESC']:after { - content: '\f0d8'; + content: '\f885'; } } } From 0bd423ef5274d02c7cfd6d250bbe255d3d998df1 Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Wed, 3 Sep 2025 13:42:18 +0200 Subject: [PATCH 55/66] fix (#1143) --- module/data/chat-message/actorRoll.mjs | 11 ++++++++--- module/data/fields/action/damageField.mjs | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/module/data/chat-message/actorRoll.mjs b/module/data/chat-message/actorRoll.mjs index b6512fbd..91f44edc 100644 --- a/module/data/chat-message/actorRoll.mjs +++ b/module/data/chat-message/actorRoll.mjs @@ -55,9 +55,14 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { } get action() { - const actionItem = this.actionItem; - if (!actionItem || !this.source.action) return null; - return actionItem.system.actionsList?.find(a => a.id === this.source.action); + const actionActor = this.actionActor, + actionItem = this.actionItem; + if (!this.source.action) return null; + if(actionItem) + return actionItem.system.actionsList?.find(a => a.id === this.source.action); + else if(actionActor?.system.attack?._id === this.source.action) + return actionActor.system.attack + return null; } get targetMode() { diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index db43cfb0..816d9cbc 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -57,8 +57,8 @@ export default class DamageField extends fields.SchemaField { damageConfig.source.message = config.message?._id ?? messageId; damageConfig.directDamage = !!damageConfig.source?.message; - if(damageConfig.source?.message && game.modules.get('dice-so-nice')?.active) - await game.dice3d.waitFor3DAnimationByMessageID(damageConfig.source.message); + // if(damageConfig.source?.message && game.modules.get('dice-so-nice')?.active) + // await game.dice3d.waitFor3DAnimationByMessageID(damageConfig.source.message); const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig); if(!damageResult) return false; From f04619f73b169b03df0fd1e6b27721512f9bfe13 Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Fri, 5 Sep 2025 02:45:05 +0200 Subject: [PATCH 56/66] Fix/1144 fix experiences roll use costs (#1150) * Temp ActionField attack type missing * Move missing attack type to getModel * Fix costs object being updated during getRealCosts --- module/data/fields/action/costField.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index f4d942b1..4c8fad88 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -94,7 +94,8 @@ export default class CostField extends fields.ArrayField { } static getRealCosts(costs) { - const realCosts = costs?.length ? costs.filter(c => c.enabled) : []; + const cloneCosts = foundry.utils.deepClone(costs), + realCosts = cloneCosts?.length ? cloneCosts.filter(c => c.enabled) : []; let mergedCosts = []; realCosts.forEach(c => { const getCost = Object.values(mergedCosts).find(gc => gc.key === c.key); From 2d92576121115f55ec42953890ad97db5130d66e Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Fri, 5 Sep 2025 02:56:38 +0200 Subject: [PATCH 57/66] Fix/1149 fix targets apply effects (#1151) * Temp ActionField attack type missing * Move missing attack type to getModel * Fix targets apply effects --- module/documents/chatMessage.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index b8667384..3567cb90 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -169,7 +169,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { if (this.system.source.item && this.system.source.action) { const action = this.getAction(actor, this.system.source.item, this.system.source.action); if (!action || !action?.applyEffects) return; - const targets = this.getTargetList(); + const targets = this.system.hitTargets; if (targets.length === 0) ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected')); this.consumeOnSuccess(); From eefb28c312155d347656ac0dabb594166f63050e Mon Sep 17 00:00:00 2001 From: joaquinpereyra98 <24190917+joaquinpereyra98@users.noreply.github.com> Date: Thu, 4 Sep 2025 22:09:47 -0300 Subject: [PATCH 58/66] [REFACTOR] Simplify the DHAppearanceSettings (#1057) Co-authored-by: Joaquin Pereyra --- lang/en.json | 51 +++-- .../settings/appearanceSettings.mjs | 215 +++++++++++------- module/data/settings/Appearance.mjs | 125 +++------- module/systemRegistration/settings.mjs | 2 +- templates/settings/appearance-settings.hbs | 116 ---------- .../appearance-settings/diceSoNice.hbs | 67 ++++++ .../settings/appearance-settings/header.hbs | 3 + .../settings/appearance-settings/main.hbs | 46 ++++ 8 files changed, 327 insertions(+), 298 deletions(-) delete mode 100644 templates/settings/appearance-settings.hbs create mode 100644 templates/settings/appearance-settings/diceSoNice.hbs create mode 100644 templates/settings/appearance-settings/header.hbs create mode 100644 templates/settings/appearance-settings/main.hbs diff --git a/lang/en.json b/lang/en.json index 82ffac7a..d5c4f037 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2152,20 +2152,43 @@ "SETTINGS": { "Appearance": { "FIELDS": { - "displayFear": { "label": "Fear Display" }, - "dualityColorScheme": { "label": "Chat Style" }, - "hideAttribution": { "label": "Hide Attribution" }, + "displayFear": { + "label": "Display Fear" + }, + "showGenericStatusEffects": { + "label": "Show Foundry Status Effects" + }, + "hideAttribution": { + "label": "Hide Attribution" + }, "expandedTitle": "Auto-expand Descriptions", - "extendCharacterDescriptions": { "label": "Characters" }, - "extendAdversaryDescriptions": { "label": "Adversaries" }, - "extendEnvironmentDescriptions": { "label": "Environments" }, - "extendItemDescriptions": { "label": "Items" }, - "expandRollMessage": "Auto-expand Message Sections", - "expandRollMessageDesc": { "label": "Description" }, - "expandRollMessageRoll": { "label": "Formula" }, - "expandRollMessageDamage": { "label": "Damage/Healing" }, - "expandRollMessageTarget": { "label": "Target" }, - "showGenericStatusEffects": { "label": "Show Foundry Status Effects" } + "extendCharacterDescriptions": { + "label": "Characters" + }, + "extendAdversaryDescriptions": { + "label": "Adversaries" + }, + "extendEnvironmentDescriptions": { + "label": "Environments" + }, + "extendItemDescriptions": { + "label": "Items" + }, + "expandRollMessage": { + "title": "Auto-expand Message Sections", + "desc": { + "label": "Description" + }, + "roll": { + "label": "Formula" + }, + "damage": { + "label": "Damage/Healing" + }, + "target": { + "label": "Target" + } + } }, "fearDisplay": { "token": "Tokens", @@ -2310,10 +2333,8 @@ "hint": "System ruler setup for displaying ranges in Daggerheart" }, "appearance": { - "title": "Appearance Settings", "label": "Appearance Settings", "hint": "Modify the look of various parts of the system", - "name": "Appearance Settings", "duality": "Duality Rolls", "diceSoNice": { "title": "Dice So Nice", diff --git a/module/applications/settings/appearanceSettings.mjs b/module/applications/settings/appearanceSettings.mjs index f0310477..bbc27a79 100644 --- a/module/applications/settings/appearanceSettings.mjs +++ b/module/applications/settings/appearanceSettings.mjs @@ -3,43 +3,48 @@ import { getDiceSoNicePreset } from '../../config/generalConfig.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; +/** + * @import {ApplicationClickAction} from "@client/applications/_types.mjs" + */ + export default class DHAppearanceSettings extends HandlebarsApplicationMixin(ApplicationV2) { - constructor() { - super({}); - - this.settings = new DhAppearance( - game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).toObject() - ); - } - - get title() { - return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title'); - } - + /**@inheritdoc */ static DEFAULT_OPTIONS = { tag: 'form', id: 'daggerheart-appearance-settings', classes: ['daggerheart', 'dialog', 'dh-style', 'setting'], position: { width: '600', height: 'auto' }, window: { + title: 'DAGGERHEART.SETTINGS.Menu.title', icon: 'fa-solid fa-gears' }, actions: { - reset: this.reset, - save: this.save, - preview: this.preview + reset: DHAppearanceSettings.#onReset, + preview: DHAppearanceSettings.#onPreview }, - form: { handler: this.updateData, submitOnChange: true } + form: { + closeOnSubmit: true, + handler: DHAppearanceSettings.#onSubmit + } }; static PARTS = { - main: { - template: 'systems/daggerheart/templates/settings/appearance-settings.hbs' - } + header: { template: 'systems/daggerheart/templates/settings/appearance-settings/header.hbs' }, + tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, + main: { template: 'systems/daggerheart/templates/settings/appearance-settings/main.hbs' }, + diceSoNice: { template: 'systems/daggerheart/templates/settings/appearance-settings/diceSoNice.hbs' }, + footer: { template: "templates/generic/form-footer.hbs" } }; /** @inheritdoc */ static TABS = { + general: { + tabs: [ + { id: 'main', label: 'DAGGERHEART.GENERAL.Tabs.general' }, + { id: 'diceSoNice', label: 'DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.title' }, + ], + initial: 'main' + }, diceSoNice: { tabs: [ { id: 'hope', label: 'DAGGERHEART.GENERAL.hope' }, @@ -51,79 +56,137 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App } }; - changeTab(tab, group, options) { - super.changeTab(tab, group, options); + /**@type {DhAppearance}*/ + setting; - this.render(); + static #localized = false; + + /** @inheritDoc */ + async _preFirstRender(_context, _options) { + await super._preFirstRender(_context, _options); + if (!DHAppearanceSettings.#localized) { + foundry.helpers.Localization.localizeDataModel(this.setting.constructor); + DHAppearanceSettings.#localized = true; + } } - async _prepareContext(_options) { - const context = await super._prepareContext(_options); - context.settingFields = this.settings; - - context.showDiceSoNice = game.modules.get('dice-so-nice')?.active; - if (game.dice3d) { - context.diceSoNiceTextures = game.dice3d.exports.TEXTURELIST; - context.diceSoNiceColorsets = game.dice3d.exports.COLORSETS; - context.diceSoNiceMaterials = Object.keys(game.dice3d.DiceFactory.material_options).map(key => ({ - key: key, - name: `DICESONICE.Material${key.capitalize()}` - })); - context.diceSoNiceSystems = []; - for (const [key, system] of game.dice3d.DiceFactory.systems.entries()) { - context.diceSoNiceSystems.push({ key, name: system.name }); - } + /** @inheritdoc */ + _configureRenderParts(options) { + const parts = super._configureRenderParts(options); + if (!game.modules.get('dice-so-nice')?.active){ + delete parts.diceSoNice; + delete parts.tabs; } + return parts; + } - context.diceTab = { - key: this.tabGroups.diceSoNice, - source: this.settings._source.diceSoNice[this.tabGroups.diceSoNice], - fields: this.settings.schema.fields.diceSoNice.fields[this.tabGroups.diceSoNice].fields - }; + /**@inheritdoc */ + async _prepareContext(options) { + const context = await super._prepareContext(options); + if (options.isFirstRender) this.setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); + + context.setting = this.setting; + context.fields = this.setting.schema.fields; + + context.tabs = this._prepareTabs('general'); + context.dsnTabs = this._prepareTabs('diceSoNice'); return context; } - static async updateData(event, element, formData) { - const updatedSettings = foundry.utils.expandObject(formData.object); + /**@inheritdoc */ + async _preparePartContext(partId, context, options) { + const partContext = await super._preparePartContext(partId, context, options); + if (partId in context.tabs) partContext.tab = partContext.tabs[partId]; + switch (partId) { + case "diceSoNice": + await this.prepareDiceSoNiceContext(partContext); + break; + case "footer": + 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" } + ]; + break; + } + return partContext; - await this.settings.updateSource(updatedSettings); - this.render(); } - static async preview() { - const source = this.settings._source.diceSoNice[this.tabGroups.diceSoNice]; - let faces = 'd12'; - switch (this.tabGroups.diceSoNice) { - case 'advantage': - case 'disadvantage': - faces = 'd6'; - } - const preset = await getDiceSoNicePreset(source, faces); - const diceSoNiceRoll = await new Roll(`1${faces}`).evaluate(); + /** + * Prepare render context for the DSN part. + * @param {ApplicationRenderContext} context + * @returns {Promise} + * @protected + */ + async prepareDiceSoNiceContext(context) { + context.diceSoNiceTextures = Object.entries(game.dice3d.exports.TEXTURELIST).reduce((acc, [k, v]) => ({ + ...acc, + [k]: v.name + }), {}); + context.diceSoNiceColorsets = Object.values(game.dice3d.exports.COLORSETS).reduce((acc, v) => ({ + ...acc, + [v.id]: v.description + }), {}); + context.diceSoNiceMaterials = Object.keys(game.dice3d.DiceFactory.material_options).reduce((acc, key) => ({ + ...acc, + [key]: `DICESONICE.Material${key.capitalize()}` + }), {}); + context.diceSoNiceSystems = Object.fromEntries([...game.dice3d.DiceFactory.systems].map(([k, v]) => [k, v.name])); + + foundry.utils.mergeObject(context.dsnTabs, [ + "hope", + "fear", + "advantage", + "disadvantage", + ].reduce((acc, key) => ({ + ...acc, + [key]: { + values: this.setting.diceSoNice[key], + fields: this.setting.schema.getField(`diceSoNice.${key}`).fields, + } + }), {})); + } + + /** + * Submit the configuration form. + * @this {DHAppearanceSettings} + * @param {SubmitEvent} event + * @param {HTMLFormElement} form + * @param {foundry.applications.ux.FormDataExtended} formData + * @returns {Promise} + */ + static async #onSubmit(event, form, formData) { + const data = this.setting.schema.clean(foundry.utils.expandObject(formData.object)); + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, data); + } + + /* -------------------------------------------- */ + + /** + * Submit the configuration form. + * @this {DHAppearanceSettings} + * @type {ApplicationClickAction} + */ + static async #onPreview(_, target) { + const formData = new foundry.applications.ux.FormDataExtended(target.closest("form")); + const { diceSoNice } = foundry.utils.expandObject(formData.object); + const { key } = target.dataset; + const faces = ['advantage', 'disadvantage'].includes(key) ? 'd6' : 'd12'; + const preset = await getDiceSoNicePreset(diceSoNice[key], faces); + const diceSoNiceRoll = await new foundry.dice.Roll(`1${faces}`).evaluate(); diceSoNiceRoll.dice[0].options.appearance = preset.appearance; diceSoNiceRoll.dice[0].options.modelFile = preset.modelFile; - await game.dice3d.showForRoll(diceSoNiceRoll, game.user, false); } - static async reset() { - this.settings = new DhAppearance(); - this.render(); - } - - static async save() { - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, this.settings.toObject()); - - this.close(); - } - - _getTabs(tabs) { - for (const v of Object.values(tabs)) { - v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active; - v.cssClass = v.active ? 'active' : ''; - } - - return tabs; + /** + * Reset the form back to default values. + * @this {DHAppearanceSettings} + * @type {ApplicationClickAction} + */ + static async #onReset() { + this.setting = new this.setting.constructor(); + this.render({ force: false }); } } diff --git a/module/data/settings/Appearance.mjs b/module/data/settings/Appearance.mjs index 36f6bb5a..4c6e01ea 100644 --- a/module/data/settings/Appearance.mjs +++ b/module/data/settings/Appearance.mjs @@ -1,100 +1,45 @@ -import { fearDisplay } from '../../config/generalConfig.mjs'; - export default class DhAppearance extends foundry.abstract.DataModel { + static LOCALIZATION_PREFIXES = ["DAGGERHEART.SETTINGS.Appearance"]; + static defineSchema() { - const fields = foundry.data.fields; + const { StringField, ColorField, BooleanField, SchemaField } = foundry.data.fields; + + // helper to create dice style schema + const diceStyle = ({ fg, bg, outline, edge }) => new SchemaField({ + foreground: new ColorField({ required: true, initial: fg }), + background: new ColorField({ required: true, initial: bg }), + outline: new ColorField({ required: true, initial: outline }), + edge: new ColorField({ required: true, initial: edge }), + texture: new StringField({ initial: 'astralsea', required: true, blank: false }), + colorset: new StringField({ initial: 'inspired', required: true, blank: false }), + material: new StringField({ initial: 'metal', required: true, blank: false }), + system: new StringField({ initial: 'standard', required: true, blank: false }) + }); + return { - displayFear: new fields.StringField({ + displayFear: new StringField({ required: true, - choices: fearDisplay, - initial: fearDisplay.token.value, - label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.displayFear.label' + choices: CONFIG.DH.GENERAL.fearDisplay, + initial: CONFIG.DH.GENERAL.fearDisplay.token.value, }), - diceSoNice: new fields.SchemaField({ - hope: new fields.SchemaField({ - foreground: new fields.ColorField({ required: true, initial: '#ffffff' }), - background: new fields.ColorField({ required: true, initial: '#ffe760' }), - outline: new fields.ColorField({ required: true, initial: '#000000' }), - edge: new fields.ColorField({ required: true, initial: '#ffffff' }), - texture: new fields.StringField({ initial: 'astralsea' }), - colorset: new fields.StringField({ initial: 'inspired' }), - material: new fields.StringField({ initial: 'metal' }), - system: new fields.StringField({ initial: 'standard' }) - }), - fear: new fields.SchemaField({ - foreground: new fields.ColorField({ required: true, initial: '#000000' }), - background: new fields.ColorField({ required: true, initial: '#0032b1' }), - outline: new fields.ColorField({ required: true, initial: '#ffffff' }), - edge: new fields.ColorField({ required: true, initial: '#000000' }), - texture: new fields.StringField({ initial: 'astralsea' }), - colorset: new fields.StringField({ initial: 'inspired' }), - material: new fields.StringField({ initial: 'metal' }), - system: new fields.StringField({ initial: 'standard' }) - }), - advantage: new fields.SchemaField({ - foreground: new fields.ColorField({ required: true, initial: '#ffffff' }), - background: new fields.ColorField({ required: true, initial: '#008000' }), - outline: new fields.ColorField({ required: true, initial: '#000000' }), - edge: new fields.ColorField({ required: true, initial: '#ffffff' }), - texture: new fields.StringField({ initial: 'astralsea' }), - colorset: new fields.StringField({ initial: 'inspired' }), - material: new fields.StringField({ initial: 'metal' }), - system: new fields.StringField({ initial: 'standard' }) - }), - disadvantage: new fields.SchemaField({ - foreground: new fields.ColorField({ required: true, initial: '#000000' }), - background: new fields.ColorField({ required: true, initial: '#b30000' }), - outline: new fields.ColorField({ required: true, initial: '#ffffff' }), - edge: new fields.ColorField({ required: true, initial: '#000000' }), - texture: new fields.StringField({ initial: 'astralsea' }), - colorset: new fields.StringField({ initial: 'inspired' }), - material: new fields.StringField({ initial: 'metal' }), - system: new fields.StringField({ initial: 'standard' }) - }) + diceSoNice: new SchemaField({ + hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }), + fear: diceStyle({ fg: '#000000', bg: '#0032b1', outline: '#ffffff', edge: '#000000' }), + advantage: diceStyle({ fg: '#ffffff', bg: '#008000', outline: '#000000', edge: '#ffffff' }), + disadvantage: diceStyle({ fg: '#000000', bg: '#b30000', outline: '#ffffff', edge: '#000000' }) }), - showGenericStatusEffects: new fields.BooleanField({ - initial: true, - label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.showGenericStatusEffects.label' + extendCharacterDescriptions: new BooleanField(), + extendAdversaryDescriptions: new BooleanField(), + extendEnvironmentDescriptions: new BooleanField(), + extendItemDescriptions: new BooleanField(), + expandRollMessage: new SchemaField({ + desc: new BooleanField(), + roll: new BooleanField(), + damage: new BooleanField(), + target: new BooleanField() }), - extendCharacterDescriptions: new fields.BooleanField({ - initial: false, - label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendCharacterDescriptions.label' - }), - extendAdversaryDescriptions: new fields.BooleanField({ - initial: false, - label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendAdversaryDescriptions.label' - }), - extendEnvironmentDescriptions: new fields.BooleanField({ - initial: false, - label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendEnvironmentDescriptions.label' - }), - extendItemDescriptions: new fields.BooleanField({ - initial: false, - label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendItemDescriptions.label' - }), - expandRollMessage: new fields.SchemaField({ - desc: new fields.BooleanField({ - initial: false, - label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessageDesc.label' - }), - roll: new fields.BooleanField({ - initial: false, - label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessageRoll.label' - }), - damage: new fields.BooleanField({ - initial: false, - label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessageDamage.label' - }), - target: new fields.BooleanField({ - initial: false, - label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessageTarget.label' - }) - }), - hideAttribution: new fields.BooleanField({ - required: true, - initial: false, - label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.hideAttribution.label' - }) + hideAttribution: new BooleanField(), + showGenericStatusEffects: new BooleanField({ initial: true }), }; } } diff --git a/module/systemRegistration/settings.mjs b/module/systemRegistration/settings.mjs index 4828ebb0..565a7740 100644 --- a/module/systemRegistration/settings.mjs +++ b/module/systemRegistration/settings.mjs @@ -72,7 +72,7 @@ const registerMenus = () => { }); game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, { - name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.appearance.title'), + name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.appearance.label'), label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.appearance.label'), hint: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.appearance.hint'), icon: 'fa-solid fa-palette', diff --git a/templates/settings/appearance-settings.hbs b/templates/settings/appearance-settings.hbs deleted file mode 100644 index aa094d69..00000000 --- a/templates/settings/appearance-settings.hbs +++ /dev/null @@ -1,116 +0,0 @@ -
    -
    -

    {{localize 'DAGGERHEART.SETTINGS.Menu.appearance.name'}}

    -
    - - {{formGroup settingFields.schema.fields.hideAttribution value=settingFields._source.hideAttribution localize=true}} - -
    - {{localize 'DAGGERHEART.GENERAL.fear'}} - {{formGroup settingFields.schema.fields.displayFear value=settingFields._source.displayFear localize=true}} - {{formGroup settingFields.schema.fields.showGenericStatusEffects value=settingFields._source.showGenericStatusEffects localize=true}} -
    - -
    - {{localize 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandedTitle'}} - {{formGroup settingFields.schema.fields.extendCharacterDescriptions value=settingFields._source.extendCharacterDescriptions localize=true}} - {{formGroup settingFields.schema.fields.extendAdversaryDescriptions value=settingFields._source.extendAdversaryDescriptions localize=true}} - {{formGroup settingFields.schema.fields.extendEnvironmentDescriptions value=settingFields._source.extendEnvironmentDescriptions localize=true}} - {{formGroup settingFields.schema.fields.extendItemDescriptions value=settingFields._source.extendItemDescriptions localize=true}} -
    - -
    - {{localize 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessage'}} - {{formGroup settingFields.schema.fields.expandRollMessage.fields.desc value=settingFields.expandRollMessage.desc localize=true}} - {{formGroup settingFields.schema.fields.expandRollMessage.fields.roll value=settingFields.expandRollMessage.roll localize=true}} - {{formGroup settingFields.schema.fields.expandRollMessage.fields.damage value=settingFields.expandRollMessage.damage localize=true}} - {{formGroup settingFields.schema.fields.expandRollMessage.fields.target value=settingFields.expandRollMessage.target localize=true}} -
    - - {{#if showDiceSoNice}} -
    - {{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.title"}} -
    {{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.hint"}}
    - -
    - -
    - -
    -
    - - -
    - -
    -
    - - {{formInput diceTab.fields.foreground value=diceTab.source.foreground localize=true}} -
    -
    - - {{formInput diceTab.fields.background value=diceTab.source.background localize=true}} -
    -
    - - {{formInput diceTab.fields.outline value=diceTab.source.outline localize=true}} -
    -
    - - {{formInput diceTab.fields.edge value=diceTab.source.edge localize=true}} -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    -
    -
    -
    - {{/if}} - -
    - - -
    -
    - \ No newline at end of file diff --git a/templates/settings/appearance-settings/diceSoNice.hbs b/templates/settings/appearance-settings/diceSoNice.hbs new file mode 100644 index 00000000..6321332d --- /dev/null +++ b/templates/settings/appearance-settings/diceSoNice.hbs @@ -0,0 +1,67 @@ +
    +
    +
    {{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.hint"}}
    + +
    + +
    + {{#each dsnTabs as |dsnTab|}} +
    +
    +
    + + {{formInput fields.system value=values.system localize=true choices=@root.diceSoNiceSystems}} +
    +
    +
    + + {{formInput fields.foreground value=values.foreground localize=true}} +
    +
    + + {{formInput fields.background value=values.background localize=true}} +
    +
    + + {{formInput fields.outline value=values.outline localize=true}} +
    +
    + + {{formInput fields.edge value=values.edge localize=true}} +
    +
    + + {{formInput fields.colorset value=values.colorset choices=@root.diceSoNiceColorsets localize=true}} +
    +
    + + {{formInput fields.texture value=values.texture choices=@root.diceSoNiceTextures localize=true}} +
    +
    + + {{formInput fields.material value=values.material choices=@root.diceSoNiceMaterials localize=true}} +
    +
    + +
    +
    +
    +
    + {{/each}} +
    +
    \ No newline at end of file diff --git a/templates/settings/appearance-settings/header.hbs b/templates/settings/appearance-settings/header.hbs new file mode 100644 index 00000000..110fd2b0 --- /dev/null +++ b/templates/settings/appearance-settings/header.hbs @@ -0,0 +1,3 @@ +
    +

    {{localize 'DAGGERHEART.SETTINGS.Menu.appearance.label'}}

    +
    \ No newline at end of file diff --git a/templates/settings/appearance-settings/main.hbs b/templates/settings/appearance-settings/main.hbs new file mode 100644 index 00000000..9cab271a --- /dev/null +++ b/templates/settings/appearance-settings/main.hbs @@ -0,0 +1,46 @@ +
    + {{formGroup + fields.displayFear + value=setting.displayFear + localize=true}} + {{formGroup + fields.showGenericStatusEffects + value=setting.showGenericStatusEffects + localize=true}} + {{formGroup + fields.hideAttribution + value=setting.hideAttribution + localize=true}} + +
    + {{localize 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandedTitle'}} + {{formGroup + fields.extendCharacterDescriptions + value=setting.extendCharacterDescriptions + localize=true}} + {{formGroup + fields.extendAdversaryDescriptions + value=setting.extendAdversaryDescriptions + localize=true}} + {{formGroup + fields.extendEnvironmentDescriptions + value=setting.extendEnvironmentDescriptions + localize=true}} + {{formGroup + fields.extendItemDescriptions + value=setting.extendItemDescriptions + localize=true}} +
    + +
    + {{localize 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessage.title'}} + {{formGroup fields.expandRollMessage.fields.desc value=setting.expandRollMessage.desc + localize=true}} + {{formGroup fields.expandRollMessage.fields.roll value=setting.expandRollMessage.roll + localize=true}} + {{formGroup fields.expandRollMessage.fields.damage + value=setting.expandRollMessage.damage localize=true}} + {{formGroup fields.expandRollMessage.fields.target + value=setting.expandRollMessage.target localize=true}} +
    +
    \ No newline at end of file From ec54da4e23847b4dfea82a3ab71ad9cacddba2c0 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Fri, 5 Sep 2025 23:47:12 +1000 Subject: [PATCH 59/66] Add and fix Tier names in level up (#1142) Co-authored-by: Chris Ryan --- lang/en.json | 3 +++ module/data/levelTier.mjs | 6 +++--- module/data/levelup.mjs | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lang/en.json b/lang/en.json index 7efca151..f1164232 100755 --- a/lang/en.json +++ b/lang/en.json @@ -475,18 +475,21 @@ }, "takeLevelUp": "Finish Level Up", "tier2": { + "name": "Tier 2", "label": "Levels 2-4", "infoLabel": "At Level 2, gain an additional Experience at +2 and gain a +1 bonus to your Proficiency.", "pretext": "Choose two options from the list below", "posttext": "Take an additional domain card of your level or lower from a domain you have access to." }, "tier3": { + "name": "Tier 3", "label": "Levels 5-7", "infoLabel": "At Level 5, take an additional Experience and clear all marks on Character Traits.", "pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.", "posttext": "Take an additional domain card of your level or lower from a domain you have access to." }, "tier4": { + "name": "Tier 4", "label": "Levels 8-10", "infoLabel": "At Level 8, take an additional Experience and clear all marks on Character Traits.", "pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.", diff --git a/module/data/levelTier.mjs b/module/data/levelTier.mjs index e42cfc54..2252e4da 100644 --- a/module/data/levelTier.mjs +++ b/module/data/levelTier.mjs @@ -169,7 +169,7 @@ export const defaultLevelTiers = { tiers: { 2: { tier: 2, - name: 'Tier 2', + name: 'DAGGERHEART.APPLICATIONS.Levelup.tier2.name', levels: { start: 2, end: 4 @@ -232,7 +232,7 @@ export const defaultLevelTiers = { }, 3: { tier: 3, - name: 'Tier 3', + name: 'DAGGERHEART.APPLICATIONS.Levelup.tier3.name', levels: { start: 5, end: 7 @@ -313,7 +313,7 @@ export const defaultLevelTiers = { }, 4: { tier: 4, - name: 'Tier 4', + name: 'DAGGERHEART.APPLICATIONS.Levelup.tier4.name', levels: { start: 8, end: 10 diff --git a/module/data/levelup.mjs b/module/data/levelup.mjs index 665b3264..4dc1c058 100644 --- a/module/data/levelup.mjs +++ b/module/data/levelup.mjs @@ -234,7 +234,7 @@ export class DhLevelup extends foundry.abstract.DataModel { const subclassInTier = subclasses.some(x => x.tier === Number(tierKey)); return { - name: tier.name, + name: game.i18n.localize(tier.name), active: this.currentLevel >= Math.min(...tier.belongingLevels), groups: Object.keys(tier.options).map(optionKey => { const option = tier.options[optionKey]; From d4a98d3f662e0668057fee13e1d827d1812d2645 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Sun, 7 Sep 2025 03:53:21 +1000 Subject: [PATCH 60/66] Add motives and tactics for Oak Treant (#1158) Co-authored-by: Chris Ryan --- .../adversary_Oak_Treant_XK78QUfY8c8Go8Uv.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/packs/adversaries/adversary_Oak_Treant_XK78QUfY8c8Go8Uv.json b/src/packs/adversaries/adversary_Oak_Treant_XK78QUfY8c8Go8Uv.json index f32a9894..22cbc3d3 100644 --- a/src/packs/adversaries/adversary_Oak_Treant_XK78QUfY8c8Go8Uv.json +++ b/src/packs/adversaries/adversary_Oak_Treant_XK78QUfY8c8Go8Uv.json @@ -105,19 +105,20 @@ "img": "icons/skills/melee/blood-slash-foam-red.webp", "type": "attack", "chatDisplay": false - } + }, + "motivesAndTactics": "Hide in plain sight, preserve the forest, root down, swing branches" }, "flags": {}, "_stats": { "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.347", + "coreVersion": "13.348", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.1.2", "createdTime": 1753922784314, - "modifiedTime": 1755259462770, - "lastModifiedBy": "VZIeX2YDvX338Zvr" + "modifiedTime": 1757057641714, + "lastModifiedBy": "mdk78Q6pOyHh6aBg" }, "_id": "XK78QUfY8c8Go8Uv", "sort": 3400000, From e258d9c5f66d0e42ea820ec7654ef13f053269ee Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Sun, 7 Sep 2025 06:05:24 +1000 Subject: [PATCH 61/66] Fixed typo in button class (#1159) Co-authored-by: Chris Ryan --- templates/dialogs/dice-roll/rollSelection.hbs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/dialogs/dice-roll/rollSelection.hbs b/templates/dialogs/dice-roll/rollSelection.hbs index 3aac0321..a2ca5a01 100644 --- a/templates/dialogs/dice-roll/rollSelection.hbs +++ b/templates/dialogs/dice-roll/rollSelection.hbs @@ -148,7 +148,7 @@ -
    From fd925407925e4d91c4f96bfb7689cf0d043af9a8 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Sun, 7 Sep 2025 06:06:17 +1000 Subject: [PATCH 62/66] Add keys, update usage for translations (#1156) Co-authored-by: Chris Ryan --- lang/en.json | 12 +++++++++++- module/applications/sheets-configs/action-config.mjs | 6 +++--- module/config/generalConfig.mjs | 8 ++++---- templates/actionTypes/cost.hbs | 10 +++++----- templates/actionTypes/range-target.hbs | 6 +++--- templates/actionTypes/uses.hbs | 6 +++--- 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/lang/en.json b/lang/en.json index f1164232..37e6cfff 100755 --- a/lang/en.json +++ b/lang/en.json @@ -992,6 +992,12 @@ "selectType": "Select Action Type", "selectAction": "Action Selection" }, + "TargetTypes": { + "any": "Any", + "friendly": "Friendly", + "hostile": "Hostile", + "self": "Self" + }, "TemplateTypes": { "circle": "Circle", "cone": "Cone", @@ -1880,7 +1886,9 @@ "tier4": "tier 4", "domains": "Domains", "downtime": "Downtime", - "rules": "Rules" + "rules": "Rules", + "configuration": "Configuration", + "base": "Base" }, "Tiers": { "singular": "Tier", @@ -1981,6 +1989,8 @@ "save": "Save", "scalable": "Scalable", "situationalBonus": "Situational Bonus", + "spent": "Spent", + "step": "Step", "stress": "Stress", "subclasses": "Subclasses", "success": "Success", diff --git a/module/applications/sheets-configs/action-config.mjs b/module/applications/sheets-configs/action-config.mjs index ea3c6f31..f673fc66 100644 --- a/module/applications/sheets-configs/action-config.mjs +++ b/module/applications/sheets-configs/action-config.mjs @@ -66,7 +66,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { group: 'primary', id: 'base', icon: null, - label: 'Base' + label: 'DAGGERHEART.GENERAL.Tabs.base' }, config: { active: false, @@ -74,7 +74,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { group: 'primary', id: 'config', icon: null, - label: 'Configuration' + label: 'DAGGERHEART.GENERAL.Tabs.configuration' }, effect: { active: false, @@ -82,7 +82,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { group: 'primary', id: 'effect', icon: null, - label: 'Effect' + label: 'DAGGERHEART.GENERAL.Tabs.effects' } }; diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 60b3a2f1..8714d44b 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -90,22 +90,22 @@ export const rangeInclusion = { export const otherTargetTypes = { friendly: { id: 'friendly', - label: 'Friendly' + label: 'DAGGERHEART.CONFIG.TargetTypes.friendly' }, hostile: { id: 'hostile', - label: 'Hostile' + label: 'DAGGERHEART.CONFIG.TargetTypes.hostile' }, any: { id: 'any', - label: 'Any' + label: 'DAGGERHEART.CONFIG.TargetTypes.any' } }; export const targetTypes = { self: { id: 'self', - label: 'Self' + label: 'DAGGERHEART.CONFIG.TargetTypes.self' }, ...otherTargetTypes }; diff --git a/templates/actionTypes/cost.hbs b/templates/actionTypes/cost.hbs index a1b7de48..7a9f33d9 100644 --- a/templates/actionTypes/cost.hbs +++ b/templates/actionTypes/cost.hbs @@ -1,6 +1,6 @@
    - Cost + {{localize "DAGGERHEART.GENERAL.Cost.single"}} {{#each source as |cost index|}} @@ -8,10 +8,10 @@ {{formField ../fields.consumeOnSuccess value=cost.consumeOnSuccess name=(concat "cost." index ".consumeOnSuccess") classes="checkbox" rootId=partId localize=true}} {{/if}}
    - {{formField ../fields.scalable label="Scalable" value=cost.scalable name=(concat "cost." index ".scalable") classes="checkbox"}} - {{formField ../fields.key choices=(@root.disableOption index @root.costOptions ../source) label="Resource" value=cost.key name=(concat "cost." index ".key") localize=true blank=false}} - {{formField ../fields.value label="Amount" value=cost.value name=(concat "cost." index ".value")}} - {{formField ../fields.step label="Step" value=cost.step name=(concat "cost." index ".step") disabled=(not cost.scalable)}} + {{formField ../fields.scalable label="DAGGERHEART.GENERAL.scalable" value=cost.scalable name=(concat "cost." index ".scalable") classes="checkbox" localize=true}} + {{formField ../fields.key choices=(@root.disableOption index @root.costOptions ../source) label="DAGGERHEART.GENERAL.resource" value=cost.key name=(concat "cost." index ".key") localize=true blank=false}} + {{formField ../fields.value label="DAGGERHEART.GENERAL.amount" value=cost.value name=(concat "cost." index ".value") localize=true}} + {{formField ../fields.step label="DAGGERHEART.GENERAL.step" value=cost.step name=(concat "cost." index ".step") disabled=(not cost.scalable) localize=true}}
    {{/each}} diff --git a/templates/actionTypes/range-target.hbs b/templates/actionTypes/range-target.hbs index 3776f0c1..143acdf8 100644 --- a/templates/actionTypes/range-target.hbs +++ b/templates/actionTypes/range-target.hbs @@ -1,12 +1,12 @@
    {{localize "DAGGERHEART.GENERAL.range"}}{{#if fields.target}} & {{localize "DAGGERHEART.GENERAL.Target.single"}}{{/if}} - {{formField fields.range value=source.range label="Range" name=(concat path "range") localize=true}} + {{formField fields.range value=source.range label="DAGGERHEART.GENERAL.range" name=(concat path "range") localize=true}} {{#if fields.target}}
    {{#if (and source.target.type (not (eq source.target.type 'self')))}} - {{ formField fields.target.amount value=source.target.amount label="Amount" name=(concat path "target.amount") }} + {{ formField fields.target.amount value=source.target.amount label="DAGGERHEART.GENERAL.amount" name=(concat path "target.amount") localize=true}} {{/if}} - {{ formField fields.target.type value=source.target.type label="Target" name=(concat path "target.type") localize=true }} + {{ formField fields.target.type value=source.target.type label="DAGGERHEART.GENERAL.Target.single" name=(concat path "target.type") localize=true }}
    {{/if}}
    \ No newline at end of file diff --git a/templates/actionTypes/uses.hbs b/templates/actionTypes/uses.hbs index 2c1c3cd4..7304f810 100644 --- a/templates/actionTypes/uses.hbs +++ b/templates/actionTypes/uses.hbs @@ -4,8 +4,8 @@ {{formField fields.consumeOnSuccess value=source.consumeOnSuccess name="uses.consumeOnSuccess" classes="checkbox" rootId=partId localize=true}} {{/if}}
    - {{formField fields.value label="Spent" value=source.value name="uses.value" rootId=partId}} - {{formField fields.max label="Max" value=source.max name="uses.max" rootId=partId}} + {{formField fields.value label="DAGGERHEART.GENERAL.spent" value=source.value name="uses.value" rootId=partId localize=true}} + {{formField fields.max label="DAGGERHEART.GENERAL.max" value=source.max name="uses.max" rootId=partId localize=true}}
    - {{formField fields.recovery label="Recovery" value=source.recovery name="uses.recovery" rootId=partId localize=true}} + {{formField fields.recovery label="DAGGERHEART.GENERAL.recovery" value=source.recovery name="uses.recovery" rootId=partId localize=true}}
    \ No newline at end of file From f1b6d3851dbed1559d88ecebc71d1bb537fd8b8b Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 7 Sep 2025 00:30:29 +0200 Subject: [PATCH 63/66] [PR] [Feature] 590 - Daggerheart Menu (#1007) * Added menu with refresh tools * Replaced menu icon --- CONTRIBUTING.md | 4 +- daggerheart.mjs | 8 +- lang/en.json | 12 ++ module/applications/_module.mjs | 1 + module/applications/levelup/levelup.mjs | 2 +- module/applications/scene/_module.mjs | 2 +- .../scene/sceneConfigSettings.mjs | 37 ++-- .../settings/appearanceSettings.mjs | 90 +++++----- .../sheets/api/application-mixin.mjs | 11 +- module/applications/sidebar/_module.mjs | 2 + module/applications/sidebar/sidebar.mjs | 33 ++++ .../sidebar/tabs/daggerheartMenu.mjs | 160 ++++++++++++++++++ module/applications/ui/chatLog.mjs | 2 +- module/applications/ui/fearTracker.mjs | 8 +- module/applications/ui/itemBrowser.mjs | 97 +++++------ module/config/itemBrowserConfig.mjs | 38 +++-- module/config/settingsConfig.mjs | 20 +-- module/data/action/baseAction.mjs | 45 +++-- module/data/chat-message/_modules.mjs | 12 +- module/data/chat-message/actorRoll.mjs | 6 +- module/data/fields/action/beastformField.mjs | 14 +- module/data/fields/action/damageField.mjs | 53 +++--- module/data/fields/action/effectsField.mjs | 25 ++- module/data/fields/action/macroField.mjs | 2 +- module/data/fields/action/rangeField.mjs | 3 +- module/data/fields/action/rollField.mjs | 28 ++- module/data/fields/action/saveField.mjs | 102 +++++------ module/data/fields/action/targetField.mjs | 13 +- module/data/fields/action/usesField.mjs | 6 +- module/data/settings/Appearance.mjs | 27 +-- module/data/settings/Automation.mjs | 8 +- module/dice/damageRoll.mjs | 3 +- module/dice/dhRoll.mjs | 9 +- module/dice/dualityRoll.mjs | 7 +- module/documents/chatMessage.mjs | 34 ++-- module/documents/item.mjs | 2 +- module/helpers/handlebarsHelper.mjs | 4 +- module/systemRegistration/handlebars.mjs | 5 +- module/systemRegistration/socket.mjs | 3 +- pull_request_template.md | 3 +- styles/less/global/elements.less | 4 +- styles/less/global/global.less | 12 +- styles/less/global/sheet.less | 2 +- .../environment/potentialAdversaries.less | 2 +- styles/less/ui/chat/refresh-message.less | 13 ++ styles/less/ui/index.less | 5 +- styles/less/ui/item-browser/item-browser.less | 5 +- styles/less/ui/settings/settings.less | 4 +- styles/less/ui/sidebar/daggerheartMenu.less | 38 +++++ styles/less/ui/sidebar/tabs.less | 8 + templates/sidebar/daggerheart-menu/main.hbs | 22 +++ templates/sidebar/tabs.hbs | 18 ++ templates/ui/chat/refreshMessage.hbs | 6 + 53 files changed, 730 insertions(+), 350 deletions(-) create mode 100644 module/applications/sidebar/_module.mjs create mode 100644 module/applications/sidebar/sidebar.mjs create mode 100644 module/applications/sidebar/tabs/daggerheartMenu.mjs create mode 100644 styles/less/ui/chat/refresh-message.less create mode 100644 styles/less/ui/sidebar/daggerheartMenu.less create mode 100644 styles/less/ui/sidebar/tabs.less create mode 100644 templates/sidebar/daggerheart-menu/main.hbs create mode 100644 templates/sidebar/tabs.hbs create mode 100644 templates/ui/chat/refreshMessage.hbs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 80c01bce..b9099005 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,12 +44,14 @@ We encourage contributors to leave comments or open Discussions when proposing s ## 🧾 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 @@ -71,6 +73,6 @@ Discussions are currently happening on GitHub — in Issues, PRs, and [GitHub Di ## 🤗 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**! +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**! 🐸🛠️ diff --git a/daggerheart.mjs b/daggerheart.mjs index 1c4c2a85..f5f5e303 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -133,6 +133,8 @@ Hooks.once('init', () => { CONFIG.ui.combat = applications.ui.DhCombatTracker; CONFIG.ui.chat = applications.ui.DhChatLog; CONFIG.ui.hotbar = applications.ui.DhHotbar; + CONFIG.ui.sidebar = applications.sidebar.DhSidebar; + CONFIG.ui.daggerheartMenu = applications.sidebar.DaggerheartMenu; CONFIG.Token.rulerClass = placeables.DhTokenRuler; CONFIG.ui.resources = applications.ui.DhFearTracker; @@ -162,7 +164,7 @@ Hooks.on('ready', async () => { if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).displayFear !== 'hide') ui.resources.render({ force: true }); - if(!(ui.compendiumBrowser instanceof applications.ui.ItemBrowser)) + if (!(ui.compendiumBrowser instanceof applications.ui.ItemBrowser)) ui.compendiumBrowser = new applications.ui.ItemBrowser(); registerCountdownHooks(); @@ -309,5 +311,5 @@ Hooks.on('moveToken', async (movedToken, data) => { } }); -Hooks.on("renderCompendiumDirectory", (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html)); -Hooks.on("renderDocumentDirectory", (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html)); +Hooks.on('renderCompendiumDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html)); +Hooks.on('renderDocumentDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html)); diff --git a/lang/en.json b/lang/en.json index d5c4f037..8de962bd 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2420,6 +2420,10 @@ "heal": "Heal", "applyHealing": "Apply Healing" }, + "refreshMessage": { + "title": "Feature Refresh", + "header": "Refreshed" + }, "reroll": { "confirmTitle": "Reroll Dice", "confirmText": "Are you sure you want to reroll?" @@ -2534,8 +2538,16 @@ "multiclassAlreadyPresent": "You already have a class and multiclass", "subclassesAlreadyPresent": "You already have a class and multiclass subclass", "noDiceSystem": "Your selected dice {system} does not have a {faces} dice", + "gmMenuRefresh": "You refreshed all actions and resources {types}", "subclassAlreadyLinked": "{name} is already a subclass in the class {class}. Remove it from there if you want it to be a subclass to this class." }, + "Sidebar": { + "daggerheartMenu": { + "title": "Daggerheart Menu", + "startSession": "Start Session", + "startScene": "Start Scene" + } + }, "Tooltip": { "disableEffect": "Disable Effect", "enableEffect": "Enable Effect", diff --git a/module/applications/_module.mjs b/module/applications/_module.mjs index 3dd0c78f..b2d3001a 100644 --- a/module/applications/_module.mjs +++ b/module/applications/_module.mjs @@ -6,5 +6,6 @@ export * as scene from './scene/_module.mjs'; export * as settings from './settings/_module.mjs'; export * as sheets from './sheets/_module.mjs'; export * as sheetConfigs from './sheets-configs/_module.mjs'; +export * as sidebar from './sidebar/_module.mjs'; export * as ui from './ui/_module.mjs'; export * as ux from './ux/_module.mjs'; diff --git a/module/applications/levelup/levelup.mjs b/module/applications/levelup/levelup.mjs index 99cc53f6..c3cc6e81 100644 --- a/module/applications/levelup/levelup.mjs +++ b/module/applications/levelup/levelup.mjs @@ -536,7 +536,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) static async viewCompendium(event, target) { const type = target.dataset.compendium ?? target.dataset.type; - const presets = { + const presets = { folder: type, render: { noFolder: true diff --git a/module/applications/scene/_module.mjs b/module/applications/scene/_module.mjs index 6dc59081..7cefd268 100644 --- a/module/applications/scene/_module.mjs +++ b/module/applications/scene/_module.mjs @@ -1 +1 @@ -export { default as DhSceneConfigSettings } from './sceneConfigSettings.mjs'; \ No newline at end of file +export { default as DhSceneConfigSettings } from './sceneConfigSettings.mjs'; diff --git a/module/applications/scene/sceneConfigSettings.mjs b/module/applications/scene/sceneConfigSettings.mjs index 2ad7421a..40f66ae2 100644 --- a/module/applications/scene/sceneConfigSettings.mjs +++ b/module/applications/scene/sceneConfigSettings.mjs @@ -1,25 +1,24 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.SceneConfig { - constructor(options, ...args) { - super(options, ...args); - } - - static buildParts() { - const { footer, ...parts } = super.PARTS; - const tmpParts = { - ...parts, - dh: { template: "systems/daggerheart/templates/scene/dh-config.hbs" }, - footer + constructor(options, ...args) { + super(options, ...args); } - return tmpParts; - } - static PARTS = DhSceneConfigSettings.buildParts(); + static buildParts() { + const { footer, ...parts } = super.PARTS; + const tmpParts = { + ...parts, + dh: { template: 'systems/daggerheart/templates/scene/dh-config.hbs' }, + footer + }; + return tmpParts; + } - static buildTabs() { - super.TABS.sheet.tabs.push({ id: "dh", icon: "fa-solid" }); - return super.TABS; - } + static PARTS = DhSceneConfigSettings.buildParts(); - static TABS = DhSceneConfigSettings.buildTabs(); + static buildTabs() { + super.TABS.sheet.tabs.push({ id: 'dh', icon: 'fa-solid' }); + return super.TABS; + } -} \ No newline at end of file + static TABS = DhSceneConfigSettings.buildTabs(); +} diff --git a/module/applications/settings/appearanceSettings.mjs b/module/applications/settings/appearanceSettings.mjs index bbc27a79..5950f961 100644 --- a/module/applications/settings/appearanceSettings.mjs +++ b/module/applications/settings/appearanceSettings.mjs @@ -33,7 +33,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, main: { template: 'systems/daggerheart/templates/settings/appearance-settings/main.hbs' }, diceSoNice: { template: 'systems/daggerheart/templates/settings/appearance-settings/diceSoNice.hbs' }, - footer: { template: "templates/generic/form-footer.hbs" } + footer: { template: 'templates/generic/form-footer.hbs' } }; /** @inheritdoc */ @@ -41,7 +41,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App general: { tabs: [ { id: 'main', label: 'DAGGERHEART.GENERAL.Tabs.general' }, - { id: 'diceSoNice', label: 'DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.title' }, + { id: 'diceSoNice', label: 'DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.title' } ], initial: 'main' }, @@ -73,7 +73,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App /** @inheritdoc */ _configureRenderParts(options) { const parts = super._configureRenderParts(options); - if (!game.modules.get('dice-so-nice')?.active){ + if (!game.modules.get('dice-so-nice')?.active) { delete parts.diceSoNice; delete parts.tabs; } @@ -83,7 +83,8 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App /**@inheritdoc */ async _prepareContext(options) { const context = await super._prepareContext(options); - if (options.isFirstRender) this.setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); + if (options.isFirstRender) + this.setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); context.setting = this.setting; context.fields = this.setting.schema.fields; @@ -99,18 +100,17 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App const partContext = await super._preparePartContext(partId, context, options); if (partId in context.tabs) partContext.tab = partContext.tabs[partId]; switch (partId) { - case "diceSoNice": + case 'diceSoNice': await this.prepareDiceSoNiceContext(partContext); break; - case "footer": + case 'footer': 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: 'Reset' }, + { type: 'submit', icon: 'fa-solid fa-floppy-disk', label: 'Save Changes' } ]; break; } return partContext; - } /** @@ -120,32 +120,44 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App * @protected */ async prepareDiceSoNiceContext(context) { - context.diceSoNiceTextures = Object.entries(game.dice3d.exports.TEXTURELIST).reduce((acc, [k, v]) => ({ - ...acc, - [k]: v.name - }), {}); - context.diceSoNiceColorsets = Object.values(game.dice3d.exports.COLORSETS).reduce((acc, v) => ({ - ...acc, - [v.id]: v.description - }), {}); - context.diceSoNiceMaterials = Object.keys(game.dice3d.DiceFactory.material_options).reduce((acc, key) => ({ - ...acc, - [key]: `DICESONICE.Material${key.capitalize()}` - }), {}); - context.diceSoNiceSystems = Object.fromEntries([...game.dice3d.DiceFactory.systems].map(([k, v]) => [k, v.name])); + context.diceSoNiceTextures = Object.entries(game.dice3d.exports.TEXTURELIST).reduce( + (acc, [k, v]) => ({ + ...acc, + [k]: v.name + }), + {} + ); + context.diceSoNiceColorsets = Object.values(game.dice3d.exports.COLORSETS).reduce( + (acc, v) => ({ + ...acc, + [v.id]: v.description + }), + {} + ); + context.diceSoNiceMaterials = Object.keys(game.dice3d.DiceFactory.material_options).reduce( + (acc, key) => ({ + ...acc, + [key]: `DICESONICE.Material${key.capitalize()}` + }), + {} + ); + context.diceSoNiceSystems = Object.fromEntries( + [...game.dice3d.DiceFactory.systems].map(([k, v]) => [k, v.name]) + ); - foundry.utils.mergeObject(context.dsnTabs, [ - "hope", - "fear", - "advantage", - "disadvantage", - ].reduce((acc, key) => ({ - ...acc, - [key]: { - values: this.setting.diceSoNice[key], - fields: this.setting.schema.getField(`diceSoNice.${key}`).fields, - } - }), {})); + foundry.utils.mergeObject( + context.dsnTabs, + ['hope', 'fear', 'advantage', 'disadvantage'].reduce( + (acc, key) => ({ + ...acc, + [key]: { + values: this.setting.diceSoNice[key], + fields: this.setting.schema.getField(`diceSoNice.${key}`).fields + } + }), + {} + ) + ); } /** @@ -169,7 +181,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App * @type {ApplicationClickAction} */ static async #onPreview(_, target) { - const formData = new foundry.applications.ux.FormDataExtended(target.closest("form")); + const formData = new foundry.applications.ux.FormDataExtended(target.closest('form')); const { diceSoNice } = foundry.utils.expandObject(formData.object); const { key } = target.dataset; const faces = ['advantage', 'disadvantage'].includes(key) ? 'd6' : 'd12'; @@ -181,10 +193,10 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App } /** - * Reset the form back to default values. - * @this {DHAppearanceSettings} - * @type {ApplicationClickAction} - */ + * Reset the form back to default values. + * @this {DHAppearanceSettings} + * @type {ApplicationClickAction} + */ static async #onReset() { this.setting = new this.setting.constructor(); this.render({ force: false }); diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 2158e48b..1a508131 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -416,11 +416,14 @@ export default function DHApplicationMixin(Base) { icon: 'fa-solid fa-ban', condition: target => { const doc = getDocFromElementSync(target); - return doc && doc.system?.actions?.some(a => a.type === "beastform"); + return doc && doc.system?.actions?.some(a => a.type === 'beastform'); }, - callback: async target => game.system.api.fields.ActionFields.BeastformField.handleActiveTransformations.call(await getDocFromElement(target)) + callback: async target => + game.system.api.fields.ActionFields.BeastformField.handleActiveTransformations.call( + await getDocFromElement(target) + ) }); - + options.unshift({ name: 'DAGGERHEART.GENERAL.damage', icon: 'fa-solid fa-explosion', @@ -433,7 +436,7 @@ export default function DHApplicationMixin(Base) { action = doc?.system?.attack ?? doc; const config = action.prepareConfig(event); config.hasRoll = false; - return action && action.workflow.get("damage").execute(config, null, true); + return action && action.workflow.get('damage').execute(config, null, true); } }); diff --git a/module/applications/sidebar/_module.mjs b/module/applications/sidebar/_module.mjs new file mode 100644 index 00000000..f19f697c --- /dev/null +++ b/module/applications/sidebar/_module.mjs @@ -0,0 +1,2 @@ +export { default as DaggerheartMenu } from './tabs/daggerheartMenu.mjs'; +export { default as DhSidebar } from './sidebar.mjs'; diff --git a/module/applications/sidebar/sidebar.mjs b/module/applications/sidebar/sidebar.mjs new file mode 100644 index 00000000..fad39ac5 --- /dev/null +++ b/module/applications/sidebar/sidebar.mjs @@ -0,0 +1,33 @@ +export default class DhSidebar extends Sidebar { + /** @override */ + static TABS = { + ...super.TABS, + daggerheartMenu: { + tooltip: 'DAGGERHEART.UI.Sidebar.daggerheartMenu.title', + img: 'systems/daggerheart/assets/logos/FoundryBorneLogoWhite.svg' + } + }; + + /** @override */ + static PARTS = { + tabs: { + id: 'tabs', + template: 'systems/daggerheart/templates/sidebar/tabs.hbs' + } + }; + + /** @override */ + async _prepareTabContext(context, options) { + context.tabs = Object.entries(this.constructor.TABS).reduce((obj, [k, v]) => { + let { documentName, gmOnly, tooltip, icon, img } = v; + if (gmOnly && !game.user.isGM) return obj; + if (documentName) { + tooltip ??= getDocumentClass(documentName).metadata.labelPlural; + icon ??= CONFIG[documentName]?.sidebarIcon; + } + obj[k] = { tooltip, icon, img }; + obj[k].active = this.tabGroups.primary === k; + return obj; + }, {}); + } +} diff --git a/module/applications/sidebar/tabs/daggerheartMenu.mjs b/module/applications/sidebar/tabs/daggerheartMenu.mjs new file mode 100644 index 00000000..cf7aeae3 --- /dev/null +++ b/module/applications/sidebar/tabs/daggerheartMenu.mjs @@ -0,0 +1,160 @@ +const { HandlebarsApplicationMixin } = foundry.applications.api; +const { AbstractSidebarTab } = foundry.applications.sidebar; +/** + * The daggerheart menu tab. + * @extends {AbstractSidebarTab} + * @mixes HandlebarsApplication + */ +export default class DaggerheartMenu extends HandlebarsApplicationMixin(AbstractSidebarTab) { + constructor(options) { + super(options); + + this.refreshSelections = DaggerheartMenu.#defaultRefreshSelections(); + } + + static #defaultRefreshSelections() { + return { + session: { selected: false, label: game.i18n.localize('DAGGERHEART.GENERAL.RefreshType.session') }, + scene: { selected: false, label: game.i18n.localize('DAGGERHEART.GENERAL.RefreshType.scene') }, + longRest: { selected: false, label: game.i18n.localize('DAGGERHEART.GENERAL.RefreshType.longrest') }, + shortRest: { selected: false, label: game.i18n.localize('DAGGERHEART.GENERAL.RefreshType.shortrest') } + }; + } + + /** @override */ + static DEFAULT_OPTIONS = { + classes: ['dh-style'], + window: { + title: 'SIDEBAR.TabSettings' + }, + actions: { + selectRefreshable: DaggerheartMenu.#selectRefreshable, + refreshActors: DaggerheartMenu.#refreshActors + } + }; + + /** @override */ + static tabName = 'daggerheartMenu'; + + /** @override */ + static PARTS = { + main: { template: 'systems/daggerheart/templates/sidebar/daggerheart-menu/main.hbs' } + }; + + /* -------------------------------------------- */ + + /** @inheritDoc */ + async _prepareContext(options) { + const context = await super._prepareContext(options); + context.refreshables = this.refreshSelections; + context.disableRefresh = Object.values(this.refreshSelections).every(x => !x.selected); + + return context; + } + + async getRefreshables(types) { + const refreshedActors = {}; + for (let actor of game.actors) { + if (['character', 'adversary'].includes(actor.type) && actor.prototypeToken.actorLink) { + const updates = {}; + for (let item of actor.items) { + if (item.system.metadata.hasResource && types.includes(item.system.resource?.recovery)) { + if (!refreshedActors[actor.id]) + refreshedActors[actor.id] = { name: actor.name, img: actor.img, refreshed: new Set() }; + refreshedActors[actor.id].refreshed.add( + game.i18n.localize(CONFIG.DH.GENERAL.refreshTypes[item.system.resource.recovery].label) + ); + + if (!updates[item.id]?.system) updates[item.id] = { system: {} }; + + const increasing = + item.system.resource.progression === CONFIG.DH.ITEM.itemResourceProgression.increasing.id; + updates[item.id].system = { + ...updates[item.id].system, + 'resource.value': increasing + ? 0 + : Roll.replaceFormulaData(item.system.resource.max, actor.getRollData()) + }; + } + if (item.system.metadata.hasActions) { + const refreshTypes = new Set(); + const actions = item.system.actions.filter(action => { + if (types.includes(action.uses.recovery)) { + refreshTypes.add(action.uses.recovery); + return true; + } + + return false; + }); + if (actions.length === 0) continue; + + if (!refreshedActors[actor.id]) + refreshedActors[actor.id] = { name: actor.name, img: actor.img, refreshed: new Set() }; + refreshedActors[actor.id].refreshed.add( + ...refreshTypes.map(type => game.i18n.localize(CONFIG.DH.GENERAL.refreshTypes[type].label)) + ); + + if (!updates[item.id]?.system) updates[item.id] = { system: {} }; + + updates[item.id].system = { + ...updates[item.id].system, + ...actions.reduce( + (acc, action) => { + acc.actions[action.id] = { 'uses.value': 0 }; + return acc; + }, + { actions: updates[item.id].system.actions ?? {} } + ) + }; + } + } + + for (let key in updates) { + const update = updates[key]; + await actor.items.get(key).update(update); + } + } + } + + return refreshedActors; + } + + /* -------------------------------------------- */ + /* Application Clicks Actions */ + /* -------------------------------------------- */ + + static async #selectRefreshable(_event, button) { + const { type } = button.dataset; + this.refreshSelections[type].selected = !this.refreshSelections[type].selected; + this.render(); + } + + static async #refreshActors() { + const refreshKeys = Object.keys(this.refreshSelections).filter(key => this.refreshSelections[key].selected); + await this.getRefreshables(refreshKeys); + const types = refreshKeys.map(x => this.refreshSelections[x].label).join(', '); + ui.notifications.info( + game.i18n.format('DAGGERHEART.UI.Notifications.gmMenuRefresh', { + types: `[${types}]` + }) + ); + this.refreshSelections = DaggerheartMenu.#defaultRefreshSelections(); + + const cls = getDocumentClass('ChatMessage'); + const msg = { + user: game.user.id, + content: await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/ui/chat/refreshMessage.hbs', + { + types: types + } + ), + title: game.i18n.localize('DAGGERHEART.UI.Chat.refreshMessage.title'), + speaker: cls.getSpeaker() + }; + + cls.create(msg); + + this.render(); + } +} diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 21762818..b95e50e1 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -112,7 +112,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo if (event.currentTarget.dataset.directDamage) { const config = action.prepareConfig(event); config.hasRoll = false; - action.workflow.get("damage").execute(config, null, true); + action.workflow.get('damage').execute(config, null, true); } else action.use(event); } diff --git a/module/applications/ui/fearTracker.mjs b/module/applications/ui/fearTracker.mjs index a346aa66..2b7c4dac 100644 --- a/module/applications/ui/fearTracker.mjs +++ b/module/applications/ui/fearTracker.mjs @@ -1,4 +1,4 @@ -import { emitAsGM, GMUpdateEvent, socketEvent } from "../../systemRegistration/socket.mjs"; +import { emitAsGM, GMUpdateEvent, socketEvent } from '../../systemRegistration/socket.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -106,6 +106,10 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV } async updateFear(value) { - return emitAsGM(GMUpdateEvent.UpdateFear, game.settings.set.bind(game.settings, CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), value); + return emitAsGM( + GMUpdateEvent.UpdateFear, + game.settings.set.bind(game.settings, CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), + value + ); } } diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index 40884817..a00f8edc 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -84,12 +84,10 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { /** @inheritDoc */ async _preRender(context, options) { this.presets = options.presets ?? {}; - + const width = this.presets?.render?.noFolder === true || this.presets?.render?.lite === true ? 600 : 850; - if(this.rendered) - this.setPosition({ width }); - else - options.position.width = width; + if (this.rendered) this.setPosition({ width }); + else options.position.width = width; await super._preRender(context, options); } @@ -100,15 +98,18 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { this.element .querySelectorAll('[data-action="selectFolder"]') - .forEach(element => element.classList.toggle('is-selected', element.dataset.folderId === this.selectedMenu.path.join('.'))); + .forEach(element => + element.classList.toggle('is-selected', element.dataset.folderId === this.selectedMenu.path.join('.')) + ); this._createSearchFilter(); - + this.element.classList.toggle('lite', this.presets?.render?.lite === true); this.element.classList.toggle('no-folder', this.presets?.render?.noFolder === true); this.element.classList.toggle('no-filter', this.presets?.render?.noFilter === true); this.element.querySelectorAll('.folder-list > [data-action="selectFolder"]').forEach(element => { - element.hidden = this.presets.render?.folders?.length && !this.presets.render.folders.includes(element.dataset.folderId); + element.hidden = + this.presets.render?.folders?.length && !this.presets.render.folders.includes(element.dataset.folderId); }); } @@ -157,7 +158,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { : []; folders.push(folder); }); - folders.sort((a, b) => a.label.localeCompare(b.label)) + folders.sort((a, b) => a.label.localeCompare(b.label)); return folders; } @@ -181,8 +182,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { await this.render({ force: true, presets: this.presets }); - if(this.selectedMenu?.data?.type?.length) - this.loadItems(); + if (this.selectedMenu?.data?.type?.length) this.loadItems(); } _replaceHTML(result, content, options) { @@ -194,53 +194,54 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { let loadTimeout = this.toggleLoader(true); const promises = []; - + game.packs.forEach(pack => { promises.push( new Promise(async resolve => { const items = await pack.getDocuments({ type__in: this.selectedMenu?.data?.type }); resolve(items); }) - ) + ); }); - + Promise.all(promises).then(async result => { - this.items = ItemBrowser.sortBy(result.flatMap(r => r), 'name'); + this.items = ItemBrowser.sortBy( + result.flatMap(r => r), + 'name' + ); this.fieldFilter = this._createFieldFilter(); if (this.presets?.filter) { - Object.entries(this.presets.filter).forEach( - ([k, v]) => { - const filter = this.fieldFilter.find(c => c.name === k) - if(filter) filter.value = v.value; - } - ); + Object.entries(this.presets.filter).forEach(([k, v]) => { + const filter = this.fieldFilter.find(c => c.name === k); + if (filter) filter.value = v.value; + }); // await this._onInputFilterBrowser(); } - - const filterList = await foundry.applications.handlebars.renderTemplate('systems/daggerheart/templates/ui/itemBrowser/filterContainer.hbs', + + const filterList = await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/ui/itemBrowser/filterContainer.hbs', { fieldFilter: this.fieldFilter, presets: this.presets, formatChoices: this.formatChoices } ); - + this.element.querySelector('.filter-content .wrapper').innerHTML = filterList; const filterContainer = this.element.querySelector('.filter-header > [data-action="expandContent"]'); - if(this.fieldFilter.length === 0) - filterContainer.setAttribute('disabled', ''); - else - filterContainer.removeAttribute('disabled'); - - const itemList = await foundry.applications.handlebars.renderTemplate('systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs', + if (this.fieldFilter.length === 0) filterContainer.setAttribute('disabled', ''); + else filterContainer.removeAttribute('disabled'); + + const itemList = await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs', { items: this.items, menu: this.selectedMenu, formatLabel: this.formatLabel } ); - + this.element.querySelector('.item-list').innerHTML = itemList; this._createFilterInputs(); @@ -255,7 +256,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { toggleLoader(state) { const container = this.element.querySelector('.item-list'); return setTimeout(() => { - container.classList.toggle("loader", state); + container.classList.toggle('loader', state); }, 100); } @@ -376,7 +377,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { for (const li of html.querySelectorAll('.item-container')) { const itemUUID = li.dataset.itemUuid, item = this.items.find(i => i.uuid === itemUUID); - if(!item) continue; + if (!item) continue; const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name); if (matchesSearch) this.#filteredItems.browser.search.add(item.id); const { input } = this.#filteredItems.browser; @@ -399,7 +400,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { item = this.items.find(i => i.uuid === itemUUID); if (!item) continue; - + const matchesMenu = this.fieldFilter.length === 0 || this.fieldFilter.every( @@ -503,19 +504,19 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { _canDragStart() { return true; } - + static injectSidebarButton(html) { - if(!game.user.isGM) return; + if (!game.user.isGM) return; const sectionId = html.dataset.tab, - menus = { + menus = { actors: { - folder: "adversaries", + folder: 'adversaries', render: { - folders: ["adversaries", "characters", "environments"] + folders: ['adversaries', 'characters', 'environments'] } }, items: { - folder: "equipments", + folder: 'equipments', render: { noFolder: true } @@ -523,20 +524,20 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { compendium: {} }; - if(Object.keys(menus).includes(sectionId)) { - const headerActions = html.querySelector(".header-actions"); + if (Object.keys(menus).includes(sectionId)) { + const headerActions = html.querySelector('.header-actions'); - const button = document.createElement("button"); - button.type = "button"; - button.classList.add("open-compendium-browser"); + const button = document.createElement('button'); + button.type = 'button'; + button.classList.add('open-compendium-browser'); button.innerHTML = ` - ${game.i18n.localize("DAGGERHEART.UI.Tooltip.compendiumBrowser")} + ${game.i18n.localize('DAGGERHEART.UI.Tooltip.compendiumBrowser')} `; - button.addEventListener("click", event => { + button.addEventListener('click', event => { ui.compendiumBrowser.open(menus[sectionId]); }); - + headerActions.append(button); } } diff --git a/module/config/itemBrowserConfig.mjs b/module/config/itemBrowserConfig.mjs index 046efd8b..e870172f 100644 --- a/module/config/itemBrowserConfig.mjs +++ b/module/config/itemBrowserConfig.mjs @@ -199,8 +199,7 @@ export const typeConfig = { key: 'system.itemFeatures', label: 'DAGGERHEART.GENERAL.features', choices: () => - Object.entries(CONFIG.DH.ITEM.weaponFeatures) - .map(([k, v]) => ({ value: k, label: v.label })), + Object.entries(CONFIG.DH.ITEM.weaponFeatures).map(([k, v]) => ({ value: k, label: v.label })), operator: 'contains3' } ] @@ -241,8 +240,7 @@ export const typeConfig = { key: 'system.itemFeatures', label: 'DAGGERHEART.GENERAL.features', choices: () => - Object.entries(CONFIG.DH.ITEM.armorFeatures) - .map(([k, v]) => ({ value: k, label: v.label })), + Object.entries(CONFIG.DH.ITEM.armorFeatures).map(([k, v]) => ({ value: k, label: v.label })), operator: 'contains3' } ] @@ -372,14 +370,30 @@ export const typeConfig = { label: 'DAGGERHEART.ITEMS.Subclass.spellcastingTrait' } ], + filters: [] + }, + beastforms: { + columns: [ + { + key: 'system.tier', + label: 'DAGGERHEART.GENERAL.Tiers.singular' + }, + { + key: 'system.mainTrait', + label: 'DAGGERHEART.GENERAL.Trait.single' + } + ], filters: [ { key: 'system.linkedClass.uuid', label: 'Class', - choices: (items) => { - const list = items.map(item => ({ value: item.system.linkedClass.uuid, label: item.system.linkedClass.name })); - return list.reduce((a,c) => { - if(!(a.find(i => i.value === c.value))) a.push(c); + choices: items => { + const list = items.map(item => ({ + value: item.system.linkedClass.uuid, + label: item.system.linkedClass.name + })); + return list.reduce((a, c) => { + if (!a.find(i => i.value === c.value)) a.push(c); return a; }, []); } @@ -417,7 +431,7 @@ export const compendiumConfig = { id: 'characters', keys: ['characters'], label: 'DAGGERHEART.UI.ItemBrowser.folders.characters', - type: ['character'], + type: ['character'] // listType: 'characters' }, adversaries: { @@ -431,7 +445,7 @@ export const compendiumConfig = { id: 'ancestries', keys: ['ancestries'], label: 'DAGGERHEART.UI.ItemBrowser.folders.ancestries', - type: ['ancestry'], + type: ['ancestry'] /* folders: { features: { id: 'features', @@ -516,7 +530,7 @@ export const compendiumConfig = { id: 'communities', keys: ['communities'], label: 'DAGGERHEART.UI.ItemBrowser.folders.communities', - type: ['community'], + type: ['community'] /* folders: { features: { id: 'features', @@ -537,7 +551,7 @@ export const compendiumConfig = { keys: ['beastforms'], label: 'DAGGERHEART.UI.ItemBrowser.folders.beastforms', type: ['beastform'], - listType: 'beastforms', + listType: 'beastforms' /* folders: { features: { id: 'features', diff --git a/module/config/settingsConfig.mjs b/module/config/settingsConfig.mjs index 0f148aeb..5232cbd9 100644 --- a/module/config/settingsConfig.mjs +++ b/module/config/settingsConfig.mjs @@ -31,20 +31,20 @@ export const gameSettings = { }; export const actionAutomationChoices = { - never: { - id: "never", - label: "Never" + never: { + id: 'never', + label: 'Never' }, showDialog: { - id: "showDialog", - label: "Show Dialog only" + id: 'showDialog', + label: 'Show Dialog only' }, // npcOnly: { - // id: "npcOnly", - // label: "Always for non-characters" + // id: "npcOnly", + // label: "Always for non-characters" // }, always: { - id: "always", - label: "Always" + id: 'always', + label: 'Always' } -} +}; diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 6e522ceb..93d34700 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -34,8 +34,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel this.extraSchemas.forEach(s => { let clsField = this.getActionField(s); - if (clsField) - schemaFields[s] = new clsField(); + if (clsField) schemaFields[s] = new clsField(); }); return schemaFields; @@ -43,7 +42,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel /** * Create a Map containing each Action step based on fields define in schema. Ordered by Fields order property. - * + * * Each step can be called individually as long as needed config is provided. * Ex: .workflow.get("damage").execute(config) * @returns {Map} @@ -53,8 +52,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel this.constructor.extraSchemas.forEach(s => { let clsField = this.constructor.getActionField(s); if (clsField?.execute) { - workflow.set(s, { order: clsField.order, execute: clsField.execute.bind(this) } ); - if( s === "damage" ) workflow.set("applyDamage", { order: 75, execute: clsField.applyDamage.bind(this) } ); + workflow.set(s, { order: clsField.order, execute: clsField.execute.bind(this) }); + if (s === 'damage') + workflow.set('applyDamage', { order: 75, execute: clsField.applyDamage.bind(this) }); } }); return new Map([...workflow.entries()].sort(([aKey, aValue], [bKey, bValue]) => aValue.order - bValue.order)); @@ -64,9 +64,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel * Getter returning the workflow property or creating it the first time the property is called */ get workflow() { - if ( this.hasOwnProperty("_workflow") ) return this._workflow; + if (this.hasOwnProperty('_workflow')) return this._workflow; const workflow = Object.freeze(this.defineWorkflow()); - Object.defineProperty(this, "_workflow", {value: workflow, writable: false}); + Object.defineProperty(this, '_workflow', { value: workflow, writable: false }); return workflow; } @@ -117,7 +117,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel /** * Prepare base data based on Action Type & Parent Type - * @param {object} parent + * @param {object} parent * @returns {object} */ static getSourceConfig(parent) { @@ -167,9 +167,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel * @param {object} config Config object usually created from prepareConfig method */ async executeWorkflow(config) { - for(const [key, part] of this.workflow) { + for (const [key, part] of this.workflow) { if (Hooks.call(`${CONFIG.DH.id}.pre${key.capitalize()}Action`, this, config) === false) return; - if(await part.execute(config) === false) return; + if ((await part.execute(config)) === false) return; if (Hooks.call(`${CONFIG.DH.id}.post${key.capitalize()}Action`, this, config) === false) return; } } @@ -185,7 +185,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel if (this.chatDisplay) await this.toChat(); let config = this.prepareConfig(event); - if(!config) return; + if (!config) return; if (Hooks.call(`${CONFIG.DH.id}.preUseAction`, this, config) === false) return; @@ -194,7 +194,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel config = await D20RollDialog.configure(null, config); if (!config) return; } - + // Execute the Action Worflow in order based of schema fields await this.executeWorkflow(config); @@ -206,7 +206,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel /** * Create the basic config common to every action type * @param {Event} event Event from the button used to trigger the Action - * @returns {object} + * @returns {object} */ prepareBaseConfig(event) { const config = { @@ -236,13 +236,12 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel /** * Create the config for that action used for its workflow * @param {Event} event Event from the button used to trigger the Action - * @returns {object} + * @returns {object} */ prepareConfig(event) { const config = this.prepareBaseConfig(event); - for(const clsField of Object.values(this.schema.fields)) { - if (clsField?.prepareConfig) - if(clsField.prepareConfig.call(this, config) === false) return false; + for (const clsField of Object.values(this.schema.fields)) { + if (clsField?.prepareConfig) if (clsField.prepareConfig.call(this, config) === false) return false; } return config; } @@ -260,11 +259,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel * Consume Action configured resources & uses. * That method is only used when we want those resources to be consumed outside of the use method workflow. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. - * @param {boolean} successCost + * @param {boolean} successCost */ async consume(config, successCost = false) { - await this.workflow.get("cost")?.execute(config, successCost); - await this.workflow.get("uses")?.execute(config, successCost); + await this.workflow.get('cost')?.execute(config, successCost); + await this.workflow.get('uses')?.execute(config, successCost); if (config.roll && !config.roll.success && successCost) { setTimeout(() => { @@ -291,13 +290,13 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel } get hasDamage() { - return this.damage?.parts?.length && this.type !== 'healing' + return this.damage?.parts?.length && this.type !== 'healing'; } get hasHealing() { - return this.damage?.parts?.length && this.type === 'healing' + return this.damage?.parts?.length && this.type === 'healing'; } - + get hasSave() { return !!this.save?.trait; } diff --git a/module/data/chat-message/_modules.mjs b/module/data/chat-message/_modules.mjs index 36c6ee9d..7e301906 100644 --- a/module/data/chat-message/_modules.mjs +++ b/module/data/chat-message/_modules.mjs @@ -1,9 +1,9 @@ -import DHAbilityUse from "./abilityUse.mjs"; -import DHActorRoll from "./actorRoll.mjs"; +import DHAbilityUse from './abilityUse.mjs'; +import DHActorRoll from './actorRoll.mjs'; export const config = { - abilityUse: DHAbilityUse, - adversaryRoll: DHActorRoll, - damageRoll: DHActorRoll, - dualityRoll: DHActorRoll + abilityUse: DHAbilityUse, + adversaryRoll: DHActorRoll, + damageRoll: DHActorRoll, + dualityRoll: DHActorRoll }; diff --git a/module/data/chat-message/actorRoll.mjs b/module/data/chat-message/actorRoll.mjs index 91f44edc..a2cb03f9 100644 --- a/module/data/chat-message/actorRoll.mjs +++ b/module/data/chat-message/actorRoll.mjs @@ -58,10 +58,8 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { const actionActor = this.actionActor, actionItem = this.actionItem; if (!this.source.action) return null; - if(actionItem) - return actionItem.system.actionsList?.find(a => a.id === this.source.action); - else if(actionActor?.system.attack?._id === this.source.action) - return actionActor.system.attack + if (actionItem) return actionItem.system.actionsList?.find(a => a.id === this.source.action); + else if (actionActor?.system.attack?._id === this.source.action) return actionActor.system.attack; return null; } diff --git a/module/data/fields/action/beastformField.mjs b/module/data/fields/action/beastformField.mjs index 62c735d0..0a1caea9 100644 --- a/module/data/fields/action/beastformField.mjs +++ b/module/data/fields/action/beastformField.mjs @@ -1,4 +1,4 @@ -import BeastformDialog from "../../../applications/dialogs/beastformDialog.mjs"; +import BeastformDialog from '../../../applications/dialogs/beastformDialog.mjs'; const fields = foundry.data.fields; @@ -49,14 +49,14 @@ export default class BeastformField extends fields.SchemaField { return await BeastformField.transform.call(this, selected, evolved, hybrid); } - + /** * Update Action Workflow config object. * Must be called within Action context. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. */ prepareConfig(config) { - if(this.actor.effects.find(x => x.type === 'beastform')) { + if (this.actor.effects.find(x => x.type === 'beastform')) { ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.beastformAlreadyApplied')); return false; } @@ -73,10 +73,10 @@ export default class BeastformField extends fields.SchemaField { /** * TODO by Harry - * @param {*} selectedForm - * @param {*} evolvedData - * @param {*} hybridData - * @returns + * @param {*} selectedForm + * @param {*} evolvedData + * @param {*} hybridData + * @returns */ static async transform(selectedForm, evolvedData, hybridData) { const formData = evolvedData?.form ? evolvedData.form.toObject() : selectedForm.toObject(); diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 816d9cbc..bab46d52 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -30,8 +30,13 @@ export default class DamageField extends fields.SchemaField { * @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example. */ static async execute(config, messageId = null, force = false) { - if(!this.hasDamage && !this.hasHealing) return; - if((this.hasRoll && DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id) && !force) return; + if (!this.hasDamage && !this.hasHealing) return; + if ( + this.hasRoll && + DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id && + !force + ) + return; let formulas = this.damage.parts.map(p => ({ formula: DamageField.getFormulaValue.call(this, p, config).getFormula(this.actor), @@ -51,9 +56,10 @@ export default class DamageField extends fields.SchemaField { }; delete damageConfig.evaluate; - if(DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) damageConfig.dialog.configure = false; + if (DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) + damageConfig.dialog.configure = false; if (config.hasSave) config.onSave = damageConfig.onSave = this.save.damageMod; - + damageConfig.source.message = config.message?._id ?? messageId; damageConfig.directDamage = !!damageConfig.source?.message; @@ -61,7 +67,7 @@ export default class DamageField extends fields.SchemaField { // await game.dice3d.waitFor3DAnimationByMessageID(damageConfig.source.message); const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig); - if(!damageResult) return false; + if (!damageResult) return false; config.damage = damageResult.damage; config.message ??= damageConfig.message; } @@ -70,19 +76,15 @@ export default class DamageField extends fields.SchemaField { * Apply Damage/Healing Action Worflow part. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. * @param {*[]} targets Arrays of targets to bypass pre-selected ones. - * @param {boolean} force If the method should be executed outside of Action workflow, for ChatMessage button for example. + * @param {boolean} force If the method should be executed outside of Action workflow, for ChatMessage button for example. */ static async applyDamage(config, targets = null, force = false) { targets ??= config.targets.filter(target => target.hit); - if(!config.damage || !targets?.length || (!DamageField.getApplyAutomation() && !force)) return; + if (!config.damage || !targets?.length || (!DamageField.getApplyAutomation() && !force)) return; for (let target of targets) { const actor = fromUuidSync(target.actorId); - if(!actor) continue; - if ( - !config.hasHealing && - config.onSave && - target.saved?.success === true - ) { + if (!actor) continue; + if (!config.hasHealing && config.onSave && target.saved?.success === true) { const mod = CONFIG.DH.ACTIONS.damageOnSave[config.onSave]?.mod ?? 1; Object.entries(config.damage).forEach(([k, v]) => { v.total = 0; @@ -97,17 +99,17 @@ export default class DamageField extends fields.SchemaField { else actor.takeDamage(config.damage, config.isDirect); } } - + /** * Return value or valueAlt from damage part * Must be called within Action context or similar. - * @param {object} part Damage Part + * @param {object} part Damage Part * @param {object} data Action getRollData * @returns Formula value object */ static getFormulaValue(part, data) { let formulaValue = part.value; - + if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt; const isAdversary = this.actor.type === 'adversary'; @@ -123,8 +125,8 @@ export default class DamageField extends fields.SchemaField { * Prepare formulas for Damage Roll * Must be called within Action context or similar. * @param {object[]} formulas Array of formatted formulas object - * @param {object} data Action getRollData - * @returns + * @param {object} data Action getRollData + * @returns */ static formatFormulas(formulas, data) { const formattedFormulas = []; @@ -145,16 +147,25 @@ export default class DamageField extends fields.SchemaField { * @returns {string} Id from settingsConfig.mjs actionAutomationChoices */ static getAutomation() { - return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.players) + return ( + (game.user.isGM && + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.gm) || + (!game.user.isGM && + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.players) + ); } - /** * Return the automation setting for applyDamage method for current user role * @returns {boolean} If applyDamage should be triggered automatically */ static getApplyAutomation() { - return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.players) + return ( + (game.user.isGM && + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.gm) || + (!game.user.isGM && + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.players) + ); } } diff --git a/module/data/fields/action/effectsField.mjs b/module/data/fields/action/effectsField.mjs index 3b8c5e43..0f205d72 100644 --- a/module/data/fields/action/effectsField.mjs +++ b/module/data/fields/action/effectsField.mjs @@ -1,4 +1,4 @@ -import { emitAsGM, GMUpdateEvent } from "../../../systemRegistration/socket.mjs"; +import { emitAsGM, GMUpdateEvent } from '../../../systemRegistration/socket.mjs'; const fields = foundry.data.fields; @@ -25,21 +25,16 @@ export default class EffectsField extends fields.ArrayField { * @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example. */ static async execute(config, targets = null, force = false) { - if(!config.hasEffect) return; + if (!config.hasEffect) return; let message = config.message ?? ui.chat.collection.get(config.parent?._id); - if(!message) { + if (!message) { const roll = new CONFIG.Dice.daggerheart.DHRoll(''); roll._evaluated = true; message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config); } - if(EffectsField.getAutomation() || force) { + if (EffectsField.getAutomation() || force) { targets ??= (message.system?.targets ?? config.targets).filter(t => !config.hasRoll || t.hit); - await emitAsGM( - GMUpdateEvent.UpdateEffect, - EffectsField.applyEffects.bind(this), - targets, - this.uuid - ); + await emitAsGM(GMUpdateEvent.UpdateEffect, EffectsField.applyEffects.bind(this), targets, this.uuid); // EffectsField.applyEffects.call(this, config.targets.filter(t => !config.hasRoll || t.hit)); } } @@ -53,8 +48,7 @@ export default class EffectsField extends fields.ArrayField { if (!this.effects?.length || !targets?.length) return; let effects = this.effects; targets.forEach(async token => { - if (this.hasSave && token.saved.success === true) - effects = this.effects.filter(e => e.onSave === true); + if (this.hasSave && token.saved.success === true) effects = this.effects.filter(e => e.onSave === true); if (!effects.length) return; effects.forEach(async e => { const actor = canvas.tokens.get(token.id)?.actor, @@ -96,6 +90,11 @@ export default class EffectsField extends fields.ArrayField { * @returns {boolean} If execute should be triggered automatically */ static getAutomation() { - return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.players) + return ( + (game.user.isGM && + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.gm) || + (!game.user.isGM && + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.players) + ); } } diff --git a/module/data/fields/action/macroField.mjs b/module/data/fields/action/macroField.mjs index 222feb2a..e37a2852 100644 --- a/module/data/fields/action/macroField.mjs +++ b/module/data/fields/action/macroField.mjs @@ -8,7 +8,7 @@ export default class MacroField extends fields.DocumentUUIDField { /** @inheritDoc */ constructor(context = {}) { - super({ type: "Macro" }, context); + super({ type: 'Macro' }, context); } /** diff --git a/module/data/fields/action/rangeField.mjs b/module/data/fields/action/rangeField.mjs index 1237e507..d0bceada 100644 --- a/module/data/fields/action/rangeField.mjs +++ b/module/data/fields/action/rangeField.mjs @@ -1,14 +1,13 @@ const fields = foundry.data.fields; export default class RangeField extends fields.StringField { - /** @inheritDoc */ constructor(context = {}) { const options = { choices: CONFIG.DH.GENERAL.range, required: false, blank: true, - label: "DAGGERHEART.GENERAL.range" + label: 'DAGGERHEART.GENERAL.range' }; super(options, context); } diff --git a/module/data/fields/action/rollField.mjs b/module/data/fields/action/rollField.mjs index cfcfb56b..98a1d5ed 100644 --- a/module/data/fields/action/rollField.mjs +++ b/module/data/fields/action/rollField.mjs @@ -5,7 +5,12 @@ export class DHActionRollData extends foundry.abstract.DataModel { static defineSchema() { return { type: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.GENERAL.rollTypes }), - trait: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.ACTOR.abilities, label: "DAGGERHEART.GENERAL.Trait.single" }), + trait: new fields.StringField({ + nullable: true, + initial: null, + choices: CONFIG.DH.ACTOR.abilities, + label: 'DAGGERHEART.GENERAL.Trait.single' + }), difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }), bonus: new fields.NumberField({ nullable: true, initial: null, integer: true }), advState: new fields.StringField({ @@ -86,14 +91,14 @@ export class DHActionRollData extends foundry.abstract.DataModel { } get rollTrait() { - if(this.parent?.actor?.type !== "character") return null; + if (this.parent?.actor?.type !== 'character') return null; switch (this.type) { case CONFIG.DH.GENERAL.rollTypes.spellcast.id: return this.parent.actor?.system?.spellcastModifierTrait?.key ?? 'agility'; case CONFIG.DH.GENERAL.rollTypes.attack.id: case CONFIG.DH.GENERAL.rollTypes.trait.id: return this.useDefault || !this.trait - ? this.parent.item.system.attack?.roll?.trait ?? 'agility' + ? (this.parent.item.system.attack?.roll?.trait ?? 'agility') : this.trait; default: return null; @@ -118,21 +123,21 @@ export default class RollField extends fields.EmbeddedDataField { * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. */ static async execute(config) { - if(!config.hasRoll) return; + if (!config.hasRoll) return; config = await this.actor.diceRoll(config); - if(!config) return false; + if (!config) return false; } - + /** * Update Action Workflow config object. * Must be called within Action context. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. */ prepareConfig(config) { - if(!config.hasRoll) return; + if (!config.hasRoll) return; config.dialog.configure = RollField.getAutomation() ? !config.dialog.configure : config.dialog.configure; - + const roll = { baseModifiers: this.roll.getModifier(), label: 'Attack', @@ -152,6 +157,11 @@ export default class RollField extends fields.EmbeddedDataField { * @returns {boolean} If execute should be triggered automatically */ static getAutomation() { - return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.players) + return ( + (game.user.isGM && + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.gm) || + (!game.user.isGM && + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.players) + ); } } diff --git a/module/data/fields/action/saveField.mjs b/module/data/fields/action/saveField.mjs index 0003a4d5..b7021135 100644 --- a/module/data/fields/action/saveField.mjs +++ b/module/data/fields/action/saveField.mjs @@ -1,4 +1,4 @@ -import { abilities } from "../../../config/actorConfig.mjs"; +import { abilities } from '../../../config/actorConfig.mjs'; const fields = foundry.data.fields; @@ -33,15 +33,15 @@ export default class SaveField extends fields.SchemaField { * @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example. */ static async execute(config, targets = null, force = false) { - if(!config.hasSave) return; + if (!config.hasSave) return; let message = config.message ?? ui.chat.collection.get(config.parent?._id); - - if(!message) { + + if (!message) { const roll = new CONFIG.Dice.daggerheart.DHRoll(''); roll._evaluated = true; message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config); } - if(SaveField.getAutomation() !== CONFIG.DH.SETTINGS.actionAutomationChoices.never.id || force) { + if (SaveField.getAutomation() !== CONFIG.DH.SETTINGS.actionAutomationChoices.never.id || force) { targets ??= config.targets.filter(t => !config.hasRoll || t.hit); await SaveField.rollAllSave.call(this, targets, config.event, message); } else return false; @@ -52,35 +52,35 @@ export default class SaveField extends fields.SchemaField { * Must be called within Action context. * @param {object[]} targets Array of formatted targets. * @param {Event} event Triggering event - * @param {ChatMessage} message The ChatMessage the triggered button comes from. + * @param {ChatMessage} message The ChatMessage the triggered button comes from. */ static async rollAllSave(targets, event, message) { - if(!targets) return; + if (!targets) return; return new Promise(resolve => { const aPromise = []; targets.forEach(target => { aPromise.push( new Promise(async subResolve => { const actor = fromUuidSync(target.actorId); - if(actor) { - const rollSave = game.user === actor.owner ? - SaveField.rollSave.call(this, actor, event) - : actor.owner - .query('reactionRoll', { - actionId: this.uuid, - actorId: actor.uuid, - event, - message - }); + if (actor) { + const rollSave = + game.user === actor.owner + ? SaveField.rollSave.call(this, actor, event) + : actor.owner.query('reactionRoll', { + actionId: this.uuid, + actorId: actor.uuid, + event, + message + }); const result = await rollSave; await SaveField.updateSaveMessage.call(this, result, message, target.id); subResolve(); } else subResolve(); }) - ) + ); }); Promise.all(aPromise).then(result => resolve()); - }) + }); } /** @@ -93,10 +93,10 @@ export default class SaveField extends fields.SchemaField { static async rollSave(actor, event) { if (!actor) return; const title = actor.isNPC - ? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll') - : game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { - ability: game.i18n.localize(abilities[this.save.trait]?.label) - }), + ? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll') + : game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { + ability: game.i18n.localize(abilities[this.save.trait]?.label) + }), rollConfig = { event, title, @@ -109,7 +109,8 @@ export default class SaveField extends fields.SchemaField { hasRoll: true, data: actor.getRollData() }; - if(SaveField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) rollConfig.dialog = { configure: false }; + if (SaveField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) + rollConfig.dialog = { configure: false }; return actor.diceRoll(rollConfig); } @@ -117,31 +118,32 @@ export default class SaveField extends fields.SchemaField { * Update a Roll ChatMessage for a token according to his Reaction Roll result. * @param {object} result Result from the Reaction Roll * @param {object} message ChatMessage to update - * @param {string} targetId Token ID + * @param {string} targetId Token ID */ static async updateSaveMessage(result, message, targetId) { if (!result) return; - const updateMsg = async function(message, targetId, result) { + const updateMsg = async function (message, targetId, result) { // setTimeout(async () => { - const chatMessage = ui.chat.collection.get(message._id), - changes = { - flags: { - [game.system.id]: { - reactionRolls: { - [targetId]: - { - result: result.roll.total, - success: result.roll.success - } + const chatMessage = ui.chat.collection.get(message._id), + changes = { + flags: { + [game.system.id]: { + reactionRolls: { + [targetId]: { + result: result.roll.total, + success: result.roll.success } } } - }; - await chatMessage.update(changes); + } + }; + await chatMessage.update(changes); // }, 100); }; if (game.modules.get('dice-so-nice')?.active) - game.dice3d.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id).then(async () => await updateMsg(message, targetId, result)); + game.dice3d + .waitFor3DAnimationByMessageID(result.message.id ?? result.message._id) + .then(async () => await updateMsg(message, targetId, result)); else await updateMsg(message, targetId, result); } @@ -150,25 +152,29 @@ export default class SaveField extends fields.SchemaField { * @returns {string} Id from settingsConfig.mjs actionAutomationChoices */ static getAutomation() { - return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.players) + return ( + (game.user.isGM && + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.gm) || + (!game.user.isGM && + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.players) + ); } /** * Send a query to an Actor owner to roll a Reaction Roll then send back the result. - * @param {object} param0 - * @param {string} param0.actionId Action ID - * @param {string} param0.actorId Actor ID - * @param {Event} param0.event Triggering event - * @param {ChatMessage} param0.message Chat Message to update - * @returns + * @param {object} param0 + * @param {string} param0.actionId Action ID + * @param {string} param0.actorId Actor ID + * @param {Event} param0.event Triggering event + * @param {ChatMessage} param0.message Chat Message to update + * @returns */ static rollSaveQuery({ actionId, actorId, event, message }) { return new Promise(async (resolve, reject) => { const actor = await fromUuid(actorId), action = await fromUuid(actionId); if (!actor || !actor?.isOwner) reject(); - SaveField.rollSave.call(action, actor, event, message) - .then(result => resolve(result)); + SaveField.rollSave.call(action, actor, event, message).then(result => resolve(result)); }); } } diff --git a/module/data/fields/action/targetField.mjs b/module/data/fields/action/targetField.mjs index 4499dcc8..486a81f1 100644 --- a/module/data/fields/action/targetField.mjs +++ b/module/data/fields/action/targetField.mjs @@ -1,7 +1,6 @@ const fields = foundry.data.fields; export default class TargetField extends fields.SchemaField { - /** @inheritDoc */ constructor(options = {}, context = {}) { const targetFields = { @@ -21,7 +20,7 @@ export default class TargetField extends fields.SchemaField { * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. */ prepareConfig(config) { - if (!this.target?.type) return config.targets = []; + if (!this.target?.type) return (config.targets = []); config.hasTarget = true; let targets; // If the Action is configured as self-targeted, set targets as the owner. @@ -62,15 +61,11 @@ export default class TargetField extends fields.SchemaField { * @returns {boolean} If both actors respect the provided type. */ static isTargetFriendly(actor, target, type) { - const actorDisposition = actor.token - ? actor.token.disposition - : actor.prototypeToken.disposition, + const actorDisposition = actor.token ? actor.token.disposition : actor.prototypeToken.disposition, targetDisposition = target.document.disposition; return ( - (type === CONFIG.DH.GENERAL.targetTypes.friendly.id && - actorDisposition === targetDisposition) || - (type === CONFIG.DH.GENERAL.targetTypes.hostile.id && - actorDisposition + targetDisposition === 0) + (type === CONFIG.DH.GENERAL.targetTypes.friendly.id && actorDisposition === targetDisposition) || + (type === CONFIG.DH.GENERAL.targetTypes.hostile.id && actorDisposition + targetDisposition === 0) ); } diff --git a/module/data/fields/action/usesField.mjs b/module/data/fields/action/usesField.mjs index d180ddf8..d1f3ebff 100644 --- a/module/data/fields/action/usesField.mjs +++ b/module/data/fields/action/usesField.mjs @@ -7,7 +7,7 @@ export default class UsesField extends fields.SchemaField { * Action Workflow order */ static order = 160; - + /** @inheritDoc */ constructor(options = {}, context = {}) { const usesFields = { @@ -62,7 +62,7 @@ export default class UsesField extends fields.SchemaField { /** * Prepare Uses object for Action Workflow * Must be called within Action context. - * @param {object} uses + * @param {object} uses * @returns {object} */ static calcUses(uses) { @@ -77,7 +77,7 @@ export default class UsesField extends fields.SchemaField { /** * Check if the Action still get atleast one unspent uses. * Must be called within Action context. - * @param {*} uses + * @param {*} uses * @returns {boolean} */ static hasUses(uses) { diff --git a/module/data/settings/Appearance.mjs b/module/data/settings/Appearance.mjs index 4c6e01ea..dfdd17e2 100644 --- a/module/data/settings/Appearance.mjs +++ b/module/data/settings/Appearance.mjs @@ -1,26 +1,27 @@ export default class DhAppearance extends foundry.abstract.DataModel { - static LOCALIZATION_PREFIXES = ["DAGGERHEART.SETTINGS.Appearance"]; + static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Appearance']; static defineSchema() { const { StringField, ColorField, BooleanField, SchemaField } = foundry.data.fields; // helper to create dice style schema - const diceStyle = ({ fg, bg, outline, edge }) => new SchemaField({ - foreground: new ColorField({ required: true, initial: fg }), - background: new ColorField({ required: true, initial: bg }), - outline: new ColorField({ required: true, initial: outline }), - edge: new ColorField({ required: true, initial: edge }), - texture: new StringField({ initial: 'astralsea', required: true, blank: false }), - colorset: new StringField({ initial: 'inspired', required: true, blank: false }), - material: new StringField({ initial: 'metal', required: true, blank: false }), - system: new StringField({ initial: 'standard', required: true, blank: false }) - }); + const diceStyle = ({ fg, bg, outline, edge }) => + new SchemaField({ + foreground: new ColorField({ required: true, initial: fg }), + background: new ColorField({ required: true, initial: bg }), + outline: new ColorField({ required: true, initial: outline }), + edge: new ColorField({ required: true, initial: edge }), + texture: new StringField({ initial: 'astralsea', required: true, blank: false }), + colorset: new StringField({ initial: 'inspired', required: true, blank: false }), + material: new StringField({ initial: 'metal', required: true, blank: false }), + system: new StringField({ initial: 'standard', required: true, blank: false }) + }); return { displayFear: new StringField({ required: true, choices: CONFIG.DH.GENERAL.fearDisplay, - initial: CONFIG.DH.GENERAL.fearDisplay.token.value, + initial: CONFIG.DH.GENERAL.fearDisplay.token.value }), diceSoNice: new SchemaField({ hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }), @@ -39,7 +40,7 @@ export default class DhAppearance extends foundry.abstract.DataModel { target: new BooleanField() }), hideAttribution: new BooleanField(), - showGenericStatusEffects: new BooleanField({ initial: true }), + showGenericStatusEffects: new BooleanField({ initial: true }) }; } } diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs index da71e899..beefac0b 100644 --- a/module/data/settings/Automation.mjs +++ b/module/data/settings/Automation.mjs @@ -97,13 +97,13 @@ export default class DhAutomation extends foundry.abstract.DataModel { damage: new fields.SchemaField({ gm: new fields.StringField({ required: true, - initial: "never", + initial: 'never', choices: CONFIG.DH.SETTINGS.actionAutomationChoices, label: 'DAGGERHEART.GENERAL.gm' }), players: new fields.StringField({ required: true, - initial: "never", + initial: 'never', choices: CONFIG.DH.SETTINGS.actionAutomationChoices, label: 'DAGGERHEART.GENERAL.player.plurial' }) @@ -111,13 +111,13 @@ export default class DhAutomation extends foundry.abstract.DataModel { save: new fields.SchemaField({ gm: new fields.StringField({ required: true, - initial: "never", + initial: 'never', choices: CONFIG.DH.SETTINGS.actionAutomationChoices, label: 'DAGGERHEART.GENERAL.gm' }), players: new fields.StringField({ required: true, - initial: "never", + initial: 'never', choices: CONFIG.DH.SETTINGS.actionAutomationChoices, label: 'DAGGERHEART.GENERAL.player.plurial' }) diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 4d293d9d..534867f8 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -47,8 +47,7 @@ export default class DamageRoll extends DHRoll { ); } await super.buildPost(roll, config, message); - if (config.source?.message) - chatMessage.update({ 'system.damage': config.damage }); + if (config.source?.message) chatMessage.update({ 'system.damage': config.damage }); } static unifyDamageRoll(rolls) { diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 0dcdd316..3865710a 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -28,7 +28,7 @@ export default class DHRoll extends Roll { static async buildConfigure(config = {}, message = {}) { config.hooks = [...this.getHooks(), '']; config.dialog ??= {}; - + for (const hook of config.hooks) { if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null; } @@ -46,7 +46,10 @@ export default class DHRoll extends Roll { } for (const hook of config.hooks) { - if (Hooks.call(`${CONFIG.DH.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false) return []; + if ( + Hooks.call(`${CONFIG.DH.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false + ) + return []; } return roll; } @@ -91,7 +94,7 @@ export default class DHRoll extends Roll { system: config, rolls: [roll] }; - + config.selectedRollMode ??= game.settings.get('core', 'rollMode'); if (roll._evaluated) { diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 93ac231e..8fedc368 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -153,10 +153,13 @@ export default class DualityRoll extends D20Roll { applyBaseBonus() { const modifiers = super.applyBaseBonus(); - + if (this.options.roll.trait && this.data.traits?.[this.options.roll.trait]) modifiers.unshift({ - label: this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id ? "DAGGERHEART.CONFIG.RollTypes.spellcast.name" : `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`, + label: + this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id + ? 'DAGGERHEART.CONFIG.RollTypes.spellcast.name' + : `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`, value: this.data.traits[this.options.roll.trait].value }); diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index 1a619a9c..d7476395 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -1,4 +1,4 @@ -import { emitAsGM, GMUpdateEvent } from "../systemRegistration/socket.mjs"; +import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs'; export default class DhpChatMessage extends foundry.documents.ChatMessage { targetHook = null; @@ -104,11 +104,11 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { }); if (itemDesc && autoExpandRoll.desc) itemDesc.setAttribute('open', ''); } - - if(!this.isAuthor && !this.speakerActor?.isOwner) { - const applyButtons = html.querySelector(".apply-buttons"); + + if (!this.isAuthor && !this.speakerActor?.isOwner) { + const applyButtons = html.querySelector('.apply-buttons'); applyButtons?.remove(); - const buttons = html.querySelectorAll(".ability-card-footer > .ability-use-button"); + const buttons = html.querySelectorAll('.ability-card-footer > .ability-use-button'); buttons.forEach(b => b.remove()); } } @@ -125,7 +125,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { html.querySelectorAll('.target-save').forEach(element => element.addEventListener('click', this.onRollSave.bind(this)) ); - + html.querySelectorAll('.roll-all-save-button').forEach(element => element.addEventListener('click', this.onRollAllSave.bind(this)) ); @@ -149,7 +149,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { event.stopPropagation(); const config = foundry.utils.deepClone(this.system); config.event = event; - this.system.action?.workflow.get("damage")?.execute(config, this._id, true); + this.system.action?.workflow.get('damage')?.execute(config, this._id, true); } async onApplyDamage(event) { @@ -171,9 +171,9 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { if (targets.length === 0) return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm')); - + this.consumeOnSuccess(); - this.system.action?.workflow.get("applyDamage")?.execute(config, targets, true); + this.system.action?.workflow.get('applyDamage')?.execute(config, targets, true); } async onRollSave(event) { @@ -187,7 +187,12 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { game.system.api.fields.ActionFields.SaveField.rollSave.call(action, token.actor, event).then(result => emitAsGM( GMUpdateEvent.UpdateSaveMessage, - game.system.api.fields.ActionFields.SaveField.updateSaveMessage.bind(action, result, this, token.id), + game.system.api.fields.ActionFields.SaveField.updateSaveMessage.bind( + action, + result, + this, + token.id + ), { action: action.uuid, message: this._id, @@ -205,7 +210,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { const targets = this.system.hitTargets, config = foundry.utils.deepClone(this.system); config.event = event; - this.system.action?.workflow.get("save")?.execute(config, targets, true); + this.system.action?.workflow.get('save')?.execute(config, targets, true); } async onApplyEffect(event) { @@ -216,16 +221,15 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { if (targets.length === 0) ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm')); this.consumeOnSuccess(); - this.system.action?.workflow.get("effects")?.execute(config, targets, true); + this.system.action?.workflow.get('effects')?.execute(config, targets, true); } filterPermTargets(targets) { - return targets.filter(t => fromUuidSync(t.actorId)?.canUserModify(game.user, "update")) + return targets.filter(t => fromUuidSync(t.actorId)?.canUserModify(game.user, 'update')); } consumeOnSuccess() { - if (!this.system.successConsumed && !this.targetSelection) - this.system.action?.consume(this.system, true); + if (!this.system.successConsumed && !this.targetSelection) this.system.action?.consume(this.system, true); } hoverTarget(event) { diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 8492f068..33daf52a 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -32,7 +32,7 @@ export default class DHItem extends foundry.documents.Item { /** @inheritDoc */ static migrateData(source) { - if(source.system?.attack && !source.system.attack.type) source.system.attack.type = "attack"; + if (source.system?.attack && !source.system.attack.type) source.system.attack.type = 'attack'; return super.migrateData(source); } diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 83220307..e6c1a2f0 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -76,10 +76,10 @@ export default class RegisterHandlebarsHelpers { /** * Pluralize helper that returns the appropriate localized string based on count - * @param {number} count - The number to check for plurality + * @param {number} count - The number to check for plurality * @param {string} baseKey - The base localization key (e.g., "DAGGERHEART.GENERAL.Target") * @returns {string} The localized singular or plural string - * + * * Usage: {{pluralize currentTargets.length "DAGGERHEART.GENERAL.Target"}} * Returns: "Target" if count is exactly 1, "Targets" if count is 0, 2+, or invalid */ diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs index fd569499..24047827 100644 --- a/module/systemRegistration/handlebars.mjs +++ b/module/systemRegistration/handlebars.mjs @@ -34,10 +34,7 @@ export const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/ui/chat/parts/damage-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/target-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/button-part.hbs', - 'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs', - - 'systems/daggerheart/templates/scene/dh-config.hbs', - + 'systems/daggerheart/templates/scene/dh-config.hbs' ]); }; diff --git a/module/systemRegistration/socket.mjs b/module/systemRegistration/socket.mjs index f3f9629b..f75c7b36 100644 --- a/module/systemRegistration/socket.mjs +++ b/module/systemRegistration/socket.mjs @@ -38,8 +38,7 @@ export const registerSocketHooks = () => { const document = data.uuid ? await fromUuid(data.uuid) : null; switch (data.action) { case GMUpdateEvent.UpdateDocument: - if (document && data.update) - await document.update(data.update); + if (document && data.update) await document.update(data.update); break; case GMUpdateEvent.UpdateEffect: if (document && data.update) diff --git a/pull_request_template.md b/pull_request_template.md index c1b8cbfa..263eb5b8 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -1,10 +1,11 @@ --- name: Pull Request about: Create a new pull request -title: "[PR] " +title: '[PR] ' labels: pr assignees: '' --- + Is this a community PR? Please go to preview tab and click [here](?expand=1&template=community_pull_request_template.md). If not, delete this line. ## Description diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index e4a2128c..65e825a9 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -603,7 +603,7 @@ display: flex; justify-content: space-between; align-items: center; - gap: .25rem .5rem; + gap: 0.25rem 0.5rem; flex-wrap: wrap; label { @@ -620,7 +620,7 @@ &.setting-two-values { display: grid; grid-template-columns: repeat(3, 1fr); - gap: .25rem .5rem; + gap: 0.25rem 0.5rem; .form-group { justify-content: end; diff --git a/styles/less/global/global.less b/styles/less/global/global.less index 4c06d42b..7e60dffc 100644 --- a/styles/less/global/global.less +++ b/styles/less/global/global.less @@ -29,11 +29,11 @@ overflow: hidden !important; div { - opacity: .5; + opacity: 0.5; } &:before { - font-family: "Font Awesome 6 Pro"; + font-family: 'Font Awesome 6 Pro'; content: '\f110'; position: absolute; height: 100%; @@ -41,11 +41,13 @@ display: flex; align-items: center; justify-content: center; - animation: spinner 1.5s linear infinite; + animation: spinner 1.5s linear infinite; } } @keyframes spinner { - to { transform: rotate(360deg); } + to { + transform: rotate(360deg); + } } -} \ No newline at end of file +} diff --git a/styles/less/global/sheet.less b/styles/less/global/sheet.less index 08e2668f..6f77a481 100755 --- a/styles/less/global/sheet.less +++ b/styles/less/global/sheet.less @@ -15,7 +15,7 @@ body.game:is(.performance-low, .noblur) { .themed.theme-dark.application.daggerheart.sheet.dh-style, &.theme-dark .application.daggerheart { background: @dark-blue; - }; + } } .application.sheet.dh-style { diff --git a/styles/less/sheets/actors/environment/potentialAdversaries.less b/styles/less/sheets/actors/environment/potentialAdversaries.less index 6fd7af65..274362a5 100644 --- a/styles/less/sheets/actors/environment/potentialAdversaries.less +++ b/styles/less/sheets/actors/environment/potentialAdversaries.less @@ -14,4 +14,4 @@ scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } -} \ No newline at end of file +} diff --git a/styles/less/ui/chat/refresh-message.less b/styles/less/ui/chat/refresh-message.less new file mode 100644 index 00000000..2fce189b --- /dev/null +++ b/styles/less/ui/chat/refresh-message.less @@ -0,0 +1,13 @@ +.daggerheart.chat.refresh-message { + header { + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; + + .subtitle { + font-size: 18; + font-weight: bold; + } + } +} diff --git a/styles/less/ui/index.less b/styles/less/ui/index.less index 49d1e009..8b0c53f6 100644 --- a/styles/less/ui/index.less +++ b/styles/less/ui/index.less @@ -2,6 +2,7 @@ @import './chat/action.less'; @import './chat/chat.less'; @import './chat/downtime.less'; +@import './chat/refresh-message.less'; @import './chat/sheet.less'; @import './combat-sidebar/combat-sidebar.less'; @@ -19,6 +20,8 @@ @import './resources/resources.less'; @import './settings/settings.less'; - @import './settings/homebrew-settings/domains.less'; @import './settings/homebrew-settings/types.less'; + +@import './sidebar/tabs.less'; +@import './sidebar/daggerheartMenu.less'; diff --git a/styles/less/ui/item-browser/item-browser.less b/styles/less/ui/item-browser/item-browser.less index 5df0482a..23844128 100644 --- a/styles/less/ui/item-browser/item-browser.less +++ b/styles/less/ui/item-browser/item-browser.less @@ -395,7 +395,7 @@ text-align: center; font-weight: bold; } - + .hint { flex: unset; } @@ -409,7 +409,8 @@ &.lite, &.no-folder { - .compendium-sidebar, .menu-path { + .compendium-sidebar, + .menu-path { display: none; } } diff --git a/styles/less/ui/settings/settings.less b/styles/less/ui/settings/settings.less index 67fe7718..788db394 100644 --- a/styles/less/ui/settings/settings.less +++ b/styles/less/ui/settings/settings.less @@ -4,7 +4,7 @@ fieldset { display: flex; flex-direction: column; - gap: .5rem; + gap: 0.5rem; &.two-columns { display: grid; @@ -127,4 +127,4 @@ text-align: center; } } -} \ No newline at end of file +} diff --git a/styles/less/ui/sidebar/daggerheartMenu.less b/styles/less/ui/sidebar/daggerheartMenu.less new file mode 100644 index 00000000..e975954c --- /dev/null +++ b/styles/less/ui/sidebar/daggerheartMenu.less @@ -0,0 +1,38 @@ +.tab.sidebar-tab.daggerheartMenu-sidebar { + padding: 0 4px; + + .menu-refresh-container { + display: flex; + flex-direction: column; + gap: 8px; + + .menu-refresh-inner-container { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + + .experience-chip { + display: flex; + align-items: center; + border-radius: 5px; + width: fit-content; + gap: 5px; + cursor: pointer; + padding: 5px; + background: light-dark(@dark-blue-10, @golden-10); + color: light-dark(@dark-blue, @golden); + + .label { + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 17px; + } + + &.selected { + background: light-dark(@dark-blue-40, @golden-40); + } + } + } + } +} diff --git a/styles/less/ui/sidebar/tabs.less b/styles/less/ui/sidebar/tabs.less new file mode 100644 index 00000000..073d3ef3 --- /dev/null +++ b/styles/less/ui/sidebar/tabs.less @@ -0,0 +1,8 @@ +#interface #ui-right #sidebar { + menu li button { + img { + width: 22px; + max-width: unset; + } + } +} diff --git a/templates/sidebar/daggerheart-menu/main.hbs b/templates/sidebar/daggerheart-menu/main.hbs new file mode 100644 index 00000000..6f31f165 --- /dev/null +++ b/templates/sidebar/daggerheart-menu/main.hbs @@ -0,0 +1,22 @@ +
    +
    + {{localize "Refresh Features"}} + + +
    +
    \ No newline at end of file diff --git a/templates/sidebar/tabs.hbs b/templates/sidebar/tabs.hbs new file mode 100644 index 00000000..9063ac5d --- /dev/null +++ b/templates/sidebar/tabs.hbs @@ -0,0 +1,18 @@ + diff --git a/templates/ui/chat/refreshMessage.hbs b/templates/ui/chat/refreshMessage.hbs new file mode 100644 index 00000000..a29baa20 --- /dev/null +++ b/templates/ui/chat/refreshMessage.hbs @@ -0,0 +1,6 @@ +
    +
    +
    {{localize "DAGGERHEART.UI.Chat.refreshMessage.header"}}
    +
    {{types}}
    +
    +
    \ No newline at end of file From 2176038ec6a9939f52eef2dfe5c1a7395d684dc6 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 7 Sep 2025 00:47:21 +0200 Subject: [PATCH 64/66] Added homebrew for armor and weapon fatures (#1166) Co-authored-by: Chris Ryan --- daggerheart.mjs | 2 + lang/en.json | 5 + .../settings/homebrewSettings.mjs | 65 +++-- .../applications/sheets-configs/_module.mjs | 3 +- .../sheets-configs/action-config.mjs | 2 +- .../sheets-configs/activeEffectConfig.mjs | 7 + .../setting-active-effect-config.mjs | 227 ++++++++++++++++++ ...eConfig.mjs => setting-feature-config.mjs} | 68 +++++- module/applications/sheets/items/armor.mjs | 2 +- module/applications/sheets/items/weapon.mjs | 2 +- module/config/itemConfig.mjs | 56 +++++ module/data/activeEffect/baseEffect.mjs | 20 ++ module/data/item/armor.mjs | 26 +- module/data/item/weapon.mjs | 18 +- module/data/settings/Homebrew.mjs | 30 ++- .../settings/downtime-config/effects.hbs | 15 ++ templates/settings/downtime-config/main.hbs | 10 +- .../homebrew-settings/itemFeatures.hbs | 35 +++ templates/sheets/activeEffect/changes.hbs | 36 +-- templates/sheets/activeEffect/settings.hbs | 8 +- 20 files changed, 560 insertions(+), 77 deletions(-) create mode 100644 module/applications/sheets-configs/setting-active-effect-config.mjs rename module/applications/sheets-configs/{downtimeConfig.mjs => setting-feature-config.mjs} (66%) create mode 100644 templates/settings/downtime-config/effects.hbs create mode 100644 templates/settings/homebrew-settings/itemFeatures.hbs diff --git a/daggerheart.mjs b/daggerheart.mjs index f5f5e303..d7aba401 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -1,5 +1,6 @@ import { SYSTEM } from './module/config/system.mjs'; import * as applications from './module/applications/_module.mjs'; +import * as data from './module/data/_module.mjs'; import * as models from './module/data/_module.mjs'; import * as documents from './module/documents/_module.mjs'; import * as dice from './module/dice/_module.mjs'; @@ -26,6 +27,7 @@ Hooks.once('init', () => { CONFIG.DH = SYSTEM; game.system.api = { applications, + data, models, documents, dice, diff --git a/lang/en.json b/lang/en.json index 8de962bd..85e941dc 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1917,6 +1917,7 @@ "roll": "Roll", "rules": "Rules", "types": "Types", + "itemFeatures": "Item Features", "questions": "Questions" }, "Tiers": { @@ -1934,6 +1935,7 @@ "amount": "Amount", "any": "Any", "armor": "Armor", + "armorFeatures": "Armor Features", "armors": "Armors", "armorScore": "Armor Score", "activeEffects": "Active Effects", @@ -2046,6 +2048,7 @@ "used": "Used", "uses": "Uses", "value": "Value", + "weaponFeatures": "Weapon Features", "weapons": "Weapons", "withThing": "With {thing}" }, @@ -2276,7 +2279,9 @@ }, "Homebrew": { "newDowntimeMove": "Downtime Move", + "newFeature": "New ItemFeature", "downtimeMoves": "Downtime Moves", + "itemFeatures": "Item Features", "nrChoices": "# Moves Per Rest", "resetMovesTitle": "Reset {type} Downtime Moves", "resetMovesText": "Are you sure you want to reset?", diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index c2ac4a89..e880f7ee 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -53,6 +53,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli settings: { template: 'systems/daggerheart/templates/settings/homebrew-settings/settings.hbs' }, domains: { template: 'systems/daggerheart/templates/settings/homebrew-settings/domains.hbs' }, types: { template: 'systems/daggerheart/templates/settings/homebrew-settings/types.hbs' }, + itemTypes: { template: 'systems/daggerheart/templates/settings/homebrew-settings/itemFeatures.hbs' }, downtime: { template: 'systems/daggerheart/templates/settings/homebrew-settings/downtime.hbs' }, footer: { template: 'systems/daggerheart/templates/settings/homebrew-settings/footer.hbs' } }; @@ -60,7 +61,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli /** @inheritdoc */ static TABS = { main: { - tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'types' }, { id: 'downtime' }], + tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'types' }, { id: 'itemFeatures' }, { id: 'downtime' }], initial: 'settings', labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } @@ -115,33 +116,53 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli } static async addItem(_, target) { - await this.settings.updateSource({ - [`restMoves.${target.dataset.type}.moves.${foundry.utils.randomID()}`]: { - name: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.newDowntimeMove'), - img: 'icons/magic/life/cross-worn-green.webp', - description: '', - actions: [] - } - }); + const { type } = target.dataset; + if (['shortRest', 'longRest'].includes(type)) { + await this.settings.updateSource({ + [`restMoves.${type}.moves.${foundry.utils.randomID()}`]: { + name: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.newDowntimeMove'), + img: 'icons/magic/life/cross-worn-green.webp', + description: '', + actions: [] + } + }); + } else if (['armorFeatures', 'weaponFeatures'].includes(type)) { + await this.settings.updateSource({ + [`itemFeatures.${type}.${foundry.utils.randomID()}`]: { + name: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.newFeature'), + img: 'icons/magic/life/cross-worn-green.webp', + description: '', + actions: [], + effects: [] + } + }); + } + this.render(); } static async editItem(_, target) { - const move = this.settings.restMoves[target.dataset.type].moves[target.dataset.id]; - const path = `restMoves.${target.dataset.type}.moves.${target.dataset.id}`; - const editedMove = await game.system.api.applications.sheetConfigs.DowntimeConfig.configure( - move, - path, - this.settings - ); - if (!editedMove) return; + const { type, id } = target.dataset; + const isDowntime = ['shortRest', 'longRest'].includes(type); + const path = isDowntime ? `restMoves.${type}.moves.${id}` : `itemFeatures.${type}.${id}`; + const featureBase = isDowntime ? this.settings.restMoves[type].moves[id] : this.settings.itemFeatures[type][id]; - await this.updateAction.bind(this)(editedMove, target.dataset.type, target.dataset.id); + const editedBase = await game.system.api.applications.sheetConfigs.SettingFeatureConfig.configure( + featureBase, + path, + this.settings, + { hasIcon: isDowntime, hasEffects: !isDowntime } + ); + if (!editedBase) return; + + await this.updateAction.bind(this)(editedBase, target.dataset.type, target.dataset.id); } async updateAction(data, type, id) { + const isDowntime = ['shortRest', 'longRest'].includes(type); + const path = isDowntime ? `restMoves.${type}.moves` : `itemFeatures.${type}`; await this.settings.updateSource({ - [`restMoves.${type}.moves.${id}`]: { + [`${path}.${id}`]: { actions: data.actions, name: data.name, icon: data.icon, @@ -149,12 +170,16 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli description: data.description } }); + this.render(); } static async removeItem(_, target) { + const { type, id } = target.dataset; + const isDowntime = ['shortRest', 'longRest'].includes(type); + const path = isDowntime ? `restMoves.${type}.moves` : `itemFeatures.${type}`; await this.settings.updateSource({ - [`restMoves.${target.dataset.type}.moves.-=${target.dataset.id}`]: null + [`${path}.-=${id}`]: null }); this.render(); } diff --git a/module/applications/sheets-configs/_module.mjs b/module/applications/sheets-configs/_module.mjs index ed062163..a8a625d0 100644 --- a/module/applications/sheets-configs/_module.mjs +++ b/module/applications/sheets-configs/_module.mjs @@ -2,7 +2,8 @@ export { default as ActionConfig } from './action-config.mjs'; export { default as CharacterSettings } from './character-settings.mjs'; export { default as AdversarySettings } from './adversary-settings.mjs'; export { default as CompanionSettings } from './companion-settings.mjs'; -export { default as DowntimeConfig } from './downtimeConfig.mjs'; +export { default as SettingActiveEffectConfig } from './setting-active-effect-config.mjs'; +export { default as SettingFeatureConfig } from './setting-feature-config.mjs'; export { default as EnvironmentSettings } from './environment-settings.mjs'; export { default as ActiveEffectConfig } from './activeEffectConfig.mjs'; export { default as DhTokenConfig } from './token-config.mjs'; diff --git a/module/applications/sheets-configs/action-config.mjs b/module/applications/sheets-configs/action-config.mjs index 96b6cc48..0f92baa1 100644 --- a/module/applications/sheets-configs/action-config.mjs +++ b/module/applications/sheets-configs/action-config.mjs @@ -138,7 +138,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { }; } - if (this.action.parent.metadata.isQuantifiable) { + if (this.action.parent.metadata?.isQuantifiable) { options.quantity = { label: 'DAGGERHEART.GENERAL.itemQuantity', group: 'Global' diff --git a/module/applications/sheets-configs/activeEffectConfig.mjs b/module/applications/sheets-configs/activeEffectConfig.mjs index 25f7d2b5..6a466c55 100644 --- a/module/applications/sheets-configs/activeEffectConfig.mjs +++ b/module/applications/sheets-configs/activeEffectConfig.mjs @@ -96,6 +96,13 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac }); } + async _prepareContext(options) { + const context = await super._prepareContext(options); + context.systemFields = context.document.system.schema.fields; + + return context; + } + async _preparePartContext(partId, context) { const partContext = await super._preparePartContext(partId, context); switch (partId) { diff --git a/module/applications/sheets-configs/setting-active-effect-config.mjs b/module/applications/sheets-configs/setting-active-effect-config.mjs new file mode 100644 index 00000000..9b57b47a --- /dev/null +++ b/module/applications/sheets-configs/setting-active-effect-config.mjs @@ -0,0 +1,227 @@ +import autocomplete from 'autocompleter'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class SettingActiveEffectConfig extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(effect) { + super({}); + + this.effect = foundry.utils.deepClone(effect); + const ignoredActorKeys = ['config', 'DhEnvironment']; + this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => { + if (!ignoredActorKeys.includes(key)) { + const model = game.system.api.models.actors[key]; + const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model); + const group = game.i18n.localize(model.metadata.label); + const choices = CONFIG.Token.documentClass + .getTrackedAttributeChoices(attributes, model) + .map(x => ({ ...x, group: group })); + acc.push(...choices); + } + return acc; + }, []); + } + + static DEFAULT_OPTIONS = { + classes: ['daggerheart', 'sheet', 'dh-style', 'active-effect-config'], + tag: 'form', + position: { + width: 560 + }, + form: { + submitOnChange: false, + closeOnSubmit: false, + handler: SettingActiveEffectConfig.#onSubmit + }, + actions: { + editImage: SettingActiveEffectConfig.#editImage, + addChange: SettingActiveEffectConfig.#addChange, + deleteChange: SettingActiveEffectConfig.#deleteChange + } + }; + + static PARTS = { + header: { template: 'systems/daggerheart/templates/sheets/activeEffect/header.hbs' }, + tabs: { template: 'templates/generic/tab-navigation.hbs' }, + details: { template: 'systems/daggerheart/templates/sheets/activeEffect/details.hbs', scrollable: [''] }, + settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' }, + changes: { + template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs', + scrollable: ['ol[data-changes]'] + }, + footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' } + }; + + static TABS = { + sheet: { + tabs: [ + { id: 'details', icon: 'fa-solid fa-book' }, + { id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' }, + { id: 'changes', icon: 'fa-solid fa-gears' } + ], + initial: 'details', + labelPrefix: 'EFFECT.TABS' + } + }; + + /**@inheritdoc */ + async _onFirstRender(context, options) { + await super._onFirstRender(context, options); + } + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.source = this.effect; + context.fields = game.system.api.documents.DhActiveEffect.schema.fields; + context.systemFields = game.system.api.data.activeEffects.BaseEffect._schema.fields; + + return context; + } + + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); + const changeChoices = this.changeChoices; + + htmlElement.querySelectorAll('.effect-change-input').forEach(element => { + autocomplete({ + input: element, + fetch: function (text, update) { + if (!text) { + update(changeChoices); + } else { + text = text.toLowerCase(); + var suggestions = changeChoices.filter(n => n.label.toLowerCase().includes(text)); + update(suggestions); + } + }, + render: function (item, search) { + const label = game.i18n.localize(item.label); + const matchIndex = label.toLowerCase().indexOf(search); + + const beforeText = label.slice(0, matchIndex); + const matchText = label.slice(matchIndex, matchIndex + search.length); + const after = label.slice(matchIndex + search.length, label.length); + + const element = document.createElement('li'); + element.innerHTML = `${beforeText}${matchText ? `${matchText}` : ''}${after}`; + if (item.hint) { + element.dataset.tooltip = game.i18n.localize(item.hint); + } + + return element; + }, + renderGroup: function (label) { + const itemElement = document.createElement('div'); + itemElement.textContent = game.i18n.localize(label); + return itemElement; + }, + onSelect: function (item) { + element.value = `system.${item.value}`; + }, + click: e => e.fetch(), + customize: function (_input, _inputRect, container) { + container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ; + }, + minLength: 0 + }); + }); + } + + async _preparePartContext(partId, context) { + if (partId in context.tabs) context.tab = context.tabs[partId]; + switch (partId) { + case 'details': + context.isActorEffect = false; + context.isItemEffect = true; + const useGeneric = game.settings.get( + CONFIG.DH.id, + CONFIG.DH.SETTINGS.gameSettings.appearance + ).showGenericStatusEffects; + if (!useGeneric) { + context.statuses = Object.values(CONFIG.DH.GENERAL.conditions).map(status => ({ + value: status.id, + label: game.i18n.localize(status.name) + })); + } + break; + case 'changes': + context.modes = Object.entries(CONST.ACTIVE_EFFECT_MODES).reduce((modes, [key, value]) => { + modes[value] = game.i18n.localize(`EFFECT.MODE_${key}`); + return modes; + }, {}); + + context.priorities = ActiveEffectConfig.DEFAULT_PRIORITIES; + break; + } + + return context; + } + + static async #onSubmit(event, form, formData) { + this.data = foundry.utils.expandObject(formData.object); + this.close(); + } + + /** + * Edit a Document image. + * @this {DocumentSheetV2} + * @type {ApplicationClickAction} + */ + static async #editImage(_event, target) { + if (target.nodeName !== 'IMG') { + throw new Error('The editImage action is available only for IMG elements.'); + } + + const attr = target.dataset.edit; + const current = foundry.utils.getProperty(this.effect, attr); + const fp = new FilePicker.implementation({ + current, + type: 'image', + callback: path => (target.src = path), + position: { + top: this.position.top + 40, + left: this.position.left + 10 + } + }); + + await fp.browse(); + } + + /** + * Add a new change to the effect's changes array. + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #addChange() { + const submitData = foundry.utils.expandObject(new FormDataExtended(this.form).object); + const changes = Object.values(submitData.changes ?? {}); + changes.push({}); + + this.effect.changes = changes; + this.render(); + } + + /** + * Delete a change from the effect's changes array. + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #deleteChange(event) { + const submitData = foundry.utils.expandObject(new FormDataExtended(this.form).object); + const changes = Object.values(submitData.changes); + const row = event.target.closest('li'); + const index = Number(row.dataset.index) || 0; + changes.splice(index, 1); + + this.effect.changes = changes; + this.render(); + } + + static async configure(effect, options = {}) { + return new Promise(resolve => { + const app = new this(effect, options); + app.addEventListener('close', () => resolve(app.data), { once: true }); + app.render({ force: true }); + }); + } +} diff --git a/module/applications/sheets-configs/downtimeConfig.mjs b/module/applications/sheets-configs/setting-feature-config.mjs similarity index 66% rename from module/applications/sheets-configs/downtimeConfig.mjs rename to module/applications/sheets-configs/setting-feature-config.mjs index 80aab900..e775f93d 100644 --- a/module/applications/sheets-configs/downtimeConfig.mjs +++ b/module/applications/sheets-configs/setting-feature-config.mjs @@ -3,8 +3,8 @@ import DHActionConfig from './action-config.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; -export default class DowntimeConfig extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(move, movePath, settings, options) { +export default class SettingFeatureConfig extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(move, movePath, settings, optionalParts, options) { super(options); this.move = move; @@ -12,6 +12,10 @@ export default class DowntimeConfig extends HandlebarsApplicationMixin(Applicati this.movePath = movePath; this.actionsPath = `${movePath}.actions`; this.settings = settings; + + const { hasIcon, hasEffects } = optionalParts; + this.hasIcon = hasIcon; + this.hasEffects = hasEffects; } get title() { @@ -30,6 +34,7 @@ export default class DowntimeConfig extends HandlebarsApplicationMixin(Applicati addItem: this.addItem, editItem: this.editItem, removeItem: this.removeItem, + addEffect: this.addEffect, resetMoves: this.resetMoves, saveForm: this.saveForm }, @@ -41,13 +46,14 @@ export default class DowntimeConfig extends HandlebarsApplicationMixin(Applicati tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, main: { template: 'systems/daggerheart/templates/settings/downtime-config/main.hbs' }, actions: { template: 'systems/daggerheart/templates/settings/downtime-config/actions.hbs' }, + effects: { template: 'systems/daggerheart/templates/settings/downtime-config/effects.hbs' }, footer: { template: 'systems/daggerheart/templates/settings/downtime-config/footer.hbs' } }; /** @inheritdoc */ static TABS = { primary: { - tabs: [{ id: 'main' }, { id: 'actions' }], + tabs: [{ id: 'main' }, { id: 'actions' }, { id: 'effects' }], initial: 'main', labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } @@ -55,6 +61,9 @@ export default class DowntimeConfig extends HandlebarsApplicationMixin(Applicati async _prepareContext(_options) { const context = await super._prepareContext(_options); + context.tabs = this._filterTabs(context.tabs); + context.hasIcon = this.hasIcon; + context.hasEffects = this.hasEffects; context.move = this.move; context.move.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML( context.move.description @@ -130,13 +139,30 @@ export default class DowntimeConfig extends HandlebarsApplicationMixin(Applicati } static async editItem(_, target) { - const actionId = target.dataset.id; - const action = this.move.actions.get(actionId); - await new DHActionConfig(action, async updatedMove => { - await this.settings.updateSource({ [`${this.actionsPath}.${actionId}`]: updatedMove }); + const { type, id } = target.dataset; + if (type === 'effect') { + const effectIndex = this.move.effects.findIndex(x => x.id === id); + const effect = this.move.effects[effectIndex]; + const updatedEffect = + await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure(effect); + if (!updatedEffect) return; + + await this.settings.updateSource({ + [`${this.movePath}.effects`]: this.move.effects.reduce((acc, effect, index) => { + acc.push(index === effectIndex ? { ...updatedEffect, id: effect.id } : effect); + return acc; + }, []) + }); this.move = foundry.utils.getProperty(this.settings, this.movePath); this.render(); - }).render(true); + } else { + const action = this.move.actions.get(id); + await new DHActionConfig(action, async updatedMove => { + await this.settings.updateSource({ [`${this.actionsPath}.${id}`]: updatedMove }); + this.move = foundry.utils.getProperty(this.settings, this.movePath); + this.render(); + }).render(true); + } } static async removeItem(_, target) { @@ -145,16 +171,38 @@ export default class DowntimeConfig extends HandlebarsApplicationMixin(Applicati this.render(); } + static async addEffect(_, target) { + const currentEffects = foundry.utils.getProperty(this.settings, `${this.movePath}.effects`); + await this.settings.updateSource({ + [`${this.movePath}.effects`]: [ + ...currentEffects, + game.system.api.data.activeEffects.BaseEffect.getDefaultObject() + ] + }); + + this.move = foundry.utils.getProperty(this.settings, this.movePath); + this.render(); + } + static resetMoves() {} + _filterTabs(tabs) { + return this.hasEffects + ? tabs + : Object.keys(tabs).reduce((acc, key) => { + if (key !== 'effects') acc[key] = tabs[key]; + return acc; + }, {}); + } + /** @override */ _onClose(options = {}) { if (!options.submitted) this.move = null; } - static async configure(move, movePath, settings, options = {}) { + static async configure(move, movePath, settings, optionalParts, options = {}) { return new Promise(resolve => { - const app = new this(move, movePath, settings, options); + const app = new this(move, movePath, settings, optionalParts, options); app.addEventListener('close', () => resolve(app.move), { once: true }); app.render({ force: true }); }); diff --git a/module/applications/sheets/items/armor.mjs b/module/applications/sheets/items/armor.mjs index bdc482c3..2550b415 100644 --- a/module/applications/sheets/items/armor.mjs +++ b/module/applications/sheets/items/armor.mjs @@ -8,7 +8,7 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) { tagifyConfigs: [ { selector: '.features-input', - options: () => CONFIG.DH.ITEM.armorFeatures, + options: () => CONFIG.DH.ITEM.orderedArmorFeatures(), callback: ArmorSheet.#onFeatureSelect } ] diff --git a/module/applications/sheets/items/weapon.mjs b/module/applications/sheets/items/weapon.mjs index 2533287b..f5c7dddf 100644 --- a/module/applications/sheets/items/weapon.mjs +++ b/module/applications/sheets/items/weapon.mjs @@ -8,7 +8,7 @@ export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) { tagifyConfigs: [ { selector: '.features-input', - options: () => CONFIG.DH.ITEM.weaponFeatures, + options: () => CONFIG.DH.ITEM.orderedWeaponFeatures(), callback: WeaponSheet.#onFeatureSelect } ] diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs index 7e9f75ea..d815181b 100644 --- a/module/config/itemConfig.mjs +++ b/module/config/itemConfig.mjs @@ -452,6 +452,34 @@ export const armorFeatures = { } }; +export const allArmorFeatures = () => { + const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures + .armorFeatures; + return { + ...armorFeatures, + ...Object.keys(homebrewFeatures).reduce((acc, key) => { + const feature = homebrewFeatures[key]; + acc[key] = { ...feature, label: feature.name }; + return acc; + }, {}) + }; +}; + +export const orderedArmorFeatures = () => { + const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures + .armorFeatures; + const allFeatures = { ...armorFeatures, ...homebrewFeatures }; + const all = Object.keys(allFeatures).map(key => { + const feature = allFeatures[key]; + return { + ...feature, + id: key, + label: feature.label ?? feature.name + }; + }); + return Object.values(all).sort((a, b) => game.i18n.localize(a.label).localeCompare(game.i18n.localize(b.label))); +}; + export const weaponFeatures = { barrier: { label: 'DAGGERHEART.CONFIG.WeaponFeature.barrier.name', @@ -1383,6 +1411,34 @@ export const weaponFeatures = { } }; +export const allWeaponFeatures = () => { + const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures + .weaponFeatures; + return { + ...weaponFeatures, + ...Object.keys(homebrewFeatures).reduce((acc, key) => { + const feature = homebrewFeatures[key]; + acc[key] = { ...feature, label: feature.name }; + return acc; + }, {}) + }; +}; + +export const orderedWeaponFeatures = () => { + const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures + .weaponFeatures; + const allFeatures = { ...weaponFeatures, ...homebrewFeatures }; + const all = Object.keys(allFeatures).map(key => { + const feature = allFeatures[key]; + return { + ...feature, + id: key, + label: feature.label ?? feature.name + }; + }); + return Object.values(all).sort((a, b) => game.i18n.localize(a.label).localeCompare(game.i18n.localize(b.label))); +}; + export const featureTypes = { ancestry: { id: 'ancestry', diff --git a/module/data/activeEffect/baseEffect.mjs b/module/data/activeEffect/baseEffect.mjs index 0ac87de0..770e3462 100644 --- a/module/data/activeEffect/baseEffect.mjs +++ b/module/data/activeEffect/baseEffect.mjs @@ -30,4 +30,24 @@ export default class BaseEffect extends foundry.abstract.TypeDataModel { }) }; } + + static getDefaultObject() { + return { + name: 'New Effect', + id: foundry.utils.randomID(), + disabled: false, + img: 'icons/magic/life/heart-cross-blue.webp', + description: '', + statuses: [], + changes: [], + system: { + rangeDependence: { + enabled: false, + type: CONFIG.DH.GENERAL.rangeInclusion.withinRange.id, + target: CONFIG.DH.GENERAL.otherTargetTypes.hostile.id, + range: CONFIG.DH.GENERAL.range.melee.id + } + } + }; + } } diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 7f70d3f7..ca1ca004 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -1,5 +1,4 @@ import AttachableItem from './attachableItem.mjs'; -import { armorFeatures } from '../../config/itemConfig.mjs'; export default class DHArmor extends AttachableItem { /** @inheritDoc */ @@ -25,7 +24,7 @@ export default class DHArmor extends AttachableItem { new fields.SchemaField({ value: new fields.StringField({ required: true, - choices: CONFIG.DH.ITEM.armorFeatures, + choices: CONFIG.DH.ITEM.allArmorFeatures, blank: true }), effectIds: new fields.ArrayField(new fields.StringField({ required: true })), @@ -60,13 +59,14 @@ export default class DHArmor extends AttachableItem { const allowed = await super._preUpdate(changes, options, user); if (allowed === false) return false; + const changedArmorFeatures = changes.system?.armorFeatures ?? []; + const removedFeatures = this.armorFeatures.filter(x => changedArmorFeatures.every(y => y.value !== x.value)); if (changes.system?.armorFeatures) { - const removed = this.armorFeatures.filter(x => !changes.system.armorFeatures.includes(x)); - const added = changes.system.armorFeatures.filter(x => !this.armorFeatures.includes(x)); + const added = changedArmorFeatures.filter(x => this.armorFeatures.every(y => y.value !== x.value)); const effectIds = []; const actionIds = []; - for (var feature of removed) { + for (var feature of removedFeatures) { effectIds.push(...feature.effectIds); actionIds.push(...feature.actionIds); } @@ -76,8 +76,9 @@ export default class DHArmor extends AttachableItem { return acc; }, {}); + const allFeatures = CONFIG.DH.ITEM.allArmorFeatures(); for (const feature of added) { - const featureData = armorFeatures[feature.value]; + const featureData = allFeatures[feature.value]; if (featureData.effects?.length > 0) { const embeddedItems = await this.parent.createEmbeddedDocuments( 'ActiveEffect', @@ -91,7 +92,7 @@ export default class DHArmor extends AttachableItem { } const newActions = {}; - if (featureData.actions?.length > 0) { + if (featureData.actions?.length > 0 || featureData.actions?.size > 0) { for (let action of featureData.actions) { const embeddedEffects = await this.parent.createEmbeddedDocuments( 'ActiveEffect', @@ -110,10 +111,12 @@ export default class DHArmor extends AttachableItem { { ...cls.getSourceConfig(this), ...action, + type: action.type, _id: actionId, name: game.i18n.localize(action.name), description: game.i18n.localize(action.description), - effects: embeddedEffects.map(x => ({ _id: x.id })) + effects: embeddedEffects.map(x => ({ _id: x.id })), + systemPath: 'actions' }, { parent: this } ); @@ -126,6 +129,10 @@ export default class DHArmor extends AttachableItem { } } + _onUpdate(a, b, c) { + super._onUpdate(a, b, c); + } + /** * Generates a list of localized tags based on this item's type-specific properties. * @returns {string[]} An array of localized tag strings. @@ -145,7 +152,8 @@ export default class DHArmor extends AttachableItem { */ _getLabels() { const labels = []; - if(this.baseScore) labels.push(`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`) + if (this.baseScore) + labels.push(`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`); return labels; } diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 07a5c028..b2d937b5 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -39,7 +39,7 @@ export default class DHWeapon extends AttachableItem { new fields.SchemaField({ value: new fields.StringField({ required: true, - choices: CONFIG.DH.ITEM.weaponFeatures, + choices: CONFIG.DH.ITEM.allWeaponFeatures, blank: true }), effectIds: new fields.ArrayField(new fields.StringField({ required: true })), @@ -116,13 +116,14 @@ export default class DHWeapon extends AttachableItem { const allowed = await super._preUpdate(changes, options, user); if (allowed === false) return false; + const changedWeaponFeatures = changes.system?.weaponFeatures ?? []; + const removedFeatures = this.weaponFeatures.filter(x => changedWeaponFeatures.every(y => y.value !== x.value)); if (changes.system?.weaponFeatures) { - const removed = this.weaponFeatures.filter(x => !changes.system.weaponFeatures.includes(x)); - const added = changes.system.weaponFeatures.filter(x => !this.weaponFeatures.includes(x)); + const added = changedWeaponFeatures.filter(x => this.weaponFeatures.every(y => y.value !== x.value)); const removedEffectsUpdate = []; const removedActionsUpdate = []; - for (let weaponFeature of removed) { + for (let weaponFeature of removedFeatures) { removedEffectsUpdate.push(...weaponFeature.effectIds); removedActionsUpdate.push(...weaponFeature.actionIds); } @@ -133,8 +134,9 @@ export default class DHWeapon extends AttachableItem { return acc; }, {}); + const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures(); for (let weaponFeature of added) { - const featureData = CONFIG.DH.ITEM.weaponFeatures[weaponFeature.value]; + const featureData = allFeatures[weaponFeature.value]; if (featureData.effects?.length > 0) { const embeddedItems = await this.parent.createEmbeddedDocuments( 'ActiveEffect', @@ -148,7 +150,7 @@ export default class DHWeapon extends AttachableItem { } const newActions = {}; - if (featureData.actions?.length > 0) { + if (featureData.actions?.length > 0 || featureData.actions?.size > 0) { for (let action of featureData.actions) { const embeddedEffects = await this.parent.createEmbeddedDocuments( 'ActiveEffect', @@ -170,10 +172,12 @@ export default class DHWeapon extends AttachableItem { { ...cls.getSourceConfig(this), ...action, + type: action.type, _id: actionId, name: game.i18n.localize(action.name), description: game.i18n.localize(action.description), - effects: embeddedEffects.map(x => ({ _id: x.id })) + effects: embeddedEffects.map(x => ({ _id: x.id })), + systemPath: 'actions' }, { parent: this } ); diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index 0719b085..ca44a3ed 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -115,7 +115,35 @@ export default class DhHomebrew extends foundry.abstract.DataModel { label: new fields.StringField({ required: true, label: 'DAGGERHEART.GENERAL.label' }), description: new fields.StringField() }) - ) + ), + itemFeatures: new fields.SchemaField({ + weaponFeatures: new fields.TypedObjectField( + new fields.SchemaField({ + name: new fields.StringField({ required: true }), + img: new fields.FilePathField({ + initial: 'icons/magic/life/cross-worn-green.webp', + categories: ['IMAGE'], + base64: false + }), + description: new fields.HTMLField(), + actions: new ActionsField(), + effects: new fields.ArrayField(new fields.ObjectField()) + }) + ), + armorFeatures: new fields.TypedObjectField( + new fields.SchemaField({ + name: new fields.StringField({ required: true }), + img: new fields.FilePathField({ + initial: 'icons/magic/life/cross-worn-green.webp', + categories: ['IMAGE'], + base64: false + }), + description: new fields.HTMLField(), + actions: new ActionsField(), + effects: new fields.ArrayField(new fields.ObjectField()) + }) + ) + }) }; } } diff --git a/templates/settings/downtime-config/effects.hbs b/templates/settings/downtime-config/effects.hbs new file mode 100644 index 00000000..f09fdb05 --- /dev/null +++ b/templates/settings/downtime-config/effects.hbs @@ -0,0 +1,15 @@ +
    +
    + {{localize "DAGGERHEART.GENERAL.Effect.plural"}} + +
    + {{#each move.effects}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" id=this.id type="effect" }} + {{/each}} +
    +
    +
    \ No newline at end of file diff --git a/templates/settings/downtime-config/main.hbs b/templates/settings/downtime-config/main.hbs index f8a972c7..7f681368 100644 --- a/templates/settings/downtime-config/main.hbs +++ b/templates/settings/downtime-config/main.hbs @@ -3,11 +3,13 @@ data-tab='{{tabs.main.id}}' data-group='{{tabs.main.group}}' > -
    - {{localize "Icon"}} + {{#if hasIcon}} +
    + {{localize "Icon"}} - -
    + +
    + {{/if}}
    {{localize "Description"}} diff --git a/templates/settings/homebrew-settings/itemFeatures.hbs b/templates/settings/homebrew-settings/itemFeatures.hbs new file mode 100644 index 00000000..1f8595de --- /dev/null +++ b/templates/settings/homebrew-settings/itemFeatures.hbs @@ -0,0 +1,35 @@ +
    +
    +
    + + {{localize "DAGGERHEART.GENERAL.weaponFeatures"}} + + + + +
    + {{#each settingFields._source.itemFeatures.weaponFeatures as |feature id|}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="weaponFeatures" id=id }} + {{/each}} +
    +
    + +
    + + {{localize "DAGGERHEART.GENERAL.armorFeatures"}} + + + + +
    + {{#each settingFields._source.itemFeatures.armorFeatures as |feature id|}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="armorFeatures" id=id }} + {{/each}} +
    +
    +
    +
    \ No newline at end of file diff --git a/templates/sheets/activeEffect/changes.hbs b/templates/sheets/activeEffect/changes.hbs index 9cf137f0..75f49e4a 100644 --- a/templates/sheets/activeEffect/changes.hbs +++ b/templates/sheets/activeEffect/changes.hbs @@ -8,24 +8,24 @@
      {{#each source.changes as |change i|}} - {{#with ../fields.changes.element.fields as |changeFields|}} -
    1. -
      - -
      -
      - {{formInput changeFields.mode name=(concat "changes." i ".mode") value=change.mode choices=@root.modes}} -
      -
      - {{formInput changeFields.value name=(concat "changes." i ".value") value=change.value}} -
      -
      - {{formInput changeFields.priority name=(concat "changes." i ".priority") value=change.priority - placeholder=(lookup ../../priorities change.mode)}} -
      -
      -
    2. - {{/with}} + {{#with ../fields.changes.element.fields as |changeFields|}} +
    3. +
      + +
      +
      + {{formInput changeFields.mode name=(concat "changes." i ".mode") value=change.mode choices=@root.modes}} +
      +
      + {{formInput changeFields.value name=(concat "changes." i ".value") value=change.value}} +
      +
      + {{formInput changeFields.priority name=(concat "changes." i ".priority") value=change.priority + placeholder=(lookup ../../priorities change.mode)}} +
      +
      +
    4. + {{/with}} {{/each}}
    \ No newline at end of file diff --git a/templates/sheets/activeEffect/settings.hbs b/templates/sheets/activeEffect/settings.hbs index 33e1d1b9..cf98c786 100644 --- a/templates/sheets/activeEffect/settings.hbs +++ b/templates/sheets/activeEffect/settings.hbs @@ -2,10 +2,10 @@
    {{localize "DAGGERHEART.ACTIVEEFFECT.Config.rangeDependence.title"}} - {{formGroup document.system.schema.fields.rangeDependence.fields.enabled value=source.system.rangeDependence.enabled localize=true }} - {{formGroup document.system.schema.fields.rangeDependence.fields.type value=source.system.rangeDependence.type localize=true }} - {{formGroup document.system.schema.fields.rangeDependence.fields.target value=source.system.rangeDependence.target localize=true }} - {{formGroup document.system.schema.fields.rangeDependence.fields.range value=source.system.rangeDependence.range localize=true }} + {{formGroup systemFields.rangeDependence.fields.enabled value=source.system.rangeDependence.enabled localize=true }} + {{formGroup systemFields.rangeDependence.fields.type value=source.system.rangeDependence.type localize=true }} + {{formGroup systemFields.rangeDependence.fields.target value=source.system.rangeDependence.target localize=true }} + {{formGroup systemFields.rangeDependence.fields.range value=source.system.rangeDependence.range localize=true }}
    From 58f039ce969d5b2c63919b3ff5dca16d785b6c83 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Sun, 7 Sep 2025 09:27:46 +1000 Subject: [PATCH 65/66] Add extra features to the Temple Enricher and fix the mousewheel issues with the Template Manager (#1147) Co-authored-by: Chris Ryan --- module/documents/templateManager.mjs | 16 ++++--- module/enrichers/TemplateEnricher.mjs | 65 +++++++++++++++++++++------ 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/module/documents/templateManager.mjs b/module/documents/templateManager.mjs index c31b1baa..cf15c2e3 100644 --- a/module/documents/templateManager.mjs +++ b/module/documents/templateManager.mjs @@ -57,7 +57,10 @@ export default class DhTemplateManager { * @param {wheel Event} event */ #onMouseWheel(event) { - if (!event.shiftKey) return; + if (!this.#activePreview) { + return; + } + if (!event.shiftKey && !event.ctrlKey) return; event.stopPropagation(); event.preventDefault(); const { moveTime, object } = this.#activePreview; @@ -66,8 +69,10 @@ export default class DhTemplateManager { if (now - (moveTime || 0) <= 16) return; this.#activePreview.moveTime = now; + const multiplier = event.shiftKey ? 0.2 : 0.1; + object.document.updateSource({ - direction: object.document.direction + event.deltaY * 0.2 + direction: object.document.direction + event.deltaY * multiplier }); object.renderFlags.set({ refresh: true }); } @@ -77,12 +82,13 @@ export default class DhTemplateManager { * @param {contextmenu Event} event */ #cancelTemplate(event) { - const { mousemove, mousedown, contextmenu } = this.#activePreview.events; + const { mousemove, mousedown, contextmenu, wheel } = this.#activePreview.events; canvas.templates._onDragLeftCancel(event); canvas.stage.off('mousemove', mousemove); canvas.stage.off('mousedown', mousedown); canvas.app.view.removeEventListener('contextmenu', contextmenu); + canvas.app.view.removeEventListener('wheel', wheel); } /** @@ -91,9 +97,9 @@ export default class DhTemplateManager { */ #confirmTemplate(event) { event.stopPropagation(); + this.#cancelTemplate(event); canvas.scene.createEmbeddedDocuments('MeasuredTemplate', [this.#activePreview.document.toObject()]); - - this.#cancelTemplate(event); + this.#activePreview = undefined; } } diff --git a/module/enrichers/TemplateEnricher.mjs b/module/enrichers/TemplateEnricher.mjs index 35a3e231..15936b29 100644 --- a/module/enrichers/TemplateEnricher.mjs +++ b/module/enrichers/TemplateEnricher.mjs @@ -3,6 +3,8 @@ export default function DhTemplateEnricher(match, _options) { let type = null, range = null, + angle = CONFIG.MeasuredTemplate.defaults.angle, + direction = 0, inline = false; parts.forEach(part => { @@ -16,14 +18,24 @@ export default function DhTemplateEnricher(match, _options) { type = matchedType; break; case 'range': - const matchedRange = Object.values(CONFIG.DH.GENERAL.templateRanges).find( - x => x.id.toLowerCase() === split[1] || x.short === split[1] - ); - range = matchedRange?.id; + if (Number.isNaN(Number(split[1]))) { + const matchedRange = Object.values(CONFIG.DH.GENERAL.templateRanges).find( + x => x.id.toLowerCase() === split[1] || x.short === split[1] + ); + range = matchedRange?.id; + } else { + range = split[1]; + } break; case 'inline': inline = true; break; + case 'angle': + angle = split[1]; + break; + case 'direction': + direction = split[1]; + break; } } }); @@ -32,10 +44,32 @@ export default function DhTemplateEnricher(match, _options) { const label = game.i18n.localize(`DAGGERHEART.CONFIG.TemplateTypes.${type}`); + const rangeDisplay = Number.isNaN(Number(range)) ? game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.name`) : range; + + let angleDisplay = ''; + if (angle != CONFIG.MeasuredTemplate.defaults.angle) { + angleDisplay = 'angle:' + angle; + + } + let directionDisplay = ''; + if (direction != 0) { + directionDisplay = 'direction:' + direction; + } + + let extraDisplay = ''; + if (angleDisplay != '' && directionDisplay != '') { + extraDisplay = ' (' + angleDisplay + '|' + directionDisplay + ')'; + } else if (angleDisplay != '') { + extraDisplay = ' (' + angleDisplay + ')'; + } else if (directionDisplay != '') { + extraDisplay = ' (' + directionDisplay + ')'; + } + const templateElement = document.createElement('span'); templateElement.innerHTML = ` - `; @@ -45,21 +79,25 @@ export default function DhTemplateEnricher(match, _options) { export const renderMeasuredTemplate = async event => { const button = event.currentTarget, type = button.dataset.type, - range = button.dataset.range; + range = button.dataset.range, + angle = button.dataset.angle, + direction = button.dataset.direction; if (!type || !range || !game.canvas.scene) return; const usedType = type === 'inFront' ? 'cone' : type === 'emanation' ? 'circle' : type; - const angle = + const usedAngle = type === CONST.MEASURED_TEMPLATE_TYPES.CONE - ? CONFIG.MeasuredTemplate.defaults.angle + ? (angle ?? CONFIG.MeasuredTemplate.defaults.angle) : type === CONFIG.DH.GENERAL.templateTypes.INFRONT ? '180' : undefined; - const baseDistance = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement[ - range - ]; + let baseDistance = range; + if (Number.isNaN(Number(range))) { + baseDistance = + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement[range]; + } const distance = type === CONFIG.DH.GENERAL.templateTypes.EMANATION ? baseDistance + 2.5 : baseDistance; const { width, height } = game.canvas.scene.dimensions; @@ -69,7 +107,8 @@ export const renderMeasuredTemplate = async event => { t: usedType, distance: distance, width: type === CONST.MEASURED_TEMPLATE_TYPES.RAY ? 5 : undefined, - angle: angle + angle: usedAngle, + direction: direction }; CONFIG.ux.TemplateManager.createPreview(data); From a57d154d45cba43775025896f6beb656845cb7d3 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Tue, 9 Sep 2025 22:03:13 -0400 Subject: [PATCH 66/66] Add space between paragraphs (#1172) --- styles/less/global/elements.less | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index 65e825a9..5d6e97d1 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -210,7 +210,13 @@ } p { - margin: 0; + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } } ul {