mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-11 19:25:21 +01:00
Merge branch 'development' into feature/313-preset-measured-templates
This commit is contained in:
commit
c02f44faf1
146 changed files with 4403 additions and 569 deletions
BIN
assets/icons/arrow-dunk.png
Normal file
BIN
assets/icons/arrow-dunk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
1
assets/icons/documents/actors/dark-squad.svg
Normal file
1
assets/icons/documents/actors/dark-squad.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><path d="M0 0h512v512H0z" fill="#000" fill-opacity="1"></path><g class="" transform="translate(0,0)" style=""><path d="M369.1 21.22c-19.2 0-36.2 10.63-47.9 26.47-11.7 15.84-18.6 37.03-18.6 60.31 0 21.1 5.7 40.5 15.5 55.7-5.7 1.6-11 3.9-15.9 6.6-10.2-8.5-22.6-13.6-35.9-13.6-19.3 0-36.3 10.6-48 26.4-4.7 6.4-8.6 13.6-11.6 21.5-4.8-2.4-9.9-4.3-15.5-5.6 9.4-15.1 14.8-34.1 14.8-54.7 0-23.2-6.9-44.43-18.6-60.27-11.7-15.84-28.7-26.5-47.9-26.5s-36.2 10.66-47.94 26.5C79.87 99.87 73 121.1 73 144.3c0 21.1 5.69 40.5 15.47 55.8-32.07 9.1-50.29 37.1-59.44 70-9.79 35.2-10.87 77.3-10.87 115.6v9.4h45.5l6.78 99.3h18.75l-7.28-106.5-4.1-80-18.65 1 3.47 67.5H36.97c.24-35.2 1.97-72.1 10.09-101.2 8.78-31.6 23.32-52.8 51.25-58.2l4.69-.1c10.3 8.8 22.9 14.2 36.5 14.2 14.1 0 26.9-5.7 37.4-15h4.6c7.8 1.2 14.4 3.5 20.1 6.7-1.2 6.6-1.9 13.5-1.9 20.6 0 21.1 5.7 40.5 15.5 55.8-32.1 9.1-50.3 37.2-59.4 70-9.8 35.2-10.9 77.3-10.9 115.6v9.4c21.7-.3 42.8.2 64.3.2l-.5-7.3-4.1-80-18.7.9 3.4 67.5h-25.6c.3-35.2 2-72.1 10.1-101.2 8.7-31.6 23.3-52.7 51.1-58.2l4.9-.1c10.3 8.8 22.8 14.2 36.4 14.2 14.1 0 27-5.7 37.5-15h4.4c15.4 2.4 26.1 8.9 34.5 18.6 8.5 9.7 14.5 23.2 18.5 39.2 7.3 29.5 7.7 66.9 7.7 102.5h-23.4l3.5-67.5-18.7-.9-4.2 82-.3 5.3c20.8 0 43.3-.3 61.9-.2v-9.4c0-38.1.5-80.6-8.4-116.3-4.4-17.8-11.3-34.1-22.4-47-9.7-11.1-22.7-19.4-38.8-23.4 9.4-15.1 14.7-34.1 14.7-54.7 0-22.5-6.4-43.2-17.5-58.8 3.9-1.8 8.1-3.1 12.7-4h4.7c10.3 8.8 22.9 14.2 36.5 14.2 14.1 0 27-5.8 37.4-15l4.6-.1c15.4 2.5 26 8.9 34.4 18.6 8.5 9.8 14.5 23.3 18.5 39.3 7.3 29.4 7.7 66.8 7.7 102.4h-23.4l3.5-67.4-18.7-1-4.1 79.7-8.6 143.1h18.7l8.2-135.7h43.1v-9.3c0-38.2.6-80.7-8.3-116.3-4.5-17.9-11.4-34.2-22.5-47-9.6-11.2-22.6-19.5-38.8-23.5 9.4-15.1 14.8-34 14.8-54.6 0-23.28-6.9-44.47-18.6-60.31-11.6-15.29-31.5-26.13-47.9-26.47zm0 18.69c12.4 0 23.9 6.69 32.9 18.87 9 12.19 14.9 29.67 14.9 49.22 0 19.5-5.9 37-14.9 49.2-9 12.2-20.5 18.9-32.9 18.9-12.3 0-23.9-6.7-32.9-18.9s-14.9-29.7-14.9-49.2c0-19.55 5.9-37.03 14.9-49.22 9-12.18 20.6-18.87 32.9-18.87zM139.5 76.22c12.4 0 23.9 6.72 32.9 18.9s14.9 29.68 14.9 49.18-5.9 37-14.9 49.2c-9 12.2-20.5 18.9-32.9 18.9-12.4 0-23.9-6.7-32.9-18.9-8.97-12.2-14.91-29.7-14.91-49.2 0-19.5 5.94-37 14.91-49.17 9-12.19 20.5-18.91 32.9-18.91zm197.8 22.34v18.64h22.5V98.56h-22.5zm41.1 0v18.64h22.5V98.56h-22.5zM107.7 134.9v18.7h22.5v-18.7h-22.5zm41.1 0v18.7h22.5v-18.7h-22.5zm117.5 40.4c12.3 0 23.8 6.7 32.8 18.9 9 12.2 15 29.7 15 49.2 0 19.6-6 37-15 49.2-9 12.2-20.5 18.9-32.8 18.9-12.4 0-24-6.7-33-18.9-8.9-12.2-14.9-29.6-14.9-49.2 0-19.5 6-37 14.9-49.2 9-12.2 20.6-18.9 33-18.9zM234.5 234v18.7h22.4V234zm41.1 0v18.7H298V234h-22.4z" fill="#fff" fill-opacity="1"></path></g></svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
|
|
@ -85,6 +85,10 @@ Hooks.once('init', () => {
|
|||
types: ['environment'],
|
||||
makeDefault: true
|
||||
});
|
||||
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Party, {
|
||||
types: ['party'],
|
||||
makeDefault: true
|
||||
});
|
||||
|
||||
CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect;
|
||||
CONFIG.ActiveEffect.dataModels = models.activeEffects.config;
|
||||
|
|
@ -163,12 +167,14 @@ Hooks.on('setup', () => {
|
|||
});
|
||||
|
||||
Hooks.on('ready', async () => {
|
||||
const appearanceSettings = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance);
|
||||
ui.resources = new CONFIG.ui.resources();
|
||||
if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).displayFear !== 'hide')
|
||||
ui.resources.render({ force: true });
|
||||
if (appearanceSettings.displayFear !== 'hide') ui.resources.render({ force: true });
|
||||
|
||||
ui.countdowns = new CONFIG.ui.countdowns();
|
||||
ui.countdowns.render({ force: true });
|
||||
if (appearanceSettings.displayCountdownUI) {
|
||||
ui.countdowns = new CONFIG.ui.countdowns();
|
||||
ui.countdowns.render({ force: true });
|
||||
}
|
||||
|
||||
if (!(ui.compendiumBrowser instanceof applications.ui.ItemBrowser))
|
||||
ui.compendiumBrowser = new applications.ui.ItemBrowser();
|
||||
|
|
|
|||
72
lang/en.json
72
lang/en.json
|
|
@ -20,7 +20,8 @@
|
|||
"character": "Character",
|
||||
"companion": "Companion",
|
||||
"adversary": "Adversary",
|
||||
"environment": "Environment"
|
||||
"environment": "Environment",
|
||||
"party": "Party"
|
||||
}
|
||||
},
|
||||
"CONTROLS": {
|
||||
|
|
@ -439,9 +440,15 @@
|
|||
},
|
||||
"HUD": {
|
||||
"tokenHUD": {
|
||||
"genericEffects": "Foundry Effects"
|
||||
"genericEffects": "Foundry Effects",
|
||||
"depositPartyTokens": "Deposit Party Tokens",
|
||||
"retrievePartyTokens": "Retrieve Party Tokens"
|
||||
}
|
||||
},
|
||||
"ImageSelect": {
|
||||
"title": "Select Image",
|
||||
"selectImage": "Select Image"
|
||||
},
|
||||
"Levelup": {
|
||||
"actions": {
|
||||
"creatureComfort": {
|
||||
|
|
@ -566,6 +573,19 @@
|
|||
"ResourceDice": {
|
||||
"title": "{name} Resource",
|
||||
"rerollDice": "Reroll Dice"
|
||||
},
|
||||
"TagTeamSelect": {
|
||||
"title": "Tag Team Roll",
|
||||
"leaderTitle": "Initiating Character",
|
||||
"membersTitle": "Participants",
|
||||
"partyTeam": "Party Team",
|
||||
"hopeCost": "Hope Cost",
|
||||
"initiatingCharacter": "Initiating Character",
|
||||
"linkMessageHint": "Make a roll from your character sheet to link it to the Tag Team Roll",
|
||||
"damageNotRolled": "Damage not rolled in chat message yet",
|
||||
"insufficientHope": "The initiating character doesn't have enough hope",
|
||||
"createTagTeam": "Create TagTeam Roll",
|
||||
"chatMessageRollTitle": "Roll"
|
||||
}
|
||||
},
|
||||
"CLASS": {
|
||||
|
|
@ -1934,6 +1954,7 @@
|
|||
"story": "Story",
|
||||
"biography": "Biography",
|
||||
"general": "General",
|
||||
"resources": "Resources",
|
||||
"foundation": "Foundation",
|
||||
"specialization": "Specialization",
|
||||
"mastery": "Mastery",
|
||||
|
|
@ -1951,6 +1972,8 @@
|
|||
"downtime": "Downtime",
|
||||
"roll": "Roll",
|
||||
"rules": "Rules",
|
||||
"partyMembers": "Party Members",
|
||||
"projects": "Projects",
|
||||
"types": "Types",
|
||||
"itemFeatures": "Item Features",
|
||||
"questions": "Questions",
|
||||
|
|
@ -2198,6 +2221,9 @@
|
|||
"displayFear": {
|
||||
"label": "Display Fear"
|
||||
},
|
||||
"displayCountdownUI": {
|
||||
"label": "Display Countdown UI"
|
||||
},
|
||||
"showGenericStatusEffects": {
|
||||
"label": "Show Foundry Status Effects"
|
||||
},
|
||||
|
|
@ -2231,7 +2257,8 @@
|
|||
"target": {
|
||||
"label": "Target"
|
||||
}
|
||||
}
|
||||
},
|
||||
"useResourcePips": { "label": "Pip Display For Resources" }
|
||||
},
|
||||
"fearDisplay": {
|
||||
"token": "Tokens",
|
||||
|
|
@ -2322,6 +2349,9 @@
|
|||
},
|
||||
"Homebrew": {
|
||||
"newDowntimeMove": "Downtime Move",
|
||||
"downtimeMove": "Downtime Move",
|
||||
"armorFeature": "Armor Feature",
|
||||
"weaponFeature": "Weapon Feaure",
|
||||
"newFeature": "New ItemFeature",
|
||||
"downtimeMoves": "Downtime Moves",
|
||||
"itemFeatures": "Item Features",
|
||||
|
|
@ -2424,7 +2454,13 @@
|
|||
"resetConfirmationText": "Are you sure you want to reset the {settings}?"
|
||||
},
|
||||
"Scene": {
|
||||
"rangeMeasurementOverride": "Override Global Range Measurement Settings"
|
||||
"FIELDS": {
|
||||
"rangeMeasurement": {
|
||||
"setting": { "label": "Setting" }
|
||||
}
|
||||
},
|
||||
"disabledText": "Daggerheart Measurements are disabled in System Settings - Variant Rules",
|
||||
"rangeMeasurement": "Range Measurement"
|
||||
}
|
||||
},
|
||||
"UI": {
|
||||
|
|
@ -2472,6 +2508,17 @@
|
|||
"title": "Effects Applied"
|
||||
},
|
||||
"featureTitle": "Class Feature",
|
||||
"groupRoll": {
|
||||
"title": "Group Roll",
|
||||
"leader": "Leader",
|
||||
"partyTeam": "Party Team",
|
||||
"team": "Team",
|
||||
"selectLeader": "Select a Leader",
|
||||
"selectMember": "Select a Member",
|
||||
"rerollTitle": "Reroll Group Roll",
|
||||
"rerollContent": "Are you sure you want to reroll your {trait} check?",
|
||||
"rerollTooltip": "Reroll"
|
||||
},
|
||||
"healingRoll": {
|
||||
"title": "Heal - {damage}",
|
||||
"heal": "Heal",
|
||||
|
|
@ -2488,8 +2535,16 @@
|
|||
},
|
||||
"resourceRoll": {
|
||||
"playerMessage": "{user} rerolled their {name}"
|
||||
},
|
||||
"tagTeam": {
|
||||
"title": "Tag Team",
|
||||
"membersTitle": "Members"
|
||||
}
|
||||
},
|
||||
"ChatLog": {
|
||||
"rerollDamage": "Reroll Damage",
|
||||
"assignTagRoll": "Assign as Tag Roll"
|
||||
},
|
||||
"Countdowns": {
|
||||
"title": "Countdowns",
|
||||
"toggleIconMode": "Toggle Icon Only",
|
||||
|
|
@ -2569,6 +2624,8 @@
|
|||
"wrongDomain": "The card isn't from one of your class domains.",
|
||||
"cardTooHighLevel": "The card is too high level!",
|
||||
"duplicateCard": "You cannot select the same card more than once.",
|
||||
"duplicateCharacter": "This actor is already registered in the party members list.",
|
||||
"onlyCharactersInPartySheet": "You can drag only characters to a party sheet.",
|
||||
"notPrimary": "The weapon is not a primary weapon!",
|
||||
"notSecondary": "The weapon is not a secondary weapon!",
|
||||
"itemTooHighTier": "The item must be from Tier1",
|
||||
|
|
@ -2603,7 +2660,9 @@
|
|||
"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.",
|
||||
"gmRequired": "This action requires an online GM"
|
||||
"gmRequired": "This action requires an online GM",
|
||||
"gmOnly": "This can only be accessed by the GM",
|
||||
"noActorOwnership": "You do not have permissions for this character"
|
||||
},
|
||||
"Sidebar": {
|
||||
"daggerheartMenu": {
|
||||
|
|
@ -2638,7 +2697,8 @@
|
|||
"remainingUses": "Uses refresh on {type}",
|
||||
"rightClickExtand": "Right-Click to extand",
|
||||
"companionPartnerLevelBlock": "The companion needs an assigned partner to level up.",
|
||||
"configureAttribution": "Configure Attribution"
|
||||
"configureAttribution": "Configure Attribution",
|
||||
"deleteItem": "Delete Item"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,11 @@ export { default as DamageDialog } from './damageDialog.mjs';
|
|||
export { default as DamageReductionDialog } from './damageReductionDialog.mjs';
|
||||
export { default as DeathMove } from './deathMove.mjs';
|
||||
export { default as Downtime } from './downtime.mjs';
|
||||
export { default as ImageSelectDialog } from './imageSelectDialog.mjs';
|
||||
export { default as MulticlassChoiceDialog } from './multiclassChoiceDialog.mjs';
|
||||
export { default as OwnershipSelection } from './ownershipSelection.mjs';
|
||||
export { default as RerollDamageDialog } from './rerollDamageDialog.mjs';
|
||||
export { default as ResourceDiceDialog } from './resourceDiceDialog.mjs';
|
||||
export { default as ActionSelectionDialog } from './actionSelectionDialog.mjs';
|
||||
export { default as GroupRollDialog } from './group-roll-dialog.mjs';
|
||||
export { default as TagTeamDialog } from './tagTeamDialog.mjs';
|
||||
|
|
|
|||
|
|
@ -276,7 +276,22 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
|||
const featureItem = item;
|
||||
app.addEventListener(
|
||||
'close',
|
||||
() => resolve({ selected: app.selected, evolved: app.evolved, hybrid: app.hybrid, item: featureItem }),
|
||||
async () => {
|
||||
const selected = app.selected.toObject();
|
||||
const data = await game.system.api.data.items.DHBeastform.getWildcardImage(
|
||||
app.configData.data.parent,
|
||||
app.selected
|
||||
);
|
||||
if (data) {
|
||||
if (!data.selectedImage) selected = null;
|
||||
else {
|
||||
if (data.usesDynamicToken) selected.system.tokenRingImg = data.selectedImage;
|
||||
else selected.system.tokenImg = data.selectedImage;
|
||||
}
|
||||
}
|
||||
|
||||
resolve({ selected: selected, evolved: app.evolved, hybrid: app.hybrid, item: featureItem });
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
app.render({ force: true });
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
updateIsAdvantage: this.updateIsAdvantage,
|
||||
selectExperience: this.selectExperience,
|
||||
toggleReaction: this.toggleReaction,
|
||||
toggleTagTeamRoll: this.toggleTagTeamRoll,
|
||||
submitRoll: this.submitRoll
|
||||
},
|
||||
form: {
|
||||
|
|
@ -120,6 +121,13 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
context.showReaction = !this.config.roll?.type && context.rollType === 'DualityRoll';
|
||||
context.reactionOverride = this.reactionOverride;
|
||||
}
|
||||
|
||||
const tagTeamSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);
|
||||
if (tagTeamSetting.members[this.actor.id] && !this.config.skips?.createMessage) {
|
||||
context.activeTagTeamRoll = true;
|
||||
context.tagTeamSelected = this.config.tagTeamSelected;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
@ -195,6 +203,11 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
}
|
||||
}
|
||||
|
||||
static toggleTagTeamRoll() {
|
||||
this.config.tagTeamSelected = !this.config.tagTeamSelected;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async submitRoll() {
|
||||
await this.close({ submitted: true });
|
||||
}
|
||||
|
|
|
|||
196
module/applications/dialogs/group-roll-dialog.mjs
Normal file
196
module/applications/dialogs/group-roll-dialog.mjs
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
import autocomplete from 'autocompleter';
|
||||
import { abilities } from '../../config/actorConfig.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class GroupRollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(actors) {
|
||||
super();
|
||||
this.actors = actors;
|
||||
this.actorLeader = {};
|
||||
this.actorsMembers = [];
|
||||
}
|
||||
|
||||
get title() {
|
||||
return 'Group Roll';
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'group-roll'],
|
||||
position: { width: 'auto', height: 'auto' },
|
||||
window: {
|
||||
title: 'DAGGERHEART.UI.Chat.groupRoll.title'
|
||||
},
|
||||
actions: {
|
||||
roll: GroupRollDialog.#roll,
|
||||
removeLeader: GroupRollDialog.#removeLeader,
|
||||
removeMember: GroupRollDialog.#removeMember
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
application: {
|
||||
id: 'group-roll',
|
||||
template: 'systems/daggerheart/templates/dialogs/group-roll/group-roll.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
const leaderChoices = this.actors.filter(x => this.actorsMembers.every(member => member.actor?.id !== x.id));
|
||||
const memberChoices = this.actors.filter(
|
||||
x => this.actorLeader?.actor?.id !== x.id && this.actorsMembers.every(member => member.actor?.id !== x.id)
|
||||
);
|
||||
|
||||
htmlElement.querySelectorAll('.leader-change-input').forEach(element => {
|
||||
autocomplete({
|
||||
input: element,
|
||||
fetch: function (text, update) {
|
||||
if (!text) {
|
||||
update(leaderChoices);
|
||||
} else {
|
||||
text = text.toLowerCase();
|
||||
var suggestions = leaderChoices.filter(n => n.name.toLowerCase().includes(text));
|
||||
update(suggestions);
|
||||
}
|
||||
},
|
||||
render: function (actor, search) {
|
||||
const actorName = game.i18n.localize(actor.name);
|
||||
const matchIndex = actorName.toLowerCase().indexOf(search);
|
||||
|
||||
const beforeText = actorName.slice(0, matchIndex);
|
||||
const matchText = actorName.slice(matchIndex, matchIndex + search.length);
|
||||
const after = actorName.slice(matchIndex + search.length, actorName.length);
|
||||
const img = document.createElement('img');
|
||||
img.src = actor.img;
|
||||
|
||||
const element = document.createElement('li');
|
||||
element.appendChild(img);
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
|
||||
element.appendChild(label);
|
||||
|
||||
return element;
|
||||
},
|
||||
renderGroup: function (label) {
|
||||
const itemElement = document.createElement('div');
|
||||
itemElement.textContent = game.i18n.localize(label);
|
||||
return itemElement;
|
||||
},
|
||||
onSelect: actor => {
|
||||
element.value = actor.uuid;
|
||||
this.actorLeader = { actor: actor, trait: 'agility', difficulty: 0 };
|
||||
this.render();
|
||||
},
|
||||
click: e => e.fetch(),
|
||||
customize: function (_input, _inputRect, container) {
|
||||
container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ;
|
||||
},
|
||||
minLength: 0
|
||||
});
|
||||
});
|
||||
|
||||
htmlElement.querySelectorAll('.team-push-input').forEach(element => {
|
||||
autocomplete({
|
||||
input: element,
|
||||
fetch: function (text, update) {
|
||||
if (!text) {
|
||||
update(memberChoices);
|
||||
} else {
|
||||
text = text.toLowerCase();
|
||||
var suggestions = memberChoices.filter(n => n.name.toLowerCase().includes(text));
|
||||
update(suggestions);
|
||||
}
|
||||
},
|
||||
render: function (actor, search) {
|
||||
const actorName = game.i18n.localize(actor.name);
|
||||
const matchIndex = actorName.toLowerCase().indexOf(search);
|
||||
|
||||
const beforeText = actorName.slice(0, matchIndex);
|
||||
const matchText = actorName.slice(matchIndex, matchIndex + search.length);
|
||||
const after = actorName.slice(matchIndex + search.length, actorName.length);
|
||||
const img = document.createElement('img');
|
||||
img.src = actor.img;
|
||||
|
||||
const element = document.createElement('li');
|
||||
element.appendChild(img);
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
|
||||
element.appendChild(label);
|
||||
|
||||
return element;
|
||||
},
|
||||
renderGroup: function (label) {
|
||||
const itemElement = document.createElement('div');
|
||||
itemElement.textContent = game.i18n.localize(label);
|
||||
return itemElement;
|
||||
},
|
||||
onSelect: actor => {
|
||||
element.value = actor.uuid;
|
||||
this.actorsMembers.push({ actor: actor, trait: 'agility', difficulty: 0 });
|
||||
this.render({ force: true });
|
||||
},
|
||||
click: e => e.fetch(),
|
||||
customize: function (_input, _inputRect, container) {
|
||||
container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ;
|
||||
},
|
||||
minLength: 0
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.leader = this.actorLeader;
|
||||
context.members = this.actorsMembers;
|
||||
context.traitList = abilities;
|
||||
|
||||
context.allSelected = this.actorsMembers.length + (this.actorLeader?.actor ? 1 : 0) === this.actors.length;
|
||||
context.rollDisabled = context.members.length === 0 || !this.actorLeader?.actor;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static updateData(event, _, formData) {
|
||||
const { actorLeader, actorsMembers } = foundry.utils.expandObject(formData.object);
|
||||
this.actorLeader = foundry.utils.mergeObject(this.actorLeader, actorLeader);
|
||||
this.actorsMembers = foundry.utils.mergeObject(this.actorsMembers, actorsMembers);
|
||||
this.render(true);
|
||||
}
|
||||
|
||||
static async #removeLeader(_, button) {
|
||||
this.actorLeader = null;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #removeMember(_, button) {
|
||||
this.actorsMembers = this.actorsMembers.filter(m => m.actor.uuid !== button.dataset.memberUuid);
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #roll() {
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = {
|
||||
leader: this.actorLeader,
|
||||
members: this.actorsMembers
|
||||
};
|
||||
const msg = {
|
||||
type: 'groupRoll',
|
||||
user: game.user.id,
|
||||
speaker: cls.getSpeaker(),
|
||||
title: game.i18n.localize('DAGGERHEART.UI.Chat.groupRoll.title'),
|
||||
system: systemData,
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/ui/chat/groupRoll.hbs',
|
||||
{ system: systemData }
|
||||
)
|
||||
};
|
||||
|
||||
cls.create(msg);
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
67
module/applications/dialogs/imageSelectDialog.mjs
Normal file
67
module/applications/dialogs/imageSelectDialog.mjs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class ImageSelectDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(titleName, images) {
|
||||
super();
|
||||
|
||||
this.titleName = titleName;
|
||||
this.images = images;
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'image-select'],
|
||||
position: {
|
||||
width: 600,
|
||||
height: 'auto'
|
||||
},
|
||||
window: {
|
||||
icon: 'fa-solid fa-paw'
|
||||
},
|
||||
actions: {
|
||||
selectImage: ImageSelectDialog.#selectImage,
|
||||
finishSelection: ImageSelectDialog.#finishSelection
|
||||
}
|
||||
};
|
||||
|
||||
get title() {
|
||||
return this.titleName;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: { template: 'systems/daggerheart/templates/dialogs/image-select/main.hbs' },
|
||||
footer: { template: 'systems/daggerheart/templates/dialogs/image-select/footer.hbs' }
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.images = this.images;
|
||||
context.selectedImage = this.selectedImage;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static #selectImage(_event, button) {
|
||||
this.selectedImage = button.dataset.image ?? button.querySelector('img').dataset.image;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static #finishSelection() {
|
||||
this.close({ submitted: true });
|
||||
}
|
||||
|
||||
async close(options = {}) {
|
||||
if (!options.submitted) this.selectedImage = null;
|
||||
|
||||
await super.close();
|
||||
}
|
||||
|
||||
static async configure(title, images) {
|
||||
return new Promise(resolve => {
|
||||
const app = new this(title, images);
|
||||
app.addEventListener('close', () => resolve(app.selectedImage), { once: true });
|
||||
app.render({ force: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
|
||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class RerollDamageDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
|
|
@ -122,6 +124,15 @@ export default class RerollDamageDialog extends HandlebarsApplicationMixin(Appli
|
|||
}, {})
|
||||
};
|
||||
await this.message.update(update);
|
||||
|
||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll });
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.Refresh,
|
||||
data: {
|
||||
refreshType: RefreshType.TagTeamRoll
|
||||
}
|
||||
});
|
||||
|
||||
await this.close();
|
||||
}
|
||||
|
||||
|
|
|
|||
315
module/applications/dialogs/tagTeamDialog.mjs
Normal file
315
module/applications/dialogs/tagTeamDialog.mjs
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
import { GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class TagTeamDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(party) {
|
||||
super();
|
||||
|
||||
this.data = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);
|
||||
this.party = party;
|
||||
|
||||
this.setupHooks = Hooks.on(socketEvent.Refresh, ({ refreshType }) => {
|
||||
if (refreshType === RefreshType.TagTeamRoll) {
|
||||
this.data = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);
|
||||
this.render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.title');
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'tag-team-dialog'],
|
||||
position: { width: 550, height: 'auto' },
|
||||
actions: {
|
||||
removeMember: TagTeamDialog.#removeMember,
|
||||
unlinkMessage: TagTeamDialog.#unlinkMessage,
|
||||
selectMessage: TagTeamDialog.#selectMessage,
|
||||
createTagTeam: TagTeamDialog.#createTagTeam
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
application: {
|
||||
id: 'tag-team-dialog',
|
||||
template: 'systems/daggerheart/templates/dialogs/tagTeamDialog.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.hopeCost = this.hopeCost;
|
||||
context.data = this.data;
|
||||
|
||||
context.memberOptions = this.party.filter(c => !this.data.members[c.id]);
|
||||
context.selectedCharacterOptions = this.party.filter(c => this.data.members[c.id]);
|
||||
|
||||
context.members = Object.keys(this.data.members).map(id => {
|
||||
const roll = this.data.members[id].messageId ? game.messages.get(this.data.members[id].messageId) : null;
|
||||
|
||||
context.usesDamage =
|
||||
context.usesDamage === undefined
|
||||
? roll?.system.hasDamage
|
||||
: context.usesDamage && roll?.system.hasDamage;
|
||||
return {
|
||||
character: this.party.find(x => x.id === id),
|
||||
selected: this.data.members[id].selected,
|
||||
roll: roll,
|
||||
damageValues: roll
|
||||
? Object.keys(roll.system.damage).map(key => ({
|
||||
key: key,
|
||||
name: game.i18n.localize(CONFIG.DH.GENERAL.healingTypes[key].label),
|
||||
total: roll.system.damage[key].total
|
||||
}))
|
||||
: null
|
||||
};
|
||||
});
|
||||
|
||||
const initiatorChar = this.party.find(x => x.id === this.data.initiator.id);
|
||||
context.initiator = {
|
||||
character: initiatorChar,
|
||||
cost: this.data.initiator.cost
|
||||
};
|
||||
|
||||
context.selectedData = Object.values(context.members).reduce(
|
||||
(acc, member) => {
|
||||
if (!member.roll) return acc;
|
||||
if (member.selected) {
|
||||
acc.result = `${member.roll.system.roll.total} ${member.roll.system.roll.result.label}`;
|
||||
}
|
||||
|
||||
if (context.usesDamage) {
|
||||
if (!acc.damageValues) acc.damageValues = {};
|
||||
for (let damage of member.damageValues) {
|
||||
if (acc.damageValues[damage.key]) {
|
||||
acc.damageValues[damage.key].total += damage.total;
|
||||
} else {
|
||||
acc.damageValues[damage.key] = foundry.utils.deepClone(damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ result: null, damageValues: null }
|
||||
);
|
||||
context.showResult = Object.values(context.members).reduce((enabled, member) => {
|
||||
if (!member.roll) return enabled;
|
||||
if (context.usesDamage) {
|
||||
enabled = enabled === null ? member.damageValues.length > 0 : enabled && member.damageValues.length > 0;
|
||||
} else {
|
||||
enabled = enabled === null ? Boolean(member.roll) : enabled && Boolean(member.roll);
|
||||
}
|
||||
|
||||
return enabled;
|
||||
}, null);
|
||||
|
||||
context.createDisabled =
|
||||
!context.selectedData.result ||
|
||||
!this.data.initiator.id ||
|
||||
Object.keys(this.data.members).length === 0 ||
|
||||
Object.values(context.members).some(x =>
|
||||
context.usesDamage ? !x.damageValues || x.damageValues.length === 0 : !x.roll
|
||||
);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
async updateSource(update) {
|
||||
await this.data.updateSource(update);
|
||||
|
||||
if (game.user.isGM) {
|
||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, this.data.toObject());
|
||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll });
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.Refresh,
|
||||
data: {
|
||||
refreshType: RefreshType.TagTeamRoll
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: {
|
||||
action: GMUpdateEvent.UpdateSetting,
|
||||
uuid: CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll,
|
||||
update: this.data.toObject(),
|
||||
refresh: { refreshType: RefreshType.TagTeamRoll }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static async updateData(_event, _element, formData) {
|
||||
const { selectedAddMember, initiator } = foundry.utils.expandObject(formData.object);
|
||||
const update = { initiator: initiator };
|
||||
if (selectedAddMember) {
|
||||
const member = await foundry.utils.fromUuid(selectedAddMember);
|
||||
update[`members.${member.id}`] = { messageId: null };
|
||||
}
|
||||
|
||||
await this.updateSource(update);
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #removeMember(_, button) {
|
||||
const update = { [`members.-=${button.dataset.characterId}`]: null };
|
||||
if (this.data.initiator.id === button.dataset.characterId) {
|
||||
update.iniator = { id: null };
|
||||
}
|
||||
|
||||
await this.updateSource(update);
|
||||
}
|
||||
|
||||
static async #unlinkMessage(_, button) {
|
||||
await this.updateSource({ [`members.${button.id}.messageId`]: null });
|
||||
}
|
||||
|
||||
static async #selectMessage(_, button) {
|
||||
const member = this.data.members[button.id];
|
||||
const currentSelected = Object.keys(this.data.members).find(key => this.data.members[key].selected);
|
||||
const curretSelectedUpdate =
|
||||
currentSelected && currentSelected !== button.id ? { [`${currentSelected}`]: { selected: false } } : {};
|
||||
await this.updateSource({
|
||||
members: {
|
||||
[`${button.id}`]: { selected: !member.selected },
|
||||
...curretSelectedUpdate
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static async #createTagTeam() {
|
||||
const mainRollId = Object.keys(this.data.members).find(key => this.data.members[key].selected);
|
||||
const mainRoll = game.messages.get(this.data.members[mainRollId].messageId);
|
||||
|
||||
if (this.data.initiator.cost) {
|
||||
const initiator = this.party.find(x => x.id === this.data.initiator.id);
|
||||
if (initiator.system.resources.hope.value < this.data.initiator.cost) {
|
||||
return ui.notifications.warn(
|
||||
game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.insufficientHope')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const secondaryRolls = Object.keys(this.data.members)
|
||||
.filter(key => key !== mainRollId)
|
||||
.map(key => game.messages.get(this.data.members[key].messageId));
|
||||
|
||||
const systemData = foundry.utils.deepClone(mainRoll).system.toObject();
|
||||
for (let roll of secondaryRolls) {
|
||||
if (roll.system.hasDamage) {
|
||||
for (let key in roll.system.damage) {
|
||||
var damage = roll.system.damage[key];
|
||||
if (systemData.damage[key]) {
|
||||
systemData.damage[key].total += damage.total;
|
||||
systemData.damage[key].parts = [...systemData.damage[key].parts, ...damage.parts];
|
||||
} else {
|
||||
systemData.damage[key] = damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
systemData.title = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.chatMessageRollTitle');
|
||||
|
||||
const cls = getDocumentClass('ChatMessage'),
|
||||
msgData = {
|
||||
type: 'dualityRoll',
|
||||
user: game.user.id,
|
||||
title: game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.title'),
|
||||
speaker: cls.getSpeaker({ actor: this.party.find(x => x.id === mainRollId) }),
|
||||
system: systemData,
|
||||
rolls: mainRoll.rolls,
|
||||
sound: null,
|
||||
flags: { core: { RollTable: true } }
|
||||
};
|
||||
|
||||
await cls.create(msgData);
|
||||
|
||||
const fearUpdate = { key: 'fear', value: null, total: null, enabled: true };
|
||||
for (let memberId of Object.keys(this.data.members)) {
|
||||
const resourceUpdates = [];
|
||||
if (systemData.roll.isCritical || systemData.roll.result.duality === 1) {
|
||||
const value =
|
||||
memberId !== this.data.initiator.id
|
||||
? 1
|
||||
: this.data.initiator.cost
|
||||
? 1 - this.data.initiator.cost
|
||||
: 1;
|
||||
resourceUpdates.push({ key: 'hope', value: value, total: -value, enabled: true });
|
||||
}
|
||||
if (systemData.roll.isCritical) resourceUpdates.push({ key: 'stress', value: -1, total: 1, enabled: true });
|
||||
if (systemData.roll.result.duality === -1) {
|
||||
fearUpdate.value = fearUpdate.value === null ? 1 : fearUpdate.value + 1;
|
||||
fearUpdate.total = fearUpdate.total === null ? -1 : fearUpdate.total - 1;
|
||||
}
|
||||
|
||||
this.party.find(x => x.id === memberId).modifyResource(resourceUpdates);
|
||||
}
|
||||
|
||||
if (fearUpdate.value) {
|
||||
this.party.find(x => x.id === mainRollId).modifyResource([fearUpdate]);
|
||||
}
|
||||
|
||||
/* Improve by fetching default from schema */
|
||||
const update = { members: [], initiator: { id: null, cost: 3 } };
|
||||
if (game.user.isGM) {
|
||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, update);
|
||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll });
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.Refresh,
|
||||
data: {
|
||||
refreshType: RefreshType.TagTeamRoll
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: {
|
||||
action: GMUpdateEvent.UpdateSetting,
|
||||
uuid: CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll,
|
||||
update: update,
|
||||
refresh: { refreshType: RefreshType.TagTeamRoll }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static async assignRoll(char, message) {
|
||||
const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);
|
||||
const character = settings.members[char.id];
|
||||
if (!character) return;
|
||||
|
||||
await settings.updateSource({ [`members.${char.id}.messageId`]: message.id });
|
||||
|
||||
if (game.user.isGM) {
|
||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, settings);
|
||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll });
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.Refresh,
|
||||
data: {
|
||||
refreshType: RefreshType.TagTeamRoll
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: {
|
||||
action: GMUpdateEvent.UpdateSetting,
|
||||
uuid: CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll,
|
||||
update: settings,
|
||||
refresh: { refreshType: RefreshType.TagTeamRoll }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async close(options = {}) {
|
||||
Hooks.off(socketEvent.Refresh, this.setupHooks);
|
||||
await super.close(options);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
import { shuffleArray } from '../../helpers/utils.mjs';
|
||||
|
||||
export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['daggerheart'],
|
||||
actions: {
|
||||
combat: DHTokenHUD.#onToggleCombat
|
||||
combat: DHTokenHUD.#onToggleCombat,
|
||||
togglePartyTokens: DHTokenHUD.#togglePartyTokens
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -14,11 +17,17 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
|
|||
}
|
||||
};
|
||||
|
||||
static #nonCombatTypes = ['environment', 'companion'];
|
||||
static #nonCombatTypes = ['environment', 'companion', 'party'];
|
||||
|
||||
async _prepareContext(options) {
|
||||
const context = await super._prepareContext(options);
|
||||
|
||||
context.partyOnCanvas =
|
||||
this.actor.type === 'party' &&
|
||||
this.actor.system.partyMembers.some(member => member.getActiveTokens().length > 0);
|
||||
context.icons.toggleParty = 'systems/daggerheart/assets/icons/arrow-dunk.png';
|
||||
context.actorType = this.actor.type;
|
||||
context.usesEffects = this.actor.type !== 'party';
|
||||
context.canToggleCombat = DHTokenHUD.#nonCombatTypes.includes(this.actor.type)
|
||||
? false
|
||||
: context.canToggleCombat;
|
||||
|
|
@ -59,6 +68,105 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
|
|||
}
|
||||
}
|
||||
|
||||
static async #togglePartyTokens(_, button) {
|
||||
const icon = button.querySelector('img');
|
||||
icon.classList.toggle('flipped');
|
||||
button.dataset.tooltip = game.i18n.localize(
|
||||
icon.classList.contains('flipped')
|
||||
? 'DAGGERHEART.APPLICATIONS.HUD.tokenHUD.retrievePartyTokens'
|
||||
: 'DAGGERHEART.APPLICATIONS.HUD.tokenHUD.depositPartyTokens'
|
||||
);
|
||||
|
||||
const animationDuration = 500;
|
||||
const activeTokens = this.actor.system.partyMembers.flatMap(member => member.getActiveTokens());
|
||||
const { x: actorX, y: actorY } = this.document;
|
||||
if (activeTokens.length > 0) {
|
||||
for (let token of activeTokens) {
|
||||
await token.document.update(
|
||||
{ x: actorX, y: actorY, alpha: 0 },
|
||||
{ animation: { duration: animationDuration } }
|
||||
);
|
||||
setTimeout(() => token.document.delete(), animationDuration);
|
||||
}
|
||||
} else {
|
||||
const activeScene = game.scenes.find(x => x.id === game.user.viewedScene);
|
||||
const partyTokenData = [];
|
||||
for (let member of this.actor.system.partyMembers) {
|
||||
const data = await member.getTokenDocument();
|
||||
partyTokenData.push(data.toObject());
|
||||
}
|
||||
const newTokens = await activeScene.createEmbeddedDocuments(
|
||||
'Token',
|
||||
partyTokenData.map(tokenData => ({
|
||||
...tokenData,
|
||||
alpha: 0,
|
||||
x: actorX,
|
||||
y: actorY
|
||||
}))
|
||||
);
|
||||
|
||||
const { sizeX, sizeY } = activeScene.grid;
|
||||
const nrRandomPositions = Math.ceil(newTokens.length / 8) * 8;
|
||||
/* This is an overcomplicated mess, but I'm stupid */
|
||||
const positions = shuffleArray(
|
||||
[...Array(nrRandomPositions).keys()].map((_, index) => {
|
||||
const nonZeroIndex = index + 1;
|
||||
const indexFloor = Math.floor(index / 8);
|
||||
const distanceCoefficient = indexFloor + 1;
|
||||
const side = 3 + indexFloor * 2;
|
||||
const sideMiddle = Math.ceil(side / 2);
|
||||
const inbetween = 1 + indexFloor * 2;
|
||||
const inbetweenMiddle = Math.ceil(inbetween / 2);
|
||||
|
||||
if (index < side) {
|
||||
const distance =
|
||||
nonZeroIndex === sideMiddle
|
||||
? 0
|
||||
: nonZeroIndex < sideMiddle
|
||||
? -nonZeroIndex
|
||||
: nonZeroIndex - sideMiddle;
|
||||
return { x: actorX - sizeX * distance, y: actorY - sizeY * distanceCoefficient };
|
||||
} else if (index < side + inbetween) {
|
||||
const inbetweenIndex = nonZeroIndex - side;
|
||||
const distance =
|
||||
inbetweenIndex === inbetweenMiddle
|
||||
? 0
|
||||
: inbetweenIndex < inbetweenMiddle
|
||||
? -inbetweenIndex
|
||||
: inbetweenIndex - inbetweenMiddle;
|
||||
return { x: actorX + sizeX * distanceCoefficient, y: actorY + sizeY * distance };
|
||||
} else if (index < 2 * side + inbetween) {
|
||||
const sideIndex = nonZeroIndex - side - inbetween;
|
||||
const distance =
|
||||
sideIndex === sideMiddle
|
||||
? 0
|
||||
: sideIndex < sideMiddle
|
||||
? sideIndex
|
||||
: -(sideIndex - sideMiddle);
|
||||
return { x: actorX + sizeX * distance, y: actorY + sizeY * distanceCoefficient };
|
||||
} else {
|
||||
const inbetweenIndex = nonZeroIndex - 2 * side - inbetween;
|
||||
const distance =
|
||||
inbetweenIndex === inbetweenMiddle
|
||||
? 0
|
||||
: inbetweenIndex < inbetweenMiddle
|
||||
? inbetweenIndex
|
||||
: -(inbetweenIndex - inbetweenMiddle);
|
||||
return { x: actorX - sizeX * distanceCoefficient, y: actorY + sizeY * distance };
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
for (let token of newTokens) {
|
||||
const position = positions.pop();
|
||||
token.update(
|
||||
{ x: position.x, y: position.y, alpha: 1 },
|
||||
{ animation: { duration: animationDuration } }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_getStatusEffectChoices() {
|
||||
// Include all HUD-enabled status effects
|
||||
const choices = {};
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
export default class DhSceneConfigSettings extends foundry.applications.sheets.SceneConfig {
|
||||
constructor(options, ...args) {
|
||||
super(options, ...args);
|
||||
}
|
||||
// static DEFAULT_OPTIONS = {
|
||||
// ...super.DEFAULT_OPTIONS,
|
||||
// form: {
|
||||
// handler: this.updateData,
|
||||
// closeOnSubmit: true
|
||||
// }
|
||||
// };
|
||||
|
||||
static buildParts() {
|
||||
const { footer, ...parts } = super.PARTS;
|
||||
const { footer, tabs, ...parts } = super.PARTS;
|
||||
const tmpParts = {
|
||||
// tabs,
|
||||
tabs: { template: 'systems/daggerheart/templates/scene/tabs.hbs' },
|
||||
...parts,
|
||||
dh: { template: 'systems/daggerheart/templates/scene/dh-config.hbs' },
|
||||
footer
|
||||
|
|
@ -16,9 +22,42 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S
|
|||
static PARTS = DhSceneConfigSettings.buildParts();
|
||||
|
||||
static buildTabs() {
|
||||
super.TABS.sheet.tabs.push({ id: 'dh', icon: 'fa-solid' });
|
||||
super.TABS.sheet.tabs.push({ id: 'dh', src: 'systems/daggerheart/assets/logos/FoundryBorneLogoWhite.svg' });
|
||||
return super.TABS;
|
||||
}
|
||||
|
||||
static TABS = DhSceneConfigSettings.buildTabs();
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
switch (partId) {
|
||||
case 'dh':
|
||||
htmlElement.querySelector('#rangeMeasurementSetting')?.addEventListener('change', async event => {
|
||||
const flagData = foundry.utils.mergeObject(this.document.flags.daggerheart, {
|
||||
rangeMeasurement: { setting: event.target.value }
|
||||
});
|
||||
this.document.flags.daggerheart = flagData;
|
||||
this.render();
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
async _preparePartContext(partId, context, options) {
|
||||
context = await super._preparePartContext(partId, context, options);
|
||||
switch (partId) {
|
||||
case 'dh':
|
||||
context.data = new game.system.api.data.scenes.DHScene(canvas.scene.flags.daggerheart);
|
||||
context.variantRules = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules);
|
||||
break;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
// static async updateData(event, _, formData) {
|
||||
// const data = foundry.utils.expandObject(formData.object);
|
||||
// this.close(data);
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,14 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
const path = isDowntime ? `restMoves.${type}.moves.${id}` : `itemFeatures.${type}.${id}`;
|
||||
const featureBase = isDowntime ? this.settings.restMoves[type].moves[id] : this.settings.itemFeatures[type][id];
|
||||
|
||||
const configTitle = isDowntime
|
||||
? game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.downtimeMove')
|
||||
: type === 'armorFeatures'
|
||||
? game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.armorFeature')
|
||||
: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.weaponFeature');
|
||||
|
||||
const editedBase = await game.system.api.applications.sheetConfigs.SettingFeatureConfig.configure(
|
||||
configTitle,
|
||||
featureBase,
|
||||
path,
|
||||
this.settings,
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ import DHActionConfig from './action-config.mjs';
|
|||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class SettingFeatureConfig extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(move, movePath, settings, optionalParts, options) {
|
||||
constructor(configTitle, move, movePath, settings, optionalParts, options) {
|
||||
super(options);
|
||||
|
||||
this.configTitle = configTitle;
|
||||
this.move = move;
|
||||
|
||||
this.movePath = movePath;
|
||||
|
|
@ -19,7 +20,7 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
|||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.downtimeMoves');
|
||||
return this.configTitle;
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
|
|
@ -200,9 +201,9 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
|||
if (!options.submitted) this.move = null;
|
||||
}
|
||||
|
||||
static async configure(move, movePath, settings, optionalParts, options = {}) {
|
||||
static async configure(configTitle, move, movePath, settings, optionalParts, options = {}) {
|
||||
return new Promise(resolve => {
|
||||
const app = new this(move, movePath, settings, optionalParts, options);
|
||||
const app = new this(configTitle, move, movePath, settings, optionalParts, options);
|
||||
app.addEventListener('close', () => resolve(app.move), { once: true });
|
||||
app.render({ force: true });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@ export { default as Adversary } from './adversary.mjs';
|
|||
export { default as Character } from './character.mjs';
|
||||
export { default as Companion } from './companion.mjs';
|
||||
export { default as Environment } from './environment.mjs';
|
||||
export { default as Party } from './party.mjs';
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
|||
position: { width: 660, height: 766 },
|
||||
window: { resizable: true },
|
||||
actions: {
|
||||
toggleHitPoints: AdversarySheet.#toggleHitPoints,
|
||||
toggleStress: AdversarySheet.#toggleStress,
|
||||
reactionRoll: AdversarySheet.#reactionRoll,
|
||||
toggleResourceDice: AdversarySheet.#toggleResourceDice,
|
||||
handleResourceDice: AdversarySheet.#handleResourceDice
|
||||
|
|
@ -75,6 +77,16 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
|||
const context = await super._prepareContext(options);
|
||||
context.systemFields.attack.fields = this.document.system.attack.schema.fields;
|
||||
|
||||
context.resources = Object.keys(this.document.system.resources).reduce((acc, key) => {
|
||||
acc[key] = this.document.system.resources[key];
|
||||
return acc;
|
||||
}, {});
|
||||
const maxResource = Math.max(context.resources.hitPoints.max, context.resources.stress.max);
|
||||
context.resources.hitPoints.emptyPips =
|
||||
context.resources.hitPoints.max < maxResource ? maxResource - context.resources.hitPoints.max : 0;
|
||||
context.resources.stress.emptyPips =
|
||||
context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
@ -155,6 +167,27 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
|||
/* Application Clicks Actions */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Toggles hitpoint resource value.
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleHitPoints(_, button) {
|
||||
const hitPointsValue = Number.parseInt(button.dataset.value);
|
||||
const newValue =
|
||||
this.document.system.resources.hitPoints.value >= hitPointsValue ? hitPointsValue - 1 : hitPointsValue;
|
||||
await this.document.update({ 'system.resources.hitPoints.value': newValue });
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles stress resource value.
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleStress(_, button) {
|
||||
const StressValue = Number.parseInt(button.dataset.value);
|
||||
const newValue = this.document.system.resources.stress.value >= StressValue ? StressValue - 1 : StressValue;
|
||||
await this.document.update({ 'system.resources.stress.value': newValue });
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a reaction roll for an Adversary.
|
||||
* @type {ApplicationClickAction}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
actions: {
|
||||
toggleVault: CharacterSheet.#toggleVault,
|
||||
rollAttribute: CharacterSheet.#rollAttribute,
|
||||
toggleHitPoints: CharacterSheet.#toggleHitPoints,
|
||||
toggleStress: CharacterSheet.#toggleStress,
|
||||
toggleArmor: CharacterSheet.#toggleArmor,
|
||||
toggleHope: CharacterSheet.#toggleHope,
|
||||
toggleLoadoutView: CharacterSheet.#toggleLoadoutView,
|
||||
openPack: CharacterSheet.#openPack,
|
||||
|
|
@ -196,6 +199,16 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
return acc;
|
||||
}, {});
|
||||
|
||||
context.resources = Object.keys(this.document.system.resources).reduce((acc, key) => {
|
||||
acc[key] = this.document.system.resources[key];
|
||||
return acc;
|
||||
}, {});
|
||||
const maxResource = Math.max(context.resources.hitPoints.max, context.resources.stress.max);
|
||||
context.resources.hitPoints.emptyPips =
|
||||
context.resources.hitPoints.max < maxResource ? maxResource - context.resources.hitPoints.max : 0;
|
||||
context.resources.stress.emptyPips =
|
||||
context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0;
|
||||
|
||||
context.inventory = {
|
||||
currency: {
|
||||
title: game.i18n.localize('DAGGERHEART.CONFIG.Gold.title'),
|
||||
|
|
@ -746,6 +759,37 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles hitpoint resource value.
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleHitPoints(_, button) {
|
||||
const hitPointsValue = Number.parseInt(button.dataset.value);
|
||||
const newValue =
|
||||
this.document.system.resources.hitPoints.value >= hitPointsValue ? hitPointsValue - 1 : hitPointsValue;
|
||||
await this.document.update({ 'system.resources.hitPoints.value': newValue });
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles stress resource value.
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleStress(_, button) {
|
||||
const StressValue = Number.parseInt(button.dataset.value);
|
||||
const newValue = this.document.system.resources.stress.value >= StressValue ? StressValue - 1 : StressValue;
|
||||
await this.document.update({ 'system.resources.stress.value': newValue });
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles ArmorScore resource value.
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleArmor(_, button, element) {
|
||||
const ArmorValue = Number.parseInt(button.dataset.value);
|
||||
const newValue = this.document.system.armor.system.marks.value >= ArmorValue ? ArmorValue - 1 : ArmorValue;
|
||||
await this.document.system.armor.update({ 'system.marks.value': newValue });
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a hope resource value.
|
||||
* @type {ApplicationClickAction}
|
||||
|
|
@ -844,6 +888,23 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
itemData.system.inVault = true;
|
||||
}
|
||||
|
||||
if (item.type === 'beastform') {
|
||||
if (this.document.effects.find(x => x.type === 'beastform')) {
|
||||
return ui.notifications.warn(
|
||||
game.i18n.localize('DAGGERHEART.UI.Notifications.beastformAlreadyApplied')
|
||||
);
|
||||
}
|
||||
|
||||
const data = await game.system.api.data.items.DHBeastform.getWildcardImage(this.document, itemData);
|
||||
if (data) {
|
||||
if (!data.selectedImage) return;
|
||||
else {
|
||||
if (data.usesDynamicToken) itemData.system.tokenRingImg = data.selectedImage;
|
||||
else itemData.system.tokenImg = data.selectedImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.document.uuid === item.parent?.uuid) return this._onSortItem(event, itemData);
|
||||
const createdItem = await this._onDropItemCreate(itemData);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export default class DhCompanionSheet extends DHBaseActorSheet {
|
|||
classes: ['actor', 'companion'],
|
||||
position: { width: 340 },
|
||||
actions: {
|
||||
toggleStress: DhCompanionSheet.#toggleStress,
|
||||
actionRoll: DhCompanionSheet.#actionRoll,
|
||||
levelManagement: DhCompanionSheet.#levelManagement
|
||||
}
|
||||
|
|
@ -50,6 +51,16 @@ export default class DhCompanionSheet extends DHBaseActorSheet {
|
|||
/* Application Clicks Actions */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Toggles stress resource value.
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleStress(_, button) {
|
||||
const StressValue = Number.parseInt(button.dataset.value);
|
||||
const newValue = this.document.system.resources.stress.value >= StressValue ? StressValue - 1 : StressValue;
|
||||
await this.document.update({ 'system.resources.stress.value': newValue });
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -143,7 +143,6 @@ export default class DhpEnvironment extends DHBaseActorSheet {
|
|||
/* Application Clicks Actions */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Toggle the used state of a resource dice.
|
||||
* @type {ApplicationClickAction}
|
||||
|
|
@ -177,5 +176,4 @@ export default class DhpEnvironment extends DHBaseActorSheet {
|
|||
}, {})
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
512
module/applications/sheets/actors/party.mjs
Normal file
512
module/applications/sheets/actors/party.mjs
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
import DHBaseActorSheet from '../api/base-actor.mjs';
|
||||
import { getDocFromElement } from '../../../helpers/utils.mjs';
|
||||
import { ItemBrowser } from '../../ui/itemBrowser.mjs';
|
||||
import FilterMenu from '../../ux/filter-menu.mjs';
|
||||
import DaggerheartMenu from '../../sidebar/tabs/daggerheartMenu.mjs';
|
||||
import { socketEvent } from '../../../systemRegistration/socket.mjs';
|
||||
import GroupRollDialog from '../../dialogs/group-roll-dialog.mjs';
|
||||
import DhpActor from '../../../documents/actor.mjs';
|
||||
import DHItem from '../../../documents/item.mjs';
|
||||
|
||||
export default class Party extends DHBaseActorSheet {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.refreshSelections = DaggerheartMenu.defaultRefreshSelections();
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['party'],
|
||||
position: {
|
||||
width: 550
|
||||
},
|
||||
window: {
|
||||
resizable: true
|
||||
},
|
||||
actions: {
|
||||
deletePartyMember: Party.#deletePartyMember,
|
||||
deleteItem: Party.#deleteItem,
|
||||
toggleHope: Party.#toggleHope,
|
||||
toggleHitPoints: Party.#toggleHitPoints,
|
||||
toggleStress: Party.#toggleStress,
|
||||
toggleArmorSlot: Party.#toggleArmorSlot,
|
||||
tempBrowser: Party.#tempBrowser,
|
||||
refeshActions: Party.#refeshActions,
|
||||
triggerRest: Party.#triggerRest,
|
||||
tagTeamRoll: Party.#tagTeamRoll,
|
||||
groupRoll: Party.#groupRoll,
|
||||
selectRefreshable: DaggerheartMenu.selectRefreshable,
|
||||
refreshActors: DaggerheartMenu.refreshActors
|
||||
},
|
||||
dragDrop: [{ dragSelector: '.actors-section .inventory-item', dropSelector: null }]
|
||||
};
|
||||
|
||||
/**@override */
|
||||
static PARTS = {
|
||||
header: { template: 'systems/daggerheart/templates/sheets/actors/party/header.hbs' },
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
partyMembers: { template: 'systems/daggerheart/templates/sheets/actors/party/party-members.hbs' },
|
||||
resources: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/party/resources.hbs',
|
||||
scrollable: ['']
|
||||
},
|
||||
/* NOT YET IMPLEMENTED */
|
||||
// projects: {
|
||||
// template: 'systems/daggerheart/templates/sheets/actors/party/projects.hbs',
|
||||
// scrollable: ['']
|
||||
// },
|
||||
inventory: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/party/inventory.hbs',
|
||||
scrollable: ['.tab.inventory .items-section']
|
||||
},
|
||||
notes: { template: 'systems/daggerheart/templates/sheets/actors/party/notes.hbs' }
|
||||
};
|
||||
|
||||
/** @inheritdoc */
|
||||
static TABS = {
|
||||
primary: {
|
||||
tabs: [
|
||||
{ id: 'partyMembers' },
|
||||
{ id: 'resources' },
|
||||
/* NOT YET IMPLEMENTED */
|
||||
// { id: 'projects' },
|
||||
{ id: 'inventory' },
|
||||
{ id: 'notes' }
|
||||
],
|
||||
initial: 'partyMembers',
|
||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||
}
|
||||
};
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options);
|
||||
this._createFilterMenus();
|
||||
this._createSearchFilter();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Prepare Context */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
|
||||
context.inventory = {
|
||||
currency: {
|
||||
title: game.i18n.localize('DAGGERHEART.CONFIG.Gold.title'),
|
||||
coins: game.i18n.localize('DAGGERHEART.CONFIG.Gold.coins'),
|
||||
handfuls: game.i18n.localize('DAGGERHEART.CONFIG.Gold.handfuls'),
|
||||
bags: game.i18n.localize('DAGGERHEART.CONFIG.Gold.bags'),
|
||||
chests: game.i18n.localize('DAGGERHEART.CONFIG.Gold.chests')
|
||||
}
|
||||
};
|
||||
|
||||
const homebrewCurrency = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).currency;
|
||||
if (homebrewCurrency.enabled) {
|
||||
context.inventory.currency = homebrewCurrency;
|
||||
}
|
||||
|
||||
if (context.inventory.length === 0) {
|
||||
context.inventory = Array(1).fill(Array(5).fill([]));
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
async _preparePartContext(partId, context, options) {
|
||||
context = await super._preparePartContext(partId, context, options);
|
||||
switch (partId) {
|
||||
case 'header':
|
||||
await this._prepareHeaderContext(context, options);
|
||||
break;
|
||||
case 'notes':
|
||||
await this._prepareNotesContext(context, options);
|
||||
break;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare render context for the Header part.
|
||||
* @param {ApplicationRenderContext} context
|
||||
* @param {ApplicationRenderOptions} options
|
||||
* @returns {Promise<void>}
|
||||
* @protected
|
||||
*/
|
||||
async _prepareHeaderContext(context, _options) {
|
||||
const { system } = this.document;
|
||||
const { TextEditor } = foundry.applications.ux;
|
||||
|
||||
context.description = await TextEditor.implementation.enrichHTML(system.description, {
|
||||
secrets: this.document.isOwner,
|
||||
relativeTo: this.document
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare render context for the Biography part.
|
||||
* @param {ApplicationRenderContext} context
|
||||
* @param {ApplicationRenderOptions} options
|
||||
* @returns {Promise<void>}
|
||||
* @protected
|
||||
*/
|
||||
async _prepareNotesContext(context, _options) {
|
||||
const { system } = this.document;
|
||||
const { TextEditor } = foundry.applications.ux;
|
||||
|
||||
const paths = {
|
||||
notes: 'notes'
|
||||
};
|
||||
|
||||
for (const [key, path] of Object.entries(paths)) {
|
||||
const value = foundry.utils.getProperty(system, path);
|
||||
context[key] = {
|
||||
field: system.schema.getField(path),
|
||||
value,
|
||||
enriched: await TextEditor.implementation.enrichHTML(value, {
|
||||
secrets: this.document.isOwner,
|
||||
relativeTo: this.document
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a hope resource value.
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleHope(_, target) {
|
||||
const hopeValue = Number.parseInt(target.dataset.value);
|
||||
const actor = await foundry.utils.fromUuid(target.dataset.actorId);
|
||||
const newValue = actor.system.resources.hope.value >= hopeValue ? hopeValue - 1 : hopeValue;
|
||||
await actor.update({ 'system.resources.hope.value': newValue });
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a hp resource value.
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleHitPoints(_, target) {
|
||||
const hitPointsValue = Number.parseInt(target.dataset.value);
|
||||
const actor = await foundry.utils.fromUuid(target.dataset.actorId);
|
||||
const newValue = actor.system.resources.hitPoints.value >= hitPointsValue ? hitPointsValue - 1 : hitPointsValue;
|
||||
await actor.update({ 'system.resources.hitPoints.value': newValue });
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a stress resource value.
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleStress(_, target) {
|
||||
const stressValue = Number.parseInt(target.dataset.value);
|
||||
const actor = await foundry.utils.fromUuid(target.dataset.actorId);
|
||||
const newValue = actor.system.resources.stress.value >= stressValue ? stressValue - 1 : stressValue;
|
||||
await actor.update({ 'system.resources.stress.value': newValue });
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a armor slot resource value.
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleArmorSlot(_, target, element) {
|
||||
const armorItem = await foundry.utils.fromUuid(target.dataset.itemUuid);
|
||||
const armorValue = Number.parseInt(target.dataset.value);
|
||||
const newValue = armorItem.system.marks.value >= armorValue ? armorValue - 1 : armorValue;
|
||||
await armorItem.update({ 'system.marks.value': newValue });
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens Compedium Browser
|
||||
*/
|
||||
static async #tempBrowser(_, target) {
|
||||
new ItemBrowser().render({ force: true });
|
||||
}
|
||||
|
||||
static async #refeshActions() {
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: 'New Section',
|
||||
icon: 'fa-solid fa-campground'
|
||||
},
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/sidebar/daggerheart-menu/main.hbs',
|
||||
{
|
||||
refreshables: DaggerheartMenu.defaultRefreshSelections()
|
||||
}
|
||||
),
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'tab', 'sidebar-tab', 'daggerheartMenu-sidebar']
|
||||
});
|
||||
|
||||
if (!confirmed) return;
|
||||
}
|
||||
|
||||
static async #triggerRest(_, button) {
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: game.i18n.localize(`DAGGERHEART.APPLICATIONS.Downtime.${button.dataset.type}.title`),
|
||||
icon: button.dataset.type === 'shortRest' ? 'fa-solid fa-utensils' : 'fa-solid fa-bed'
|
||||
},
|
||||
content: 'This will trigger a dialog to players make their downtime moves, are you sure?',
|
||||
classes: ['daggerheart', 'dialog', 'dh-style']
|
||||
});
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
this.document.system.partyMembers.forEach(actor => {
|
||||
game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.DowntimeTrigger,
|
||||
data: {
|
||||
actorId: actor.uuid,
|
||||
downtimeType: button.dataset.type
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static async downtimeMoveQuery({ actorId, downtimeType }) {
|
||||
const actor = await foundry.utils.fromUuid(actorId);
|
||||
if (!actor || !actor?.isOwner) reject();
|
||||
new game.system.api.applications.dialogs.Downtime(actor, downtimeType === 'shortRest').render({
|
||||
force: true
|
||||
});
|
||||
}
|
||||
|
||||
static async #tagTeamRoll() {
|
||||
new game.system.api.applications.dialogs.TagTeamDialog(this.document.system.partyMembers).render({
|
||||
force: true
|
||||
});
|
||||
}
|
||||
|
||||
static async #groupRoll(params) {
|
||||
new GroupRollDialog(this.document.system.partyMembers).render({ force: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of ContextMenu options for Consumable and Loot.
|
||||
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
|
||||
* @this {CharacterSheet}
|
||||
* @protected
|
||||
*/
|
||||
static #getItemContextOptions() {
|
||||
return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true });
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
/* Filter Tracking */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* The currently active search filter.
|
||||
* @type {foundry.applications.ux.SearchFilter}
|
||||
*/
|
||||
#search = {};
|
||||
|
||||
/**
|
||||
* The currently active search filter.
|
||||
* @type {FilterMenu}
|
||||
*/
|
||||
#menu = {};
|
||||
|
||||
/**
|
||||
* Tracks which item IDs are currently displayed, organized by filter type and section.
|
||||
* @type {{
|
||||
* inventory: {
|
||||
* search: Set<string>,
|
||||
* menu: Set<string>
|
||||
* },
|
||||
* loadout: {
|
||||
* search: Set<string>,
|
||||
* menu: Set<string>
|
||||
* },
|
||||
* }}
|
||||
*/
|
||||
#filteredItems = {
|
||||
inventory: {
|
||||
search: new Set(),
|
||||
menu: new Set()
|
||||
},
|
||||
loadout: {
|
||||
search: new Set(),
|
||||
menu: new Set()
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Search Inputs */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Create and initialize search filter instances for the inventory and loadout sections.
|
||||
*
|
||||
* 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() {
|
||||
//Filters could be a application option if needed
|
||||
const filters = [
|
||||
{
|
||||
key: 'inventory',
|
||||
input: 'input[type="search"].search-inventory',
|
||||
content: '[data-application-part="inventory"] .items-section',
|
||||
callback: this._onSearchFilterInventory.bind(this)
|
||||
}
|
||||
];
|
||||
|
||||
for (const { key, input, content, callback } of filters) {
|
||||
const filter = new foundry.applications.ux.SearchFilter({
|
||||
inputSelector: input,
|
||||
contentSelector: content,
|
||||
callback
|
||||
});
|
||||
filter.bind(this.element);
|
||||
this.#search[key] = filter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle invetory items search and filtering.
|
||||
* @param {KeyboardEvent} event The keyboard input event.
|
||||
* @param {string} query The input search string.
|
||||
* @param {RegExp} rgx The regular expression query that should be matched against.
|
||||
* @param {HTMLElement} html The container to filter items from.
|
||||
* @protected
|
||||
*/
|
||||
async _onSearchFilterInventory(_event, query, rgx, html) {
|
||||
this.#filteredItems.inventory.search.clear();
|
||||
|
||||
for (const li of html.querySelectorAll('.inventory-item')) {
|
||||
const item = await getDocFromElement(li);
|
||||
const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
|
||||
if (matchesSearch) this.#filteredItems.inventory.search.add(item.id);
|
||||
const { menu } = this.#filteredItems.inventory;
|
||||
li.hidden = !(menu.has(item.id) && matchesSearch);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Filter Menus */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
_createFilterMenus() {
|
||||
//Menus could be a application option if needed
|
||||
const menus = [
|
||||
{
|
||||
key: 'inventory',
|
||||
container: '[data-application-part="inventory"]',
|
||||
content: '.items-section',
|
||||
callback: this._onMenuFilterInventory.bind(this),
|
||||
target: '.filter-button',
|
||||
filters: FilterMenu.invetoryFilters
|
||||
}
|
||||
];
|
||||
|
||||
menus.forEach(m => {
|
||||
const container = this.element.querySelector(m.container);
|
||||
this.#menu[m.key] = new FilterMenu(container, m.target, m.filters, m.callback, {
|
||||
contentSelector: m.content
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when filters change
|
||||
* @param {PointerEvent} event
|
||||
* @param {HTMLElement} html
|
||||
* @param {import('../ux/filter-menu.mjs').FilterItem[]} filters
|
||||
*/
|
||||
async _onMenuFilterInventory(_event, html, filters) {
|
||||
this.#filteredItems.inventory.menu.clear();
|
||||
|
||||
for (const li of html.querySelectorAll('.inventory-item')) {
|
||||
const item = await getDocFromElement(li);
|
||||
|
||||
const matchesMenu =
|
||||
filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f));
|
||||
if (matchesMenu) this.#filteredItems.inventory.menu.add(item.id);
|
||||
|
||||
const { search } = this.#filteredItems.inventory;
|
||||
li.hidden = !(search.has(item.id) && matchesMenu);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
async _onDragStart(event) {
|
||||
const item = event.currentTarget.closest('.inventory-item');
|
||||
|
||||
if (item) {
|
||||
const adversaryData = { type: 'Actor', uuid: item.dataset.itemUuid };
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(adversaryData));
|
||||
event.dataTransfer.setDragImage(item, 60, 0);
|
||||
}
|
||||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
// Prevent event bubbling to avoid duplicate handling
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
const item = await foundry.utils.fromUuid(data.uuid);
|
||||
|
||||
if (item instanceof DhpActor) {
|
||||
const currentMembers = this.document.system.partyMembers.map(x => x.uuid);
|
||||
if (currentMembers.includes(data.uuid)) {
|
||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.duplicateCharacter'));
|
||||
}
|
||||
|
||||
await this.document.update({ 'system.partyMembers': [...currentMembers, item.uuid] });
|
||||
} else if (item instanceof DHItem) {
|
||||
this.document.createEmbeddedDocuments('Item', [item.toObject()]);
|
||||
} else {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.onlyCharactersInPartySheet'));
|
||||
}
|
||||
}
|
||||
|
||||
static async #deletePartyMember(event, target) {
|
||||
const doc = await getDocFromElement(target.closest('.inventory-item'));
|
||||
|
||||
if (!event.shiftKey) {
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
|
||||
type: game.i18n.localize('TYPES.Actor.adversary'),
|
||||
name: doc.name
|
||||
})
|
||||
},
|
||||
content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: doc.name })
|
||||
});
|
||||
|
||||
if (!confirmed) return;
|
||||
}
|
||||
|
||||
const currentMembers = this.document.system.partyMembers.map(x => x.uuid);
|
||||
const newMemberdList = currentMembers.filter(uuid => uuid !== doc.uuid);
|
||||
await this.document.update({ 'system.partyMembers': newMemberdList });
|
||||
}
|
||||
|
||||
static async #deleteItem(event, target) {
|
||||
const doc = await getDocFromElement(target.closest('.inventory-item'));
|
||||
if (!event.shiftKey) {
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
|
||||
type: game.i18n.localize('TYPES.Actor.party'),
|
||||
name: doc.name
|
||||
})
|
||||
},
|
||||
content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: doc.name })
|
||||
});
|
||||
|
||||
if (!confirmed) return;
|
||||
}
|
||||
|
||||
this.document.deleteEmbeddedDocuments('Item', [doc.id]);
|
||||
}
|
||||
}
|
||||
|
|
@ -61,6 +61,10 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.isNPC = this.document.isNPC;
|
||||
context.useResourcePips = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.appearance
|
||||
).useResourcePips;
|
||||
context.showAttribution = !game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
|
||||
.hideAttribution;
|
||||
|
||||
|
|
|
|||
|
|
@ -203,10 +203,10 @@ export default class ClassSheet extends DHBaseItemSheet {
|
|||
|
||||
if (target === 'subclasses') {
|
||||
const subclass = await foundry.utils.fromUuid(uuid);
|
||||
await subclass.update({ 'system.linkedClass': null });
|
||||
await subclass?.update({ 'system.linkedClass': null });
|
||||
}
|
||||
|
||||
await this.document.update({ [`system.${target}`]: prop.filter(i => i.uuid !== uuid).map(x => x.uuid) });
|
||||
await this.document.update({ [`system.${target}`]: prop.filter(i => i && i.uuid !== uuid).map(x => x.uuid) });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
|||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.refreshSelections = DaggerheartMenu.#defaultRefreshSelections();
|
||||
this.refreshSelections = DaggerheartMenu.defaultRefreshSelections();
|
||||
}
|
||||
|
||||
static #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') },
|
||||
|
|
@ -138,7 +138,7 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
|||
types: `[${types}]`
|
||||
})
|
||||
);
|
||||
this.refreshSelections = DaggerheartMenu.#defaultRefreshSelections();
|
||||
this.refreshSelections = DaggerheartMenu.defaultRefreshSelections();
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const msg = {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import { abilities } from '../../config/actorConfig.mjs';
|
||||
import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
|
||||
export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
|
@ -35,7 +38,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
// }
|
||||
// },
|
||||
{
|
||||
name: 'Reroll Damage',
|
||||
name: game.i18n.localize('DAGGERHEART.UI.ChatLog.rerollDamage'),
|
||||
icon: '<i class="fa-solid fa-dice"></i>',
|
||||
condition: li => {
|
||||
const message = game.messages.get(li.dataset.messageId);
|
||||
|
|
@ -65,6 +68,18 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
html.querySelectorAll('.reroll-button').forEach(element =>
|
||||
element.addEventListener('click', event => this.rerollEvent(event, data.message))
|
||||
);
|
||||
html.querySelectorAll('.group-roll-button').forEach(element =>
|
||||
element.addEventListener('click', event => this.groupRollButton(event, data.message))
|
||||
);
|
||||
html.querySelectorAll('.group-roll-reroll').forEach(element =>
|
||||
element.addEventListener('click', event => this.groupRollReroll(event, data.message))
|
||||
);
|
||||
html.querySelectorAll('.group-roll-success').forEach(element =>
|
||||
element.addEventListener('click', event => this.groupRollSuccessEvent(event, data.message))
|
||||
);
|
||||
html.querySelectorAll('.group-roll-header-expand-section').forEach(element =>
|
||||
element.addEventListener('click', this.groupRollExpandSection)
|
||||
);
|
||||
};
|
||||
|
||||
setupHooks() {
|
||||
|
|
@ -164,6 +179,169 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
'system.roll': newRoll,
|
||||
'rolls': [parsedRoll]
|
||||
});
|
||||
|
||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll });
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.Refresh,
|
||||
data: {
|
||||
refreshType: RefreshType.TagTeamRoll
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async groupRollButton(event, message) {
|
||||
const path = event.currentTarget.dataset.path;
|
||||
const { actor: actorData, trait } = foundry.utils.getProperty(message.system, path);
|
||||
const actor = game.actors.get(actorData._id);
|
||||
|
||||
if (!actor.testUserPermission(game.user, 'OWNER')) {
|
||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noActorOwnership'));
|
||||
}
|
||||
|
||||
const traitLabel = game.i18n.localize(abilities[trait].label);
|
||||
const config = {
|
||||
event: event,
|
||||
title: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${actor.name}`,
|
||||
headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
||||
ability: traitLabel
|
||||
}),
|
||||
roll: {
|
||||
trait: trait,
|
||||
advantage: 0,
|
||||
modifiers: [{ label: traitLabel, value: actor.system.traits[trait].value }]
|
||||
},
|
||||
hasRoll: true,
|
||||
skips: {
|
||||
createMessage: true,
|
||||
resources: true
|
||||
}
|
||||
};
|
||||
const result = await actor.diceRoll({
|
||||
...config,
|
||||
headerTitle: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${actor.name}`,
|
||||
title: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
||||
ability: traitLabel
|
||||
})
|
||||
});
|
||||
|
||||
const newMessageData = foundry.utils.deepClone(message.system);
|
||||
foundry.utils.setProperty(newMessageData, `${path}.result`, result.roll);
|
||||
const renderData = { system: new game.system.api.models.chatMessages.config.groupRoll(newMessageData) };
|
||||
|
||||
const updatedContent = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/ui/chat/groupRoll.hbs',
|
||||
{ ...renderData, user: game.user }
|
||||
);
|
||||
const mess = game.messages.get(message._id);
|
||||
|
||||
await emitAsGM(
|
||||
GMUpdateEvent.UpdateDocument,
|
||||
mess.update.bind(mess),
|
||||
{
|
||||
...renderData,
|
||||
content: updatedContent
|
||||
},
|
||||
mess.uuid
|
||||
);
|
||||
}
|
||||
|
||||
async groupRollReroll(event, message) {
|
||||
const path = event.currentTarget.dataset.path;
|
||||
const { actor: actorData, trait } = foundry.utils.getProperty(message.system, path);
|
||||
const actor = game.actors.get(actorData._id);
|
||||
|
||||
if (!actor.testUserPermission(game.user, 'OWNER')) {
|
||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noActorOwnership'));
|
||||
}
|
||||
|
||||
const traitLabel = game.i18n.localize(abilities[trait].label);
|
||||
|
||||
const config = {
|
||||
event: event,
|
||||
title: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${actor.name}`,
|
||||
headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
||||
ability: traitLabel
|
||||
}),
|
||||
roll: {
|
||||
trait: trait,
|
||||
advantage: 0,
|
||||
modifiers: [{ label: traitLabel, value: actor.system.traits[trait].value }]
|
||||
},
|
||||
hasRoll: true,
|
||||
skips: {
|
||||
createMessage: true
|
||||
}
|
||||
};
|
||||
const result = await actor.diceRoll({
|
||||
...config,
|
||||
headerTitle: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${actor.name}`,
|
||||
title: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
||||
ability: traitLabel
|
||||
})
|
||||
});
|
||||
|
||||
const newMessageData = foundry.utils.deepClone(message.system);
|
||||
foundry.utils.setProperty(newMessageData, `${path}.result`, { ...result.roll, rerolled: true });
|
||||
const renderData = { system: new game.system.api.models.chatMessages.config.groupRoll(newMessageData) };
|
||||
|
||||
const updatedContent = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/ui/chat/groupRoll.hbs',
|
||||
{ ...renderData, user: game.user }
|
||||
);
|
||||
const mess = game.messages.get(message._id);
|
||||
await emitAsGM(
|
||||
GMUpdateEvent.UpdateDocument,
|
||||
mess.update.bind(mess),
|
||||
{
|
||||
...renderData,
|
||||
content: updatedContent
|
||||
},
|
||||
mess.uuid
|
||||
);
|
||||
}
|
||||
|
||||
async groupRollSuccessEvent(event, message) {
|
||||
if (!game.user.isGM) {
|
||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.gmOnly'));
|
||||
}
|
||||
|
||||
const { path, success } = event.currentTarget.dataset;
|
||||
const { actor: actorData } = foundry.utils.getProperty(message.system, path);
|
||||
const actor = game.actors.get(actorData._id);
|
||||
|
||||
if (!actor.testUserPermission(game.user, 'OWNER')) {
|
||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noActorOwnership'));
|
||||
}
|
||||
|
||||
const newMessageData = foundry.utils.deepClone(message.system);
|
||||
foundry.utils.setProperty(newMessageData, `${path}.manualSuccess`, Boolean(success));
|
||||
const renderData = { system: new game.system.api.models.chatMessages.config.groupRoll(newMessageData) };
|
||||
|
||||
const updatedContent = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/ui/chat/groupRoll.hbs',
|
||||
{ ...renderData, user: game.user }
|
||||
);
|
||||
const mess = game.messages.get(message._id);
|
||||
await emitAsGM(
|
||||
GMUpdateEvent.UpdateDocument,
|
||||
mess.update.bind(mess),
|
||||
{
|
||||
...renderData,
|
||||
content: updatedContent
|
||||
},
|
||||
mess.uuid
|
||||
);
|
||||
}
|
||||
|
||||
async groupRollExpandSection(event) {
|
||||
event.target
|
||||
.closest('.group-roll-header-expand-section')
|
||||
.querySelectorAll('i')
|
||||
.forEach(element => {
|
||||
element.classList.toggle('fa-angle-up');
|
||||
element.classList.toggle('fa-angle-down');
|
||||
});
|
||||
event.target.closest('.group-roll-section').querySelector('.group-roll-content').classList.toggle('closed');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,13 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
|||
return frame;
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
async _onFirstRender(context, options) {
|
||||
await super._onFirstRender(context, options);
|
||||
|
||||
this.toggleCollapsedPosition(undefined, !ui.sidebar.expanded);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext(options) {
|
||||
const context = await super._prepareContext(options);
|
||||
|
|
@ -124,6 +131,8 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
|||
}
|
||||
|
||||
toggleCollapsedPosition = async (_, collapsed) => {
|
||||
if (!this.element) return;
|
||||
|
||||
this.sidebarCollapsed = collapsed;
|
||||
if (!collapsed) this.element.classList.add('expanded');
|
||||
else this.element.classList.remove('expanded');
|
||||
|
|
@ -188,10 +197,13 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
|||
Hooks.on(socketEvent.Refresh, this.cooldownRefresh.bind());
|
||||
}
|
||||
|
||||
close(options) {
|
||||
async close(options) {
|
||||
/* Opt out of Foundry's standard behavior of closing all application windows marked as UI when Escape is pressed */
|
||||
if (options.closeKey) return;
|
||||
|
||||
Hooks.off('collapseSidebar', this.toggleCollapsedPosition);
|
||||
Hooks.off(socketEvent.Refresh, this.cooldownRefresh);
|
||||
super.close(options);
|
||||
return super.close(options);
|
||||
}
|
||||
|
||||
static async updateCountdowns(progressType) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { emitAsGM, GMUpdateEvent, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
import { emitAsGM, GMUpdateEvent } from '../../systemRegistration/socket.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
|
|
|
|||
|
|
@ -93,16 +93,17 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
if (lite === true) {
|
||||
this.compendiumBrowserTypeKey = 'compendiumBrowserLite';
|
||||
}
|
||||
const userPresetPosition = game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS[`${this.compendiumBrowserTypeKey}`].position) ;
|
||||
|
||||
const userPresetPosition = game.user.getFlag(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.FLAGS[`${this.compendiumBrowserTypeKey}`].position
|
||||
);
|
||||
|
||||
options.position = userPresetPosition ?? ItemBrowser.DEFAULT_OPTIONS.position;
|
||||
|
||||
|
||||
if (!userPresetPosition) {
|
||||
const width = noFolder === true || 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);
|
||||
|
|
|
|||
|
|
@ -18,30 +18,37 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur
|
|||
|
||||
static getRangeLabels(distanceValue, settings) {
|
||||
let result = { distance: distanceValue, units: '' };
|
||||
const rangeMeasurementOverride = canvas.scene.flags.daggerheart?.rangeMeasurementOverride;
|
||||
const sceneRangeMeasurement = canvas.scene.flags.daggerheart?.rangeMeasurement;
|
||||
|
||||
if (rangeMeasurementOverride === true) {
|
||||
const { disable, custom } = CONFIG.DH.GENERAL.sceneRangeMeasurementSetting;
|
||||
if (sceneRangeMeasurement?.setting === disable.id) {
|
||||
result.distance = distanceValue;
|
||||
result.units = canvas.scene?.grid?.units;
|
||||
return result;
|
||||
}
|
||||
if (distanceValue <= settings.melee) {
|
||||
|
||||
const melee = sceneRangeMeasurement?.setting === custom.id ? sceneRangeMeasurement.melee : settings.melee;
|
||||
const veryClose =
|
||||
sceneRangeMeasurement?.setting === custom.id ? sceneRangeMeasurement.veryClose : settings.veryClose;
|
||||
const close = sceneRangeMeasurement?.setting === custom.id ? sceneRangeMeasurement.close : settings.close;
|
||||
const far = sceneRangeMeasurement?.setting === custom.id ? sceneRangeMeasurement.far : settings.far;
|
||||
if (distanceValue <= melee) {
|
||||
result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.melee.name');
|
||||
return result;
|
||||
}
|
||||
if (distanceValue <= settings.veryClose) {
|
||||
if (distanceValue <= veryClose) {
|
||||
result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.veryClose.name');
|
||||
return result;
|
||||
}
|
||||
if (distanceValue <= settings.close) {
|
||||
if (distanceValue <= close) {
|
||||
result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.close.name');
|
||||
return result;
|
||||
}
|
||||
if (distanceValue <= settings.far) {
|
||||
if (distanceValue <= far) {
|
||||
result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.far.name');
|
||||
return result;
|
||||
}
|
||||
if (distanceValue > settings.far) {
|
||||
if (distanceValue > far) {
|
||||
result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.veryFar.name');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -166,14 +166,19 @@ export const healingTypes = {
|
|||
|
||||
export const defeatedConditions = () => {
|
||||
const defeated = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).defeated;
|
||||
return Object.values(defeatedConditionChoices).map(x => ({
|
||||
...x,
|
||||
img: defeated[`${x.id}Icon`],
|
||||
description: `DAGGERHEART.CONFIG.Condition.${x.id}.description`
|
||||
}));
|
||||
return Object.keys(defeatedConditionChoices).reduce((acc, key) => {
|
||||
const choice = defeatedConditionChoices[key];
|
||||
acc[key] = {
|
||||
...choice,
|
||||
img: defeated[`${choice.id}Icon`],
|
||||
description: `DAGGERHEART.CONFIG.Condition.${choice.id}.description`
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const defeatedConditionChoices = {
|
||||
export const defeatedConditionChoices = {
|
||||
defeated: {
|
||||
id: 'defeated',
|
||||
name: 'DAGGERHEART.CONFIG.Condition.defeated.name'
|
||||
|
|
@ -680,3 +685,18 @@ export const countdownAppMode = {
|
|||
textIcon: 'text-icon',
|
||||
iconOnly: 'icon-only'
|
||||
};
|
||||
|
||||
export const sceneRangeMeasurementSetting = {
|
||||
disable: {
|
||||
id: 'disable',
|
||||
label: 'Disable Daggerheart Range Measurement'
|
||||
},
|
||||
default: {
|
||||
id: 'default',
|
||||
label: 'Default'
|
||||
},
|
||||
custom: {
|
||||
id: 'custom',
|
||||
label: 'Custom'
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ export const gameSettings = {
|
|||
},
|
||||
LevelTiers: 'LevelTiers',
|
||||
Countdowns: 'Countdowns',
|
||||
LastMigrationVersion: 'LastMigrationVersion'
|
||||
LastMigrationVersion: 'LastMigrationVersion',
|
||||
TagTeamRoll: 'TagTeamRoll'
|
||||
};
|
||||
|
||||
export const actionAutomationChoices = {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export { default as DhCombat } from './combat.mjs';
|
||||
export { default as DhCombatant } from './combatant.mjs';
|
||||
export { default as DhTagTeamRoll } from './tagTeamRoll.mjs';
|
||||
|
||||
export * as actions from './action/_module.mjs';
|
||||
export * as activeEffects from './activeEffect/_module.mjs';
|
||||
|
|
@ -7,3 +8,4 @@ export * as actors from './actor/_module.mjs';
|
|||
export * as chatMessages from './chat-message/_modules.mjs';
|
||||
export * as fields from './fields/_module.mjs';
|
||||
export * as items from './item/_module.mjs';
|
||||
export * as scenes from './scene/_module.mjs';
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ import DhCharacter from './character.mjs';
|
|||
import DhCompanion from './companion.mjs';
|
||||
import DhAdversary from './adversary.mjs';
|
||||
import DhEnvironment from './environment.mjs';
|
||||
import DhParty from './party.mjs';
|
||||
|
||||
export { DhCharacter, DhCompanion, DhAdversary, DhEnvironment };
|
||||
export { DhCharacter, DhCompanion, DhAdversary, DhEnvironment, DhParty };
|
||||
|
||||
export const config = {
|
||||
character: DhCharacter,
|
||||
companion: DhCompanion,
|
||||
adversary: DhAdversary,
|
||||
environment: DhEnvironment
|
||||
environment: DhEnvironment,
|
||||
party: DhParty
|
||||
};
|
||||
|
|
|
|||
|
|
@ -170,4 +170,13 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
_getTags() {
|
||||
const tags = [
|
||||
game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${this.tier}`),
|
||||
`${game.i18n.localize(`DAGGERHEART.CONFIG.AdversaryType.${this.type}.label`)}`,
|
||||
`${game.i18n.localize('DAGGERHEART.GENERAL.difficulty')}: ${this.difficulty}`
|
||||
];
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -673,4 +673,8 @@ export default class DhCharacter extends BaseDataActor {
|
|||
this.companion.updateLevel(1);
|
||||
}
|
||||
}
|
||||
|
||||
_getTags() {
|
||||
return [this.class.value?.name, this.class.subclass?.name, this.community?.name, this.ancestry?.name].filter((t) => !!t);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
48
module/data/actor/party.mjs
Normal file
48
module/data/actor/party.mjs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import BaseDataActor from './base.mjs';
|
||||
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
||||
|
||||
export default class DhParty extends BaseDataActor {
|
||||
/**@inheritdoc */
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
partyMembers: new ForeignDocumentUUIDArrayField({ type: 'Actor' }),
|
||||
notes: new fields.HTMLField(),
|
||||
gold: new fields.SchemaField({
|
||||
coins: new fields.NumberField({ initial: 0, integer: true }),
|
||||
handfuls: new fields.NumberField({ initial: 1, integer: true }),
|
||||
bags: new fields.NumberField({ initial: 0, integer: true }),
|
||||
chests: new fields.NumberField({ initial: 0, integer: true })
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@inheritdoc */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/actors/dark-squad.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
this.partyMembers = this.partyMembers.filter(p => !!p);
|
||||
|
||||
// Register this party to all members
|
||||
if (game.actors.get(this.parent.id) === this.parent) {
|
||||
for (const member of this.partyMembers) {
|
||||
member.parties?.add(this.parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onDelete(options, userId) {
|
||||
super._onDelete(options, userId);
|
||||
|
||||
// Clear this party from all members that aren't deleted
|
||||
for (const member of this.partyMembers) {
|
||||
member.parties?.delete(this.parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import DHAbilityUse from './abilityUse.mjs';
|
||||
import DHActorRoll from './actorRoll.mjs';
|
||||
import DHGroupRoll from './groupRoll.mjs';
|
||||
import DHSystemMessage from './systemMessage.mjs';
|
||||
|
||||
export const config = {
|
||||
|
|
@ -7,5 +8,6 @@ export const config = {
|
|||
adversaryRoll: DHActorRoll,
|
||||
damageRoll: DHActorRoll,
|
||||
dualityRoll: DHActorRoll,
|
||||
groupRoll: DHGroupRoll,
|
||||
systemMessage: DHSystemMessage
|
||||
};
|
||||
|
|
|
|||
39
module/data/chat-message/groupRoll.mjs
Normal file
39
module/data/chat-message/groupRoll.mjs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import { abilities } from '../../config/actorConfig.mjs';
|
||||
|
||||
export default class DHGroupRoll extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
leader: new fields.EmbeddedDataField(GroupRollMemberField),
|
||||
members: new fields.ArrayField(new fields.EmbeddedDataField(GroupRollMemberField))
|
||||
};
|
||||
}
|
||||
|
||||
get totalModifier() {
|
||||
return this.members.reduce((acc, m) => {
|
||||
if (m.manualSuccess === null) return acc;
|
||||
|
||||
return acc + (m.manualSuccess ? 1 : -1);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
class GroupRollMemberField extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
actor: new fields.ObjectField(),
|
||||
trait: new fields.StringField({ choices: abilities }),
|
||||
difficulty: new fields.StringField(),
|
||||
result: new fields.ObjectField({ nullable: true, initial: null }),
|
||||
manualSuccess: new fields.BooleanField({ nullable: true, initial: null })
|
||||
};
|
||||
}
|
||||
|
||||
/* Can be expanded if we handle automation of success/failure */
|
||||
get success() {
|
||||
return manualSuccess;
|
||||
}
|
||||
}
|
||||
|
|
@ -79,7 +79,7 @@ export default class BeastformField extends fields.SchemaField {
|
|||
* @returns
|
||||
*/
|
||||
static async transform(selectedForm, evolvedData, hybridData) {
|
||||
const formData = evolvedData?.form ? evolvedData.form.toObject() : selectedForm.toObject();
|
||||
const formData = evolvedData?.form ? evolvedData.form.toObject() : selectedForm;
|
||||
const beastformEffect = formData.effects.find(x => x.type === 'beastform');
|
||||
if (!beastformEffect) {
|
||||
ui.notifications.error('DAGGERHEART.UI.Notifications.beastformMissingEffect');
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ export default class DamageField extends fields.SchemaField {
|
|||
const targetDamage = [];
|
||||
const damagePromises = [];
|
||||
for (let target of targets) {
|
||||
const actor = fromUuidSync(target.actorId);
|
||||
const actor = foundry.utils.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;
|
||||
|
|
@ -98,17 +98,16 @@ export default class DamageField extends fields.SchemaField {
|
|||
});
|
||||
}
|
||||
|
||||
const token = game.scenes.find(x => x.active).tokens.find(x => x.id === target.id);
|
||||
if (config.hasHealing)
|
||||
damagePromises.push(
|
||||
actor
|
||||
.takeHealing(config.damage)
|
||||
.then(updates => targetDamage.push({ token: actor.token ?? actor.prototypeToken, updates }))
|
||||
actor.takeHealing(config.damage).then(updates => targetDamage.push({ token, updates }))
|
||||
);
|
||||
else
|
||||
damagePromises.push(
|
||||
actor
|
||||
.takeDamage(config.damage, config.isDirect)
|
||||
.then(updates => targetDamage.push({ token: actor.token ?? actor.prototypeToken, updates }))
|
||||
.then(updates => targetDamage.push({ token, updates }))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -131,6 +131,12 @@ export default class DHArmor extends AttachableItem {
|
|||
|
||||
_onUpdate(a, b, c) {
|
||||
super._onUpdate(a, b, c);
|
||||
|
||||
if (this.actor?.type === 'character') {
|
||||
for (const party of this.actor.parties) {
|
||||
party.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -33,11 +33,13 @@ export default class DHBeastform extends BaseDataItem {
|
|||
tokenImg: new fields.FilePathField({
|
||||
initial: 'icons/svg/mystery-man.svg',
|
||||
categories: ['IMAGE'],
|
||||
wildcard: true,
|
||||
base64: false
|
||||
}),
|
||||
tokenRingImg: new fields.FilePathField({
|
||||
initial: 'icons/svg/mystery-man.svg',
|
||||
categories: ['IMAGE'],
|
||||
wildcard: true,
|
||||
base64: false
|
||||
}),
|
||||
tokenSize: new fields.SchemaField({
|
||||
|
|
@ -108,6 +110,30 @@ export default class DHBeastform extends BaseDataItem {
|
|||
};
|
||||
}
|
||||
|
||||
static async getWildcardImage(actor, beastform) {
|
||||
const usesDynamicToken = actor.prototypeToken.ring.enabled && beastform.system.tokenRingImg;
|
||||
const tokenPath = usesDynamicToken ? beastform.system.tokenRingImg : beastform.system.tokenImg;
|
||||
const usesWildcard = tokenPath.includes('*');
|
||||
if (usesWildcard) {
|
||||
const filePicker = new foundry.applications.apps.FilePicker.implementation(tokenPath);
|
||||
const { files } = await foundry.applications.apps.FilePicker.implementation.browse(
|
||||
filePicker.activeSource,
|
||||
tokenPath,
|
||||
{
|
||||
wildcard: true,
|
||||
type: 'image'
|
||||
}
|
||||
);
|
||||
const selectedImage = await game.system.api.applications.dialogs.ImageSelectDialog.configure(
|
||||
game.i18n.localize('DAGGERHEART.APPLICATIONS.ImageSelect.title'),
|
||||
files
|
||||
);
|
||||
return { usesDynamicToken, selectedImage };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async _preCreate() {
|
||||
if (!this.actor) return;
|
||||
|
||||
|
|
|
|||
1
module/data/scene/_module.mjs
Normal file
1
module/data/scene/_module.mjs
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default as DHScene } from './scene.mjs';
|
||||
19
module/data/scene/scene.mjs
Normal file
19
module/data/scene/scene.mjs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
export default class DHScene extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
rangeMeasurement: new fields.SchemaField({
|
||||
setting: new fields.StringField({
|
||||
choices: CONFIG.DH.GENERAL.sceneRangeMeasurementSetting,
|
||||
initial: CONFIG.DH.GENERAL.sceneRangeMeasurementSetting.default.id,
|
||||
label: 'DAGGERHEART.SETTINGS.Scene.FIELDS.rangeMeasurement.setting.label'
|
||||
}),
|
||||
melee: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.melee.name' }),
|
||||
veryClose: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.veryClose.name' }),
|
||||
close: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.close.name' }),
|
||||
far: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.far.name' })
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -18,11 +18,13 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
|||
});
|
||||
|
||||
return {
|
||||
useResourcePips: new BooleanField({ initial: false }),
|
||||
displayFear: new StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.GENERAL.fearDisplay,
|
||||
initial: CONFIG.DH.GENERAL.fearDisplay.token.value
|
||||
}),
|
||||
displayCountdownUI: new BooleanField({ initial: true }),
|
||||
diceSoNice: new SchemaField({
|
||||
hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }),
|
||||
fear: diceStyle({ fg: '#000000', bg: '#0032b1', outline: '#ffffff', edge: '#000000' }),
|
||||
|
|
|
|||
|
|
@ -69,19 +69,19 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
|||
characterDefault: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.GENERAL.defeatedConditionChoices,
|
||||
initial: 'unconscious',
|
||||
initial: CONFIG.DH.GENERAL.defeatedConditionChoices.unconscious.id,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.characterDefault.label'
|
||||
}),
|
||||
adversaryDefault: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.GENERAL.defeatedConditionChoices,
|
||||
initial: 'defeated',
|
||||
initial: CONFIG.DH.GENERAL.defeatedConditionChoices.dead.id,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.adversaryDefault.label'
|
||||
}),
|
||||
companionDefault: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.GENERAL.defeatedConditionChoices,
|
||||
initial: 'defeated',
|
||||
initial: CONFIG.DH.GENERAL.defeatedConditionChoices.defeated.id,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.companionDefault.label'
|
||||
}),
|
||||
deadIcon: new fields.FilePathField({
|
||||
|
|
|
|||
20
module/data/tagTeamRoll.mjs
Normal file
20
module/data/tagTeamRoll.mjs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { DhCharacter } from './actor/_module.mjs';
|
||||
|
||||
export default class DhTagTeamRoll extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
initiator: new fields.SchemaField({
|
||||
id: new fields.StringField({ nullable: true, initial: null }),
|
||||
cost: new fields.NumberField({ integer: true, min: 0, initial: 3 })
|
||||
}),
|
||||
members: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
messageId: new fields.StringField({ required: true, nullable: true, initial: null }),
|
||||
selected: new fields.BooleanField({ required: true, initial: false })
|
||||
})
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import DamageDialog from '../applications/dialogs/damageDialog.mjs';
|
||||
import { RefreshType, socketEvent } from '../systemRegistration/socket.mjs';
|
||||
import DHRoll from './dhRoll.mjs';
|
||||
|
||||
export default class DamageRoll extends DHRoll {
|
||||
|
|
@ -338,5 +339,13 @@ export default class DamageRoll extends DHRoll {
|
|||
parts: damageParts
|
||||
}
|
||||
});
|
||||
|
||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll });
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.Refresh,
|
||||
data: {
|
||||
refreshType: RefreshType.TagTeamRoll
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ export default class DHRoll extends Roll {
|
|||
config.hooks = [...this.getHooks(), ''];
|
||||
config.dialog ??= {};
|
||||
|
||||
const actorIdSplit = config.source.actor.split('.');
|
||||
const tagTeamSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);
|
||||
config.tagTeamSelected = tagTeamSettings.members[actorIdSplit[actorIdSplit.length - 1]];
|
||||
|
||||
for (const hook of config.hooks) {
|
||||
if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null;
|
||||
}
|
||||
|
|
@ -66,8 +70,13 @@ export default class DHRoll extends Roll {
|
|||
if (Hooks.call(`${CONFIG.DH.id}.postRoll${hook.capitalize()}`, config, message) === false) return null;
|
||||
}
|
||||
|
||||
// Create Chat Message
|
||||
if (!config.source?.message) config.message = await this.toMessage(roll, config);
|
||||
if (config.skips?.createMessage) {
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
await game.dice3d.showForRoll(roll, game.user, true);
|
||||
}
|
||||
} else if (!config.source?.message) {
|
||||
config.message = await this.toMessage(roll, config);
|
||||
}
|
||||
}
|
||||
|
||||
static postEvaluate(roll, config = {}) {
|
||||
|
|
@ -100,6 +109,10 @@ export default class DHRoll extends Roll {
|
|||
if (roll._evaluated) {
|
||||
const message = await cls.create(msgData, { rollMode: config.selectedRollMode });
|
||||
|
||||
if (config.tagTeamSelected) {
|
||||
game.system.api.applications.dialogs.TagTeamDialog.assignRoll(message.speakerActor, message);
|
||||
}
|
||||
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
await game.dice3d.waitFor3DAnimationByMessageID(message.id);
|
||||
}
|
||||
|
|
@ -228,10 +241,11 @@ export const registerRollDiceHooks = () => {
|
|||
if (
|
||||
!config.source?.actor ||
|
||||
(game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) ||
|
||||
config.actionType === 'reaction'
|
||||
config.actionType === 'reaction' ||
|
||||
config.tagTeamSelected ||
|
||||
config.skips?.resources
|
||||
)
|
||||
return;
|
||||
|
||||
const actor = await fromUuid(config.source.actor);
|
||||
let updates = [];
|
||||
if (!actor) return;
|
||||
|
|
|
|||
|
|
@ -256,9 +256,11 @@ export default class DualityRoll extends D20Roll {
|
|||
});
|
||||
newRoll.extra = newRoll.extra.slice(2);
|
||||
|
||||
const tagTeamSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);
|
||||
Hooks.call(`${CONFIG.DH.id}.postRollDuality`, {
|
||||
source: { actor: message.system.source.actor ?? '' },
|
||||
targets: message.system.targets,
|
||||
tagTeamSelected: Object.values(tagTeamSettings.members).some(x => x.messageId === message._id),
|
||||
roll: newRoll,
|
||||
rerolledRoll:
|
||||
newRoll.result.duality !== message.system.roll.result.duality ? message.system.roll : undefined
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import { createScrollText, damageKeyToNumber } from '../helpers/utils.mjs';
|
|||
import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs';
|
||||
|
||||
export default class DhpActor extends Actor {
|
||||
parties = new Set();
|
||||
|
||||
#scrollTextQueue = [];
|
||||
#scrollTextInterval;
|
||||
|
||||
|
|
@ -74,7 +76,7 @@ export default class DhpActor extends Actor {
|
|||
|
||||
// Configure prototype token settings
|
||||
const prototypeToken = {};
|
||||
if (['character', 'companion'].includes(this.type))
|
||||
if (['character', 'companion', 'party'].includes(this.type))
|
||||
Object.assign(prototypeToken, {
|
||||
sight: { enabled: true },
|
||||
actorLink: true,
|
||||
|
|
@ -83,6 +85,20 @@ export default class DhpActor extends Actor {
|
|||
this.updateSource({ prototypeToken });
|
||||
}
|
||||
|
||||
_onUpdate(changes, options, userId) {
|
||||
super._onUpdate(changes, options, userId);
|
||||
for (const party of this.parties) {
|
||||
party.render();
|
||||
}
|
||||
}
|
||||
|
||||
_onDelete(options, userId) {
|
||||
super._onDelete(options, userId);
|
||||
for (const party of this.parties) {
|
||||
party.render();
|
||||
}
|
||||
}
|
||||
|
||||
async updateLevel(newLevel) {
|
||||
if (!['character', 'companion'].includes(this.type) || newLevel === this.system.levelData.level.changed) return;
|
||||
|
||||
|
|
@ -808,4 +824,14 @@ export default class DhpActor extends Actor {
|
|||
|
||||
return await super.importFromJSON(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an array of localized tag.
|
||||
* @returns {string[]} An array of localized tag strings.
|
||||
*/
|
||||
_getTags() {
|
||||
const tags = [];
|
||||
if (this.system._getTags) tags.push(...this.system._getTags());
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
|
||||
import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../systemRegistration/socket.mjs';
|
||||
|
||||
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||
targetHook = null;
|
||||
|
|
@ -16,7 +16,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
const html = await super.renderHTML({ actor: actorData, author: this.author });
|
||||
|
||||
if (this.flags.core?.RollTable) {
|
||||
html.querySelector('.roll-buttons.apply-buttons').remove();
|
||||
html.querySelector('.roll-buttons.apply-buttons')?.remove();
|
||||
}
|
||||
|
||||
this.enrichChatMessage(html);
|
||||
|
|
@ -155,7 +155,15 @@ 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);
|
||||
await this.system.action?.workflow.get('damage')?.execute(config, this._id, true);
|
||||
|
||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll });
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.Refresh,
|
||||
data: {
|
||||
refreshType: RefreshType.TagTeamRoll
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async onApplyDamage(event) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { parseInlineParams } from './parser.mjs';
|
||||
|
||||
export default function DhLookupEnricher(match, { rollData }) {
|
||||
const results = parseInlineParams(match[1], { first: 'formula'});
|
||||
const results = parseInlineParams(match[1], { first: 'formula' });
|
||||
const element = document.createElement('span');
|
||||
element.textContent = Roll.replaceFormulaData(String(results.formula), rollData);
|
||||
return element;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @param {string} paramString The parameter inside the brackets of something like @Template[] to parse
|
||||
* @param {string} paramString The parameter inside the brackets of something like @Template[] to parse
|
||||
* @param {Object} options
|
||||
* @param {string} options.first If set, the first parameter is treated as a value with this as its key
|
||||
* @returns {Record<string, string | undefined> | null}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ export default class RegisterHandlebarsHelpers {
|
|||
setVar: this.setVar,
|
||||
empty: this.empty,
|
||||
pluralize: this.pluralize,
|
||||
positive: this.positive
|
||||
positive: this.positive,
|
||||
isNullish: this.isNullish
|
||||
});
|
||||
}
|
||||
static add(a, b) {
|
||||
|
|
@ -94,4 +95,8 @@ export default class RegisterHandlebarsHelpers {
|
|||
static positive(a) {
|
||||
return Math.abs(Number(a));
|
||||
}
|
||||
|
||||
static isNullish(a) {
|
||||
return a === null || a === undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -418,3 +418,15 @@ export async function createEmbeddedItemsWithEffects(actor, baseData) {
|
|||
export const slugify = name => {
|
||||
return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', '');
|
||||
};
|
||||
|
||||
export function shuffleArray(array) {
|
||||
let currentIndex = array.length;
|
||||
while (currentIndex != 0) {
|
||||
let randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
currentIndex--;
|
||||
|
||||
[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
export const preloadHandlebarsTemplates = async function () {
|
||||
foundry.applications.handlebars.loadTemplates({
|
||||
'daggerheart.inventory-item-compact': 'systems/daggerheart/templates/sheets/global/partials/inventory-item-compact.hbs',
|
||||
'daggerheart.inventory-item-compact':
|
||||
'systems/daggerheart/templates/sheets/global/partials/inventory-item-compact.hbs',
|
||||
'daggerheart.inventory-items':
|
||||
'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs',
|
||||
'daggerheart.inventory-item': 'systems/daggerheart/templates/sheets/global/partials/inventory-item-V2.hbs'
|
||||
});
|
||||
return foundry.applications.handlebars.loadTemplates([
|
||||
'templates/generic/tab-navigation.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/action-item.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/domain-card-item.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/item-resource.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/resource-section.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/resource-bar.hbs',
|
||||
'systems/daggerheart/templates/components/card-preview.hbs',
|
||||
'systems/daggerheart/templates/levelup/parts/selectable-card-preview.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { RefreshType, socketEvent } from './socket.mjs';
|
||||
|
||||
export async function runMigrations() {
|
||||
let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion);
|
||||
if (!lastMigrationVersion) lastMigrationVersion = game.system.version;
|
||||
|
|
@ -158,7 +160,8 @@ export async function runMigrations() {
|
|||
...countdown,
|
||||
type: type,
|
||||
ownership: Object.keys(countdown.ownership.players).reduce((acc, key) => {
|
||||
acc[key] = countdown.ownership.players[key].type;
|
||||
acc[key] =
|
||||
countdown.ownership.players[key].type === 1 ? 2 : countdown.ownership.players[key].type;
|
||||
return acc;
|
||||
}, {}),
|
||||
progress: {
|
||||
|
|
@ -179,6 +182,12 @@ export async function runMigrations() {
|
|||
});
|
||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, countdownSettings);
|
||||
|
||||
game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.Refresh,
|
||||
data: { refreshType: RefreshType.Countdown }
|
||||
});
|
||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown });
|
||||
|
||||
lastMigrationVersion = '1.2.0';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
DhHomebrewSettings,
|
||||
DhVariantRuleSettings
|
||||
} from '../applications/settings/_module.mjs';
|
||||
import { DhTagTeamRoll } from '../data/_module.mjs';
|
||||
|
||||
export const registerDHSettings = () => {
|
||||
registerMenuSettings();
|
||||
|
|
@ -122,4 +123,10 @@ const registerNonConfigSettings = () => {
|
|||
config: false,
|
||||
type: DhCountdowns
|
||||
});
|
||||
|
||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: DhTagTeamRoll
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import DamageReductionDialog from '../applications/dialogs/damageReductionDialog.mjs';
|
||||
import Party from '../applications/sheets/actors/party.mjs';
|
||||
|
||||
export function handleSocketEvent({ action = null, data = {} } = {}) {
|
||||
switch (action) {
|
||||
|
|
@ -11,13 +12,17 @@ export function handleSocketEvent({ action = null, data = {} } = {}) {
|
|||
case socketEvent.Refresh:
|
||||
Hooks.call(socketEvent.Refresh, data);
|
||||
break;
|
||||
case socketEvent.DowntimeTrigger:
|
||||
Party.downtimeMoveQuery(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export const socketEvent = {
|
||||
GMUpdate: 'DhGMUpdate',
|
||||
Refresh: 'DhRefresh',
|
||||
DhpFearUpdate: 'DhFearUpdate'
|
||||
DhpFearUpdate: 'DhFearUpdate',
|
||||
DowntimeTrigger: 'DowntimeTrigger'
|
||||
};
|
||||
|
||||
export const GMUpdateEvent = {
|
||||
|
|
@ -30,7 +35,8 @@ export const GMUpdateEvent = {
|
|||
};
|
||||
|
||||
export const RefreshType = {
|
||||
Countdown: 'DhCoundownRefresh'
|
||||
Countdown: 'DhCoundownRefresh',
|
||||
TagTeamRoll: 'DhTagTeamRollRefresh'
|
||||
};
|
||||
|
||||
export const registerSocketHooks = () => {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 1,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/mammals/rodent-rat-diseaed-gray.webp",
|
||||
"tokenRingImg": "icons/creatures/mammals/rodent-rat-diseaed-gray.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -125,12 +125,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753570913893,
|
||||
"modifiedTime": 1755395235599,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762786932553,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "a9UoCwtrbgKk02mK",
|
||||
"sort": 500000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 3,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/fish/fish-marlin-swordfight-blue.webp",
|
||||
"tokenRingImg": "icons/creatures/fish/fish-marlin-swordfight-blue.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -130,12 +130,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753626985883,
|
||||
"modifiedTime": 1755395436609,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787103412,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "ItBVeCl2u5uetgy7",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 1,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/tentacles/tentacles-octopus-black-pink.webp",
|
||||
"tokenRingImg": "icons/creatures/tentacles/tentacles-octopus-black-pink.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -124,12 +124,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753575463467,
|
||||
"modifiedTime": 1755395249086,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762786937661,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "qqzdFCxyYupWZK23",
|
||||
"sort": 200000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 2,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/reptiles/turtle-shell-glowing-green.webp",
|
||||
"tokenRingImg": "icons/creatures/reptiles/turtle-shell-glowing-green.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -132,10 +132,10 @@
|
|||
"exportSource": null,
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.1.2",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753580987168,
|
||||
"modifiedTime": 1761502671010,
|
||||
"lastModifiedBy": "fBcTgyTzoARBvohY"
|
||||
"modifiedTime": 1762786983275,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "8pUHJv3BYdjA4Qdf",
|
||||
"sort": 100000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 4,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/fish/squid-kraken-teal.webp",
|
||||
"tokenRingImg": "icons/creatures/fish/squid-kraken-teal.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -133,12 +133,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753628697986,
|
||||
"modifiedTime": 1755395495481,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787224941,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "wT4xbF99I55yjKZV",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 3,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/mammals/wolf-shadow-black.webp",
|
||||
"tokenRingImg": "icons/creatures/mammals/wolf-shadow-black.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -130,12 +130,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753625648103,
|
||||
"modifiedTime": 1755395413225,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787119852,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "afbMt4Ld6nY3mw0N",
|
||||
"sort": 100000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 3,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/birds/corvid-flying-wings-purple.webp",
|
||||
"tokenRingImg": "icons/creatures/birds/corvid-flying-wings-purple.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -130,12 +130,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753626865938,
|
||||
"modifiedTime": 1755395430043,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787132432,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "b4BMnTbJ3iPPidSb",
|
||||
"sort": 200000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 1,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/mammals/rabbit-movement-glowing-green.webp",
|
||||
"tokenRingImg": "icons/creatures/mammals/rabbit-movement-glowing-green.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -124,12 +124,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753573035973,
|
||||
"modifiedTime": 1755395259885,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762786944642,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "iDmOtiHJJ80AIAVT",
|
||||
"sort": 100000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "evolved",
|
||||
"tier": 3,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/magical/humanoid-giant-forest-blue.webp",
|
||||
"tokenRingImg": "icons/creatures/magical/humanoid-giant-forest-blue.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -100,12 +100,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753627165434,
|
||||
"modifiedTime": 1755395443346,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787146758,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "mqP6z4Wg4K3oDAom",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "hybrid",
|
||||
"tier": 3,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/magical/humanoid-silhouette-glowing-pink.webp",
|
||||
"tokenRingImg": "icons/creatures/magical/humanoid-silhouette-glowing-pink.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -118,12 +118,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753627303526,
|
||||
"modifiedTime": 1755395450377,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787159027,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "rRUtgcUjimlpPhnn",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 4,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/mammals/beast-horned-scaled-glowing-orange.webp",
|
||||
"tokenRingImg": "icons/creatures/mammals/beast-horned-scaled-glowing-orange.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -134,12 +134,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753627711166,
|
||||
"modifiedTime": 1755395470445,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787235514,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "qjwMzPn33aKZACkv",
|
||||
"sort": 100000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 3,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/reptiles/lizard-iguana-green.webp",
|
||||
"tokenRingImg": "icons/creatures/reptiles/lizard-iguana-green.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -130,12 +130,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753626720443,
|
||||
"modifiedTime": 1755395421310,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787174916,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "94tvcC3D5Kp4lzuN",
|
||||
"sort": 300000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 2,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/environment/creatures/horse-tan.webp",
|
||||
"tokenRingImg": "icons/environment/creatures/horse-tan.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -130,12 +130,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753617739175,
|
||||
"modifiedTime": 1755395313904,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787003407,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "zRLjqKx4Rn2TjivL",
|
||||
"sort": 200000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 4,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/reptiles/dragon-winged-blue.webp",
|
||||
"tokenRingImg": "icons/creatures/reptiles/dragon-winged-blue.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -133,12 +133,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753628382723,
|
||||
"modifiedTime": 1755395486698,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787250619,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "jV6EuEacyQlHW4SN",
|
||||
"sort": 200000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "evolved",
|
||||
"tier": 4,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/eyes/lizard-single-slit-pink.webp",
|
||||
"tokenRingImg": "icons/creatures/eyes/lizard-single-slit-pink.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -106,12 +106,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753628844905,
|
||||
"modifiedTime": 1755395505081,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787259530,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "kObobka52JdpWBSu",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "hybrid",
|
||||
"tier": 4,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/magical/spirit-undead-horned-blue.webp",
|
||||
"tokenRingImg": "icons/creatures/magical/spirit-undead-horned-blue.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -118,12 +118,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753628965658,
|
||||
"modifiedTime": 1755395511998,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787273314,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "WAbxCf2An8qmxyJ1",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 1,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/mammals/deer-antlers-glowing-blue.webp",
|
||||
"tokenRingImg": "icons/creatures/mammals/deer-antlers-glowing-blue.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -124,12 +124,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753574930310,
|
||||
"modifiedTime": 1755395265901,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762786950257,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "CItO8yX6amQaqyk7",
|
||||
"sort": 300000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 1,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/mammals/wolf-howl-moon-forest-blue.webp",
|
||||
"tokenRingImg": "icons/creatures/mammals/wolf-howl-moon-forest-blue.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -130,12 +130,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753575274807,
|
||||
"modifiedTime": 1755395272135,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762786928080,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "YLisKYYhAGca50WM",
|
||||
"sort": 400000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 2,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/abilities/cougar-roar-rush-orange.webp",
|
||||
"tokenRingImg": "icons/creatures/abilities/cougar-roar-rush-orange.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -130,12 +130,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753621789186,
|
||||
"modifiedTime": 1755395328437,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787017340,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "33oFSZ1PwFqInHPe",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 2,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/abilities/bear-roar-bite-brown-green.webp",
|
||||
"tokenRingImg": "icons/creatures/abilities/bear-roar-bite-brown-green.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -130,12 +130,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753582598510,
|
||||
"modifiedTime": 1755395305302,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787029819,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "m8BVTuJI1wCvzTcf",
|
||||
"sort": 300000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 1,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/invertebrates/spider-mandibles-brown.webp",
|
||||
"tokenRingImg": "icons/creatures/invertebrates/spider-mandibles-brown.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -130,12 +130,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753576016472,
|
||||
"modifiedTime": 1755395283720,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762786957384,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "A4TVRY0D5r9EiVwA",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 2,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/reptiles/serpent-horned-green.webp",
|
||||
"tokenRingImg": "icons/creatures/reptiles/serpent-horned-green.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -130,12 +130,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753621251793,
|
||||
"modifiedTime": 1755395321271,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787068105,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "1XrZWGDttBAAUxR1",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 4,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/abilities/dragon-breath-purple.webp",
|
||||
"tokenRingImg": "icons/creatures/abilities/dragon-breath-purple.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -133,12 +133,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753628213224,
|
||||
"modifiedTime": 1755395476212,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787283468,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "5BABxRe2XVrYTj8N",
|
||||
"sort": 300000,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 2,
|
||||
"tokenImg": "icons/svg/mystery-man.svg",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenImg": "icons/creatures/birds/raptor-owl-flying-moon.webp",
|
||||
"tokenRingImg": "icons/creatures/birds/raptor-owl-flying-moon.webp",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
|
|
@ -130,12 +130,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753624952844,
|
||||
"modifiedTime": 1755395334887,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762787081429,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "mZ4Wlqtss2FlNNvL",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"type": "effect",
|
||||
"_id": "4yQ56hSL5LBkzrV6",
|
||||
"systemPath": "actions",
|
||||
"description": "",
|
||||
"description": "<p><strong>Spend a Hope</strong> to move up to Far range without rolling.</p>",
|
||||
"chatDisplay": true,
|
||||
"actionType": "action",
|
||||
"cost": [
|
||||
|
|
@ -23,12 +23,12 @@
|
|||
],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": null,
|
||||
"max": "",
|
||||
"recovery": null
|
||||
},
|
||||
"effects": [],
|
||||
"target": {
|
||||
"type": null,
|
||||
"type": "",
|
||||
"amount": null
|
||||
},
|
||||
"name": "Spend Hope",
|
||||
|
|
@ -56,12 +56,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753569752255,
|
||||
"modifiedTime": 1755395530698,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762786188592,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "xLS5YT1B6yeCiNTg",
|
||||
"sort": 2700000,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"type": "attack",
|
||||
"_id": "9lvrqQKTEB3NRwvM",
|
||||
"systemPath": "actions",
|
||||
"description": "",
|
||||
"description": "<p>When an attack roll against you would succeed, you can <strong>mark a Stress</strong> and roll a <strong>d4</strong>. Add the result to your Evasion against this attack.</p>",
|
||||
"chatDisplay": true,
|
||||
"actionType": "action",
|
||||
"cost": [
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": null,
|
||||
"max": "",
|
||||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
"type": null,
|
||||
"type": "",
|
||||
"amount": null
|
||||
},
|
||||
"effects": [],
|
||||
|
|
@ -80,12 +80,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753574925665,
|
||||
"modifiedTime": 1755395566717,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762786470855,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "a7Qvmm14nx9BCysA",
|
||||
"sort": 2300000,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"type": "effect",
|
||||
"_id": "p8DESOMGA6dLwEMz",
|
||||
"systemPath": "actions",
|
||||
"description": "",
|
||||
"description": "<p>When you succeed on an attack against a target within Melee range, you can <strong>mark a Stress</strong> to make the target temporarily <em>Vulnerable</em>.</p>",
|
||||
"chatDisplay": true,
|
||||
"actionType": "action",
|
||||
"cost": [
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": null,
|
||||
"max": "",
|
||||
"recovery": null
|
||||
},
|
||||
"effects": [
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
}
|
||||
],
|
||||
"target": {
|
||||
"type": null,
|
||||
"type": "",
|
||||
"amount": null
|
||||
},
|
||||
"name": "Mark Stress",
|
||||
|
|
@ -101,12 +101,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753575250590,
|
||||
"modifiedTime": 1755395576634,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762786502471,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "8u0HkK3WgtU9lWYs",
|
||||
"sort": 2400000,
|
||||
|
|
|
|||
|
|
@ -28,75 +28,20 @@
|
|||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"resultBased": false,
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false,
|
||||
"formula": ""
|
||||
},
|
||||
"multiplier": "prof",
|
||||
"dice": "d8",
|
||||
"bonus": 6,
|
||||
"flatMultiplier": 1
|
||||
},
|
||||
"applyTo": "hitPoints",
|
||||
"type": [
|
||||
"physical"
|
||||
],
|
||||
"base": false,
|
||||
"valueAlt": {
|
||||
"multiplier": "prof",
|
||||
"flatMultiplier": 1,
|
||||
"dice": "d6",
|
||||
"bonus": null,
|
||||
"custom": {
|
||||
"enabled": false,
|
||||
"formula": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"resultBased": false,
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false,
|
||||
"formula": ""
|
||||
},
|
||||
"multiplier": "flat",
|
||||
"flatMultiplier": 2,
|
||||
"dice": "d8",
|
||||
"bonus": null
|
||||
},
|
||||
"applyTo": "hitPoints",
|
||||
"type": [
|
||||
"physical"
|
||||
],
|
||||
"base": false,
|
||||
"valueAlt": {
|
||||
"multiplier": "prof",
|
||||
"flatMultiplier": 1,
|
||||
"dice": "d6",
|
||||
"bonus": null,
|
||||
"custom": {
|
||||
"enabled": false,
|
||||
"formula": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"resultBased": false,
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
"formula": "1"
|
||||
"formula": "(@prof+2)@basicAttackDamageDice"
|
||||
},
|
||||
"multiplier": "prof",
|
||||
"flatMultiplier": 1,
|
||||
"dice": "d6",
|
||||
"bonus": null
|
||||
},
|
||||
"applyTo": "stress",
|
||||
"applyTo": "hitPoints",
|
||||
"type": [],
|
||||
"base": false,
|
||||
"valueAlt": {
|
||||
"multiplier": "prof",
|
||||
|
|
@ -104,11 +49,9 @@
|
|||
"dice": "d6",
|
||||
"bonus": null,
|
||||
"custom": {
|
||||
"enabled": false,
|
||||
"formula": ""
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"type": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"includeBase": false
|
||||
|
|
@ -207,12 +150,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.350",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.1.2",
|
||||
"systemVersion": "1.1.0",
|
||||
"createdTime": 1753621786000,
|
||||
"modifiedTime": 1762341337917,
|
||||
"lastModifiedBy": "9HOfUKAXuCu7hUPY"
|
||||
"modifiedTime": 1756041242273,
|
||||
"lastModifiedBy": "vUIbuan0U50nfKBE"
|
||||
},
|
||||
"_id": "0ey4kM9ssj2otHvb",
|
||||
"sort": 600000,
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@
|
|||
"type": "effect",
|
||||
"_id": "eow90DYK6cUWOk7g",
|
||||
"systemPath": "actions",
|
||||
"description": "",
|
||||
"description": "<p>When you succeed on an attack against a target within Melee range, the target becomes temporarily Poisoned. A Poisoned creature takes <strong>1d10</strong> direct physical damage each time they act.</p>",
|
||||
"chatDisplay": true,
|
||||
"actionType": "action",
|
||||
"cost": [],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": null,
|
||||
"max": "",
|
||||
"recovery": null
|
||||
},
|
||||
"effects": [
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
"startRound": null,
|
||||
"startTurn": null
|
||||
},
|
||||
"description": "<p><span style=\"color:rgb(239, 230, 216);font-family:Montserrat, sans-serif;font-size:14px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;orphans:2;text-align:start;text-indent:0px;text-transform:none;widows:2;word-spacing:0px;-webkit-text-stroke-width:0px;white-space:normal;background-color:rgba(24, 22, 46, 0.376);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;display:inline !important;float:none\">A Poisoned creature takes </span><span style=\"box-sizing:border-box;scrollbar-width:thin;scrollbar-color:rgb(93, 20, 43) rgba(0, 0, 0, 0);color:rgb(239, 230, 216);font-family:Montserrat, sans-serif;font-size:14px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;letter-spacing:normal;orphans:2;text-align:start;text-indent:0px;text-transform:none;widows:2;word-spacing:0px;-webkit-text-stroke-width:0px;white-space:normal;background-color:rgba(24, 22, 46, 0.376);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial\"><strong>1d10</strong></span><span style=\"color:rgb(239, 230, 216);font-family:Montserrat, sans-serif;font-size:14px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;orphans:2;text-align:start;text-indent:0px;text-transform:none;widows:2;word-spacing:0px;-webkit-text-stroke-width:0px;white-space:normal;background-color:rgba(24, 22, 46, 0.376);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;display:inline !important;float:none\"> direct physical damage each time they act.</p>",
|
||||
"description": "<p><span style=\"color:rgb(239, 230, 216);font-family:Montserrat, sans-serif;font-size:14px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;orphans:2;text-align:start;text-indent:0px;text-transform:none;widows:2;word-spacing:0px;-webkit-text-stroke-width:0px;white-space:normal;background-color:rgba(24, 22, 46, 0.376);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;display:inline !important;float:none\">A Poisoned creature takes </span><span style=\"box-sizing:border-box;scrollbar-width:thin;scrollbar-color:rgb(93, 20, 43) rgba(0, 0, 0, 0);color:rgb(239, 230, 216);font-family:Montserrat, sans-serif;font-size:14px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;letter-spacing:normal;orphans:2;text-align:start;text-indent:0px;text-transform:none;widows:2;word-spacing:0px;-webkit-text-stroke-width:0px;white-space:normal;background-color:rgba(24, 22, 46, 0.376);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial\"><strong>1d10</strong></span><span style=\"color:rgb(239, 230, 216);font-family:Montserrat, sans-serif;font-size:14px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;orphans:2;text-align:start;text-indent:0px;text-transform:none;widows:2;word-spacing:0px;-webkit-text-stroke-width:0px;white-space:normal;background-color:rgba(24, 22, 46, 0.376);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;display:inline !important;float:none\"> direct physical damage each time they act.</span></p>",
|
||||
"tint": "#ffffff",
|
||||
"statuses": [],
|
||||
"sort": 0,
|
||||
|
|
@ -91,12 +91,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753576004121,
|
||||
"modifiedTime": 1755395614203,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762786649810,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "2KlTnfzO03vneVS8",
|
||||
"sort": 1600000,
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@
|
|||
"type": "attack",
|
||||
"_id": "vRU6xutkp3VYxZ0i",
|
||||
"systemPath": "actions",
|
||||
"description": "",
|
||||
"description": "<p>You can temporarily <em>Restrain</em> a target within Close range by succeeding on a Finesse Roll against them.</p>",
|
||||
"chatDisplay": true,
|
||||
"actionType": "action",
|
||||
"cost": [],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": null,
|
||||
"max": "",
|
||||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
|
|
@ -117,12 +117,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.347",
|
||||
"coreVersion": "13.350",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "1.0.5",
|
||||
"systemVersion": "1.2.0",
|
||||
"createdTime": 1753576005315,
|
||||
"modifiedTime": 1755395606969,
|
||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
||||
"modifiedTime": 1762786690091,
|
||||
"lastModifiedBy": "gtrqalNxfl7iRwL8"
|
||||
},
|
||||
"_id": "D73fS1iM4SZPFimu",
|
||||
"sort": 1700000,
|
||||
|
|
|
|||
|
|
@ -448,7 +448,7 @@
|
|||
white-space: nowrap;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
color: light-dark(@beige, @dark);
|
||||
color: @dark;
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,14 @@
|
|||
.application.daggerheart.dialog.dh-style.views.roll-selection {
|
||||
.dialog-header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
.dialog-header-inner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
width: auto;
|
||||
|
|
@ -37,6 +44,29 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tag-team-controller {
|
||||
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: var(--font-size-14);
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: light-dark(@dark-blue-40, @golden-40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.roll-dialog-container {
|
||||
|
|
|
|||
50
styles/less/dialog/group-roll/group-roll.less
Normal file
50
styles/less/dialog/group-roll/group-roll.less
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
@import '../../utils/colors.less';
|
||||
|
||||
.application.daggerheart.group-roll {
|
||||
fieldset.one-column {
|
||||
min-width: 500px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.actor-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.actor-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
.actor-check-info {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
.form-fields {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
max-width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
.tooltip-container {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
33
styles/less/dialog/image-select/sheet.less
Normal file
33
styles/less/dialog/image-select/sheet.less
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
.daggerheart.dh-style.dialog.image-select {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.images-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
||||
img {
|
||||
width: 136px;
|
||||
height: 136px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
opacity: 0.4;
|
||||
|
||||
&.selected {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -30,3 +30,8 @@
|
|||
@import './multiclass-choice/sheet.less';
|
||||
|
||||
@import './reroll-dialog/sheet.less';
|
||||
|
||||
@import './group-roll/group-roll.less';
|
||||
@import './tag-team-dialog/sheet.less';
|
||||
|
||||
@import './image-select/sheet.less';
|
||||
|
|
|
|||
178
styles/less/dialog/tag-team-dialog/sheet.less
Normal file
178
styles/less/dialog/tag-team-dialog/sheet.less
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
.daggerheart.dialog.dh-style.views.tag-team-dialog {
|
||||
.tag-team-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
.tag-team-data-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.form-group {
|
||||
flex: 0;
|
||||
|
||||
label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&.flex-group {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
h2 {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
select {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.participants-container {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
.participant-outer-container {
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
border-radius: 6px;
|
||||
|
||||
&.selected,
|
||||
&:hover {
|
||||
background-color: light-dark(@golden-40, @golden-40);
|
||||
}
|
||||
|
||||
.participant-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
|
||||
.participant-inner-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
img {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.participant-labels {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
|
||||
.participant-label-title {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.participant-label-info {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
|
||||
.participant-label-info-part {
|
||||
border: 1px solid light-dark(white, white);
|
||||
border-radius: 4px;
|
||||
padding: 2px 4px;
|
||||
background-color: light-dark(@beige-80, @soft-white-shadow);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.participant-empty-roll-container {
|
||||
border: 1px dashed white;
|
||||
padding: 8px 2px;
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.participant-roll-outer-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
|
||||
h4 {
|
||||
text-align: center;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
}
|
||||
|
||||
.participant-roll-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: nowrap;
|
||||
|
||||
.participant-roll-text-container {
|
||||
padding: 0 8px;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.damage-values-container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
gap: 8px;
|
||||
|
||||
.damage-container {
|
||||
border: 1px solid light-dark(white, white);
|
||||
border-radius: 6px;
|
||||
padding: 0 4px;
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.result-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.result-damages-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
|
||||
.result-damage-container {
|
||||
border: 1px solid light-dark(white, white);
|
||||
border-radius: 6px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.roll-leader-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -67,6 +67,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.standard-form {
|
||||
font-family: @font-body;
|
||||
}
|
||||
|
||||
&.two-big-buttons {
|
||||
.window-content {
|
||||
padding-top: 0;
|
||||
|
|
|
|||
|
|
@ -801,7 +801,7 @@
|
|||
padding: 10px 0;
|
||||
text-align: center;
|
||||
font-size: var(--font-size-16);
|
||||
color: light-dark(@beige, @dark);
|
||||
color: @dark;
|
||||
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
|
|
@ -834,7 +834,7 @@
|
|||
font-variant: small-caps;
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
color: light-dark(@dark-blue-60, @beige-80);
|
||||
color: light-dark(@dark-blue, @beige-80);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,3 +19,4 @@
|
|||
@import './filter-menu.less';
|
||||
@import './tab-attachments.less';
|
||||
@import './dice.less';
|
||||
@import './resource-bar.less';
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@
|
|||
flex: 0 0 40px;
|
||||
height: 40px;
|
||||
position: relative;
|
||||
&:has(.roll-img) {
|
||||
&:has(.roll-img) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
|
@ -87,6 +87,7 @@
|
|||
|
||||
&.actor-img {
|
||||
border-radius: 50%;
|
||||
object-position: top center;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,6 +123,10 @@
|
|||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
&.padded {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
flex: 1;
|
||||
align-self: center;
|
||||
|
|
@ -248,9 +253,9 @@
|
|||
|
||||
&.inventory-item-compact {
|
||||
display: grid;
|
||||
grid-template:
|
||||
"img name controls" auto
|
||||
"img labels labels" 1fr
|
||||
grid-template:
|
||||
'img name controls' auto
|
||||
'img labels labels' 1fr
|
||||
/ 40px 1fr min-content;
|
||||
column-gap: 8px;
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue