Merge branch 'main' into release

This commit is contained in:
WBHarry 2026-04-03 23:09:50 +02:00
commit f6b3c297bd
20 changed files with 123 additions and 172 deletions

View file

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

View file

@ -113,7 +113,9 @@
},
"displayInChat": "Display in chat",
"deleteTriggerTitle": "Delete Trigger",
"deleteTriggerContent": "Are you sure you want to delete the {trigger} trigger?"
"deleteTriggerContent": "Are you sure you want to delete the {trigger} trigger?",
"advantageState": "Advantage State",
"damageOnSave": "Damage on Save"
},
"RollField": {
"diceRolling": {
@ -125,7 +127,7 @@
}
},
"Settings": {
"attackBonus": "Attack Bonus",
"attackModifier": "Attack Modifier",
"attackName": "Attack Name",
"criticalThreshold": "Critical Threshold",
"includeBase": { "label": "Include Item Damage" },

View file

@ -16,9 +16,11 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
...member.toObject(),
uuid: member.uuid,
id: member.id,
selected: false
selected: false,
owned: member.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)
}));
this.intiator = null;
this.initiator = null;
this.openForAllPlayers = true;
this.tabGroups.application = Object.keys(party.system.tagTeam.members).length
@ -80,6 +82,18 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
for (const element of htmlElement.querySelectorAll('.roll-type-select'))
element.addEventListener('change', this.updateRollType.bind(this));
htmlElement
.querySelector('.initiator-member-field')
?.addEventListener('input', this.updateInitiatorMemberField.bind(this));
htmlElement
.querySelector('.initiator-cost-field')
?.addEventListener('input', this.updateInitiatorCostField.bind(this));
htmlElement
.querySelector('.openforall-field')
?.addEventListener('change', this.updateOpenForAllField.bind(this));
}
_configureRenderParts(options) {
@ -135,9 +149,12 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
const selectedMembers = partContext.memberSelection.filter(x => x.selected);
partContext.allSelected = selectedMembers.length === 2;
partContext.canStartTagTeam = partContext.allSelected && this.initiator;
partContext.canStartTagTeam =
partContext.allSelected && this.initiator?.memberId && typeof this.initiator?.cost === 'number';
partContext.initiator = this.initiator;
partContext.initiatorOptions = selectedMembers.map(x => ({ value: x.id, label: x.name }));
partContext.initiatorOptions = selectedMembers
.filter(actor => actor.owned)
.map(x => ({ value: x.id, label: x.name }));
partContext.initiatorDisabled = !selectedMembers.length;
partContext.openForAllPlayers = this.openForAllPlayers;
@ -230,14 +247,15 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
}
static async updateData(event, _, formData) {
const { initiator, openForAllPlayers, ...partyData } = foundry.utils.expandObject(formData.object);
this.initiator = initiator;
this.openForAllPlayers = openForAllPlayers !== undefined ? openForAllPlayers : this.openForAllPlayers;
const partyData = foundry.utils.expandObject(formData.object);
this.updatePartyData(partyData, this.getUpdatingParts(event.target));
}
async updatePartyData(update, updatingParts, options = { render: true }) {
if (!game.users.activeGM)
return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.gmRequired'));
const gmUpdate = async update => {
await this.party.update(update);
this.render({ parts: updatingParts });
@ -374,6 +392,23 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
);
}
updateInitiatorMemberField(event) {
if (!this.initiator) this.initiator = {};
this.initiator.memberId = event.target.value;
this.render();
}
updateInitiatorCostField(event) {
if (!this.initiator) this.initiator = {};
this.initiator.cost = event.target.value ? Number.parseInt(event.target.value) : null;
this.render();
}
updateOpenForAllField(event) {
this.openForAllPlayers = event.target.checked;
this.render();
}
static async #removeRoll(_, button) {
this.updatePartyData(
{

View file

@ -21,7 +21,6 @@ export default class DHActionConfig extends DHActionBaseConfig {
static async addEffect(_event) {
if (!this.action.effects) return;
const effectData = this._addEffectData.bind(this)();
const data = this.action.toObject();
const created = await this.action.item.createEmbeddedDocuments('ActiveEffect', [

View file

@ -18,7 +18,7 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur
static getRangeLabels(distanceValue, settings) {
let result = { distance: distanceValue, units: '' };
if (!settings.enabled) return result;
if (!settings.enabled || !canvas.scene) return result;
const sceneRangeMeasurement = canvas.scene.flags.daggerheart?.rangeMeasurement;
const { disable, custom } = CONFIG.DH.GENERAL.sceneRangeMeasurementSetting;

View file

@ -232,7 +232,7 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
return {
multiplier: new fields.StringField({
choices: CONFIG.DH.GENERAL.multiplierTypes,
initial: 'prof',
initial: 'flat',
label: 'DAGGERHEART.ACTIONS.Config.damage.multiplier',
nullable: false,
required: true
@ -244,7 +244,7 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
}),
dice: new fields.StringField({
choices: CONFIG.DH.GENERAL.diceTypes,
initial: 'd6',
initial: CONFIG.DH.GENERAL.diceTypes.d6,
label: 'DAGGERHEART.GENERAL.Dice.single',
nullable: false,
required: true

View file

@ -95,7 +95,7 @@ export default class DhTokenManager {
: this.#actor;
const tokenData = await actor.getTokenDocument();
const result = await canvas.scene.createEmbeddedDocuments('Token', [
{ ...tokenData, x: this.#activePreview.document.x, y: this.#activePreview.document.y }
{ ...tokenData.toObject(), x: this.#activePreview.document.x, y: this.#activePreview.document.y }
]);
this.#activePreview = undefined;

View file

@ -63,14 +63,12 @@ export const renderMeasuredTemplate = async event => {
const usedAngle =
type === CONE ? (angle ?? CONFIG.MeasuredTemplate.defaults.angle) : type === INFRONT ? '180' : undefined;
let baseDistance = range;
if (Number.isNaN(Number(range))) {
baseDistance = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement[
range
];
}
let baseDistance = getTemplateDistance(range);
const dimensionConstant = game.scenes.active.grid.size / game.scenes.active.grid.distance;
const { grid, distance } = CONFIG.Scene.documentClass.schema.fields.grid.fields;
const sceneGridSize = canvas.scene?.grid.size ?? grid.size.initial;
const sceneGridDistance = canvas.scene?.grid.distance ?? distance.getInitialValue();
const dimensionConstant = sceneGridSize / sceneGridDistance;
baseDistance *= dimensionConstant;
@ -115,3 +113,18 @@ export const renderMeasuredTemplate = async event => {
{ create: true }
);
};
const getTemplateDistance = range => {
const rangeNumber = Number(range);
if (!Number.isNaN(rangeNumber)) return rangeNumber;
const { custom } = CONFIG.DH.GENERAL.sceneRangeMeasurementSetting;
const sceneMeasurements = canvas.scene?.flags.daggerheart?.rangeMeasurement;
const globalMeasurements = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.variantRules
).rangeMeasurement;
const settings = sceneMeasurements?.setting === custom.id ? sceneMeasurements : globalMeasurements;
return settings[range];
};

View file

@ -44,7 +44,9 @@
"flatMultiplier": 1
},
"applyTo": "hitPoints",
"type": [],
"type": [
"magical"
],
"base": false,
"valueAlt": {
"multiplier": "prof",
@ -87,57 +89,6 @@
"name": "Cast",
"img": "icons/skills/melee/spear-tips-three-green.webp",
"range": "veryClose"
},
"CUKoYyDxQhNc0pLs": {
"type": "damage",
"_id": "CUKoYyDxQhNc0pLs",
"systemPath": "actions",
"description": "<p>If a target you hit is <em>Vulnerable</em>, they take an extra <strong>1d8</strong> damage.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [],
"uses": {
"value": null,
"max": "",
"recovery": null
},
"damage": {
"parts": {
"hitPoints": {
"value": {
"custom": {
"enabled": false
},
"multiplier": "flat",
"flatMultiplier": 1,
"dice": "d8",
"bonus": null
},
"applyTo": "hitPoints",
"type": [],
"base": false,
"resultBased": false,
"valueAlt": {
"multiplier": "prof",
"flatMultiplier": 1,
"dice": "d6",
"bonus": null,
"custom": {
"enabled": false
}
}
}
},
"includeBase": false
},
"target": {
"type": "any",
"amount": null
},
"effects": [],
"name": "Damage Against Vulnerable",
"img": "icons/skills/melee/spear-tips-three-purple.webp",
"range": ""
}
},
"attribution": {

View file

@ -5,22 +5,14 @@
"_id": "ijWppQzSOqVCb3rE",
"img": "icons/weapons/axes/axe-battle-jagged.webp",
"system": {
"description": "",
"description": "<strong>Protective:</strong> +1 to Armor Score",
"actions": {},
"attached": [],
"tier": 3,
"equipped": false,
"secondary": false,
"burden": "twoHanded",
"weaponFeatures": [
{
"value": "protective",
"effectIds": [
"qTxADRsQnKiYfOiQ"
],
"actionIds": []
}
],
"weaponFeatures": [],
"attack": {
"name": "Attack",
"img": "icons/skills/melee/blood-slash-foam-red.webp",
@ -111,8 +103,8 @@
"effects": [
{
"name": "Protective",
"description": "Add your character's Tier to your Armor Score",
"img": "icons/skills/melee/shield-block-gray-orange.webp",
"description": "+1 to Armor Score",
"img": "icons/magic/defensive/shield-barrier-deflect-teal.webp",
"_id": "vnR4Zhnb0rOqwrFw",
"type": "base",
"system": {
@ -122,7 +114,7 @@
"phase": "initial",
"priority": 20,
"value": {
"max": "ITEM.@system.tier"
"max": "1"
}
}
]

View file

@ -2,7 +2,7 @@
"id": "daggerheart",
"title": "Daggerheart",
"description": "An unofficial implementation of the Daggerheart system",
"version": "2.0.0",
"version": "2.0.1",
"compatibility": {
"minimum": "14.359",
"verified": "14.359",

View file

@ -4,21 +4,6 @@
<a><i class="fa-solid fa-plus icon-button" data-action="addEffect"></i></a>
</legend>
<ul class="items-list">
{{!-- {{#each source as | effect index |}}
<div class="inventory-item" data-effect-id="{{effect._id}}" data-action="editEffect">
{{#with (@root.getEffectDetails effect._id) as | details |}}
<img class="item-img" src="{{img}}">
<div class="item-label">
<span class="item-name">{{name}}</span>
</div>
{{/with}}
<input type="hidden" name="effects.{{index}}._id" value="{{effect._id}}">
{{#if @root.source.save.trait}}{{formInput ../fields.onSave value=effect.onSave name=(concat "effects." index ".onSave") dataset=(object tooltip=(localize "DAGGERHEART.UI.Tooltip.appliedEvenIfSuccessful") tooltipDirection="UP")}}{{/if}}
<div class="controls">
<a data-tooltip="{{localize "CONTROLS.CommonDelete"}}" data-action="removeEffect" data-index="{{index}}"><i class="fas fa-trash"></i></a>
</div>
</div>
{{/each}} --}}
{{#each source as | effect index |}}
<div class="inventory-item single-img" data-effect-id="{{effect._id}}" data-action="editEffect">
<div class="inventory-item-header">

View file

@ -1,12 +1,12 @@
<fieldset class="one-column">
<legend>{{localize "DAGGERHEART.GENERAL.range"}}{{#if fields.target}} & {{localize "DAGGERHEART.GENERAL.Target.single"}}{{/if}}</legend>
{{formField fields.range value=source.range label="DAGGERHEART.GENERAL.range" name=(concat path "range") localize=true}}
{{formField fields.range value=source.range label=(localize "DAGGERHEART.GENERAL.range") name=(concat path "range") localize=true}}
{{#if fields.target}}
<div class="nest-inputs">
{{#if (and source.target.type (not (eq source.target.type 'self')))}}
{{ formField fields.target.amount value=source.target.amount label="DAGGERHEART.GENERAL.amount" name=(concat path "target.amount") localize=true}}
{{ formField fields.target.amount value=source.target.amount label=(localize "DAGGERHEART.GENERAL.amount") name=(concat path "target.amount") localize=true}}
{{/if}}
{{ formField fields.target.type value=source.target.type label="DAGGERHEART.GENERAL.Target.single" name=(concat path "target.type") localize=true }}
{{ formField fields.target.type value=source.target.type label=(localize "DAGGERHEART.GENERAL.Target.single") name=(concat path "target.type") localize=true }}
</div>
{{/if}}
</fieldset>

View file

@ -4,7 +4,7 @@
{{#if @root.hasBaseDamage}}{{formInput fields.useDefault name="roll.useDefault" value=source.useDefault dataset=(object tooltip="Use default Item values" tooltipDirection="UP")}}{{/if}}
</legend>
{{formField fields.type label="Type" name="roll.type" value=source.type localize=true choices=@root.getRollTypeOptions}}
{{formField fields.type label="DAGGERHEART.GENERAL.type" name="roll.type" value=source.type localize=true choices=@root.getRollTypeOptions localize=true}}
{{#if (eq source.type "diceSet")}}
<div class="nest-inputs">
{{formField fields.diceRolling.fields.multiplier name="roll.diceRolling.multiplier" value=source.diceRolling.multiplier localize=true}}
@ -17,13 +17,13 @@
<div class="nest-inputs">
{{#unless (eq source.type 'spellcast')}}
{{#if @root.isNPC}}
{{formField fields.bonus label="Bonus" name="roll.bonus" value=source.bonus placeholder=@root.baseAttackBonus disabled=(not source.type)}}
{{formField fields.bonus label="DAGGERHEART.GENERAL.Modifier.single" name="roll.bonus" value=source.bonus placeholder=@root.baseAttackBonus disabled=(not source.type) localize=true}}
{{else}}
{{formField fields.trait label="Trait" name="roll.trait" value=source.trait localize=true disabled=(not source.type)}}
{{formField fields.trait label="DAGGERHEART.GENERAL.Trait.single" name="roll.trait" value=source.trait localize=true disabled=(not source.type)}}
{{/if}}
{{/unless}}
{{formField fields.difficulty label="Difficulty" name="roll.difficulty" value=source.difficulty disabled=(not source.type)}}
{{formField fields.advState label= "Advantage State" name="roll.advState" value=source.advState localize=true disabled=(not source.type)}}
{{formField fields.difficulty label="DAGGERHEART.GENERAL.difficulty" name="roll.difficulty" value=source.difficulty localize=true disabled=(not source.type)}}
{{formField fields.advState label="DAGGERHEART.ACTIONS.Config.advantageState" name="roll.advState" value=source.advState localize=true localize=true disabled=(not source.type)}}
</div>
{{/if}}
</fieldset>

View file

@ -2,8 +2,8 @@
<legend>{{localize "DAGGERHEART.GENERAL.Roll.reaction"}}</legend>
<p class="hint">{{localize "DAGGERHEART.ACTIONS.Settings.saveHint"}}</p>
<div class="nest-inputs">
{{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)}}
{{formField fields.trait label="DAGGERHEART.GENERAL.Trait.single" name="save.trait" value=source.trait localize=true}}
{{formField fields.difficulty label="DAGGERHEART.GENERAL.difficulty" name="save.difficulty" value=source.difficulty disabled=(not source.trait) placeholder=@root.baseSaveDifficulty localize=true}}
{{formField fields.damageMod label="DAGGERHEART.ACTIONS.Config.damageOnSave" name="save.damageMod" value=source.damageMod localize=true localize=true disabled=(not source.trait)}}
</div>
</fieldset>

View file

@ -17,19 +17,24 @@
<div class="form-group">
<label>{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.FIELDS.initiator.memberId.label"}}</label>
<div class="form-fields">
<select name="initiator.memberId" {{#if initiatorDisabled}}disabled{{/if}}>
<select class="initiator-member-field" {{#if initiatorDisabled}}disabled{{/if}}>
{{selectOptions initiatorOptions selected=initiator.memberId blank="" }}
</select>
</div>
</div>
{{formGroup tagTeamFields.initiator.fields.cost name="initiator.cost" value=initiator.cost disabled=initiatorDisabled localize=true }}
<div class="form-group">
<label>{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.FIELDS.initiator.cost.label"}}</label>
<div class="form-fields">
<input type="text" data-dtype="Number" value="{{initiator.cost}}" class="initiator-cost-field" {{#if initiatorDisabled}}disabled{{/if}} />
</div>
</div>
</div>
<footer>
<button type="button" data-action="startTagTeamRoll" {{#unless canStartTagTeam}}disabled{{/unless}}>{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.startTagTeamRoll"}} <i class="fa-solid fa-arrow-right-long"></i></button>
<div class="finish-tools {{#unless canStartTagTeam}}inactive{{/unless}}">
<span>{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.openDialogForAll"}}</span>
<input type="checkbox" name="openForAllPlayers" {{#unless canStartTagTeam}}disabled{{/unless}} {{checked openForAllPlayers}} />
<input type="checkbox" class="openforall-field" {{#unless canStartTagTeam}}disabled{{/unless}} {{checked openForAllPlayers}} />
</div>
</footer>
</section>

View file

@ -11,7 +11,7 @@
<fieldset class="one-column">
<legend>{{localize "DAGGERHEART.GENERAL.attack"}}</legend>
<div class="nest-inputs">
{{formField systemFields.attack.fields.roll.fields.bonus value=document._source.system.attack.roll.bonus label="DAGGERHEART.ACTIONS.Settings.attackBonus" name="system.attack.roll.bonus" localize=true}}
{{formField systemFields.attack.fields.roll.fields.bonus value=document._source.system.attack.roll.bonus label="DAGGERHEART.ACTIONS.Settings.attackModifier" name="system.attack.roll.bonus" localize=true}}
{{formField systemFields.attack.fields.range value=document._source.system.attack.range label="DAGGERHEART.GENERAL.range" name="system.attack.range" localize=true}}
{{#if systemFields.attack.fields.target.fields}}
{{ formField systemFields.attack.fields.target.fields.type value=document._source.system.attack.target.type label="DAGGERHEART.GENERAL.Target.single" name="system.attack.target.type" localize=true }}

View file

@ -1,9 +1,9 @@
<div class="item-description-outer-container">
{{#if features.length}}
{{#if features.length}}
<div class="item-description-outer-container">
<div class="item-description-container">
{{#each features as | feature |}}
<div><strong>{{localize feature.label}}</strong>: {{{localize feature.description}}}</div>
{{/each}}
</div>
{{/if}}
</div>
</div>
{{/if}}

View file

@ -1,9 +1,9 @@
<div class="item-description-outer-container">
{{#if features.length}}
{{#if features.length}}
<div class="item-description-outer-container">
<div class="item-description-container">
{{#each features as | feature |}}
<div><strong>{{localize feature.label}}</strong>: {{{localize feature.description}}}</div>
{{/each}}
</div>
{{/if}}
</div>
</div>
{{/if}}

View file

@ -11,19 +11,21 @@
{{> ".scene" }}
</menu>
{{/with}}
<menu id="scene-navigation-levels" class="scene-levels scene-navigation-menu flexcol levels-{{ scenes.levels.length }}">
{{#each scenes.levels}}
<li class="level-row">
{{#with button}}
<button type="button" class="ui-control icon fa-solid {{ css }}" data-action="cycleLevel"
data-direction="{{ direction }}" aria-label="{{ label }}"></button>
{{/with}}
<div class="ui-control scene scene-level {{ css }}" data-scene-id="{{ sceneId }}" data-level-id="{{ id }}" data-action="viewLevel">
<span class="ellipsis">{{ name }}</span>
</div>
</li>
{{/each}}
</menu>
{{#if scenes.levels}}
<menu id="scene-navigation-levels" class="scene-levels scene-navigation-menu flexcol" style="--max-levels: {{ scenes.levels.length }}">
{{#each scenes.levels}}
<li class="level-row">
{{#with button}}
<button type="button" class="ui-control icon fa-solid {{ css }}" data-action="cycleLevel"
data-direction="{{ direction }}" aria-label="{{ label }}"></button>
{{/with}}
<div class="ui-control scene scene-level {{ css }}" data-scene-id="{{ sceneId }}" data-level-id="{{ id }}" data-action="viewLevel">
<span class="ellipsis">{{ name }}</span>
</div>
</li>
{{/each}}
</menu>
{{/if}}
<menu id="scene-navigation-active" class="scene-navigation-menu flexcol">
{{#each scenes.active}}
{{> ".scene" }}
@ -34,39 +36,6 @@
{{> ".scene" }}
{{/each}}
</menu>
{{!-- <menu id="scene-navigation-active" class="scene-navigation-menu flexcol">
{{#each scenes.active as |scene|}}
<li class="scene-wrapper">
<ul>
<li class="ui-control scene {{scene.cssClass}}" data-scene-id="{{scene.id}}" data-action="viewScene" {{#if scene.tooltip}}data-tooltip-text="{{scene.tooltip}}"{{/if}}>
<span class="scene-name ellipsis">{{scene.name}}</span>
{{#if scene.users}}
<ul class="scene-players">
{{#each scene.users as |user|}}
<li class="scene-player" style="--color-bg:{{user.color}}; --color-border:{{user.border}}"
data-tooltip aria-label="{{user.name}}">{{user.letter}}</li>
{{/each}}
</ul>
{{/if}}
</li>
</ul>
{{#if scene.hasEnvironments}}
<button class="ui-control scene-environment {{#if (gt scene.environments.length 1)}}many-environments{{/if}}" data-action="openSceneEnvironment" data-scene-id="{{scene.id}}"><img src="{{scene.environmentImage}}" /> </button>
{{/if}}
</li>
{{/each}}
</menu>
<menu id="scene-navigation-inactive" class="scene-navigation-menu flexcol">
{{#each scenes.inactive as |scene|}}
<li class="scene-wrapper">
<ul>
<li class="ui-control scene {{scene.cssClass}}" data-scene-id="{{scene.id}}" data-action="viewScene" {{#if scene.tooltip}}data-tooltip-text="{{scene.tooltip}}"{{/if}}>
<span class="scene-name ellipsis">{{scene.name}}</span>
</li>
</ul>
</li>
{{/each}}
</menu> --}}
</nav>
{{#*inline ".scene"}}