mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-06-05 20:34:15 +02:00
Compare commits
73 commits
24993970da
...
5dbcd94480
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dbcd94480 | ||
|
|
d98a7c951e | ||
|
|
3c36c5747d | ||
|
|
bcf274f1d0 | ||
|
|
df4a2c5d57 | ||
|
|
646ebc8bdf | ||
|
|
6448666579 | ||
|
|
d0c29ede56 | ||
|
|
98ce49b928 | ||
|
|
318d00b47d | ||
|
|
c8d0df87c8 | ||
|
|
983f48b415 | ||
|
|
bfd483698b | ||
|
|
3eb33a71af | ||
|
|
3fbc1e97c6 | ||
|
|
729e8bca42 | ||
|
|
53f15a7fde | ||
|
|
c23ac61ee5 | ||
|
|
d3141059ac | ||
|
|
61db7ca371 | ||
|
|
2bc1c04c93 | ||
|
|
493998cc95 | ||
|
|
251d7e4e13 | ||
|
|
a209b035c8 | ||
|
|
9487b07e43 | ||
|
|
f1a530f57f | ||
|
|
ddf4747310 | ||
|
|
ac72012387 | ||
|
|
1ab8170d2f | ||
|
|
48f9ffc318 | ||
|
|
fa6f9d56b8 | ||
|
|
c2f8b34ef2 | ||
|
|
de0ab9d047 | ||
|
|
b9416ead5a | ||
|
|
ccc4186e42 | ||
|
|
e529dd0f88 | ||
|
|
0e8c3dc74a | ||
|
|
58824d5bbf | ||
|
|
e095587305 | ||
|
|
f7f1bdce2b | ||
|
|
e4a3f105dc | ||
|
|
2931377d53 | ||
|
|
53e8da77c6 | ||
|
|
bae9006f64 | ||
|
|
273f666784 | ||
|
|
d782b25254 | ||
|
|
f4c21a6a1b | ||
|
|
da06381748 | ||
|
|
ed53d9ed4c | ||
|
|
b631525b6e | ||
|
|
b145f515d0 | ||
|
|
b23095cb2f | ||
|
|
2f589c1b8e | ||
|
|
10a608a1a5 | ||
|
|
6a2d09caac | ||
|
|
4504379fcf | ||
|
|
d152bfc906 | ||
|
|
b91d943dd1 | ||
|
|
ac5f84fff7 | ||
|
|
d78c6b1183 | ||
|
|
98049bd76b | ||
|
|
ab412367f9 | ||
|
|
0492507bd1 | ||
|
|
47960fdd61 | ||
|
|
d372f3df9b | ||
|
|
e6c27926d0 | ||
|
|
88e64531b4 | ||
|
|
dd2aa10871 | ||
|
|
855f4549ec | ||
|
|
6b4de71a0a | ||
|
|
46e552eb3d | ||
|
|
bc3c09fa2e | ||
|
|
829a6161ff |
204 changed files with 3073 additions and 2234 deletions
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
* text=auto eol=lf
|
||||
*.json text eol=lf
|
||||
1
.github/workflows/deploy.yml
vendored
1
.github/workflows/deploy.yml
vendored
|
|
@ -37,6 +37,7 @@ jobs:
|
|||
url: https://github.com/${{github.repository}}
|
||||
manifest: https://raw.githubusercontent.com/${{github.repository}}/v14/system.json
|
||||
download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/system.zip
|
||||
flags.hotReload: false
|
||||
|
||||
# Create a zip file with all files required by the module to add to the release
|
||||
- run: zip -r ./system.zip system.json README.md LICENSE build/daggerheart.js build/tagify.css styles/daggerheart.css assets/ templates/ packs/ lang/
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,3 +6,4 @@ Build
|
|||
build
|
||||
foundry
|
||||
styles/daggerheart.css
|
||||
styles/daggerheart.css.map
|
||||
|
|
|
|||
1
assets/icons/documents/actors/drama-masks.svg
Normal file
1
assets/icons/documents/actors/drama-masks.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="M418.813 30.625c-21.178 26.27-49.712 50.982-84.125 70.844-36.778 21.225-75.064 33.62-110.313 38.06a310.317 310.317 0 0 0 6.813 18.25c16.01.277 29.366-.434 36.406-1.5l9.47-1.53 8.436-1.28.22 10.186a307.48 307.48 0 0 1-1.095 18.72l56.625 8.843c.86-.095 1.713-.15 2.563-.157 11.188-.114 21.44 7.29 24.468 18.593.657 2.448.922 4.903.845 7.313 5.972-2.075 11.753-4.305 17.28-6.72l9.595-4.188 2.313 10.22a340.211 340.211 0 0 1 7.375 48.062C438.29 247.836 468.438 225.71 493 197.5c-3.22-36.73-16.154-78.04-39.125-117.813a290.509 290.509 0 0 0-2.22-3.78l-27.56 71.374c5.154.762 10.123 3.158 14.092 7.126 9.81 9.807 9.813 25.69 0 35.5-9.812 9.81-25.722 9.807-35.53 0-8.86-8.858-9.69-22.68-2.532-32.5l38.938-100.844a322.02 322.02 0 0 0-20.25-25.937zM51.842 118.72c-8.46 17.373-15.76 36.198-21.187 56.436-14.108 52.617-13.96 103.682-2.812 143.438 13.3-2.605 26.442-3.96 39.312-4.03 1.855-.012 3.688.02 5.53.06 20.857.48 40.98 4.332 59.97 11.5a355.064 355.064 0 0 1-1.656-34.218c0-27.8 3.135-54.377 9-78.937l2.47-10.407 9.655 4.562c29.467 13.98 66.194 23.424 106.28 25.22 5.136-20.05 8.19-39.78 9.408-58.75-35.198 4.83-75.387 2.766-116.407-8.22-38.363-10.272-72.314-26.78-99.562-46.656zm230.594 82.218c-1.535 10.452-3.615 21.03-6.218 31.687a312.754 312.754 0 0 0 46-3.97 24.98 24.98 0 0 1-1.532-21.748l-38.25-5.97zM105 201.375l4.156 18.22-21.594 4.905c8.75 5.174 13.353 15.703 10.594 26-3.32 12.394-16.045 19.758-28.437 16.438-12.394-3.32-19.76-16.075-16.44-28.47a23.235 23.235 0 0 1 3.126-6.874l-21.062 4.78-4.125-18.218 73.78-16.78zm388.594 22.813c-25.53 25.46-55.306 45.445-86.906 60.5.05 2.397.093 4.8.093 7.218 0 9.188-.354 18.232-1.03 27.125 16.635 1.33 32.045-1.7 45.344-9.374 25.925-14.962 40.608-45.694 42.5-85.47zm-338.844 3c-4.03 19.993-6.33 41.31-6.406 63.593l.125-.342c30.568 10.174 62.622 17.572 95.25 21.375l7.5.875.718 7.5 5.687 60.125-18.625 1.75-2.53-26.75a23.117 23.117 0 0 1-14.845.968c-12.393-3.32-19.76-16.042-16.438-28.436.285-1.06.647-2.08 1.063-3.063a496.627 496.627 0 0 1-57.406-14.53c2.69 49.62 16.154 94.04 36.094 126.656 22.366 36.588 52.13 57.78 83.968 57.78 31.838.003 61.602-21.19 83.97-57.78 19.536-31.96 32.846-75.244 35.905-123.656a499.132 499.132 0 0 1-48.25 11.656c1.914 4.57 2.415 9.78 1.033 14.938-3.322 12.394-16.045 19.758-28.438 16.437a23.01 23.01 0 0 1-2.125-.686l-2.5 26.47-18.594-1.752 5.688-60.125.72-7.5 7.498-.875c29.245-3.407 57.995-9.717 85.657-18.312v-1.594c0-21.573-2.27-42.23-6.064-61.75C351.132 242.653 313.092 250 272.312 250c-43.59 0-83.986-8.658-117.562-22.813zm-87.5 105.968c-10.87.102-21.995 1.22-33.375 3.313 12.695 31.62 33.117 53.07 59 60 16.9 4.523 34.896 2.536 52.813-5.25-4.382-13.89-7.874-28.606-10.344-43.97-21.115-9.623-43.934-14.32-68.094-14.094zm137.5 80.22h130.813c-40.082 44.594-92.623 42.844-130.813 0z" fill="#fff" fill-opacity="1"></path></g></svg>
|
||||
|
After Width: | Height: | Size: 3 KiB |
|
|
@ -196,6 +196,11 @@ Hooks.once('init', () => {
|
|||
makeDefault: true,
|
||||
label: sheetLabel('TYPES.Actor.environment')
|
||||
});
|
||||
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.NPC, {
|
||||
types: ['npc'],
|
||||
makeDefault: true,
|
||||
label: sheetLabel('TYPES.Actor.npc')
|
||||
});
|
||||
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Party, {
|
||||
types: ['party'],
|
||||
makeDefault: true,
|
||||
|
|
@ -441,3 +446,33 @@ Hooks.on('canvasTearDown', canvas => {
|
|||
Hooks.on('canvasReady', canas => {
|
||||
game.system.registeredTriggers.registerSceneTriggers(canvas.scene);
|
||||
});
|
||||
|
||||
/** Make the user to select a document type, instead of having a default doc type for them to accidentally keep */
|
||||
Hooks.on('renderDialogV2', (_dialog, html) => {
|
||||
if (!html.classList.contains('dialog')) return;
|
||||
const cls = html.classList.contains('item-create')
|
||||
? documents.DHItem.implementation
|
||||
: html.classList.contains('actor-create')
|
||||
? documents.DhpActor.implementation
|
||||
: null;
|
||||
if (!cls) return;
|
||||
|
||||
const form = html.querySelector('form');
|
||||
const submit = html.querySelector('button[type=submit]');
|
||||
const select = html.querySelector('select[name=type]');
|
||||
const nameInput = html.querySelector('input[name=name]');
|
||||
if (!form || !select || !submit || !nameInput) return;
|
||||
|
||||
nameInput.placeholder = cls.defaultName({});
|
||||
const emptyOption = document.createElement('option');
|
||||
emptyOption.value = '';
|
||||
emptyOption.selected = true;
|
||||
select.required = true;
|
||||
select.prepend(emptyOption);
|
||||
submit.addEventListener('click', event => {
|
||||
if (!form.reportValidity()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
// Less configuration
|
||||
var gulp = require('gulp');
|
||||
var less = require('gulp-less');
|
||||
var sourcemaps = require('gulp-sourcemaps');
|
||||
|
||||
gulp.task('less', function (cb) {
|
||||
gulp.src('styles/daggerheart.less').pipe(less()).on('error', console.error.bind(console)).pipe(gulp.dest('styles'));
|
||||
gulp.src('styles/daggerheart.less')
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(less())
|
||||
.on('error', console.error.bind(console))
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest('styles'));
|
||||
cb();
|
||||
});
|
||||
|
||||
|
|
|
|||
28
lang/en.json
28
lang/en.json
|
|
@ -23,6 +23,7 @@
|
|||
"companion": "Companion",
|
||||
"adversary": "Adversary",
|
||||
"environment": "Environment",
|
||||
"npc": "NPC",
|
||||
"party": "Party"
|
||||
}
|
||||
},
|
||||
|
|
@ -333,6 +334,11 @@
|
|||
},
|
||||
"newAdversary": "New Adversary"
|
||||
},
|
||||
"NPC": {
|
||||
"FIELDS": {
|
||||
"motives": { "label": "Motives" }
|
||||
}
|
||||
},
|
||||
"Party": {
|
||||
"Subtitle": {
|
||||
"character": "{community} {ancestry} | {subclass} {class}",
|
||||
|
|
@ -705,19 +711,13 @@
|
|||
},
|
||||
"PendingReactionsDialog": {
|
||||
"title": "Pending Reaction Rolls Found",
|
||||
"unfinishedRolls": "Some Tokens still need to roll their Reaction Roll.",
|
||||
"confirmation": "Are you sure you want to continue ?",
|
||||
"warning": "Undone reaction rolls will be considered as failed"
|
||||
"unfinishedRolls": "Some Tokens have not finished their Reaction Rolls.",
|
||||
"warning": "Unfinished reaction rolls will be considered as failed.",
|
||||
"confirmation": "Are you sure you want to continue?"
|
||||
},
|
||||
"ReactionRoll": {
|
||||
"title": "Reaction Roll: {trait}"
|
||||
},
|
||||
"RerollDialog": {
|
||||
"title": "Reroll",
|
||||
"damageTitle": "Reroll Damage",
|
||||
"deselectDiceNotification": "Deselect one of the selected dice first",
|
||||
"acceptCurrentRolls": "Accept Current Rolls"
|
||||
},
|
||||
"ResourceDice": {
|
||||
"title": "{name} Resource",
|
||||
"rerollDice": "Reroll Dice"
|
||||
|
|
@ -778,7 +778,9 @@
|
|||
"title": "Group Roll"
|
||||
},
|
||||
"TokenConfig": {
|
||||
"actorSizeUsed": "Actor size is set, determining the dimensions"
|
||||
"actorSizeUsed": "Actor size is set, determining the dimensions",
|
||||
"tokenSize": "Token Size",
|
||||
"sizeCategory": "Size Category"
|
||||
}
|
||||
},
|
||||
"CLASS": {
|
||||
|
|
@ -2565,10 +2567,11 @@
|
|||
"tokenImg": { "label": "Token Image" },
|
||||
"tokenRingImg": { "label": "Subject Texture" },
|
||||
"tokenSize": {
|
||||
"placeholder": "Using character dimensions",
|
||||
"disabledPlaceholder": "Set by character size",
|
||||
"placeholder": "Token Size",
|
||||
"disabledPlaceholder": "Token Size",
|
||||
"height": { "label": "Height" },
|
||||
"width": { "label": "Width" },
|
||||
"depth": { "label": "Depth" },
|
||||
"scale": { "label": "Token Scale" }
|
||||
},
|
||||
"evolved": {
|
||||
|
|
@ -3094,6 +3097,7 @@
|
|||
}
|
||||
},
|
||||
"ChatLog": {
|
||||
"rerollActionRoll": "Reroll Action",
|
||||
"rerollDamage": "Reroll Damage",
|
||||
"assignTagRoll": "Assign as Tag Roll"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export default class CompendiumBrowserSettings extends HandlebarsApplicationMixi
|
|||
const excludedSourceData = this.browserSettings.excludedSources;
|
||||
const excludedPackData = this.browserSettings.excludedPacks;
|
||||
context.typePackCollections = game.packs.reduce((acc, pack) => {
|
||||
const { type, label, packageType, packageName: basePackageName, id } = pack.metadata;
|
||||
const { type, label, packageType, packageName: basePackageName, name, id } = pack.metadata;
|
||||
if (!CompendiumBrowserSettings.#browserPackTypes.includes(type)) return acc;
|
||||
|
||||
const isWorldPack = packageType === 'world';
|
||||
|
|
@ -68,13 +68,15 @@ export default class CompendiumBrowserSettings extends HandlebarsApplicationMixi
|
|||
if (!acc[type].sources[packageName])
|
||||
acc[type].sources[packageName] = { label: sourceLabel, checked: sourceChecked, packs: [] };
|
||||
|
||||
const checked = !excludedPackData[id] || !excludedPackData[id].excludedDocumentTypes.includes(type);
|
||||
const included =
|
||||
!excludedPackData[packageName] ||
|
||||
!excludedPackData[packageName][name]?.excludedDocumentTypes.includes(type);
|
||||
|
||||
acc[type].sources[packageName].packs.push({
|
||||
pack: id,
|
||||
name,
|
||||
type,
|
||||
label: id === game.system.id ? game.system.title : game.i18n.localize(label),
|
||||
checked: checked
|
||||
checked: included
|
||||
});
|
||||
|
||||
return acc;
|
||||
|
|
@ -106,16 +108,16 @@ export default class CompendiumBrowserSettings extends HandlebarsApplicationMixi
|
|||
toggleTypedPack(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
const { type, pack } = event.target.dataset;
|
||||
const currentlyExcluded = this.browserSettings.excludedPacks[pack]
|
||||
? this.browserSettings.excludedPacks[pack].excludedDocumentTypes.includes(type)
|
||||
const { type, source, packName } = event.target.dataset;
|
||||
const currentlyExcluded = this.browserSettings.excludedPacks[source]?.[packName]
|
||||
? this.browserSettings.excludedPacks[source][packName].excludedDocumentTypes.includes(type)
|
||||
: false;
|
||||
|
||||
if (!this.browserSettings.excludedPacks[pack])
|
||||
this.browserSettings.excludedPacks[pack] = { excludedDocumentTypes: [] };
|
||||
this.browserSettings.excludedPacks[pack].excludedDocumentTypes = currentlyExcluded
|
||||
? this.browserSettings.excludedPacks[pack].excludedDocumentTypes.filter(x => x !== type)
|
||||
: [...(this.browserSettings.excludedPacks[pack]?.excludedDocumentTypes ?? []), type];
|
||||
this.browserSettings.excludedPacks[source] ??= {};
|
||||
this.browserSettings.excludedPacks[source][packName] ??= { excludedDocumentTypes: [] };
|
||||
this.browserSettings.excludedPacks[source][packName].excludedDocumentTypes = currentlyExcluded
|
||||
? this.browserSettings.excludedPacks[source][packName].excludedDocumentTypes.filter(x => x !== type)
|
||||
: [...(this.browserSettings.excludedPacks[source][packName]?.excludedDocumentTypes ?? []), type];
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ export { default as ImageSelectDialog } from './imageSelectDialog.mjs';
|
|||
export { default as ItemTransferDialog } from './itemTransfer.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 TagTeamDialog } from './tagTeamDialog.mjs';
|
||||
|
|
|
|||
|
|
@ -175,14 +175,14 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
this.disadvantage = advantage === -1;
|
||||
|
||||
this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage;
|
||||
if (this.config.roll.advantage === 0) return this.render();
|
||||
|
||||
if (this.config.roll.advantage === 1 && this.config.data.rules.roll.defaultAdvantageDice) {
|
||||
const faces = Number.parseInt(this.config.data.rules.roll.defaultAdvantageDice);
|
||||
this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;
|
||||
} else if (this.config.roll.advantage === -1 && this.config.data.rules.roll.defaultDisadvantageDice) {
|
||||
const faces = Number.parseInt(this.config.data.rules.roll.defaultDisadvantageDice);
|
||||
this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;
|
||||
}
|
||||
const defaultFaces =
|
||||
this.config.roll.advantage === 1
|
||||
? this.config.data.rules.roll.defaultAdvantageDice
|
||||
: this.config.data.rules.roll.defaultDisadvantageDice;
|
||||
const faces = Number.parseInt(defaultFaces);
|
||||
this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
|
|||
|
||||
let returnMessage = game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.avoidScar');
|
||||
if (config.roll.fate.value <= this.actor.system.levelData.level.current) {
|
||||
const maxHope = this.actor.system.resources.hope.max + this.actor.system.scars;
|
||||
const newScarAmount = this.actor.system.scars + 1;
|
||||
await this.actor.update({
|
||||
system: {
|
||||
|
|
@ -64,7 +65,7 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
|
|||
}
|
||||
});
|
||||
|
||||
if (newScarAmount >= this.actor.system.resources.hope.max) {
|
||||
if (newScarAmount >= maxHope) {
|
||||
await this.actor.setDeathMoveDefeated(CONFIG.DH.GENERAL.defeatedConditionChoices.dead.id);
|
||||
return game.i18n.format('DAGGERHEART.UI.Chat.deathMove.journeysEnd', { scars: newScarAmount });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,12 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
const context = await super._prepareContext(_options);
|
||||
|
||||
context.isGM = game.user.isGM;
|
||||
context.isEditable = this.getIsEditable();
|
||||
context.isEditable =
|
||||
game.user.isGM ||
|
||||
this.party.system.partyMembers.some(actor => {
|
||||
const selected = Boolean(this.party.system.groupRoll.participants[actor.id]);
|
||||
return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER);
|
||||
});
|
||||
context.fields = this.party.system.schema.fields.groupRoll.fields;
|
||||
context.data = this.party.system.groupRoll;
|
||||
context.traitOptions = CONFIG.DH.ACTOR.abilities;
|
||||
|
|
@ -265,13 +270,6 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
];
|
||||
}
|
||||
|
||||
getIsEditable() {
|
||||
return this.party.system.partyMembers.some(actor => {
|
||||
const selected = Boolean(this.party.system.groupRoll.participants[actor.id]);
|
||||
return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER);
|
||||
});
|
||||
}
|
||||
|
||||
groupRollRefresh = ({ refreshType, action, parts }) => {
|
||||
if (refreshType !== RefreshType.GroupRoll) return;
|
||||
|
||||
|
|
@ -358,8 +356,6 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
});
|
||||
|
||||
if (!result) return;
|
||||
// todo: move logic to actor.rollTrait() or actor.diceRoll()
|
||||
if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
||||
|
||||
const rollData = result.messageRoll.toJSON();
|
||||
delete rollData.options.messageRoll;
|
||||
|
|
|
|||
|
|
@ -1,280 +0,0 @@
|
|||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class RerollDamageDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(message, options = {}) {
|
||||
super(options);
|
||||
|
||||
this.message = message;
|
||||
this.damage = Object.keys(message.system.damage).reduce((acc, typeKey) => {
|
||||
const type = message.system.damage[typeKey];
|
||||
acc[typeKey] = Object.keys(type.parts).reduce((acc, partKey) => {
|
||||
const part = type.parts[partKey];
|
||||
acc[partKey] = Object.keys(part.dice).reduce((acc, diceKey) => {
|
||||
const dice = part.dice[diceKey];
|
||||
const activeResults = dice.results.filter(x => x.active);
|
||||
acc[diceKey] = {
|
||||
dice: dice.dice,
|
||||
selectedResults: activeResults.length,
|
||||
maxSelected: activeResults.length,
|
||||
results: activeResults.map(x => ({ ...x, selected: true }))
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: 'reroll-dialog',
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'reroll-dialog'],
|
||||
window: {
|
||||
icon: 'fa-solid fa-dice'
|
||||
},
|
||||
actions: {
|
||||
toggleResult: RerollDamageDialog.#toggleResult,
|
||||
selectRoll: RerollDamageDialog.#selectRoll,
|
||||
doReroll: RerollDamageDialog.#doReroll,
|
||||
save: RerollDamageDialog.#save
|
||||
}
|
||||
};
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
id: 'main',
|
||||
template: 'systems/daggerheart/templates/dialogs/rerollDialog/damage/main.hbs'
|
||||
},
|
||||
footer: {
|
||||
id: 'footer',
|
||||
template: 'systems/daggerheart/templates/dialogs/rerollDialog/footer.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.damageTitle');
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelectorAll('.to-reroll-input').forEach(element => {
|
||||
element.addEventListener('change', this.toggleDice.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.damage = this.damage;
|
||||
context.disabledReroll = !this.getRerollDice().length;
|
||||
context.saveDisabled = !this.isSelectionDone();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async #save() {
|
||||
const update = {
|
||||
'system.damage': Object.keys(this.damage).reduce((acc, typeKey) => {
|
||||
const type = this.damage[typeKey];
|
||||
let typeTotal = 0;
|
||||
const messageType = this.message.system.damage[typeKey];
|
||||
const parts = Object.keys(type).map(partKey => {
|
||||
const part = type[partKey];
|
||||
const messagePart = messageType.parts[partKey];
|
||||
let partTotal = messagePart.modifierTotal;
|
||||
const dice = Object.keys(part).map(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
const total = dice.results.reduce((acc, result) => {
|
||||
if (result.active) acc += result.result;
|
||||
return acc;
|
||||
}, 0);
|
||||
partTotal += total;
|
||||
const messageDice = messagePart.dice[diceKey];
|
||||
return {
|
||||
...messageDice,
|
||||
total: total,
|
||||
results: dice.results.map(x => ({
|
||||
...x,
|
||||
hasRerolls: dice.results.length > 1
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
typeTotal += partTotal;
|
||||
return {
|
||||
...messagePart,
|
||||
total: partTotal,
|
||||
dice: dice
|
||||
};
|
||||
});
|
||||
|
||||
acc[typeKey] = {
|
||||
...messageType,
|
||||
total: typeTotal,
|
||||
parts: parts
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
|
||||
await this.message.update(update);
|
||||
await this.close();
|
||||
}
|
||||
|
||||
getRerollDice() {
|
||||
const rerollDice = [];
|
||||
Object.keys(this.damage).forEach(typeKey => {
|
||||
const type = this.damage[typeKey];
|
||||
Object.keys(type).forEach(partKey => {
|
||||
const part = type[partKey];
|
||||
Object.keys(part).forEach(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
Object.keys(dice.results).forEach(resultKey => {
|
||||
const result = dice.results[resultKey];
|
||||
if (result.toReroll) {
|
||||
rerollDice.push({
|
||||
...result,
|
||||
dice: dice.dice,
|
||||
type: typeKey,
|
||||
part: partKey,
|
||||
dice: diceKey,
|
||||
result: resultKey
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return rerollDice;
|
||||
}
|
||||
|
||||
isSelectionDone() {
|
||||
const diceFinishedData = [];
|
||||
Object.keys(this.damage).forEach(typeKey => {
|
||||
const type = this.damage[typeKey];
|
||||
Object.keys(type).forEach(partKey => {
|
||||
const part = type[partKey];
|
||||
Object.keys(part).forEach(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
const selected = dice.results.reduce((acc, result) => acc + (result.active ? 1 : 0), 0);
|
||||
diceFinishedData.push(selected === dice.maxSelected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return diceFinishedData.every(x => x);
|
||||
}
|
||||
|
||||
toggleDice(event) {
|
||||
const target = event.target;
|
||||
const { type, part, dice } = target.dataset;
|
||||
const toggleDice = this.damage[type][part][dice];
|
||||
|
||||
const existingDiceRerolls = this.getRerollDice().filter(
|
||||
x => x.type === type && x.part === part && x.dice === dice
|
||||
);
|
||||
|
||||
const allRerolled = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length;
|
||||
|
||||
toggleDice.toReroll = !allRerolled;
|
||||
toggleDice.results.forEach(result => {
|
||||
if (result.active) {
|
||||
result.toReroll = !allRerolled;
|
||||
}
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static #toggleResult(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
const target = event.target.closest('.to-reroll-result');
|
||||
const { type, part, dice, result } = target.dataset;
|
||||
const toggleDice = this.damage[type][part][dice];
|
||||
const toggleResult = toggleDice.results[result];
|
||||
toggleResult.toReroll = !toggleResult.toReroll;
|
||||
|
||||
const existingDiceRerolls = this.getRerollDice().filter(
|
||||
x => x.type === type && x.part === part && x.dice === dice
|
||||
);
|
||||
|
||||
const allToReroll = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length;
|
||||
toggleDice.toReroll = allToReroll;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #selectRoll(_, button) {
|
||||
const { type, part, dice, result } = button.dataset;
|
||||
|
||||
const diceVal = this.damage[type][part][dice];
|
||||
const diceResult = diceVal.results[result];
|
||||
if (!diceResult.active && diceVal.results.filter(x => x.active).length === diceVal.maxSelected) {
|
||||
return ui.notifications.warn(
|
||||
game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.deselectDiceNotification')
|
||||
);
|
||||
}
|
||||
|
||||
if (diceResult.active) {
|
||||
diceVal.toReroll = false;
|
||||
diceResult.toReroll = false;
|
||||
}
|
||||
|
||||
diceVal.selectedResults += diceResult.active ? -1 : 1;
|
||||
diceResult.active = !diceResult.active;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #doReroll() {
|
||||
const toReroll = this.getRerollDice().map(x => {
|
||||
const { type, part, dice, result } = x;
|
||||
const diceData = this.damage[type][part][dice].results[result];
|
||||
return {
|
||||
...diceData,
|
||||
dice: this.damage[type][part][dice].dice,
|
||||
typeKey: type,
|
||||
partKey: part,
|
||||
diceKey: dice,
|
||||
resultsIndex: result
|
||||
};
|
||||
});
|
||||
|
||||
const roll = await new Roll(toReroll.map(x => `1${x.dice}`).join(' + ')).evaluate();
|
||||
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const diceSoNiceRoll = {
|
||||
_evaluated: true,
|
||||
dice: roll.dice,
|
||||
options: { appearance: {} }
|
||||
};
|
||||
|
||||
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
|
||||
}
|
||||
|
||||
toReroll.forEach((data, index) => {
|
||||
const { typeKey, partKey, diceKey, resultsIndex } = data;
|
||||
const rerolledDice = roll.dice[index];
|
||||
|
||||
const dice = this.damage[typeKey][partKey][diceKey];
|
||||
dice.toReroll = false;
|
||||
dice.results[resultsIndex].active = false;
|
||||
dice.results[resultsIndex].discarded = true;
|
||||
dice.results[resultsIndex].toReroll = false;
|
||||
dice.results.splice(dice.results.length, 0, {
|
||||
...rerolledDice.results[0],
|
||||
toReroll: false,
|
||||
selected: true
|
||||
});
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,279 +0,0 @@
|
|||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class RerollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(message, options = {}) {
|
||||
super(options);
|
||||
|
||||
this.message = message;
|
||||
this.damage = Object.keys(message.system.damage).reduce((acc, typeKey) => {
|
||||
const type = message.system.damage[typeKey];
|
||||
acc[typeKey] = Object.keys(type.parts).reduce((acc, partKey) => {
|
||||
const part = type.parts[partKey];
|
||||
acc[partKey] = Object.keys(part.dice).reduce((acc, diceKey) => {
|
||||
const dice = part.dice[diceKey];
|
||||
const activeResults = dice.results.filter(x => x.active);
|
||||
acc[diceKey] = {
|
||||
dice: dice.dice,
|
||||
selectedResults: activeResults.length,
|
||||
maxSelected: activeResults.length,
|
||||
results: activeResults.map(x => ({ ...x, selected: true }))
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: 'reroll-dialog',
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'reroll-dialog'],
|
||||
window: {
|
||||
icon: 'fa-solid fa-dice'
|
||||
},
|
||||
actions: {
|
||||
toggleResult: RerollDialog.#toggleResult,
|
||||
selectRoll: RerollDialog.#selectRoll,
|
||||
doReroll: RerollDialog.#doReroll,
|
||||
save: RerollDialog.#save
|
||||
}
|
||||
};
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
id: 'main',
|
||||
template: 'systems/daggerheart/templates/dialogs/rerollDialog/main.hbs'
|
||||
},
|
||||
footer: {
|
||||
id: 'footer',
|
||||
template: 'systems/daggerheart/templates/dialogs/rerollDialog/footer.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.title');
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelectorAll('.to-reroll-input').forEach(element => {
|
||||
element.addEventListener('change', this.toggleDice.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.damage = this.damage;
|
||||
context.disabledReroll = !this.getRerollDice().length;
|
||||
context.saveDisabled = !this.isSelectionDone();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async #save() {
|
||||
const update = {
|
||||
'system.damage': Object.keys(this.damage).reduce((acc, typeKey) => {
|
||||
const type = this.damage[typeKey];
|
||||
let typeTotal = 0;
|
||||
const messageType = this.message.system.damage[typeKey];
|
||||
const parts = Object.keys(type).map(partKey => {
|
||||
const part = type[partKey];
|
||||
const messagePart = messageType.parts[partKey];
|
||||
let partTotal = messagePart.modifierTotal;
|
||||
const dice = Object.keys(part).map(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
const total = dice.results.reduce((acc, result) => {
|
||||
if (result.active) acc += result.result;
|
||||
return acc;
|
||||
}, 0);
|
||||
partTotal += total;
|
||||
const messageDice = messagePart.dice[diceKey];
|
||||
return {
|
||||
...messageDice,
|
||||
total: total,
|
||||
results: dice.results.map(x => ({
|
||||
...x,
|
||||
hasRerolls: dice.results.length > 1
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
typeTotal += partTotal;
|
||||
return {
|
||||
...messagePart,
|
||||
total: partTotal,
|
||||
dice: dice
|
||||
};
|
||||
});
|
||||
|
||||
acc[typeKey] = {
|
||||
...messageType,
|
||||
total: typeTotal,
|
||||
parts: parts
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
await this.message.update(update);
|
||||
await this.close();
|
||||
}
|
||||
|
||||
getRerollDice() {
|
||||
const rerollDice = [];
|
||||
Object.keys(this.damage).forEach(typeKey => {
|
||||
const type = this.damage[typeKey];
|
||||
Object.keys(type).forEach(partKey => {
|
||||
const part = type[partKey];
|
||||
Object.keys(part).forEach(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
Object.keys(dice.results).forEach(resultKey => {
|
||||
const result = dice.results[resultKey];
|
||||
if (result.toReroll) {
|
||||
rerollDice.push({
|
||||
...result,
|
||||
dice: dice.dice,
|
||||
type: typeKey,
|
||||
part: partKey,
|
||||
dice: diceKey,
|
||||
result: resultKey
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return rerollDice;
|
||||
}
|
||||
|
||||
isSelectionDone() {
|
||||
const diceFinishedData = [];
|
||||
Object.keys(this.damage).forEach(typeKey => {
|
||||
const type = this.damage[typeKey];
|
||||
Object.keys(type).forEach(partKey => {
|
||||
const part = type[partKey];
|
||||
Object.keys(part).forEach(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
const selected = dice.results.reduce((acc, result) => acc + (result.active ? 1 : 0), 0);
|
||||
diceFinishedData.push(selected === dice.maxSelected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return diceFinishedData.every(x => x);
|
||||
}
|
||||
|
||||
toggleDice(event) {
|
||||
const target = event.target;
|
||||
const { type, part, dice } = target.dataset;
|
||||
const toggleDice = this.damage[type][part][dice];
|
||||
|
||||
const existingDiceRerolls = this.getRerollDice().filter(
|
||||
x => x.type === type && x.part === part && x.dice === dice
|
||||
);
|
||||
|
||||
const allRerolled = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length;
|
||||
|
||||
toggleDice.toReroll = !allRerolled;
|
||||
toggleDice.results.forEach(result => {
|
||||
if (result.active) {
|
||||
result.toReroll = !allRerolled;
|
||||
}
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static #toggleResult(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
const target = event.target.closest('.to-reroll-result');
|
||||
const { type, part, dice, result } = target.dataset;
|
||||
const toggleDice = this.damage[type][part][dice];
|
||||
const toggleResult = toggleDice.results[result];
|
||||
toggleResult.toReroll = !toggleResult.toReroll;
|
||||
|
||||
const existingDiceRerolls = this.getRerollDice().filter(
|
||||
x => x.type === type && x.part === part && x.dice === dice
|
||||
);
|
||||
|
||||
const allToReroll = existingDiceRerolls.length === toggleDice.results.length;
|
||||
toggleDice.toReroll = allToReroll;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #selectRoll(_, button) {
|
||||
const { type, part, dice, result } = button.dataset;
|
||||
|
||||
const diceVal = this.damage[type][part][dice];
|
||||
const diceResult = diceVal.results[result];
|
||||
if (!diceResult.active && diceVal.results.filter(x => x.active).length === diceVal.maxSelected) {
|
||||
return ui.notifications.warn(
|
||||
game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.deselectDiceNotification')
|
||||
);
|
||||
}
|
||||
|
||||
if (diceResult.active) {
|
||||
diceVal.toReroll = false;
|
||||
diceResult.toReroll = false;
|
||||
}
|
||||
|
||||
diceVal.selectedResults += diceResult.active ? -1 : 1;
|
||||
diceResult.active = !diceResult.active;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #doReroll() {
|
||||
const toReroll = this.getRerollDice().map(x => {
|
||||
const { type, part, dice, result } = x;
|
||||
const diceData = this.damage[type][part][dice].results[result];
|
||||
return {
|
||||
...diceData,
|
||||
dice: this.damage[type][part][dice].dice,
|
||||
typeKey: type,
|
||||
partKey: part,
|
||||
diceKey: dice,
|
||||
resultsIndex: result
|
||||
};
|
||||
});
|
||||
|
||||
const roll = await new Roll(toReroll.map(x => `1${x.dice}`).join(' + ')).evaluate();
|
||||
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const diceSoNiceRoll = {
|
||||
_evaluated: true,
|
||||
dice: roll.dice,
|
||||
options: { appearance: {} }
|
||||
};
|
||||
|
||||
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
|
||||
}
|
||||
|
||||
toReroll.forEach((data, index) => {
|
||||
const { typeKey, partKey, diceKey, resultsIndex } = data;
|
||||
const rerolledDice = roll.dice[index];
|
||||
|
||||
const dice = this.damage[typeKey][partKey][diceKey];
|
||||
dice.toReroll = false;
|
||||
dice.results[resultsIndex].active = false;
|
||||
dice.results[resultsIndex].discarded = true;
|
||||
dice.results[resultsIndex].toReroll = false;
|
||||
dice.results.splice(dice.results.length, 0, {
|
||||
...rerolledDice.results[0],
|
||||
toReroll: false,
|
||||
selected: true
|
||||
});
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { itemAbleRollParse } from '../../helpers/utils.mjs';
|
||||
import { itemAbleRollParse, triggerChatRollFx } from '../../helpers/utils.mjs';
|
||||
|
||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ export default class ResourceDiceDialog extends HandlebarsApplicationMixin(Appli
|
|||
const max = itemAbleRollParse(this.item.system.resource.max, this.actor, this.item);
|
||||
const diceFormula = `${max}${this.item.system.resource.dieFaces}`;
|
||||
const roll = await new Roll(diceFormula).evaluate();
|
||||
if (game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true);
|
||||
await triggerChatRollFx([roll]);
|
||||
this.rollValues = roll.terms[0].results.map(x => ({ value: x.result, used: false }));
|
||||
this.resetUsed = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
tag: 'form',
|
||||
classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'tag-team-dialog'],
|
||||
position: { width: 550, height: 'auto' },
|
||||
window: {
|
||||
icon: 'fa-solid fa-user-group'
|
||||
},
|
||||
actions: {
|
||||
toggleSelectMember: TagTeamDialog.#toggleSelectMember,
|
||||
startTagTeamRoll: TagTeamDialog.#startTagTeamRoll,
|
||||
|
|
@ -113,7 +116,12 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.isEditable = this.getIsEditable();
|
||||
context.isEditable =
|
||||
game.user.isGM ||
|
||||
this.party.system.partyMembers.some(actor => {
|
||||
const selected = Boolean(this.party.system.tagTeam.members[actor.id]);
|
||||
return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER);
|
||||
});
|
||||
context.fields = this.party.system.schema.fields.tagTeam.fields;
|
||||
context.data = this.party.system.tagTeam;
|
||||
context.rollTypes = CONFIG.DH.GENERAL.tagTeamRollTypes;
|
||||
|
|
@ -176,57 +184,56 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
}
|
||||
|
||||
if (Object.keys(this.party.system.tagTeam.members).includes(partId)) {
|
||||
const data = this.party.system.tagTeam.members[partId];
|
||||
const actor = game.actors.get(partId);
|
||||
|
||||
const rollOptions = [];
|
||||
const damageRollOptions = [];
|
||||
for (const item of actor.items) {
|
||||
if (item.system.metadata.hasActions) {
|
||||
const actions = [...item.system.actions, ...(item.system.attack ? [item.system.attack] : [])];
|
||||
for (const action of actions) {
|
||||
if (action.hasRoll) {
|
||||
const actionItem = {
|
||||
value: action.uuid,
|
||||
label: action.name,
|
||||
group: item.name,
|
||||
baseAction: action.baseAction
|
||||
};
|
||||
|
||||
if (action.hasDamage) damageRollOptions.push(actionItem);
|
||||
else rollOptions.push(actionItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected);
|
||||
const critSelected = !selectedRoll
|
||||
? undefined
|
||||
: (selectedRoll?.rollData?.options?.roll?.isCritical ?? false);
|
||||
|
||||
const damage = data.rollData?.options?.damage;
|
||||
partContext.hasDamage |= Boolean(damage);
|
||||
const critHitPointsDamage = await this.getCriticalDamage(damage);
|
||||
|
||||
partContext.members[partId] = {
|
||||
...data,
|
||||
roll: data.roll,
|
||||
isEditable: actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER),
|
||||
key: partId,
|
||||
readyToRoll: Boolean(data.rollChoice),
|
||||
hasRolled: Boolean(data.rollData),
|
||||
rollOptions,
|
||||
damageRollOptions,
|
||||
damage: damage,
|
||||
critDamage: critHitPointsDamage,
|
||||
useCritDamage: critSelected || (critSelected === undefined && data.rollData?.options?.roll?.isCritical)
|
||||
};
|
||||
const data = await this.#prepareMemberContext(partId);
|
||||
partContext.hasDamage |= Boolean(data?.damage);
|
||||
partContext.members[partId] = data;
|
||||
}
|
||||
|
||||
return partContext;
|
||||
}
|
||||
|
||||
async #prepareMemberContext(partId) {
|
||||
const data = this.party.system.tagTeam.members[partId] ?? {};
|
||||
const actor = game.actors.get(partId);
|
||||
if (!actor) console.error(`Failed to get actor ${partId}`);
|
||||
|
||||
const rollOptions = [];
|
||||
const damageRollOptions = [];
|
||||
for (const item of actor?.items ?? []) {
|
||||
if (!item.system.metadata.hasActions) continue;
|
||||
const actions = [...item.system.actions, ...(item.system.attack ? [item.system.attack] : [])];
|
||||
for (const action of actions) {
|
||||
if (action.hasRoll) {
|
||||
const collection = action.hasDamage ? damageRollOptions : rollOptions;
|
||||
collection.push({
|
||||
value: action.uuid,
|
||||
label: action.name,
|
||||
group: item.name,
|
||||
baseAction: action.baseAction
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected);
|
||||
const critSelected = !selectedRoll ? undefined : (selectedRoll?.rollData?.options?.roll?.isCritical ?? false);
|
||||
const damage = data.rollData?.options?.damage;
|
||||
|
||||
return {
|
||||
...data,
|
||||
roll: data.roll,
|
||||
isEditable: actor?.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER),
|
||||
key: partId,
|
||||
readyToRoll: Boolean(data.rollChoice),
|
||||
hasRolled: Boolean(data.rollData),
|
||||
rollOptions,
|
||||
damageRollOptions,
|
||||
damage: damage,
|
||||
critDamage: await this.getCriticalDamage(damage),
|
||||
useCritDamage: critSelected || (critSelected === undefined && data.rollData?.options?.roll?.isCritical)
|
||||
};
|
||||
}
|
||||
|
||||
getUpdatingParts(target) {
|
||||
const { initialization, rollSelection, result } = this.constructor.PARTS;
|
||||
const isInitialization = this.tabGroups.application === initialization.id;
|
||||
|
|
@ -270,13 +277,6 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
);
|
||||
}
|
||||
|
||||
getIsEditable() {
|
||||
return this.party.system.partyMembers.some(actor => {
|
||||
const selected = Boolean(this.party.system.tagTeam.members[actor.id]);
|
||||
return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER);
|
||||
});
|
||||
}
|
||||
|
||||
tagTeamRefresh = ({ refreshType, action, parts }) => {
|
||||
if (refreshType !== RefreshType.TagTeamRoll) return;
|
||||
|
||||
|
|
@ -431,8 +431,6 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
|
||||
if (!result) return;
|
||||
|
||||
if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
||||
|
||||
const rollData = result.messageRoll.toJSON();
|
||||
delete rollData.options.messageRoll;
|
||||
this.updatePartyData(
|
||||
|
|
@ -648,42 +646,50 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
}
|
||||
|
||||
async getJoinedRoll({ overrideIsCritical, displayVersion } = {}) {
|
||||
const memberValues = Object.values(this.party.system.tagTeam.members);
|
||||
const selectedRoll = memberValues.find(x => x.selected);
|
||||
let baseMainRoll = selectedRoll ?? memberValues[0];
|
||||
let baseSecondaryRoll = selectedRoll
|
||||
? memberValues.find(x => !x.selected)
|
||||
: memberValues.length > 1
|
||||
? memberValues[1]
|
||||
: null;
|
||||
try {
|
||||
const memberValues = Object.values(this.party.system.tagTeam.members);
|
||||
const selectedRoll = memberValues.find(x => x.selected);
|
||||
const baseMainRoll = selectedRoll ?? memberValues[0];
|
||||
const baseSecondaryRoll = selectedRoll
|
||||
? memberValues.find(x => !x.selected)
|
||||
: memberValues.length > 1
|
||||
? memberValues[1]
|
||||
: null;
|
||||
|
||||
if (!baseMainRoll?.rollData || !baseSecondaryRoll) return null;
|
||||
if (!baseMainRoll?.rollData || !baseSecondaryRoll) return null;
|
||||
|
||||
const mainRoll = new MemberData(baseMainRoll.toObject());
|
||||
const secondaryRollData = new MemberData(baseSecondaryRoll.toObject()).rollData;
|
||||
const systemData = mainRoll.rollData.options;
|
||||
const isCritical = overrideIsCritical ?? systemData.roll.isCritical;
|
||||
if (isCritical) systemData.damage = await this.getCriticalDamage(systemData.damage);
|
||||
const mainRoll = new MemberData(baseMainRoll.toObject());
|
||||
const secondaryRollData = new MemberData(baseSecondaryRoll.toObject()).rollData;
|
||||
const systemData = mainRoll.rollData.options;
|
||||
const isCritical = overrideIsCritical ?? systemData.roll.isCritical;
|
||||
if (isCritical) systemData.damage = await this.getCriticalDamage(systemData.damage);
|
||||
|
||||
if (secondaryRollData?.options.hasDamage) {
|
||||
const secondaryDamage = (displayVersion ? overrideIsCritical : isCritical)
|
||||
? await this.getCriticalDamage(secondaryRollData.options.damage)
|
||||
: secondaryRollData.options.damage;
|
||||
if (systemData.damage) {
|
||||
for (const key in secondaryDamage) {
|
||||
const damage = secondaryDamage[key];
|
||||
systemData.damage[key].formula = [systemData.damage[key].formula, damage.formula]
|
||||
.filter(x => x)
|
||||
.join(' + ');
|
||||
systemData.damage[key].total += damage.total;
|
||||
systemData.damage[key].parts.push(...damage.parts);
|
||||
if (secondaryRollData?.options.hasDamage) {
|
||||
const secondaryDamage = (displayVersion ? overrideIsCritical : isCritical)
|
||||
? await this.getCriticalDamage(secondaryRollData.options.damage)
|
||||
: secondaryRollData.options.damage;
|
||||
if (systemData.damage) {
|
||||
for (const [key, damage] of Object.entries(secondaryDamage ?? {})) {
|
||||
if (key in systemData.damage) {
|
||||
systemData.damage[key].formula = [systemData.damage[key]?.formula, damage.formula]
|
||||
.filter(x => x)
|
||||
.join(' + ');
|
||||
systemData.damage[key].total += damage.total;
|
||||
systemData.damage[key].parts.push(...damage.parts);
|
||||
} else {
|
||||
systemData.damage[key] = damage;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
systemData.damage = secondaryDamage;
|
||||
}
|
||||
} else {
|
||||
systemData.damage = secondaryDamage;
|
||||
}
|
||||
}
|
||||
|
||||
return mainRoll;
|
||||
return mainRoll;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async #onCancelRoll(_event, _button, options = { confirm: true }) {
|
||||
|
|
|
|||
|
|
@ -124,7 +124,9 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
|
|||
const animationDuration = 500;
|
||||
const scene = game.scenes.get(game.user.viewedScene);
|
||||
/* getDependentTokens returns already removed tokens with id = null. Need to filter that until it's potentially fixed from Foundry */
|
||||
const activeTokens = actors.flatMap(member => member.getDependentTokens({ scenes: scene }).filter(x => x._id));
|
||||
const activeTokens = actors.flatMap(member =>
|
||||
member.getDependentTokens({ scenes: scene }).filter(x => x._id && !x._destroyed)
|
||||
);
|
||||
const { x: actorX, y: actorY } = this.document;
|
||||
if (activeTokens.length > 0) {
|
||||
for (let token of activeTokens) {
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ export default class DhCharacterLevelUp extends LevelUpBase {
|
|||
if (multiclasses?.[0]) {
|
||||
const data = multiclasses[0];
|
||||
const multiclass = data.data.length > 0 ? await foundry.utils.fromUuid(data.data[0]) : {};
|
||||
const subclasses = (await multiclass?.system?.fetchSubclasses()) ?? [];
|
||||
|
||||
context.multiclass = {
|
||||
...data,
|
||||
|
|
@ -175,13 +176,12 @@ export default class DhCharacterLevelUp extends LevelUpBase {
|
|||
alreadySelected
|
||||
};
|
||||
}) ?? [],
|
||||
subclasses:
|
||||
multiclass?.system?.subclasses.map(subclass => ({
|
||||
...subclass,
|
||||
uuid: subclass.uuid,
|
||||
selected: data.secondaryData.subclass === subclass.uuid,
|
||||
disabled: data.secondaryData.subclass && data.secondaryData.subclass !== subclass.uuid
|
||||
})) ?? [],
|
||||
subclasses: subclasses.map(subclass => ({
|
||||
...subclass,
|
||||
uuid: subclass.uuid,
|
||||
selected: data.secondaryData.subclass === subclass.uuid,
|
||||
disabled: data.secondaryData.subclass && data.secondaryData.subclass !== subclass.uuid
|
||||
})),
|
||||
compendium: 'classes',
|
||||
limit: 1
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { DhHomebrew } from '../../data/settings/_module.mjs';
|
||||
import { Resource } from '../../data/settings/Homebrew.mjs';
|
||||
import { slugify } from '../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
|
|
@ -403,12 +402,12 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
const domainName = button.form.elements.domainName.value;
|
||||
if (!domainName) return;
|
||||
|
||||
const newSlug = slugify(domainName);
|
||||
const newSlug = domainName.slugify();
|
||||
const existingDomains = [
|
||||
...Object.values(this.settings.domains),
|
||||
...Object.values(CONFIG.DH.DOMAIN.domains)
|
||||
];
|
||||
if (existingDomains.find(x => slugify(game.i18n.localize(x.label)) === newSlug)) {
|
||||
if (existingDomains.find(x => x.id === newSlug)) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.domains.duplicateDomain'));
|
||||
return;
|
||||
}
|
||||
|
|
@ -529,7 +528,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
const identifier = button.form.elements.identifier.value;
|
||||
if (!identifier) return;
|
||||
|
||||
const sluggedIdentifier = slugify(identifier);
|
||||
const sluggedIdentifier = identifier.slugify();
|
||||
|
||||
await this.settings.updateSource({
|
||||
[`resources.${actorType}.resources.${sluggedIdentifier}`]: Resource.getDefaultResourceData(identifier)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ export { default as ActionConfig } from './action-config.mjs';
|
|||
export { default as ActionSettingsConfig } from './action-settings-config.mjs';
|
||||
export { default as CharacterSettings } from './character-settings.mjs';
|
||||
export { default as AdversarySettings } from './adversary-settings.mjs';
|
||||
export { default as NPCSettings } from './npc-settings.mjs';
|
||||
export { default as CompanionSettings } from './companion-settings.mjs';
|
||||
export { default as SettingFeatureConfig } from './setting-feature-config.mjs';
|
||||
export { default as EnvironmentSettings } from './environment-settings.mjs';
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
};
|
||||
}
|
||||
|
||||
if (this.action.parent.metadata?.isQuantifiable) {
|
||||
if (this.action.parent.metadata.isInventoryItem) {
|
||||
options.quantity = {
|
||||
label: 'DAGGERHEART.GENERAL.itemQuantity',
|
||||
group: 'Global'
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
* @returns {ChangeChoice { value: string, label: string, hint: string, group: string }[]}
|
||||
*/
|
||||
static getChangeChoices() {
|
||||
const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty'];
|
||||
const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty', 'DhNPC'];
|
||||
|
||||
const getAllLeaves = (root, group, parentPath = '') => {
|
||||
const leaves = [];
|
||||
|
|
@ -175,6 +175,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
const partContext = await super._preparePartContext(partId, context);
|
||||
switch (partId) {
|
||||
case 'details':
|
||||
partContext.isItemEffect = partContext.isItemEffect || this.options.isSetting;
|
||||
const useGeneric = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.appearance
|
||||
|
|
|
|||
85
module/applications/sheets-configs/npc-settings.mjs
Normal file
85
module/applications/sheets-configs/npc-settings.mjs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import DHBaseActorSettings from '../sheets/api/actor-setting.mjs';
|
||||
|
||||
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
||||
|
||||
export default class DHNPCSettings extends DHBaseActorSettings {
|
||||
/**@inheritdoc */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['npc-settings'],
|
||||
position: { width: 455, height: 'auto' },
|
||||
actions: {},
|
||||
dragDrop: [
|
||||
{ dragSelector: null, dropSelector: '.tab.features' },
|
||||
{ dragSelector: '.feature-item', dropSelector: null }
|
||||
]
|
||||
};
|
||||
|
||||
/**@override */
|
||||
static PARTS = {
|
||||
header: {
|
||||
id: 'header',
|
||||
template: 'systems/daggerheart/templates/sheets-settings/npc-settings/header.hbs'
|
||||
},
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
details: {
|
||||
id: 'details',
|
||||
template: 'systems/daggerheart/templates/sheets-settings/npc-settings/details.hbs'
|
||||
},
|
||||
features: {
|
||||
id: 'features',
|
||||
template: 'systems/daggerheart/templates/sheets-settings/npc-settings/features.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
/** @override */
|
||||
static TABS = {
|
||||
primary: {
|
||||
tabs: [{ id: 'details' }, { id: 'features' }],
|
||||
initial: 'details',
|
||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(options) {
|
||||
const context = await super._prepareContext(options);
|
||||
|
||||
const featureForms = ['passive', 'action', 'reaction'];
|
||||
context.features = context.document.system.features.sort((a, b) =>
|
||||
a.system.featureForm !== b.system.featureForm
|
||||
? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm)
|
||||
: a.sort - b.sort
|
||||
);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
async _onDragStart(event) {
|
||||
const featureItem = event.currentTarget.closest('.feature-item');
|
||||
|
||||
if (featureItem) {
|
||||
const feature = this.actor.items.get(featureItem.id);
|
||||
const featureData = { type: 'Item', uuid: feature.uuid, fromInternal: true };
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(featureData));
|
||||
event.dataTransfer.setDragImage(featureItem.querySelector('img'), 60, 0);
|
||||
}
|
||||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
event.stopPropagation();
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
|
||||
const item = await fromUuid(data.uuid);
|
||||
if (item?.type === 'feature') {
|
||||
if (data.fromInternal && item.parent?.uuid === this.actor.uuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemData = item.toObject();
|
||||
delete itemData._id;
|
||||
|
||||
await this.actor.createEmbeddedDocuments('Item', [itemData]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,4 +2,5 @@ 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 NPC } from './npc.mjs';
|
||||
export { default as Party } from './party.mjs';
|
||||
|
|
|
|||
|
|
@ -31,6 +31,16 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
|||
dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]',
|
||||
dropSelector: null
|
||||
}
|
||||
],
|
||||
contextMenus: [
|
||||
{
|
||||
handler: DHBaseActorSheet.getBaseAttackContextOptions,
|
||||
selector: '[data-item-uuid][data-type="attack"]',
|
||||
options: {
|
||||
parentClassHooks: false,
|
||||
fixed: true
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,22 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
}
|
||||
],
|
||||
contextMenus: [
|
||||
{
|
||||
handler: CharacterSheet.#getCreationMainContextOptions,
|
||||
selector: '.character-details [data-action="editDoc"]',
|
||||
options: {
|
||||
parentClassHooks: false,
|
||||
fixed: true
|
||||
}
|
||||
},
|
||||
{
|
||||
handler: DHBaseActorSheet.getBaseAttackContextOptions,
|
||||
selector: '[data-item-uuid][data-type="attack"]',
|
||||
options: {
|
||||
parentClassHooks: false,
|
||||
fixed: true
|
||||
}
|
||||
},
|
||||
{
|
||||
handler: CharacterSheet.#getDomainCardContextOptions,
|
||||
selector: '[data-item-uuid][data-type="domainCard"]',
|
||||
|
|
@ -176,6 +192,9 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
for (const input of form.querySelectorAll('input:not([type=search]), .editor.prosemirror')) {
|
||||
input.disabled = disabled;
|
||||
}
|
||||
for (const element of form.querySelectorAll('.input[contenteditable]')) {
|
||||
element.classList.toggle('disabled', disabled);
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
|
|
@ -319,6 +338,56 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
/* Context Menu */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
static #getCreationMainContextOptions() {
|
||||
/** Returns true if the item is managed by the level up wizard. Such items shouldn't allow things like manual removal */
|
||||
function isItemWizardManaged(item) {
|
||||
const actor = item?.actor;
|
||||
if (!actor) return false;
|
||||
|
||||
// If levelup automation is off in general or for this character, all items are unmanaged
|
||||
// This is disabled until we have proper granted feature removal, for now this feature is to correct errors
|
||||
// const levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto;
|
||||
// if (!levelupAuto) return false;
|
||||
|
||||
// Core items aren't part of levelup data. TODO: add some way to flag a specific character as no auto leveling
|
||||
const classPair = actor.system.class;
|
||||
const coreItems = [actor.system.ancestry, actor.system.community, classPair?.value, classPair?.subclass];
|
||||
if (coreItems.includes(item)) return true;
|
||||
|
||||
const levelups = Object.values(actor.system.levelData?.levelups) ?? [];
|
||||
const uuid = item.uuid;
|
||||
const sourceUuid = item._stats.compendiumSource; // on older characters this may be missing
|
||||
return levelups.some(data => {
|
||||
if (item.type === 'subclass') {
|
||||
const selectedSubclasses = data.selections.map(s => s.secondaryData?.subclass).filter(s => !!s);
|
||||
return sourceUuid
|
||||
? selectedSubclasses.includes(sourceUuid)
|
||||
: selectedSubclasses.length && item.system.isMulticlass;
|
||||
}
|
||||
|
||||
const matchesCard = data.achievements.domainCards.some(i => i.itemUuid === uuid);
|
||||
const matchesSelection = data.selections.some(s => s.itemUuid === uuid);
|
||||
return matchesCard || matchesSelection;
|
||||
});
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
label: 'CONTROLS.CommonDelete',
|
||||
icon: 'fa-solid fa-trash',
|
||||
visible: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc?.isOwner && !isItemWizardManaged(doc);
|
||||
},
|
||||
onClick: async (event, target) => {
|
||||
const doc = await getDocFromElement(target);
|
||||
if (event.shiftKey) return doc.delete();
|
||||
else return doc.deleteDialog();
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of ContextMenu options for DomainCards.
|
||||
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
|
||||
|
|
@ -335,7 +404,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
const doc = getDocFromElementSync(target);
|
||||
return doc?.isOwner && doc.system.inVault;
|
||||
},
|
||||
callback: async target => {
|
||||
onClick: async (_, target) => {
|
||||
const doc = await getDocFromElement(target);
|
||||
const actorLoadout = doc.actor.system.loadoutSlot;
|
||||
if (actorLoadout.available) return doc.update({ 'system.inVault': false });
|
||||
|
|
@ -349,7 +418,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
const doc = getDocFromElementSync(target);
|
||||
return doc?.isOwner && doc.system.inVault;
|
||||
},
|
||||
callback: async (target, event) => {
|
||||
onClick: async (event, target) => {
|
||||
const doc = await getDocFromElement(target);
|
||||
const actorLoadout = doc.actor.system.loadoutSlot;
|
||||
if (!actorLoadout.available) {
|
||||
|
|
@ -388,7 +457,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
const doc = getDocFromElementSync(target);
|
||||
return doc?.isOwner && !doc.system.inVault;
|
||||
},
|
||||
callback: async target => (await getDocFromElement(target)).update({ 'system.inVault': true })
|
||||
onClick: async (_, target) => (await getDocFromElement(target)).update({ 'system.inVault': true })
|
||||
}
|
||||
].map(option => ({
|
||||
...option,
|
||||
|
|
@ -414,7 +483,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
const doc = getDocFromElementSync(target);
|
||||
return doc.isOwner && doc && !doc.system.equipped;
|
||||
},
|
||||
callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target)
|
||||
onClick: (event, target) => CharacterSheet.#toggleEquipItem.call(this, event, target)
|
||||
},
|
||||
{
|
||||
label: 'unequip',
|
||||
|
|
@ -423,7 +492,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
const doc = getDocFromElementSync(target);
|
||||
return doc.isOwner && doc && doc.system.equipped;
|
||||
},
|
||||
callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target)
|
||||
onClick: (event, target) => CharacterSheet.#toggleEquipItem.call(this, event, target)
|
||||
}
|
||||
].map(option => ({
|
||||
...option,
|
||||
|
|
@ -718,7 +787,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
? {
|
||||
'system.linkedClass.uuid': {
|
||||
key: 'system.linkedClass.uuid',
|
||||
value: this.document.system.class.value._stats.compendiumSource
|
||||
value: this.document.system.class.value?._stats.compendiumSource
|
||||
}
|
||||
}
|
||||
: undefined,
|
||||
|
|
@ -984,7 +1053,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
game.tooltip.activate(target, {
|
||||
html,
|
||||
locked: true,
|
||||
cssClass: 'bordered-tooltip',
|
||||
cssClass: 'bordered-tooltip dh-style',
|
||||
direction: 'DOWN'
|
||||
});
|
||||
|
||||
|
|
@ -1080,7 +1149,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
game.tooltip.activate(target, {
|
||||
html,
|
||||
locked: true,
|
||||
cssClass: 'bordered-tooltip',
|
||||
cssClass: 'bordered-tooltip dh-style',
|
||||
direction: 'DOWN',
|
||||
noOffset: true
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,17 @@ export default class DhCompanionSheet extends DHBaseActorSheet {
|
|||
toggleStress: DhCompanionSheet.#toggleStress,
|
||||
actionRoll: DhCompanionSheet.#actionRoll,
|
||||
levelManagement: DhCompanionSheet.#levelManagement
|
||||
}
|
||||
},
|
||||
contextMenus: [
|
||||
{
|
||||
handler: DHBaseActorSheet.getBaseAttackContextOptions,
|
||||
selector: '[data-item-uuid][data-type="attack"]',
|
||||
options: {
|
||||
parentClassHooks: false,
|
||||
fixed: true
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
|
|
|
|||
136
module/applications/sheets/actors/npc.mjs
Normal file
136
module/applications/sheets/actors/npc.mjs
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
import DHBaseActorSheet from '../api/base-actor.mjs';
|
||||
|
||||
export default class NPCSheet extends DHBaseActorSheet {
|
||||
/** @inheritDoc */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['npc'],
|
||||
position: { width: 660, height: 600 },
|
||||
window: { resizable: true },
|
||||
actions: {},
|
||||
window: {
|
||||
resizable: true,
|
||||
controls: [
|
||||
{
|
||||
icon: 'fa-solid fa-signature',
|
||||
label: 'DAGGERHEART.UI.Tooltip.configureAttribution',
|
||||
action: 'editAttribution'
|
||||
}
|
||||
]
|
||||
},
|
||||
dragDrop: [
|
||||
{
|
||||
dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]',
|
||||
dropSelector: null
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
header: { template: 'systems/daggerheart/templates/sheets/actors/npc/header.hbs' },
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/actors/npc/navigation.hbs' },
|
||||
features: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/npc/features.hbs',
|
||||
scrollable: ['.feature-section']
|
||||
},
|
||||
notes: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/npc/notes.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
/** @inheritdoc */
|
||||
static TABS = {
|
||||
primary: {
|
||||
tabs: [{ id: 'notes' }, { id: 'features' }],
|
||||
initial: 'notes',
|
||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||
}
|
||||
};
|
||||
|
||||
/** @inheritdoc */
|
||||
_prepareTabs(group) {
|
||||
const result = super._prepareTabs(group);
|
||||
if (group === 'primary') {
|
||||
result.features.empty = this.document.system.features.length === 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
async _preparePartContext(partId, context, options) {
|
||||
context = await super._preparePartContext(partId, context, options);
|
||||
switch (partId) {
|
||||
case 'header':
|
||||
await this._prepareHeaderContext(context, options);
|
||||
break;
|
||||
case 'features':
|
||||
await this._prepareFeaturesContext(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 Features part.
|
||||
* @param {ApplicationRenderContext} context
|
||||
* @param {ApplicationRenderOptions} options
|
||||
* @returns {Promise<void>}
|
||||
* @protected
|
||||
*/
|
||||
async _prepareFeaturesContext(context, _options) {
|
||||
const featureForms = ['passive', 'action', 'reaction'];
|
||||
context.features = this.document.system.features.sort((a, b) =>
|
||||
a.system.featureForm !== b.system.featureForm
|
||||
? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm)
|
||||
: a.sort - b.sort
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,6 @@ export default class Party extends DHBaseActorSheet {
|
|||
actions: {
|
||||
openDocument: Party.#openDocument,
|
||||
deletePartyMember: Party.#deletePartyMember,
|
||||
deleteItem: Party.#deleteItem,
|
||||
toggleHope: Party.#toggleHope,
|
||||
toggleHitPoints: Party.#toggleHitPoints,
|
||||
toggleStress: Party.#toggleStress,
|
||||
|
|
@ -44,12 +43,10 @@ export default class Party extends DHBaseActorSheet {
|
|||
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' },
|
||||
/* NOT YET IMPLEMENTED */
|
||||
// projects: {
|
||||
// template: 'systems/daggerheart/templates/sheets/actors/party/projects.hbs',
|
||||
// scrollable: ['']
|
||||
// },
|
||||
partyMembers: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/party/party-members.hbs',
|
||||
scrollable: ['']
|
||||
},
|
||||
inventory: {
|
||||
template: 'systems/daggerheart/templates/sheets/actors/party/inventory.hbs',
|
||||
scrollable: ['.tab.inventory .items-section']
|
||||
|
|
@ -60,19 +57,13 @@ export default class Party extends DHBaseActorSheet {
|
|||
/** @inheritdoc */
|
||||
static TABS = {
|
||||
primary: {
|
||||
tabs: [
|
||||
{ id: 'partyMembers' },
|
||||
/* NOT YET IMPLEMENTED */
|
||||
// { id: 'projects' },
|
||||
{ id: 'inventory' },
|
||||
{ id: 'notes' }
|
||||
],
|
||||
tabs: [{ id: 'partyMembers' }, { id: 'inventory' }, { id: 'notes' }],
|
||||
initial: 'partyMembers',
|
||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||
}
|
||||
};
|
||||
|
||||
static ALLOWED_ACTOR_TYPES = ['character', 'companion', 'adversary'];
|
||||
static ALLOWED_ACTOR_TYPES = ['character', 'companion', 'adversary', 'npc'];
|
||||
static DICE_ROLL_ACTOR_TYPES = ['character'];
|
||||
|
||||
async _onRender(context, options) {
|
||||
|
|
@ -506,23 +497,4 @@ export default class Party extends DHBaseActorSheet {
|
|||
const newMembersList = currentMembers.filter(uuid => uuid !== doc.uuid);
|
||||
await this.document.update({ 'system.partyMembers': newMembersList });
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ export default function DHApplicationMixin(Base) {
|
|||
classes: ['daggerheart', 'sheet', 'dh-style'],
|
||||
actions: {
|
||||
triggerContextMenu: DHSheetV2.#triggerContextMenu,
|
||||
createDoc: DHSheetV2.#createDoc,
|
||||
createDoc: DHSheetV2.#onCreateDoc,
|
||||
editDoc: DHSheetV2.#editDoc,
|
||||
deleteDoc: DHSheetV2.#deleteDoc,
|
||||
toChat: DHSheetV2.#toChat,
|
||||
|
|
@ -97,8 +97,8 @@ export default function DHApplicationMixin(Base) {
|
|||
viewItem: DHSheetV2.#viewItem,
|
||||
toggleEffect: DHSheetV2.#toggleEffect,
|
||||
toggleExtended: DHSheetV2.#toggleExtended,
|
||||
addNewItem: DHSheetV2.#addNewItem,
|
||||
browseItem: DHSheetV2.#browseItem,
|
||||
addNewItem: DHSheetV2.#onAddNewItem,
|
||||
browseItem: DHSheetV2.#onBrowseItem,
|
||||
editAttribution: DHSheetV2.#editAttribution
|
||||
},
|
||||
contextMenus: [
|
||||
|
|
@ -424,7 +424,7 @@ export default function DHApplicationMixin(Base) {
|
|||
const target = element.closest('[data-item-uuid]');
|
||||
return !target.dataset.disabled && target.dataset.itemType !== 'beastform';
|
||||
},
|
||||
callback: async target => (await getDocFromElement(target)).update({ disabled: true })
|
||||
onClick: async (_, target) => (await getDocFromElement(target)).update({ disabled: true })
|
||||
},
|
||||
{
|
||||
label: 'enableEffect',
|
||||
|
|
@ -433,7 +433,7 @@ export default function DHApplicationMixin(Base) {
|
|||
const target = element.closest('[data-item-uuid]');
|
||||
return target.dataset.disabled && target.dataset.itemType !== 'beastform';
|
||||
},
|
||||
callback: async target => (await getDocFromElement(target)).update({ disabled: false })
|
||||
onClick: async (_, target) => (await getDocFromElement(target)).update({ disabled: false })
|
||||
}
|
||||
].map(option => ({
|
||||
...option,
|
||||
|
|
@ -478,7 +478,9 @@ export default function DHApplicationMixin(Base) {
|
|||
(doc?.isOwner && (!doc?.hasOwnProperty('systemPath') || doc?.inCollection))
|
||||
);
|
||||
},
|
||||
callback: async target => (await getDocFromElement(target)).sheet.render({ force: true })
|
||||
onClick: async (_, target) => {
|
||||
return (await getDocFromElement(target)).sheet.render({ force: true });
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -493,7 +495,7 @@ export default function DHApplicationMixin(Base) {
|
|||
!foundry.utils.isEmpty(doc?.damage?.parts);
|
||||
return doc?.isOwner && hasDamage;
|
||||
},
|
||||
callback: async (target, event) => {
|
||||
onClick: async (event, target) => {
|
||||
const doc = await getDocFromElement(target),
|
||||
action = doc?.system?.attack ?? doc;
|
||||
const config = action.prepareConfig(event);
|
||||
|
|
@ -513,7 +515,7 @@ export default function DHApplicationMixin(Base) {
|
|||
const doc = getDocFromElementSync(target);
|
||||
return doc?.isOwner && !(doc.type === 'domainCard' && doc.system.inVault);
|
||||
},
|
||||
callback: async (target, event) => (await getDocFromElement(target)).use(event)
|
||||
onClick: async (event, target) => (await getDocFromElement(target)).use(event)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -521,7 +523,7 @@ export default function DHApplicationMixin(Base) {
|
|||
options.push({
|
||||
label: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
|
||||
icon: 'fa-solid fa-message',
|
||||
callback: async target => (await getDocFromElement(target)).toChat(this.document.uuid)
|
||||
onClick: async (_, target) => (await getDocFromElement(target)).toChat(this.document.uuid)
|
||||
});
|
||||
|
||||
if (deletable)
|
||||
|
|
@ -533,7 +535,7 @@ export default function DHApplicationMixin(Base) {
|
|||
const doc = getDocFromElementSync(target);
|
||||
return doc?.isOwner !== false && target.dataset.itemType !== 'beastform';
|
||||
},
|
||||
callback: async (target, event) => {
|
||||
onClick: async (event, target) => {
|
||||
const doc = await getDocFromElement(target);
|
||||
if (event.shiftKey) return doc.delete();
|
||||
else return doc.deleteDialog();
|
||||
|
|
@ -639,7 +641,7 @@ export default function DHApplicationMixin(Base) {
|
|||
/* Application Clicks Actions */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
static async #addNewItem(event, target) {
|
||||
static async #onAddNewItem(event, target) {
|
||||
const createChoice = await foundry.applications.api.DialogV2.wait({
|
||||
classes: ['dh-style', 'two-big-buttons'],
|
||||
buttons: [
|
||||
|
|
@ -658,11 +660,11 @@ export default function DHApplicationMixin(Base) {
|
|||
|
||||
if (!createChoice) return;
|
||||
|
||||
if (createChoice === 'browse') return DHSheetV2.#browseItem.call(this, event, target);
|
||||
else return DHSheetV2.#createDoc.call(this, event, target);
|
||||
if (createChoice === 'browse') return DHSheetV2.#onBrowseItem.call(this, event, target);
|
||||
else return DHSheetV2.#onCreateDoc.call(this, event, target);
|
||||
}
|
||||
|
||||
static async #browseItem(event, target) {
|
||||
static async #onBrowseItem(_event, target) {
|
||||
const type = target.dataset.compendium ?? target.dataset.type;
|
||||
|
||||
const presets = {
|
||||
|
|
@ -713,7 +715,7 @@ export default function DHApplicationMixin(Base) {
|
|||
* Create an embedded document.
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #createDoc(event, target) {
|
||||
static async #onCreateDoc(event, target) {
|
||||
const { documentClass, type, inVault, disabled } = target.dataset;
|
||||
const parentIsItem = this.document.documentName === 'Item';
|
||||
const featureOnCharacter = this.document.parent?.type === 'character' && type === 'feature';
|
||||
|
|
@ -725,7 +727,7 @@ export default function DHApplicationMixin(Base) {
|
|||
: null
|
||||
: this.document;
|
||||
|
||||
let systemData = null;
|
||||
let systemData = {};
|
||||
if (featureOnCharacter) {
|
||||
systemData = {
|
||||
originItemType: this.document.type,
|
||||
|
|
@ -738,15 +740,18 @@ export default function DHApplicationMixin(Base) {
|
|||
|
||||
const data = {
|
||||
name: cls.defaultName({ type, parent }),
|
||||
type
|
||||
type,
|
||||
system: systemData
|
||||
};
|
||||
|
||||
if (systemData) data.system = systemData;
|
||||
|
||||
if (inVault) data['system.inVault'] = true;
|
||||
if (disabled) data.disabled = true;
|
||||
if (type === 'domainCard' && parent?.system.domains?.length) {
|
||||
data.system.domain = parent.system.domains[0];
|
||||
|
||||
if (type === 'domainCard') {
|
||||
if (parent?.system.domains?.length) data.system.domain = parent.system.domains[0];
|
||||
if (inVault) data.system.inVault = true;
|
||||
} else if (type === 'weapon') {
|
||||
// Passing an empty system object to weapon causes validation failure due to attack action initialization
|
||||
// todo: determine why, fix it at its source, then remove this fallback
|
||||
delete data.system;
|
||||
}
|
||||
|
||||
const doc = await cls.create(data, { parent, renderSheet: !event.shiftKey });
|
||||
|
|
|
|||
|
|
@ -166,6 +166,15 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
}
|
||||
}
|
||||
|
||||
/** Add support for input content editables */
|
||||
_toggleDisabled(disabled) {
|
||||
super._toggleDisabled(disabled);
|
||||
const form = this.form;
|
||||
for (const element of form.querySelectorAll('.input[contenteditable]')) {
|
||||
element.classList.toggle('disabled', disabled);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Context Menu */
|
||||
/* -------------------------------------------- */
|
||||
|
|
@ -180,6 +189,43 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of ContextMenu options for the base attack.
|
||||
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
|
||||
* @this {CharacterSheet}
|
||||
* @protected
|
||||
*/
|
||||
static getBaseAttackContextOptions() {
|
||||
/**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */
|
||||
return [
|
||||
{
|
||||
label: 'DAGGERHEART.CONFIG.RollTypes.attack.name',
|
||||
icon: 'fa-solid fa-burst',
|
||||
onClick: async (event, target) => (await getDocFromElement(target)).use(event)
|
||||
},
|
||||
{
|
||||
label: 'DAGGERHEART.GENERAL.damage',
|
||||
icon: 'fa-solid fa-explosion',
|
||||
onClick: async (event, target) => {
|
||||
const doc = await getDocFromElement(target),
|
||||
action = doc?.system?.attack ?? doc;
|
||||
const config = action.prepareConfig(event);
|
||||
config.effects = await game.system.api.data.actions.actionsTypes.base.getEffects(
|
||||
this.document,
|
||||
doc
|
||||
);
|
||||
config.hasRoll = false;
|
||||
return action && action.workflow.get('damage').execute(config, null, true);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
|
||||
icon: 'fa-solid fa-message',
|
||||
onClick: async (_, target) => (await getDocFromElement(target)).toChat(this.document.uuid)
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Application Listener Actions */
|
||||
/* -------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
options.push({
|
||||
name: 'CONTROLS.CommonDelete',
|
||||
icon: '<i class="fa-solid fa-trash"></i>',
|
||||
callback: async target => {
|
||||
onClick: async (_, target) => {
|
||||
const feature = await getDocFromElement(target);
|
||||
if (!feature) return;
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
|
|
|
|||
|
|
@ -103,6 +103,19 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
_getEntryContextOptions() {
|
||||
return [
|
||||
...super._getEntryContextOptions(),
|
||||
{
|
||||
label: 'DAGGERHEART.UI.ChatLog.rerollActionRoll',
|
||||
icon: '<i class="fa-solid fa-dice"></i>',
|
||||
visible: li => {
|
||||
const message = game.messages.get(li.dataset.messageId);
|
||||
return message.system.hasRoll && (game.user.isGM || message.isAuthor);
|
||||
},
|
||||
callback: async li => {
|
||||
const message = game.messages.get(li.dataset.messageId);
|
||||
const reroll = await message.rolls[0].reroll({ liveRoll: true });
|
||||
message.update({ rolls: [reroll] });
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'DAGGERHEART.UI.ChatLog.rerollDamage',
|
||||
icon: '<i class="fa-solid fa-dice"></i>',
|
||||
|
|
@ -113,9 +126,10 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
: false;
|
||||
return (game.user.isGM || message.isAuthor) && hasRolledDamage;
|
||||
},
|
||||
callback: li => {
|
||||
callback: async li => {
|
||||
const message = game.messages.get(li.dataset.messageId);
|
||||
new game.system.api.applications.dialogs.RerollDamageDialog(message).render({ force: true });
|
||||
const update = await message.system.getRerolledDamage();
|
||||
message.update(update);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
|
|||
|
|
@ -84,19 +84,49 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the dialog used to edit the name of the currently viewed Combat encounter.
|
||||
* @this {CombatTracker}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static async #onEditName() {
|
||||
const combat = this.viewed;
|
||||
if (!combat || !game.user.isGM) return null;
|
||||
const field = combat.schema.fields.name;
|
||||
const inputHTML = field.toFormGroup({}, { name: 'name', value: combat.name, autofocus: true }).outerHTML;
|
||||
const formData = await foundry.applications.api.DialogV2.input({
|
||||
window: { icon: 'fa-solid fa-tag', title: 'COMBAT.ACTIONS.EditNameTitle' },
|
||||
position: { width: 480 },
|
||||
content: inputHTML
|
||||
});
|
||||
await combat.update({ name: formData.name || '' });
|
||||
}
|
||||
|
||||
_getCombatContextOptions() {
|
||||
return [
|
||||
{
|
||||
label: 'COMBAT.ClearMovementHistories',
|
||||
icon: '<i class="fa-solid fa-shoe-prints"></i>',
|
||||
visible: () => game.user.isGM && this.viewed?.combatants.size > 0,
|
||||
callback: () => this.viewed.clearMovementHistories()
|
||||
label: 'COMBAT.ACTIONS.EditName',
|
||||
icon: 'fa-solid fa-tag',
|
||||
visible: () => game.user.isGM && !!this.viewed,
|
||||
onClick: () => DhCombatTracker.#onEditName.call(this)
|
||||
},
|
||||
{
|
||||
label: 'COMBAT.Delete',
|
||||
icon: '<i class="fa-solid fa-trash"></i>',
|
||||
label: 'COMBAT.ACTIONS.LinkToScene',
|
||||
icon: '<i class="fa-solid fa-link"></i>',
|
||||
visible: () => game.user.isGM && !this.scene,
|
||||
onClick: () => this.viewed.toggleSceneLink()
|
||||
},
|
||||
{
|
||||
label: 'COMBAT.ACTIONS.UnlinkFromScene',
|
||||
icon: '<i class="fa-solid fa-unlink"></i>',
|
||||
visible: () => game.user.isGM && !!this.scene,
|
||||
onClick: () => this.viewed.toggleSceneLink()
|
||||
},
|
||||
{
|
||||
label: 'COMBAT.End',
|
||||
icon: 'fa-solid fa-xmark',
|
||||
visible: () => game.user.isGM && !!this.viewed,
|
||||
callback: () => this.viewed.endCombat()
|
||||
onClick: () => this.viewed.endCombat()
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
@ -133,7 +163,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
|||
canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'),
|
||||
type: combatant.actor?.system?.type,
|
||||
img: await this._getCombatantThumbnail(combatant),
|
||||
disposition: combatant.token.disposition
|
||||
disposition: combatant.token?.disposition
|
||||
};
|
||||
|
||||
turn.css = [turn.active ? 'active' : null, hidden ? 'hide' : null, isDefeated ? 'defeated' : null].filterJoin(
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { getDocFromElement } from '../../helpers/utils.mjs';
|
||||
import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
|
@ -47,7 +48,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
expandContent: this.expandContent,
|
||||
resetFilters: this.resetFilters,
|
||||
sortList: this.sortList,
|
||||
openSettings: this.openSettings
|
||||
openSettings: this.openSettings,
|
||||
viewSheet: this.#onViewSheet
|
||||
},
|
||||
position: {
|
||||
left: 100,
|
||||
|
|
@ -109,8 +111,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
CONFIG.DH.id,
|
||||
CONFIG.DH.FLAGS[`${this.compendiumBrowserTypeKey}`].position
|
||||
);
|
||||
|
||||
options.position = userPresetPosition ?? ItemBrowser.DEFAULT_OPTIONS.position;
|
||||
delete options.position.zIndex;
|
||||
|
||||
if (!userPresetPosition) {
|
||||
const width = noFolder === true || lite === true ? 600 : 850;
|
||||
|
|
@ -306,7 +308,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
{
|
||||
items: this.items,
|
||||
menu: this.selectedMenu,
|
||||
formatLabel: this.formatLabel
|
||||
formatLabel: this.formatLabel,
|
||||
viewSheet: this.items[0] instanceof Actor
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -568,6 +571,11 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
}
|
||||
}
|
||||
|
||||
static async #onViewSheet(_, target) {
|
||||
const document = await getDocFromElement(target);
|
||||
document?.sheet?.render(true);
|
||||
}
|
||||
|
||||
_createDragProcess() {
|
||||
new foundry.applications.ux.DragDrop.implementation({
|
||||
dragSelector: '.item-container',
|
||||
|
|
@ -606,7 +614,16 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
items: {
|
||||
folder: 'equipments',
|
||||
render: {
|
||||
noFolder: true
|
||||
folders: [
|
||||
'equipments',
|
||||
'ancestries',
|
||||
'classes',
|
||||
'subclasses',
|
||||
'domains',
|
||||
'communities',
|
||||
'beastforms'
|
||||
// excluded: features
|
||||
]
|
||||
}
|
||||
},
|
||||
compendium: {}
|
||||
|
|
|
|||
|
|
@ -249,9 +249,6 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
|||
|
||||
/** @inheritDoc */
|
||||
_drawBar(number, bar, data) {
|
||||
const val = Number(data.value);
|
||||
const pct = Math.clamp(val, 0, data.max) / data.max;
|
||||
|
||||
// Determine sizing
|
||||
const { width, height } = this.document.getSize();
|
||||
const s = canvas.dimensions.uiScale;
|
||||
|
|
@ -259,17 +256,19 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
|||
const bh = 8 * (this.document.height >= 2 ? 1.5 : 1) * s;
|
||||
|
||||
// Determine the color to use
|
||||
const fillColor =
|
||||
number === 0 ? foundry.utils.Color.fromRGB([1, 0, 0]) : foundry.utils.Color.fromString('#0032b1');
|
||||
const Color = foundry.utils.Color;
|
||||
const fillColor = number === 0 ? Color.fromRGB([1, 0, 0]) : Color.fromString('#0032b1');
|
||||
const emptyColor = Color.fromRGB([0, 0, 0]);
|
||||
|
||||
// Draw the bar
|
||||
const widthUnit = bw / data.max;
|
||||
// Draw the bar (accounting floating point numbers from bar animations)
|
||||
const widthUnit = bw / Math.ceil(data.max);
|
||||
bar.clear().lineStyle(s, 0x000000, 1.0);
|
||||
const sections = [...Array(data.max).keys()];
|
||||
for (let mark of sections) {
|
||||
const sections = [...Array(Math.ceil(data.max)).keys()];
|
||||
for (const mark of sections) {
|
||||
const x = mark * widthUnit;
|
||||
const marked = mark + 1 <= data.value;
|
||||
const color = marked ? fillColor : foundry.utils.Color.fromRGB([0, 0, 0]);
|
||||
const marked = mark < Math.ceil(data.value);
|
||||
const remainder = mark === Math.ceil(data.value) - 1 ? data.value % 1 : 0;
|
||||
const color = !marked ? emptyColor : remainder ? emptyColor.mix(fillColor, remainder) : fillColor;
|
||||
if (mark === 0 || mark === sections.length - 1) {
|
||||
bar.beginFill(color, marked ? 1.0 : 0.5).drawRect(x, 0, widthUnit, bh, 2 * s); // Would like drawRoundedRect, but it's very troublsome with the corners. Leaving for now.
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,49 @@ export const typeConfig = {
|
|||
}
|
||||
]
|
||||
},
|
||||
environments: {
|
||||
columns: [
|
||||
{
|
||||
key: 'system.tier',
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.singular'
|
||||
},
|
||||
{
|
||||
key: 'system.type',
|
||||
label: 'DAGGERHEART.GENERAL.type',
|
||||
format: type => {
|
||||
if (!type) return '-';
|
||||
|
||||
return CONFIG.DH.ACTOR.environmentTypes[type].label;
|
||||
}
|
||||
}
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
key: 'system.tier',
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.singular',
|
||||
field: 'system.api.models.actors.DhEnvironment.schema.fields.tier'
|
||||
},
|
||||
{
|
||||
key: 'system.type',
|
||||
label: 'DAGGERHEART.GENERAL.type',
|
||||
field: 'system.api.models.actors.DhEnvironment.schema.fields.type'
|
||||
},
|
||||
{
|
||||
key: 'system.difficulty',
|
||||
name: 'difficulty.min',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.difficultyMin',
|
||||
field: 'system.api.models.actors.DhEnvironment.schema.fields.difficulty',
|
||||
operator: 'gte'
|
||||
},
|
||||
{
|
||||
key: 'system.difficulty',
|
||||
name: 'difficulty.max',
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.difficultyMax',
|
||||
field: 'system.api.models.actors.DhEnvironment.schema.fields.difficulty',
|
||||
operator: 'lte'
|
||||
}
|
||||
]
|
||||
},
|
||||
items: {
|
||||
columns: [
|
||||
{
|
||||
|
|
@ -400,10 +443,12 @@ export const typeConfig = {
|
|||
const list = [];
|
||||
for (const item of items.filter(item => item.system.linkedClass)) {
|
||||
const linkedClass = await foundry.utils.fromUuid(item.system.linkedClass);
|
||||
list.push({
|
||||
value: linkedClass.uuid,
|
||||
label: linkedClass.name
|
||||
});
|
||||
if (linkedClass) {
|
||||
list.push({
|
||||
value: linkedClass.uuid,
|
||||
label: linkedClass.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return list.reduce((a, c) => {
|
||||
|
|
@ -559,7 +604,8 @@ export const compendiumConfig = {
|
|||
id: 'environments',
|
||||
keys: ['environments'],
|
||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.environments',
|
||||
type: ['environment']
|
||||
type: ['environment'],
|
||||
listType: 'environments'
|
||||
},
|
||||
beastforms: {
|
||||
id: 'beastforms',
|
||||
|
|
|
|||
|
|
@ -75,7 +75,12 @@ export default class DHAttackAction extends DHDamageAction {
|
|||
const useAltDamage = this.actor?.effects?.find(x => x.type === 'horde')?.active;
|
||||
for (const { value, valueAlt, type } of damage.parts) {
|
||||
const usedValue = useAltDamage ? valueAlt : value;
|
||||
const str = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {});
|
||||
const damageString = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {});
|
||||
const str = damageString
|
||||
? damageString
|
||||
: game.i18n.format('DAGGERHEART.GENERAL.missingX', {
|
||||
x: game.i18n.localize('DAGGERHEART.GENERAL.damage')
|
||||
});
|
||||
|
||||
const icons = Array.from(type)
|
||||
.map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon)
|
||||
|
|
|
|||
|
|
@ -148,10 +148,14 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
: null;
|
||||
}
|
||||
|
||||
/** Returns true if the action is usable */
|
||||
/**
|
||||
* Returns true if the action is usable.
|
||||
* An action is usable on any actor type. For example, an adversary might have a base attack action.
|
||||
*/
|
||||
get usable() {
|
||||
const actor = this.actor;
|
||||
return this.isOwner && actor?.type === 'character';
|
||||
const pack = actor?.pack ? game.packs.get(actor.pack) : null;
|
||||
return !pack?.locked && this.isOwner;
|
||||
}
|
||||
|
||||
static getRollType(parent) {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default class DhCountdownAction extends DHBaseAction {
|
|||
|
||||
/** @inheritDoc */
|
||||
static migrateData(source) {
|
||||
for (const countdown of source.countdown) {
|
||||
for (const countdown of Object.values(source.countdown)) {
|
||||
if (countdown.progress.max) {
|
||||
countdown.progress.startFormula = countdown.progress.max;
|
||||
countdown.progress.start = 1;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export default class BeastformEffect extends BaseEffect {
|
|||
static migrateData(source) {
|
||||
if (!source.characterTokenData.tokenSize.height) source.characterTokenData.tokenSize.height = 1;
|
||||
if (!source.characterTokenData.tokenSize.width) source.characterTokenData.tokenSize.width = 1;
|
||||
if (!source.characterTokenData.tokenSize.depth) source.characterTokenData.tokenSize.depth = 1;
|
||||
|
||||
return super.migrateData(source);
|
||||
}
|
||||
|
|
@ -52,7 +53,8 @@ export default class BeastformEffect extends BaseEffect {
|
|||
if (this.parent.parent.type === 'character') {
|
||||
const baseUpdate = {
|
||||
height: this.characterTokenData.tokenSize.height,
|
||||
width: this.characterTokenData.tokenSize.width
|
||||
width: this.characterTokenData.tokenSize.width,
|
||||
depth: this.characterTokenData.tokenSize.depth
|
||||
};
|
||||
const update = {
|
||||
...baseUpdate,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
import DhCharacter from './character.mjs';
|
||||
import DhCompanion from './companion.mjs';
|
||||
import DhAdversary from './adversary.mjs';
|
||||
import DhNPC from './npc.mjs';
|
||||
import DhEnvironment from './environment.mjs';
|
||||
import DhParty from './party.mjs';
|
||||
|
||||
export { DhCharacter, DhCompanion, DhAdversary, DhEnvironment, DhParty };
|
||||
export { DhCharacter, DhCompanion, DhAdversary, DhNPC, DhEnvironment, DhParty };
|
||||
|
||||
export const config = {
|
||||
character: DhCharacter,
|
||||
companion: DhCompanion,
|
||||
adversary: DhAdversary,
|
||||
npc: DhNPC,
|
||||
environment: DhEnvironment,
|
||||
party: DhParty
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@ import { ActionField } from '../fields/actionField.mjs';
|
|||
import { commonActorRules } from './base.mjs';
|
||||
import DhCreature from './creature.mjs';
|
||||
import { bonusField } from '../fields/actorField.mjs';
|
||||
import { calculateExpectedValue, parseTermsFromSimpleFormula } from '../../helpers/utils.mjs';
|
||||
import { adversaryExpectedDamage, adversaryScalingData } from '../../config/actorConfig.mjs';
|
||||
import { getTierAdjustedAdversary } from './tierAdjustment.mjs';
|
||||
|
||||
export default class DhpAdversary extends DhCreature {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Adversary'];
|
||||
|
|
@ -206,205 +205,6 @@ export default class DhpAdversary extends DhCreature {
|
|||
/** Returns source data for this actor adjusted to a new tier, which can be used to create a new actor. */
|
||||
adjustForTier(tier) {
|
||||
const source = this.parent.toObject(true);
|
||||
|
||||
/** @type {(2 | 3 | 4)[]} */
|
||||
const tiers = new Array(Math.abs(tier - this.tier))
|
||||
.fill(0)
|
||||
.map((_, idx) => idx + Math.min(tier, this.tier) + 1);
|
||||
if (tier < this.tier) tiers.reverse();
|
||||
const typeData = adversaryScalingData[source.system.type] ?? adversaryScalingData[source.system.standard];
|
||||
const tierEntries = tiers.map(t => ({ tier: t, ...typeData[t] }));
|
||||
|
||||
// Apply simple tier changes
|
||||
const scale = tier > this.tier ? 1 : -1;
|
||||
for (const entry of tierEntries) {
|
||||
source.system.difficulty += scale * entry.difficulty;
|
||||
source.system.damageThresholds.major += scale * entry.majorThreshold;
|
||||
source.system.damageThresholds.severe += scale * entry.severeThreshold;
|
||||
source.system.resources.hitPoints.max += scale * entry.hp;
|
||||
source.system.resources.stress.max += scale * entry.stress;
|
||||
source.system.attack.roll.bonus += scale * entry.attack;
|
||||
}
|
||||
|
||||
// Get the mean and standard deviation of expected damage in the previous and new tier
|
||||
// The data we have is for attack scaling, but we reuse this for action scaling later
|
||||
const expectedDamageData = adversaryExpectedDamage[source.system.type] ?? adversaryExpectedDamage.basic;
|
||||
const damageMeta = {
|
||||
currentDamageRange: { tier: source.system.tier, ...expectedDamageData[source.system.tier] },
|
||||
newDamageRange: { tier, ...expectedDamageData[tier] },
|
||||
type: 'attack'
|
||||
};
|
||||
|
||||
// Update damage of base attack
|
||||
try {
|
||||
this.#adjustActionDamage(source.system.attack, damageMeta);
|
||||
} catch (err) {
|
||||
ui.notifications.warn('Failed to convert attack damage of adversary');
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
// Update damage of each item action, making sure to also update the description if possible
|
||||
const damageRegex = /@Damage\[([^\[\]]*)\]({[^}]*})?/g;
|
||||
for (const item of source.items) {
|
||||
// Replace damage inlines with new formulas
|
||||
for (const withDescription of [item.system, ...Object.values(item.system.actions)]) {
|
||||
withDescription.description = withDescription.description.replace(damageRegex, (match, inner) => {
|
||||
const { value: formula } = parseInlineParams(inner);
|
||||
if (!formula || !type) return match;
|
||||
|
||||
try {
|
||||
const adjusted = this.#calculateAdjustedDamage(formula, { ...damageMeta, type: 'action' });
|
||||
const newFormula = [
|
||||
adjusted.diceQuantity ? `${adjusted.diceQuantity}d${adjusted.faces}` : null,
|
||||
adjusted.bonus
|
||||
]
|
||||
.filter(p => !!p)
|
||||
.join('+');
|
||||
return match.replace(formula, newFormula);
|
||||
} catch {
|
||||
return match;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update damage in item actions
|
||||
// Parse damage, and convert all formula matches in the descriptions to the new damage
|
||||
for (const action of Object.values(item.system.actions)) {
|
||||
try {
|
||||
const result = this.#adjustActionDamage(action, { ...damageMeta, type: 'action' });
|
||||
if (!result) continue;
|
||||
|
||||
for (const { previousFormula, formula } of Object.values(result)) {
|
||||
const oldFormulaRegexp = new RegExp(
|
||||
previousFormula.replace(' ', '').replace('+', '(?:\\s)?\\+(?:\\s)?')
|
||||
);
|
||||
item.system.description = item.system.description.replace(oldFormulaRegexp, formula);
|
||||
action.description = action.description.replace(oldFormulaRegexp, formula);
|
||||
}
|
||||
} catch (err) {
|
||||
ui.notifications.warn(`Failed to convert action damage for item ${item.name}`);
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally set the tier of the source data, now that everything is complete
|
||||
source.system.tier = tier;
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a damage object to a new damage range
|
||||
* @returns {{ diceQuantity: number; faces: number; bonus: number }} the adjusted result as a combined term
|
||||
* @throws error if the formula is the wrong type
|
||||
*/
|
||||
#calculateAdjustedDamage(formula, { currentDamageRange, newDamageRange, type }) {
|
||||
const terms = parseTermsFromSimpleFormula(formula);
|
||||
const flatTerms = terms.filter(t => t.diceQuantity === 0);
|
||||
const diceTerms = terms.filter(t => t.diceQuantity > 0);
|
||||
if (flatTerms.length > 1 || diceTerms.length > 1) {
|
||||
throw new Error('invalid formula for conversion');
|
||||
}
|
||||
const value = {
|
||||
...(diceTerms[0] ?? { diceQuantity: 0, faces: 1 }),
|
||||
bonus: flatTerms[0]?.bonus ?? 0
|
||||
};
|
||||
const previousExpected = calculateExpectedValue(value);
|
||||
if (previousExpected === 0) return value; // nothing to do
|
||||
|
||||
const dieSizes = [4, 6, 8, 10, 12, 20];
|
||||
const steps = newDamageRange.tier - currentDamageRange.tier;
|
||||
const increasing = steps > 0;
|
||||
const deviation = (previousExpected - currentDamageRange.mean) / currentDamageRange.deviation;
|
||||
const expected = Math.max(1, newDamageRange.mean + newDamageRange.deviation * deviation);
|
||||
|
||||
// If this was just a flat number, convert to the expected damage and exit
|
||||
if (value.diceQuantity === 0) {
|
||||
value.bonus = Math.round(expected);
|
||||
return value;
|
||||
}
|
||||
|
||||
const getExpectedDie = () => calculateExpectedValue({ diceQuantity: 1, faces: value.faces }) || 1;
|
||||
const getBaseAverage = () => calculateExpectedValue({ ...value, bonus: 0 });
|
||||
|
||||
// Check the number of base overages over the expected die. In the end, if the bonus inflates too much, we add a die
|
||||
const baseOverages = Math.floor(value.bonus / getExpectedDie());
|
||||
|
||||
// Prestep. Change number of dice for attacks, bump up/down for actions
|
||||
// We never bump up to d20, though we might bump down from it
|
||||
if (type === 'attack') {
|
||||
const minimum = increasing ? value.diceQuantity : 0;
|
||||
value.diceQuantity = Math.max(minimum, newDamageRange.tier);
|
||||
} else {
|
||||
const currentIdx = dieSizes.indexOf(value.faces);
|
||||
value.faces = dieSizes[Math.clamp(currentIdx + steps, 0, 4)];
|
||||
}
|
||||
|
||||
value.bonus = Math.round(expected - getBaseAverage());
|
||||
|
||||
// Attempt to handle negative values.
|
||||
// If we can do it with only step downs, do so. Otherwise remove tier dice, and try again
|
||||
if (value.bonus < 0) {
|
||||
let stepsRequired = Math.ceil(Math.abs(value.bonus) / value.diceQuantity);
|
||||
const currentIdx = dieSizes.indexOf(value.faces);
|
||||
|
||||
// If step downs alone don't suffice, change the flat modifier, then calculate steps required again
|
||||
// If this isn't sufficient, the result will be slightly off. This is unlikely to happen
|
||||
if (type !== 'attack' && stepsRequired > currentIdx && value.diceQuantity > 0) {
|
||||
value.diceQuantity -= increasing ? 1 : Math.abs(steps);
|
||||
value.bonus = Math.round(expected - getBaseAverage());
|
||||
if (value.bonus >= 0) return value; // complete
|
||||
}
|
||||
|
||||
stepsRequired = Math.ceil(Math.abs(value.bonus) / value.diceQuantity);
|
||||
value.faces = dieSizes[Math.max(0, currentIdx - stepsRequired)];
|
||||
value.bonus = Math.max(0, Math.round(expected - getBaseAverage()));
|
||||
}
|
||||
|
||||
// If value is really high, we add a number of dice based on the number of overages
|
||||
// This attempts to preserve a similar amount of variance when increasing an action
|
||||
const overagesToRemove = Math.floor(value.bonus / getExpectedDie()) - baseOverages;
|
||||
if (type !== 'attack' && increasing && overagesToRemove > 0) {
|
||||
value.diceQuantity += overagesToRemove;
|
||||
value.bonus = Math.round(expected - getBaseAverage());
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates damage to reflect a specific value.
|
||||
* @throws if damage structure is invalid for conversion
|
||||
* @returns the converted formula and value as a simplified term, or null if it doesn't deal HP damage
|
||||
*/
|
||||
#adjustActionDamage(action, damageMeta) {
|
||||
if (!action.damage?.parts.hitPoints) return null;
|
||||
|
||||
const result = {};
|
||||
for (const property of ['value', 'valueAlt']) {
|
||||
const data = action.damage.parts.hitPoints[property];
|
||||
const previousFormula = data.custom.enabled
|
||||
? data.custom.formula
|
||||
: [data.flatMultiplier ? `${data.flatMultiplier}${data.dice}` : 0, data.bonus ?? 0]
|
||||
.filter(p => !!p)
|
||||
.join('+');
|
||||
const value = this.#calculateAdjustedDamage(previousFormula, damageMeta);
|
||||
const formula = [value.diceQuantity ? `${value.diceQuantity}d${value.faces}` : null, value.bonus]
|
||||
.filter(p => !!p)
|
||||
.join('+');
|
||||
if (value.diceQuantity) {
|
||||
data.custom.enabled = false;
|
||||
data.bonus = value.bonus;
|
||||
data.dice = `d${value.faces}`;
|
||||
data.flatMultiplier = value.diceQuantity;
|
||||
} else if (!value.diceQuantity) {
|
||||
data.custom.enabled = true;
|
||||
data.custom.formula = formula;
|
||||
}
|
||||
|
||||
result[property] = { previousFormula, formula, value };
|
||||
}
|
||||
|
||||
return result;
|
||||
return getTierAdjustedAdversary(source, tier);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -577,6 +577,8 @@ export default class DhCharacter extends DhCreature {
|
|||
communityFeatures = [],
|
||||
classFeatures = [],
|
||||
subclassFeatures = [],
|
||||
multiclassFeatures = [],
|
||||
multiclassSubclassFeatures = [],
|
||||
companionFeatures = [],
|
||||
features = [];
|
||||
|
||||
|
|
@ -586,9 +588,9 @@ export default class DhCharacter extends DhCreature {
|
|||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.community.id) {
|
||||
communityFeatures.push(item);
|
||||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.class.id) {
|
||||
classFeatures.push(item);
|
||||
(item.system.multiclassOrigin ? multiclassFeatures : classFeatures).push(item);
|
||||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) {
|
||||
subclassFeatures.push(item);
|
||||
(item.system.multiclassOrigin ? multiclassSubclassFeatures : subclassFeatures).push(item);
|
||||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.companion.id) {
|
||||
companionFeatures.push(item);
|
||||
} else if (item.type === 'feature' && !item.system.type) {
|
||||
|
|
@ -617,6 +619,24 @@ export default class DhCharacter extends DhCreature {
|
|||
type: 'subclass',
|
||||
values: subclassFeatures
|
||||
},
|
||||
...(multiclassFeatures.length
|
||||
? {
|
||||
multiclassFeatures: {
|
||||
title: `${game.i18n.localize('DAGGERHEART.GENERAL.multiclass')} - ${this.multiclass.value?.name}`,
|
||||
type: 'multiclass',
|
||||
values: multiclassFeatures
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
...(multiclassSubclassFeatures.length
|
||||
? {
|
||||
multiclassSubclassFeatures: {
|
||||
title: `${game.i18n.localize('DAGGERHEART.GENERAL.multiclass')} ${game.i18n.localize('TYPES.Item.subclass')} - ${this.multiclass.subclass?.name}`,
|
||||
type: 'multiclassSubclass',
|
||||
values: multiclassSubclassFeatures
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
companionFeatures: {
|
||||
title: game.i18n.localize('DAGGERHEART.ACTORS.Character.companionFeatures'),
|
||||
type: 'companion',
|
||||
|
|
@ -840,12 +860,13 @@ export default class DhCharacter extends DhCreature {
|
|||
const newHopeMax = this.resources.hope.max + diff;
|
||||
const newHopeValue = Math.min(newHopeMax, this.resources.hope.value);
|
||||
if (newHopeValue != this.resources.hope.value) {
|
||||
if (!changes.system.resources.hope) changes.system.resources.hope = { value: 0 };
|
||||
|
||||
changes.system.resources.hope = {
|
||||
...changes.system.resources.hope,
|
||||
value: changes.system.resources.hope.value + newHopeValue
|
||||
};
|
||||
changes.system = foundry.utils.mergeObject(changes.system ?? {}, {
|
||||
resources: {
|
||||
hope: {
|
||||
value: (changes.system?.resources?.hope?.value ?? 0) + newHopeMax
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
43
module/data/actor/npc.mjs
Normal file
43
module/data/actor/npc.mjs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import DHNPCSettings from '../../applications/sheets-configs/npc-settings.mjs';
|
||||
import BaseDataActor from './base.mjs';
|
||||
|
||||
export default class DhpNPC extends BaseDataActor {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.NPC'];
|
||||
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Actor.npc',
|
||||
type: 'npc',
|
||||
settingSheet: DHNPCSettings,
|
||||
hasResistances: false,
|
||||
hasAttribution: true
|
||||
});
|
||||
}
|
||||
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
difficulty: new fields.NumberField({
|
||||
nullable: true,
|
||||
initial: null,
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.difficulty'
|
||||
}),
|
||||
description: new fields.HTMLField({ label: 'DAGGERHEART.GENERAL.description' }),
|
||||
motives: new fields.StringField(),
|
||||
notes: new fields.HTMLField()
|
||||
};
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/actors/drama-masks.svg';
|
||||
|
||||
get features() {
|
||||
return this.parent.items.filter(x => x.type === 'feature');
|
||||
}
|
||||
|
||||
isItemValid(source) {
|
||||
return super.isItemValid(source) || source.type === 'feature';
|
||||
}
|
||||
}
|
||||
218
module/data/actor/tierAdjustment.mjs
Normal file
218
module/data/actor/tierAdjustment.mjs
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
import { calculateExpectedValue, parseTermsFromSimpleFormula } from '../../helpers/utils.mjs';
|
||||
import { adversaryExpectedDamage, adversaryScalingData } from '../../config/actorConfig.mjs';
|
||||
|
||||
export function getTierAdjustedAdversary(source, tier) {
|
||||
const currentTier = source.tier ?? 1;
|
||||
|
||||
/** @type {(2 | 3 | 4)[]} */
|
||||
const tiers = new Array(Math.abs(tier - currentTier))
|
||||
.fill(0)
|
||||
.map((_, idx) => idx + Math.min(tier, currentTier) + 1);
|
||||
if (tier < currentTier) tiers.reverse();
|
||||
const typeData = adversaryScalingData[source.system.type] ?? adversaryScalingData[source.system.standard];
|
||||
const tierEntries = tiers.map(t => ({ tier: t, ...typeData[t] }));
|
||||
|
||||
// Apply simple tier changes
|
||||
const scale = tier > currentTier ? 1 : -1;
|
||||
for (const entry of tierEntries) {
|
||||
source.system.difficulty += scale * entry.difficulty;
|
||||
source.system.damageThresholds.major += scale * entry.majorThreshold;
|
||||
source.system.damageThresholds.severe += scale * entry.severeThreshold;
|
||||
source.system.resources.hitPoints.max += scale * entry.hp;
|
||||
source.system.resources.stress.max += scale * entry.stress;
|
||||
source.system.attack.roll.bonus += scale * entry.attack;
|
||||
}
|
||||
|
||||
// Get the mean and standard deviation of expected damage in the previous and new tier
|
||||
// The data we have is for attack scaling, but we reuse this for action scaling later
|
||||
const expectedDamageData = adversaryExpectedDamage[source.system.type] ?? adversaryExpectedDamage.basic;
|
||||
const damageMeta = {
|
||||
currentDamageRange: { tier: source.system.tier, ...expectedDamageData[source.system.tier] },
|
||||
newDamageRange: { tier, ...expectedDamageData[tier] }
|
||||
};
|
||||
|
||||
// Store initial attack damage for abilities that have you deal a "standard attack"
|
||||
const initialAttack = {
|
||||
type: source.system.attack.damage?.parts.hitPoints?.type?.toSorted(),
|
||||
value: getDamagePartsFormula(source.system.attack.damage?.parts.hitPoints?.value)
|
||||
};
|
||||
|
||||
// Update damage of base attack.
|
||||
try {
|
||||
const damage = source.system.attack.damage;
|
||||
if (!damage?.parts.hitPoints) throw new Error('Unexpected missing attack in adversary');
|
||||
|
||||
for (const property of ['value', 'valueAlt']) {
|
||||
const data = damage.parts.hitPoints[property];
|
||||
const previousFormula = getDamagePartsFormula(data);
|
||||
const { value, formula } = calculateAdjustedDamage(previousFormula, 'attack', damageMeta);
|
||||
applyAdjustedDamage(data, value, formula);
|
||||
}
|
||||
} catch (err) {
|
||||
ui.notifications.warn('Failed to convert attack damage of adversary');
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
// Update damage of each item action, making sure to also update the description if possible
|
||||
const damageRegex = /@Damage\[([^\[\]]*)\]({[^}]*})?/g;
|
||||
for (const item of source.items) {
|
||||
// Replace damage inlines with new formulas. Keep a record for a specific check later
|
||||
const descriptionFormulas = [];
|
||||
for (const withDescription of [item.system, ...Object.values(item.system.actions)]) {
|
||||
withDescription.description = withDescription.description.replace(damageRegex, (match, inner) => {
|
||||
const { value: formula } = parseInlineParams(inner);
|
||||
if (!formula || !type) return match;
|
||||
|
||||
try {
|
||||
const newFormula = calculateAdjustedDamage(formula, 'action', damageMeta)?.formula;
|
||||
descriptionFormulas.push(formula);
|
||||
return match.replace(formula, newFormula);
|
||||
} catch {
|
||||
return match;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update damage in item actions and convert all formula matches in the descriptions to the new damage
|
||||
for (const action of Object.values(item.system.actions)) {
|
||||
if (!action.damage?.parts.hitPoints) continue;
|
||||
try {
|
||||
// Apply conversions and save a record. If it matches attack damage *and* Its not in the description, use attack conversion instead
|
||||
const result = [];
|
||||
for (const property of ['value', 'valueAlt']) {
|
||||
const { [property]: data, type: damageType } = action.damage.parts.hitPoints;
|
||||
const previousFormula = getDamagePartsFormula(data);
|
||||
const isActuallyAttack =
|
||||
previousFormula === initialAttack.value &&
|
||||
foundry.utils.equals(damageType.toSorted(), initialAttack.type) &&
|
||||
!descriptionFormulas.includes(previousFormula);
|
||||
const type = isActuallyAttack ? 'attack' : 'action';
|
||||
const { value, formula } = calculateAdjustedDamage(previousFormula, type, damageMeta);
|
||||
applyAdjustedDamage(data, value, formula);
|
||||
result.push({ previousFormula, formula });
|
||||
}
|
||||
|
||||
// Override text in the description with those values
|
||||
for (const { previousFormula, formula } of Object.values(result)) {
|
||||
const oldFormulaRegexp = new RegExp(
|
||||
previousFormula.replace(' ', '').replace('+', '(?:\\s)?\\+(?:\\s)?')
|
||||
);
|
||||
item.system.description = item.system.description.replace(oldFormulaRegexp, formula);
|
||||
action.description = action.description.replace(oldFormulaRegexp, formula);
|
||||
}
|
||||
} catch (err) {
|
||||
ui.notifications.warn(`Failed to convert action damage for item ${item.name}`);
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally set the tier of the source data, now that everything is complete
|
||||
source.system.tier = tier;
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a damage object to a new damage range
|
||||
* @returns {{ diceQuantity: number; faces: number; bonus: number }} the adjusted result as a combined term
|
||||
* @throws error if the formula is the wrong type
|
||||
*/
|
||||
function calculateAdjustedDamage(formula, type, { currentDamageRange, newDamageRange }) {
|
||||
const terms = parseTermsFromSimpleFormula(formula);
|
||||
const flatTerms = terms.filter(t => t.diceQuantity === 0);
|
||||
const diceTerms = terms.filter(t => t.diceQuantity > 0);
|
||||
if (flatTerms.length > 1 || diceTerms.length > 1) {
|
||||
throw new Error('invalid formula for conversion');
|
||||
}
|
||||
const value = {
|
||||
...(diceTerms[0] ?? { diceQuantity: 0, faces: 1 }),
|
||||
bonus: flatTerms[0]?.bonus ?? 0
|
||||
};
|
||||
const previousExpected = calculateExpectedValue(value);
|
||||
if (previousExpected === 0) return value; // nothing to do
|
||||
|
||||
const dieSizes = [4, 6, 8, 10, 12, 20];
|
||||
const steps = newDamageRange.tier - currentDamageRange.tier;
|
||||
const increasing = steps > 0;
|
||||
const deviation = (previousExpected - currentDamageRange.mean) / currentDamageRange.deviation;
|
||||
const expected = Math.max(1, newDamageRange.mean + newDamageRange.deviation * deviation);
|
||||
|
||||
// If this was just a flat number, convert to the expected damage and exit
|
||||
if (value.diceQuantity === 0) {
|
||||
value.bonus = Math.round(expected);
|
||||
return value;
|
||||
}
|
||||
|
||||
const getExpectedDie = () => calculateExpectedValue({ diceQuantity: 1, faces: value.faces }) || 1;
|
||||
const getBaseAverage = () => calculateExpectedValue({ ...value, bonus: 0 });
|
||||
|
||||
// Check the number of base overages over the expected die. In the end, if the bonus inflates too much, we add a die
|
||||
const baseOverages = Math.floor(value.bonus / getExpectedDie());
|
||||
|
||||
// Prestep. Change number of dice for attacks, bump up/down for actions
|
||||
// We never bump up to d20, though we might bump down from it
|
||||
if (type === 'attack') {
|
||||
const minimum = increasing ? value.diceQuantity : 0;
|
||||
value.diceQuantity = Math.max(minimum, newDamageRange.tier);
|
||||
} else {
|
||||
const currentIdx = dieSizes.indexOf(value.faces);
|
||||
value.faces = dieSizes[Math.clamp(currentIdx + steps, 0, 4)];
|
||||
}
|
||||
|
||||
value.bonus = Math.round(expected - getBaseAverage());
|
||||
|
||||
// Attempt to handle negative values.
|
||||
// If we can do it with only step downs, do so. Otherwise remove tier dice, and try again
|
||||
if (value.bonus < 0) {
|
||||
let stepsRequired = Math.ceil(Math.abs(value.bonus) / value.diceQuantity);
|
||||
const currentIdx = dieSizes.indexOf(value.faces);
|
||||
|
||||
// If step downs alone don't suffice, change the flat modifier, then calculate steps required again
|
||||
// If this isn't sufficient, the result will be slightly off. This is unlikely to happen
|
||||
if (type !== 'attack' && stepsRequired > currentIdx && value.diceQuantity > 0) {
|
||||
value.diceQuantity -= increasing ? 1 : Math.abs(steps);
|
||||
value.bonus = Math.round(expected - getBaseAverage());
|
||||
if (value.bonus >= 0) return value; // complete
|
||||
}
|
||||
|
||||
stepsRequired = Math.ceil(Math.abs(value.bonus) / value.diceQuantity);
|
||||
value.faces = dieSizes[Math.max(0, currentIdx - stepsRequired)];
|
||||
value.bonus = Math.max(0, Math.round(expected - getBaseAverage()));
|
||||
}
|
||||
|
||||
// If value is really high, we add a number of dice based on the number of overages
|
||||
// This attempts to preserve a similar amount of variance when increasing an action
|
||||
const overagesToRemove = Math.floor(value.bonus / getExpectedDie()) - baseOverages;
|
||||
if (type !== 'attack' && increasing && overagesToRemove > 0) {
|
||||
value.diceQuantity += overagesToRemove;
|
||||
value.bonus = Math.round(expected - getBaseAverage());
|
||||
}
|
||||
|
||||
const newFormula = [value.diceQuantity ? `${value.diceQuantity}d${value.faces}` : null, value.bonus]
|
||||
.filter(p => !!p)
|
||||
.join('+');
|
||||
return { value, formula: newFormula };
|
||||
}
|
||||
|
||||
function getDamagePartsFormula(data) {
|
||||
return data.custom.enabled
|
||||
? data.custom.formula
|
||||
: [data.flatMultiplier ? `${data.flatMultiplier}${data.dice}` : 0, data.bonus ?? 0].filter(p => !!p).join('+');
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates damage to reflect a specific value.
|
||||
* @throws if damage structure is invalid for conversion
|
||||
* @returns the converted formula and value as a simplified term, or null if it doesn't deal HP damage
|
||||
*/
|
||||
function applyAdjustedDamage(diceData, value, formula) {
|
||||
if (value.diceQuantity) {
|
||||
diceData.custom.enabled = false;
|
||||
diceData.bonus = value.bonus;
|
||||
diceData.dice = `d${value.faces}`;
|
||||
diceData.flatMultiplier = value.diceQuantity;
|
||||
} else if (!value.diceQuantity) {
|
||||
diceData.custom.enabled = true;
|
||||
diceData.custom.formula = formula;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { triggerChatRollFx } from '../../helpers/utils.mjs';
|
||||
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
const targetsField = () =>
|
||||
|
|
@ -130,6 +132,35 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
|||
});
|
||||
}
|
||||
|
||||
/* TODO: Change how damage data is stored somehow to enable better rerolling */
|
||||
async getRerolledDamage() {
|
||||
if (!this.damage) return;
|
||||
|
||||
const rerolls = [];
|
||||
const update = { system: { damage: {} } };
|
||||
for (const partKey in this.damage) {
|
||||
const part = this.damage[partKey];
|
||||
const testRoll = Roll.fromData(part.parts[0].roll);
|
||||
const rerolled = await testRoll.reroll();
|
||||
rerolls.push(rerolled);
|
||||
|
||||
if (!update.system.damage[partKey]) update.system.damage[partKey] = { parts: [part.parts[0]] };
|
||||
const partData = update.system.damage[partKey].parts[0];
|
||||
update.system.damage[partKey].total = rerolled.total;
|
||||
partData.modifierTotal = rerolled.terms.reduce((acc, x) => {
|
||||
if (x.isDeterministic && !x.operator) acc += x.total;
|
||||
return acc;
|
||||
}, 0);
|
||||
partData.dice = rerolled.dice.map(d => ({ ...d.toJSON(), dice: d.denomination }));
|
||||
partData.total = rerolled.total;
|
||||
partData.roll = rerolled.toJSON();
|
||||
}
|
||||
|
||||
await triggerChatRollFx(rerolls);
|
||||
|
||||
return update;
|
||||
}
|
||||
|
||||
registerTargetHook() {
|
||||
if (!this.parent.isAuthor || !this.hasTarget) return;
|
||||
if (this.targetMode && this.parent.targetHook !== null) {
|
||||
|
|
|
|||
|
|
@ -11,11 +11,13 @@ export default class CompendiumBrowserSettings extends foundry.abstract.DataMode
|
|||
})
|
||||
),
|
||||
excludedPacks: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
excludedDocumentTypes: new fields.ArrayField(
|
||||
new fields.StringField({ required: true, choices: CONST.SYSTEM_SPECIFIC_COMPENDIUM_TYPES })
|
||||
)
|
||||
})
|
||||
new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
excludedDocumentTypes: new fields.ArrayField(
|
||||
new fields.StringField({ required: true, choices: CONST.SYSTEM_SPECIFIC_COMPENDIUM_TYPES })
|
||||
)
|
||||
})
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
@ -28,7 +30,7 @@ export default class CompendiumBrowserSettings extends foundry.abstract.DataMode
|
|||
const excludedSourceData = this.excludedSources[packageName];
|
||||
if (excludedSourceData && excludedSourceData.excludedDocumentTypes.includes(pack.metadata.type)) return true;
|
||||
|
||||
const excludedPackData = this.excludedPacks[item.pack];
|
||||
const excludedPackData = this.excludedPacks[packageName]?.[pack.metadata.name];
|
||||
if (excludedPackData && excludedPackData.excludedDocumentTypes.includes(pack.metadata.type)) return true;
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ export default class CostField extends fields.ArrayField {
|
|||
static calcCosts(costs) {
|
||||
const resources = CostField.getResources.call(this, costs);
|
||||
let filteredCosts = costs;
|
||||
if (this.parent?.metadata.isQuantifiable && this.parent.consumeOnUse === false) {
|
||||
if (this.parent?.isInventoryItem && this.parent.consumeOnUse === false) {
|
||||
filteredCosts = filteredCosts.filter(c => c.key !== 'quantity');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,9 +72,6 @@ export default class DamageField extends fields.SchemaField {
|
|||
damageConfig.source.message = messageId;
|
||||
damageConfig.directDamage = !!damageConfig.source?.message;
|
||||
|
||||
// if(damageConfig.source?.message && game.modules.get('dice-so-nice')?.active)
|
||||
// await game.dice3d.waitFor3DAnimationByMessageID(damageConfig.source.message);
|
||||
|
||||
const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig);
|
||||
if (!damageResult) return false;
|
||||
if (damageResult.actionChatMessageHandled) config.actionChatMessageHandled = true;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { emitGMUpdate, GMUpdateEvent } from '../../../systemRegistration/socket.mjs';
|
||||
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class EffectsField extends fields.ArrayField {
|
||||
|
|
@ -34,8 +32,7 @@ export default class EffectsField extends fields.ArrayField {
|
|||
}
|
||||
if (EffectsField.getAutomation() || force) {
|
||||
targets ??= (message.system?.targets ?? config.targets).filter(t => !config.hasRoll || t.hit);
|
||||
await emitGMUpdate(GMUpdateEvent.UpdateEffect, EffectsField.applyEffects.bind(this), targets, this.uuid);
|
||||
// EffectsField.applyEffects.call(this, config.targets.filter(t => !config.hasRoll || t.hit));
|
||||
EffectsField.applyEffects.call(this, targets);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +56,7 @@ export default class EffectsField extends fields.ArrayField {
|
|||
if (!token) return;
|
||||
|
||||
const messageToken = token.document ?? token;
|
||||
const conditionImmunities = messageToken.actor.system.rules.conditionImmunities ?? {};
|
||||
const conditionImmunities = messageToken.actor.system.rules?.conditionImmunities ?? {};
|
||||
messageTargets.push({
|
||||
token: messageToken,
|
||||
conditionImmunities: Object.values(conditionImmunities).some(x => x)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { itemAbleRollParse } from '../../../helpers/utils.mjs';
|
||||
import { itemAbleRollParse, triggerChatRollFx } from '../../../helpers/utils.mjs';
|
||||
import FormulaField from '../formulaField.mjs';
|
||||
|
||||
const fields = foundry.data.fields;
|
||||
|
|
@ -40,7 +40,7 @@ export default class DHSummonField extends fields.ArrayField {
|
|||
const roll = new Roll(itemAbleRollParse(summon.count, this.actor, this.item));
|
||||
await roll.evaluate();
|
||||
const count = roll.total;
|
||||
if (!roll.isDeterministic && game.modules.get('dice-so-nice')?.active) rolls.push(roll);
|
||||
if (!roll.isDeterministic) rolls.push(roll);
|
||||
|
||||
const actor = await DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID));
|
||||
/* Extending summon data in memory so it's available in actionField.toChat. Think it's harmless, but ugly. Could maybe find a better way. */
|
||||
|
|
@ -56,7 +56,7 @@ export default class DHSummonField extends fields.ArrayField {
|
|||
}
|
||||
}
|
||||
|
||||
if (rolls.length) await Promise.all(rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true)));
|
||||
if (rolls.length) await triggerChatRollFx(rolls);
|
||||
|
||||
this.actor.sheet?.minimize();
|
||||
DHSummonField.handleSummon(summonData, this.actor);
|
||||
|
|
|
|||
|
|
@ -141,16 +141,6 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
static migrateDocumentData(source) {
|
||||
if (!source.system.armor) {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ export default class DHBeastform extends BaseDataItem {
|
|||
}),
|
||||
scale: new fields.NumberField({ nullable: false, min: 0.2, max: 3, step: 0.05, initial: 1 }),
|
||||
height: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }),
|
||||
width: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true })
|
||||
width: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }),
|
||||
depth: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true })
|
||||
}),
|
||||
mainTrait: new fields.StringField({
|
||||
required: true,
|
||||
|
|
@ -192,7 +193,8 @@ export default class DHBeastform extends BaseDataItem {
|
|||
tokenSize: {
|
||||
scale: this.parent.parent.prototypeToken.texture.scaleX,
|
||||
height: this.parent.parent.prototypeToken.height,
|
||||
width: this.parent.parent.prototypeToken.width
|
||||
width: this.parent.parent.prototypeToken.width,
|
||||
depth: this.parent.parent.prototypeToken.depth
|
||||
}
|
||||
},
|
||||
advantageOn: this.advantageOn,
|
||||
|
|
@ -211,10 +213,12 @@ export default class DHBeastform extends BaseDataItem {
|
|||
: null;
|
||||
const width = autoTokenSize ?? this.tokenSize.width;
|
||||
const height = autoTokenSize ?? this.tokenSize.height;
|
||||
const depth = autoTokenSize ?? this.tokenSize.depth;
|
||||
|
||||
const prototypeTokenUpdate = {
|
||||
height,
|
||||
width,
|
||||
depth,
|
||||
texture: {
|
||||
src: this.tokenImg,
|
||||
scaleX: this.tokenSize.scale,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import BaseDataItem from './base.mjs';
|
|||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
||||
import ItemLinkFields from '../fields/itemLinkFields.mjs';
|
||||
import { addLinkedItemsDiff, getFeaturesHTMLData, updateLinkedItemApps } from '../../helpers/utils.mjs';
|
||||
import { addLinkedItemsDiff, fromUuids, getFeaturesHTMLData, updateLinkedItemApps } from '../../helpers/utils.mjs';
|
||||
|
||||
export default class DHClass extends BaseDataItem {
|
||||
/** @inheritDoc */
|
||||
|
|
@ -73,15 +73,16 @@ export default class DHClass extends BaseDataItem {
|
|||
const uuids = [this.parent.uuid, this.parent._stats?.compendiumSource].filter(u => !!u);
|
||||
const subclasses = game.items.filter(x => x.type === 'subclass' && uuids.includes(x.system.linkedClass));
|
||||
for (const pack of game.packs) {
|
||||
const packIds = [];
|
||||
const indexes = await pack.getIndex({ fields: ['system.linkedClass'] });
|
||||
for (const index of indexes) {
|
||||
if (index.type !== 'subclass') continue;
|
||||
if (!uuids.includes(index.system?.linkedClass)) continue;
|
||||
if (subclasses.find(x => x.uuid === index.uuid)) continue;
|
||||
|
||||
const subclass = await foundry.utils.fromUuid(index.uuid);
|
||||
subclasses.push(subclass);
|
||||
packIds.push(index._id);
|
||||
}
|
||||
|
||||
if (packIds.length > 0) subclasses.push(...(await pack.getDocuments({ _id__in: packIds })));
|
||||
}
|
||||
|
||||
return subclasses;
|
||||
|
|
@ -216,6 +217,10 @@ export default class DHClass extends BaseDataItem {
|
|||
classItems.push(contentLink.outerHTML);
|
||||
}
|
||||
|
||||
// Preload all class features for acquisition from the cache
|
||||
// todo: make feature acquisition async and replace feature helpers for methods
|
||||
await fromUuids(this._source.features.map(f => f.item));
|
||||
|
||||
const hopeFeatures = await getFeaturesHTMLData(this.hopeFeatures);
|
||||
const classFeatures = await getFeaturesHTMLData(this.classFeatures);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { getFeaturesHTMLData } from '../../helpers/utils.mjs';
|
||||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||
import { fromUuids, getFeaturesHTMLData } from '../../helpers/utils.mjs';
|
||||
import ItemLinkFields from '../fields/itemLinkFields.mjs';
|
||||
import BaseDataItem from './base.mjs';
|
||||
|
||||
|
|
@ -56,38 +55,30 @@ export default class DHSubclass extends BaseDataItem {
|
|||
if (allowed === false) return;
|
||||
|
||||
if (this.actor?.type === 'character') {
|
||||
const dataUuid = data.uuid ?? data._stats.compendiumSource ?? `Item.${data._id}`;
|
||||
if (this.actor.system.class.subclass) {
|
||||
if (this.actor.system.multiclass.subclass) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassesAlreadyPresent'));
|
||||
return false;
|
||||
} else {
|
||||
const multiclass = this.actor.items.find(x => x.type === 'class' && x.system.isMulticlass);
|
||||
if (!multiclass) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.missingMulticlass'));
|
||||
return false;
|
||||
}
|
||||
const { value: actorClass, subclass: existingSubclass } = this.actor.system.class;
|
||||
const { value: multiclass, subclass: existingMultisubclass } = this.actor.system.multiclass;
|
||||
if (!actorClass && !multiclass) {
|
||||
ui.notifications.warn('DAGGERHEART.UI.Notifications.missingClass', { localize: true });
|
||||
return false;
|
||||
}
|
||||
if (existingSubclass && existingMultisubclass) {
|
||||
ui.notifications.warn('DAGGERHEART.UI.Notifications.subclassesAlreadyPresent', { localize: true });
|
||||
return false;
|
||||
}
|
||||
if (existingSubclass && !multiclass) {
|
||||
ui.notifications.warn('DAGGERHEART.UI.Notifications.missingMulticlass', { localize: true });
|
||||
return false;
|
||||
}
|
||||
|
||||
if (multiclass.system.subclasses.every(x => x.uuid !== dataUuid)) {
|
||||
ui.notifications.error(
|
||||
game.i18n.localize('DAGGERHEART.UI.Notifications.subclassNotInMulticlass')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.updateSource({ isMulticlass: true });
|
||||
}
|
||||
} else {
|
||||
const actorClass = this.actor.items.find(x => x.type === 'class' && !x.system.isMulticlass);
|
||||
if (!actorClass) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.missingClass'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((await actorClass.system.fetchSubclasses()).every(x => x.uuid !== dataUuid)) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassNotInClass'));
|
||||
return false;
|
||||
}
|
||||
const match = [multiclass, actorClass].find(
|
||||
c => c && (c._stats.compendiumSource ?? c.uuid) === this.linkedClass
|
||||
);
|
||||
if (!match) {
|
||||
const key = multiclass ? 'subclassNotInMulticlass' : 'subclassNotInClass';
|
||||
ui.notifications.warn(`DAGGERHEART.UI.Notifications.${key}`, { localize: true });
|
||||
return false;
|
||||
} else if (match.system.isMulticlass) {
|
||||
await this.updateSource({ isMulticlass: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -99,6 +90,11 @@ export default class DHSubclass extends BaseDataItem {
|
|||
const spellcastTrait = this.spellcastingTrait
|
||||
? game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.spellcastingTrait].label)
|
||||
: null;
|
||||
|
||||
// Preload all class features for acquisition from the cache
|
||||
// todo: make feature acquisition async and replace feature helpers for methods
|
||||
await fromUuids(this._source.features.map(f => f.item));
|
||||
|
||||
const foundationFeatures = await getFeaturesHTMLData(this.foundationFeatures);
|
||||
const specializationFeatures = await getFeaturesHTMLData(this.specializationFeatures);
|
||||
const masteryFeatures = await getFeaturesHTMLData(this.masteryFeatures);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
||||
import { triggerChatRollFx } from '../helpers/utils.mjs';
|
||||
import DHRoll from './dhRoll.mjs';
|
||||
|
||||
export default class D20Roll extends DHRoll {
|
||||
|
|
@ -224,4 +225,15 @@ export default class D20Roll extends DHRoll {
|
|||
resetFormula() {
|
||||
return (this._formula = this.constructor.getFormula(this.terms));
|
||||
}
|
||||
|
||||
async reroll(options) {
|
||||
const result = await super.reroll(options);
|
||||
if (this instanceof game.system.api.dice.DualityRoll) return result;
|
||||
|
||||
if (options?.liveRoll) {
|
||||
await triggerChatRollFx([result]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import DamageDialog from '../applications/dialogs/damageDialog.mjs';
|
||||
import { parseRallyDice } from '../helpers/utils.mjs';
|
||||
import { parseRallyDice, triggerChatRollFx } from '../helpers/utils.mjs';
|
||||
import DHRoll from './dhRoll.mjs';
|
||||
|
||||
export default class DamageRoll extends DHRoll {
|
||||
|
|
@ -18,7 +18,12 @@ export default class DamageRoll extends DHRoll {
|
|||
if (config.evaluate !== false) for (const roll of config.roll) await roll.roll.evaluate();
|
||||
|
||||
roll._evaluated = true;
|
||||
const parts = config.roll.map(r => this.postEvaluate(r));
|
||||
|
||||
const parts = [];
|
||||
for (const roll of config.roll) {
|
||||
parts.push(this.postEvaluate(roll));
|
||||
roll.roll = JSON.stringify(roll.roll.toJSON());
|
||||
}
|
||||
|
||||
config.damage = this.unifyDamageRoll(parts);
|
||||
}
|
||||
|
|
@ -38,25 +43,24 @@ export default class DamageRoll extends DHRoll {
|
|||
const chatMessage = config.source?.message
|
||||
? ui.chat.collection.get(config.source.message)
|
||||
: getDocumentClass('ChatMessage').applyMode({}, config.rollMode ?? 'public');
|
||||
|
||||
const diceRolls = [];
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const pool = foundry.dice.terms.PoolTerm.fromRolls(
|
||||
Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll))
|
||||
),
|
||||
diceRoll = Roll.fromTerms([pool]);
|
||||
await game.dice3d.showForRoll(
|
||||
diceRoll,
|
||||
game.user,
|
||||
true,
|
||||
chatMessage.whisper?.length > 0 ? chatMessage.whisper : null,
|
||||
chatMessage.blind
|
||||
);
|
||||
config.mute = true;
|
||||
const pool = foundry.dice.terms.PoolTerm.fromRolls(
|
||||
Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll))
|
||||
);
|
||||
diceRolls.push(Roll.fromTerms([pool]));
|
||||
}
|
||||
|
||||
await triggerChatRollFx(diceRolls, {
|
||||
whisper: chatMessage.whisper?.length > 0 ? chatMessage.whisper : null,
|
||||
blind: chatMessage.blind
|
||||
});
|
||||
await super.buildPost(roll, config, message);
|
||||
|
||||
if (config.source?.message) {
|
||||
chatMessage.update({ 'system.damage': config.damage });
|
||||
|
||||
if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -319,9 +323,10 @@ export default class DamageRoll extends DHRoll {
|
|||
const newIndex = parsedDiceTerms[dice].results.length;
|
||||
await term.reroll(`/r1=${termResult.result}`);
|
||||
|
||||
const diceRolls = [];
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const newResult = parsedDiceTerms[dice].results[newIndex];
|
||||
const diceSoNiceRoll = {
|
||||
diceRolls.push({
|
||||
_evaluated: true,
|
||||
dice: [
|
||||
new foundry.dice.terms.Die({
|
||||
|
|
@ -332,11 +337,10 @@ export default class DamageRoll extends DHRoll {
|
|||
})
|
||||
],
|
||||
options: { appearance: {} }
|
||||
};
|
||||
|
||||
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
|
||||
});
|
||||
}
|
||||
|
||||
await triggerChatRollFx(diceRolls);
|
||||
await parsedRoll.evaluate();
|
||||
|
||||
const results = parsedRoll.dice[dice].results.map(result => ({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
||||
import { triggerChatRollFx } from '../helpers/utils.mjs';
|
||||
|
||||
export default class DHRoll extends Roll {
|
||||
baseTerms = [];
|
||||
|
|
@ -75,9 +76,7 @@ export default class DHRoll extends Roll {
|
|||
}
|
||||
|
||||
if (config.skips?.createMessage) {
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
await game.dice3d.showForRoll(roll, game.user, true);
|
||||
}
|
||||
await triggerChatRollFx([roll]);
|
||||
} else if (!config.source?.message) {
|
||||
config.message = await this.toMessage(roll, config);
|
||||
}
|
||||
|
|
@ -85,6 +84,7 @@ export default class DHRoll extends Roll {
|
|||
|
||||
static postEvaluate(roll, config = {}) {
|
||||
return {
|
||||
...roll.options.roll,
|
||||
total: roll.total,
|
||||
formula: roll.formula,
|
||||
dice: roll.dice.map(d => ({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ResourceUpdateMap } from '../../data/action/baseAction.mjs';
|
||||
import { updateResourcesForDualityReroll } from '../helpers.mjs';
|
||||
|
||||
export default class DualityDie extends foundry.dice.terms.Die {
|
||||
constructor(options) {
|
||||
|
|
@ -12,24 +12,6 @@ export default class DualityDie extends foundry.dice.terms.Die {
|
|||
return roll.withHope ? 1 : roll.withFear ? -1 : 0;
|
||||
}
|
||||
|
||||
#updateResources(oldDuality, newDuality, actor) {
|
||||
const { hopeFear } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
|
||||
if (game.user.isGM ? !hopeFear.gm : !hopeFear.players) return;
|
||||
|
||||
const updates = [];
|
||||
const hope = (newDuality >= 0 ? 1 : 0) - (oldDuality >= 0 ? 1 : 0);
|
||||
const stress = (newDuality === 0 ? 1 : 0) - (oldDuality === 0 ? 1 : 0);
|
||||
const fear = (newDuality === -1 ? 1 : 0) - (oldDuality === -1 ? 1 : 0);
|
||||
|
||||
if (hope !== 0) updates.push({ key: 'hope', value: hope, total: -1 * hope, enabled: true });
|
||||
if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, total: stress, enabled: true });
|
||||
if (fear !== 0) updates.push({ key: 'fear', value: fear, total: -1 * fear, enabled: true });
|
||||
|
||||
const resourceUpdates = new ResourceUpdateMap(actor);
|
||||
resourceUpdates.addResources(updates);
|
||||
resourceUpdates.updateResources();
|
||||
}
|
||||
|
||||
async reroll(modifier, options) {
|
||||
const oldDuality = this.#getDualityState(options.liveRoll.roll);
|
||||
await super.reroll(modifier, options);
|
||||
|
|
@ -57,7 +39,7 @@ export default class DualityDie extends foundry.dice.terms.Die {
|
|||
if (options.liveRoll.isReaction) return;
|
||||
|
||||
const newDuality = this.#getDualityState(options.liveRoll.roll);
|
||||
this.#updateResources(oldDuality, newDuality, options.liveRoll.actor);
|
||||
updateResourcesForDualityReroll(oldDuality, newDuality, options.liveRoll.actor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
||||
import D20Roll from './d20Roll.mjs';
|
||||
import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
|
||||
import { updateResourcesForDualityReroll } from './helpers.mjs';
|
||||
|
||||
export default class DualityRoll extends D20Roll {
|
||||
_advantageNumber = 1;
|
||||
|
|
@ -130,32 +132,34 @@ export default class DualityRoll extends D20Roll {
|
|||
}
|
||||
|
||||
createBaseDice() {
|
||||
if (
|
||||
this.dice[0] instanceof game.system.api.dice.diceTypes.HopeDie &&
|
||||
this.dice[1] instanceof game.system.api.dice.diceTypes.FearDie
|
||||
) {
|
||||
this.terms = [this.terms[0], this.terms[1], this.terms[2]];
|
||||
return;
|
||||
}
|
||||
this.terms = [this.terms[0], this.terms[1], this.terms[2]];
|
||||
|
||||
this.terms[0] = new game.system.api.dice.diceTypes.HopeDie({
|
||||
faces: this.data.rules.dualityRoll?.defaultHopeDice ?? 12
|
||||
faces: this.terms[0]?.faces ?? this.data.rules.dualityRoll?.defaultHopeDice ?? 12
|
||||
});
|
||||
this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' });
|
||||
this.terms[2] = new game.system.api.dice.diceTypes.FearDie({
|
||||
faces: this.data.rules.dualityRoll?.defaultFearDice ?? 12
|
||||
faces: this.terms[2]?.faces ?? this.data.rules.dualityRoll?.defaultFearDice ?? 12
|
||||
});
|
||||
}
|
||||
|
||||
applyAdvantage() {
|
||||
if (this.hasAdvantage || this.hasDisadvantage) {
|
||||
const dieFaces = this.advantageFaces,
|
||||
advDie = new foundry.dice.terms.Die({ faces: dieFaces, number: this.advantageNumber });
|
||||
if (this.advantageNumber > 1) advDie.modifiers = ['kh'];
|
||||
this.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }),
|
||||
advDie
|
||||
);
|
||||
const advDieClass = this.hasAdvantage
|
||||
? game.system.api.dice.diceTypes.AdvantageDie
|
||||
: this.hasDisadvantage
|
||||
? game.system.api.dice.diceTypes.DisadvantageDie
|
||||
: null;
|
||||
if (advDieClass) {
|
||||
const advDie = new advDieClass({ faces: this.advantageFaces, number: this.advantageNumber });
|
||||
if (this.terms.length < 4) {
|
||||
if (this.advantageNumber > 1) advDie.modifiers = ['kh'];
|
||||
this.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }),
|
||||
advDie
|
||||
);
|
||||
} else {
|
||||
this.terms[4] = advDie;
|
||||
}
|
||||
}
|
||||
if (this.rallyFaces)
|
||||
this.terms.push(
|
||||
|
|
@ -380,4 +384,40 @@ export default class DualityRoll extends D20Roll {
|
|||
if (currentCombatant?.actorId == config.data.id) ui.combat.setCombatantSpotlight(currentCombatant.id);
|
||||
}
|
||||
}
|
||||
|
||||
async reroll(options) {
|
||||
const oldDuality = this.withHope ? 1 : this.withFear ? -1 : 0;
|
||||
const rerolled = DualityRoll.fromData((await super.reroll(options)).toJSON());
|
||||
|
||||
if (options?.liveRoll) {
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const diceAppearance = await getDiceSoNicePresets(
|
||||
rerolled,
|
||||
rerolled.dHope.denomination,
|
||||
rerolled.dFear.denomination
|
||||
);
|
||||
rerolled.dHope.options.appearance = diceAppearance.hope.appearance;
|
||||
rerolled.dFear.options.appearance = diceAppearance.fear.appearance;
|
||||
if (rerolled.dAdvantage) rerolled.dAdvantage.options.appearance = diceAppearance.advantage.appearance;
|
||||
if (rerolled.dDisadvantage)
|
||||
rerolled.dDisadvantage.options.appearance = diceAppearance.disadvantage.appearance;
|
||||
|
||||
await game.dice3d.showForRoll(rerolled, game.user, true);
|
||||
} else {
|
||||
foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
||||
}
|
||||
|
||||
if (this.options.actionType === 'reaction') return;
|
||||
|
||||
const newDuality = rerolled.withHope ? 1 : rerolled.withFear ? -1 : 0;
|
||||
const actor = await foundry.utils.fromUuid(this.options.source.actor);
|
||||
updateResourcesForDualityReroll(oldDuality, newDuality, actor);
|
||||
}
|
||||
|
||||
return rerolled;
|
||||
}
|
||||
|
||||
fromJSON(json) {
|
||||
return super.fromJSON(json);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
17
module/dice/helpers.mjs
Normal file
17
module/dice/helpers.mjs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
export function updateResourcesForDualityReroll(oldDuality, newDuality, actor) {
|
||||
const { hopeFear } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
|
||||
if (game.user.isGM ? !hopeFear.gm : !hopeFear.players) return;
|
||||
|
||||
const updates = [];
|
||||
const hope = (newDuality >= 0 ? 1 : 0) - (oldDuality >= 0 ? 1 : 0);
|
||||
const stress = (newDuality === 0 ? 1 : 0) - (oldDuality === 0 ? 1 : 0);
|
||||
const fear = (newDuality === -1 ? 1 : 0) - (oldDuality === -1 ? 1 : 0);
|
||||
|
||||
if (hope !== 0) updates.push({ key: 'hope', value: hope, total: -1 * hope, enabled: true });
|
||||
if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, total: stress, enabled: true });
|
||||
if (fear !== 0) updates.push({ key: 'fear', value: fear, total: -1 * fear, enabled: true });
|
||||
|
||||
const resourceUpdates = new ResourceUpdateMap(actor);
|
||||
resourceUpdates.addResources(updates);
|
||||
resourceUpdates.updateResources();
|
||||
}
|
||||
|
|
@ -65,6 +65,11 @@ export default class DhpActor extends Actor {
|
|||
};
|
||||
}
|
||||
|
||||
static createDialog(data, createOptions, options, renderOptions) {
|
||||
options.classes = [options.classes ?? [], 'actor-create'].flat(); // handled in hook
|
||||
return super.createDialog(data, createOptions, options, renderOptions);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritDoc */
|
||||
|
|
@ -109,13 +114,33 @@ export default class DhpActor extends Actor {
|
|||
});
|
||||
}
|
||||
|
||||
if (this.type === 'npc') {
|
||||
Object.assign(update, {
|
||||
prototypeToken: {
|
||||
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.updateSource(update);
|
||||
}
|
||||
|
||||
/** Perform a render, debounced in order to prevent overloading repeat render requests */
|
||||
renderDebounced = foundry.utils.debounce(options => {
|
||||
return this.render(options);
|
||||
}, 10);
|
||||
|
||||
_onUpdateDescendantDocuments(parent, collection, documents, changes, options, userId) {
|
||||
super._onUpdateDescendantDocuments(parent, collection, documents, changes, options, userId);
|
||||
for (const party of this.parties) {
|
||||
party.renderDebounced({ parts: ['partyMembers'] });
|
||||
}
|
||||
}
|
||||
|
||||
_onUpdate(changes, options, userId) {
|
||||
super._onUpdate(changes, options, userId);
|
||||
for (const party of this.parties) {
|
||||
party.render({ parts: ['partyMembers'] });
|
||||
party.renderDebounced({ parts: ['partyMembers'] });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -134,17 +159,20 @@ export default class DhpActor extends Actor {
|
|||
_onDelete(options, userId) {
|
||||
super._onDelete(options, userId);
|
||||
for (const party of this.parties) {
|
||||
party.render({ parts: ['partyMembers'] });
|
||||
party.renderDebounced({ parts: ['partyMembers'] });
|
||||
}
|
||||
}
|
||||
|
||||
async updateLevel(newLevel) {
|
||||
if (!['character', 'companion'].includes(this.type) || newLevel === this.system.levelData.level.changed) return;
|
||||
|
||||
const tiers = Object.values(game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers);
|
||||
const maxLevel = tiers.reduce((acc, tier) => Math.max(acc, tier.levels.end), 0);
|
||||
const multiclassMinLevel = Math.min(
|
||||
maxLevel,
|
||||
...tiers.filter(t => t.options.multiclass).map(t => t.levels.start)
|
||||
);
|
||||
if (newLevel > this.system.levelData.level.current) {
|
||||
const maxLevel = Object.values(
|
||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers
|
||||
).reduce((acc, tier) => Math.max(acc, tier.levels.end), 0);
|
||||
if (newLevel > maxLevel) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.tooHighLevel'));
|
||||
}
|
||||
|
|
@ -219,18 +247,19 @@ export default class DhpActor extends Actor {
|
|||
this.system.multiclass.subclass.update({ 'system.featureState': subclassFeatureState.multiclass });
|
||||
}
|
||||
|
||||
if (multiclass) {
|
||||
const multiclassItem = this.items.find(x => x.uuid === multiclass.itemUuid);
|
||||
const multiclassFeatures = this.items.filter(
|
||||
x => x.system.originItemType === 'class' && x.system.multiclassOrigin
|
||||
);
|
||||
const subclassFeatures = this.items.filter(
|
||||
x => x.system.originItemType === 'subclass' && x.system.multiclassOrigin
|
||||
// Remove multiclass if we're removing a multiclass feature or if we're below the multiclass minimum level
|
||||
// Multclasses cannot be manually removed on the sheet, so this allows recovering in the case of errors
|
||||
if (multiclass || newLevel < multiclassMinLevel) {
|
||||
const multiclassItems = this.items.filter(
|
||||
x =>
|
||||
x.uuid === multiclass?.itemUuid ||
|
||||
x.system.isMulticlass ||
|
||||
(['class', 'subclass'].includes(x.system.originItemType) && x.system.multiclassOrigin)
|
||||
);
|
||||
|
||||
this.deleteEmbeddedDocuments(
|
||||
'Item',
|
||||
[multiclassItem, ...multiclassFeatures, ...subclassFeatures].map(x => x.id)
|
||||
multiclassItems.map(x => x.id)
|
||||
);
|
||||
|
||||
this.update({
|
||||
|
|
@ -269,6 +298,7 @@ export default class DhpActor extends Actor {
|
|||
|
||||
async levelUp(levelupData) {
|
||||
const levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto;
|
||||
const getStatsWithSource = document => ({ ...(document._stats ?? {}), compendiumSource: document.uuid });
|
||||
|
||||
const levelups = {};
|
||||
for (var levelKey of Object.keys(levelupData)) {
|
||||
|
|
@ -381,8 +411,8 @@ export default class DhpActor extends Actor {
|
|||
const embeddedItem = await this.createEmbeddedDocuments('Item', [
|
||||
{
|
||||
...multiclassData,
|
||||
uuid: multiclassItem.uuid,
|
||||
_stats: multiclassItem._stats,
|
||||
uuid: multiclassItem.uuid, // todo: replace with setting an id and using keepId
|
||||
_stats: getStatsWithSource(multiclassItem),
|
||||
system: {
|
||||
...multiclassData.system,
|
||||
features: multiclassData.system.features.filter(x => x.type !== 'hope'),
|
||||
|
|
@ -395,8 +425,8 @@ export default class DhpActor extends Actor {
|
|||
await this.createEmbeddedDocuments('Item', [
|
||||
{
|
||||
...subclassData,
|
||||
uuid: subclassItem.uuid,
|
||||
_stats: subclassItem._stats,
|
||||
uuid: subclassItem.uuid, // todo: replace with setting an id and using keepId
|
||||
_stats: getStatsWithSource(subclassItem),
|
||||
system: {
|
||||
...subclassData.system,
|
||||
isMulticlass: true
|
||||
|
|
@ -416,8 +446,8 @@ export default class DhpActor extends Actor {
|
|||
const embeddedItem = await this.createEmbeddedDocuments('Item', [
|
||||
{
|
||||
...cardData,
|
||||
uuid: cardItem.uuid,
|
||||
_stats: cardItem._stats,
|
||||
uuid: cardItem.uuid, // todo: replace with setting an id and using keepId
|
||||
_stats: getStatsWithSource(cardItem),
|
||||
system: {
|
||||
...cardData.system,
|
||||
inVault: true
|
||||
|
|
@ -438,8 +468,7 @@ export default class DhpActor extends Actor {
|
|||
const embeddedItem = await this.createEmbeddedDocuments('Item', [
|
||||
{
|
||||
...cardData,
|
||||
uuid: cardItem.uuid,
|
||||
_stats: cardItem._stats,
|
||||
_stats: getStatsWithSource(cardItem),
|
||||
system: {
|
||||
...cardData.system,
|
||||
inVault: true
|
||||
|
|
|
|||
|
|
@ -183,7 +183,11 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
if (pendingingSaves.length) {
|
||||
const confirm = await foundry.applications.api.DialogV2.confirm({
|
||||
window: { title: game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.title') },
|
||||
content: `<p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}</p><p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}</p><p><i>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}</i></p>`
|
||||
content: `
|
||||
<p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}</p>
|
||||
<p><i>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}</i></p>
|
||||
<p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}</p>
|
||||
`
|
||||
});
|
||||
if (!confirm) return;
|
||||
}
|
||||
|
|
@ -247,8 +251,24 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
const targets = this.filterPermTargets(this.system.hitTargets),
|
||||
config = foundry.utils.deepClone(this.system);
|
||||
config.event = event;
|
||||
|
||||
if (targets.length === 0)
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
|
||||
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
|
||||
else if (config.hasSave) {
|
||||
const pendingingSaves = targets.filter(t => t.saved.success === null);
|
||||
if (pendingingSaves.length) {
|
||||
const confirm = await foundry.applications.api.DialogV2.confirm({
|
||||
window: { title: game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.title') },
|
||||
content: `
|
||||
<p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}</p>
|
||||
<p><i>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}</i></p>
|
||||
<p>${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}</p>
|
||||
`
|
||||
});
|
||||
if (!confirm) return;
|
||||
}
|
||||
}
|
||||
|
||||
this.consumeOnSuccess();
|
||||
this.system.action?.workflow.get('effects')?.execute(config, targets, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,13 +73,16 @@ export default class DHItem extends foundry.documents.Item {
|
|||
/** Returns true if the item can be used */
|
||||
get usable() {
|
||||
const actor = this.actor;
|
||||
const actionsList = this.system.actionsList;
|
||||
return this.isOwner && actor?.type === 'character' && (actionsList?.size || actionsList?.length);
|
||||
const pack = actor?.pack ? game.packs.get(actor.pack) : null;
|
||||
const hasActions = this.system.actionsList?.size || this.system.actionsList?.length;
|
||||
const isValidType = actor?.type === 'character' || this.type === 'feature';
|
||||
return !pack?.locked && this.isOwner && isValidType && hasActions;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
static async createDialog(data = {}, createOptions = {}, options = {}) {
|
||||
const { folders, types, template, context = {}, ...dialogOptions } = options;
|
||||
dialogOptions.classes = [options.classes ?? [], 'item-create'].flat(); // handled in hook
|
||||
|
||||
if (types?.length === 0) {
|
||||
throw new Error('The array of sub-types to restrict to must not be empty.');
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export default class DhScene extends Scene {
|
|||
const prototype = tokenDoc.actor?.prototypeToken ?? tokenDoc;
|
||||
this.#sizeSyncBatch.set(tokenDoc.id, {
|
||||
size: tokenSize,
|
||||
prototypeSize: { width: prototype.width, height: prototype.height },
|
||||
prototypeSize: { width: prototype.width, height: prototype.height, depth: prototype.depth },
|
||||
position: { x: tokenDoc.x, y: tokenDoc.y, elevation: tokenDoc.elevation }
|
||||
});
|
||||
this.#processSyncBatch();
|
||||
|
|
@ -36,11 +36,13 @@ export default class DhScene extends Scene {
|
|||
const tokenSize = tokenSizes[size];
|
||||
const width = size !== CONFIG.DH.ACTOR.tokenSize.custom.id ? tokenSize : prototypeSize.width;
|
||||
const height = size !== CONFIG.DH.ACTOR.tokenSize.custom.id ? tokenSize : prototypeSize.height;
|
||||
const depth = size !== CONFIG.DH.ACTOR.tokenSize.custom.id ? tokenSize : prototypeSize.depth;
|
||||
const updatedPosition = DHToken.getSnappedPositionInSquareGrid(this.grid, position, width, height);
|
||||
return {
|
||||
_id,
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
...updatedPosition
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ export default class DHToken extends CONFIG.Token.documentClass {
|
|||
if (tokenSize && actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) {
|
||||
document.updateSource({
|
||||
width: tokenSize,
|
||||
height: tokenSize
|
||||
height: tokenSize,
|
||||
depth: tokenSize
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -90,7 +91,7 @@ export default class DHToken extends CONFIG.Token.documentClass {
|
|||
) {
|
||||
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
||||
const tokenSize = tokenSizes[update.system.size];
|
||||
if (tokenSize !== this.width || tokenSize !== this.height) {
|
||||
if (tokenSize !== this.width || tokenSize !== this.height || tokenSize !== this.depth) {
|
||||
this.parent?.syncTokenDimensions(this, update.system.size);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export default class DhTokenManager {
|
|||
if (tokenSize && actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) {
|
||||
tokenData.width = tokenSize;
|
||||
tokenData.height = tokenSize;
|
||||
tokenData.depth = tokenSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { AdversaryBPPerEncounter, BaseBPPerEncounter } from '../config/encounter
|
|||
export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager {
|
||||
#wide = false;
|
||||
#bordered = false;
|
||||
#active = false;
|
||||
|
||||
async activate(element, options = {}) {
|
||||
const { TextEditor } = foundry.applications.ux;
|
||||
|
|
|
|||
|
|
@ -449,14 +449,9 @@ export async function createEmbeddedItemsWithEffects(actor, baseData) {
|
|||
effects: data.effects?.map(effect => effect.toObject())
|
||||
});
|
||||
}
|
||||
|
||||
await actor.createEmbeddedDocuments('Item', effectData);
|
||||
}
|
||||
|
||||
export const slugify = name => {
|
||||
return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', '');
|
||||
};
|
||||
|
||||
export function shuffleArray(array) {
|
||||
let currentIndex = array.length;
|
||||
while (currentIndex != 0) {
|
||||
|
|
@ -869,3 +864,57 @@ export function camelize(str) {
|
|||
})
|
||||
.replace(/\s+/g, '');
|
||||
}
|
||||
|
||||
/** Bulk load a list of documents using uuids. Returns the documents in the same order */
|
||||
export async function fromUuids(uuids) {
|
||||
// Set up base entries. Each step works on a sublist of these objects
|
||||
const entries = uuids.map(uuid => ({
|
||||
uuid,
|
||||
parsed: foundry.utils.parseUuid(uuid),
|
||||
value: foundry.utils.fromUuidSync(uuid)
|
||||
}));
|
||||
|
||||
// Handle missing uuids for embedded documents first
|
||||
// A value may be index data, so we check if its a document
|
||||
const packEmbeddedEntries = entries.filter(
|
||||
e =>
|
||||
!(e.value instanceof Document) &&
|
||||
e.parsed.collection instanceof foundry.documents.collections.CompendiumCollection &&
|
||||
e.parsed.embedded.length > 0
|
||||
);
|
||||
await Promise.all(
|
||||
packEmbeddedEntries.map(async e => {
|
||||
e.value = await fromUuid(e.uuid);
|
||||
return true;
|
||||
})
|
||||
);
|
||||
|
||||
// Handle missing top level pack stuff, by batching per pack
|
||||
const missingTopLevel = entries.filter(e => !(e.value instanceof Document) && e.value?.pack);
|
||||
for (const packGroup of Object.values(Object.groupBy(missingTopLevel, e => e.value.pack))) {
|
||||
const pack = game.packs.get(packGroup[0].value.pack);
|
||||
if (!pack) continue;
|
||||
|
||||
const ids = packGroup.map(p => p.parsed.id);
|
||||
const documents = await pack.getDocuments({ _id__in: ids });
|
||||
for (const p of packGroup) {
|
||||
p.value = documents.find(d => d.id === p.parsed.id) ?? p.value;
|
||||
}
|
||||
}
|
||||
|
||||
return entries.map(e => e.value);
|
||||
}
|
||||
/**
|
||||
* Triggers DiceSoNice rolls or dice roll audio for rolls. Not used for duality rolls.
|
||||
* @param { Roll[] } rolls
|
||||
* @return { void }
|
||||
*/
|
||||
export async function triggerChatRollFx(rolls, options = { whisper: false, blind: false }) {
|
||||
const { whisper, blind } = options;
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const rerollPromises = rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true, whisper, blind));
|
||||
await Promise.allSettled(rerollPromises);
|
||||
} else {
|
||||
foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
477
package-lock.json
generated
477
package-lock.json
generated
|
|
@ -9,6 +9,7 @@
|
|||
"autocompleter": "^9.3.2",
|
||||
"gulp": "^5.0.0",
|
||||
"gulp-less": "^5.0.0",
|
||||
"gulp-sourcemaps": "^3.0.0",
|
||||
"rollup": "^4.40.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -202,6 +203,132 @@
|
|||
"node": ">17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@gulp-sourcemaps/identity-map": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz",
|
||||
"integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^6.4.1",
|
||||
"normalize-path": "^3.0.0",
|
||||
"postcss": "^7.0.16",
|
||||
"source-map": "^0.6.0",
|
||||
"through2": "^3.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@gulp-sourcemaps/identity-map/node_modules/acorn": {
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
|
||||
"integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@gulp-sourcemaps/identity-map/node_modules/picocolors": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
|
||||
"integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@gulp-sourcemaps/identity-map/node_modules/postcss": {
|
||||
"version": "7.0.39",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
|
||||
"integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picocolors": "^0.2.1",
|
||||
"source-map": "^0.6.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
}
|
||||
},
|
||||
"node_modules/@gulp-sourcemaps/identity-map/node_modules/through2": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
|
||||
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "2 || 3"
|
||||
}
|
||||
},
|
||||
"node_modules/@gulp-sourcemaps/map-sources": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz",
|
||||
"integrity": "sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"normalize-path": "^2.0.1",
|
||||
"through2": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@gulp-sourcemaps/map-sources/node_modules/normalize-path": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
|
||||
"integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"remove-trailing-separator": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@gulp-sourcemaps/map-sources/node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@gulp-sourcemaps/map-sources/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@gulp-sourcemaps/map-sources/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@gulp-sourcemaps/map-sources/node_modules/through2": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
||||
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readable-stream": "~2.3.6",
|
||||
"xtend": "~4.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@gulpjs/messages": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@gulpjs/messages/-/messages-1.1.0.tgz",
|
||||
|
|
@ -894,6 +1021,18 @@
|
|||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/atob": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
||||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"bin": {
|
||||
"atob": "bin/atob.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/autocompleter": {
|
||||
"version": "9.3.2",
|
||||
"resolved": "https://registry.npmjs.org/autocompleter/-/autocompleter-9.3.2.tgz",
|
||||
|
|
@ -1482,6 +1621,12 @@
|
|||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
|
|
@ -1496,6 +1641,17 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/css": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz",
|
||||
"integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
"source-map": "^0.6.1",
|
||||
"source-map-resolve": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/css-declaration-sorter": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz",
|
||||
|
|
@ -1667,6 +1823,19 @@
|
|||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/d": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz",
|
||||
"integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"es5-ext": "^0.10.64",
|
||||
"type": "^2.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "2.30.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
|
||||
|
|
@ -1700,6 +1869,35 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/debug-fabulous": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz",
|
||||
"integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "3.X",
|
||||
"memoizee": "0.4.X",
|
||||
"object-assign": "4.X"
|
||||
}
|
||||
},
|
||||
"node_modules/debug-fabulous/node_modules/debug": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/decode-uri-component": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
||||
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
|
|
@ -1741,6 +1939,15 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-newline": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
|
||||
"integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
|
||||
|
|
@ -1905,6 +2112,58 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es5-ext": {
|
||||
"version": "0.10.64",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
|
||||
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"es6-iterator": "^2.0.3",
|
||||
"es6-symbol": "^3.1.3",
|
||||
"esniff": "^2.0.1",
|
||||
"next-tick": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-iterator": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"d": "1",
|
||||
"es5-ext": "^0.10.35",
|
||||
"es6-symbol": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-symbol": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz",
|
||||
"integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d": "^1.0.2",
|
||||
"ext": "^1.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-weak-map": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz",
|
||||
"integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d": "1",
|
||||
"es5-ext": "^0.10.46",
|
||||
"es6-iterator": "^2.0.3",
|
||||
"es6-symbol": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||
|
|
@ -2106,6 +2365,21 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/esniff": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
|
||||
"integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d": "^1.0.1",
|
||||
"es5-ext": "^0.10.62",
|
||||
"event-emitter": "^0.3.5",
|
||||
"type": "^2.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
|
||||
|
|
@ -2176,6 +2450,16 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/event-emitter": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
|
||||
"integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"d": "1",
|
||||
"es5-ext": "~0.10.14"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
|
||||
|
|
@ -2194,6 +2478,15 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ext": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
|
||||
"integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"type": "^2.7.2"
|
||||
}
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
|
|
@ -2820,6 +3113,86 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/gulp-sourcemaps": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz",
|
||||
"integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@gulp-sourcemaps/identity-map": "^2.0.1",
|
||||
"@gulp-sourcemaps/map-sources": "^1.0.0",
|
||||
"acorn": "^6.4.1",
|
||||
"convert-source-map": "^1.0.0",
|
||||
"css": "^3.0.0",
|
||||
"debug-fabulous": "^1.0.0",
|
||||
"detect-newline": "^2.0.0",
|
||||
"graceful-fs": "^4.0.0",
|
||||
"source-map": "^0.6.0",
|
||||
"strip-bom-string": "^1.0.0",
|
||||
"through2": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/gulp-sourcemaps/node_modules/acorn": {
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
|
||||
"integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/gulp-sourcemaps/node_modules/convert-source-map": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/gulp-sourcemaps/node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/gulp-sourcemaps/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/gulp-sourcemaps/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/gulp-sourcemaps/node_modules/through2": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
||||
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readable-stream": "~2.3.6",
|
||||
"xtend": "~4.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/gulplog": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gulplog/-/gulplog-2.2.0.tgz",
|
||||
|
|
@ -3248,6 +3621,12 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-promise": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
|
||||
"integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-reference": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
|
||||
|
|
@ -3333,6 +3712,12 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
|
|
@ -3647,6 +4032,15 @@
|
|||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz",
|
||||
"integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es5-ext": "~0.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.17",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
|
||||
|
|
@ -3692,6 +4086,25 @@
|
|||
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/memoizee": {
|
||||
"version": "0.4.17",
|
||||
"resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz",
|
||||
"integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d": "^1.0.2",
|
||||
"es5-ext": "^0.10.64",
|
||||
"es6-weak-map": "^2.0.3",
|
||||
"event-emitter": "^0.3.5",
|
||||
"is-promise": "^2.2.2",
|
||||
"lru-queue": "^0.1.0",
|
||||
"next-tick": "^1.1.0",
|
||||
"timers-ext": "^0.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
|
|
@ -3779,8 +4192,7 @@
|
|||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/mute-stdout": {
|
||||
"version": "2.0.0",
|
||||
|
|
@ -3846,6 +4258,12 @@
|
|||
"node": ">= 4.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/next-tick": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/node-gyp-build": {
|
||||
"version": "4.8.4",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
|
||||
|
|
@ -4827,6 +5245,12 @@
|
|||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/promise.series": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/promise.series/-/promise.series-0.2.0.tgz",
|
||||
|
|
@ -5376,7 +5800,6 @@
|
|||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -5390,6 +5813,17 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-resolve": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz",
|
||||
"integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==",
|
||||
"deprecated": "See https://github.com/lydell/source-map-resolve#deprecated",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"atob": "^2.1.2",
|
||||
"decode-uri-component": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sparkles": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sparkles/-/sparkles-2.1.0.tgz",
|
||||
|
|
@ -5515,6 +5949,15 @@
|
|||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-bom-string": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
|
||||
"integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/style-inject": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz",
|
||||
|
|
@ -5650,6 +6093,19 @@
|
|||
"readable-stream": "3"
|
||||
}
|
||||
},
|
||||
"node_modules/timers-ext": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz",
|
||||
"integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"es5-ext": "^0.10.64",
|
||||
"next-tick": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz",
|
||||
|
|
@ -5696,6 +6152,12 @@
|
|||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||
},
|
||||
"node_modules/type": {
|
||||
"version": "2.7.3",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz",
|
||||
"integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
|
|
@ -5989,6 +6451,15 @@
|
|||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
"autocompleter": "^9.3.2",
|
||||
"gulp": "^5.0.0",
|
||||
"gulp-less": "^5.0.0",
|
||||
"gulp-sourcemaps": "^3.0.0",
|
||||
"rollup": "^4.40.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
text-align: center;
|
||||
font-size: var(--font-size-16);
|
||||
margin: 0 4px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
color: light-dark(@dark, @beige);
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
width: 120px;
|
||||
|
|
@ -164,7 +164,7 @@
|
|||
|
||||
.hybrid-data {
|
||||
padding: 0 2px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
color: light-dark(@dark, @beige);
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
|
|
@ -191,7 +191,7 @@
|
|||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 0 4px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
color: light-dark(@dark, @beige);
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
|
|
@ -226,7 +226,7 @@
|
|||
gap: 4px;
|
||||
|
||||
.trait-card {
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
padding: 2px;
|
||||
opacity: 0.4;
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@
|
|||
font-weight: bold;
|
||||
padding: 0 2px;
|
||||
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
color: light-dark(@beige, @dark);
|
||||
opacity: 0.4;
|
||||
|
|
@ -114,9 +114,6 @@
|
|||
|
||||
.card-preview-container {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card-preview-container {
|
||||
border-color: light-dark(@dark-blue, @golden);
|
||||
}
|
||||
|
||||
|
|
@ -206,7 +203,7 @@
|
|||
height: 16px;
|
||||
width: 110px;
|
||||
min-height: unset;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
color: light-dark(@beige, @beige);
|
||||
background-color: light-dark(var(--color-warm-3), var(--color-warm-3));
|
||||
|
||||
|
|
@ -233,7 +230,7 @@
|
|||
.suggested-trait-container {
|
||||
width: 56px;
|
||||
white-space: nowrap;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
color: light-dark(@beige, @dark);
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
|
|
@ -348,7 +345,7 @@
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
|
||||
.nav-section-text {
|
||||
|
|
@ -386,7 +383,7 @@
|
|||
width: 56px;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
color: light-dark(@beige, @dark);
|
||||
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||
|
|
@ -450,7 +447,7 @@
|
|||
height: 100%;
|
||||
|
||||
.simple-equipment {
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
|
@ -469,7 +466,7 @@
|
|||
top: -8px;
|
||||
font-size: var(--font-size-12);
|
||||
white-space: nowrap;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
color: @dark;
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
border-top: 0;
|
||||
|
||||
a {
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.4;
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@
|
|||
|
||||
i {
|
||||
font-size: 18px;
|
||||
// color: light-dark(@dark-blue, @golden);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@
|
|||
|
||||
.mark-container {
|
||||
cursor: pointer;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
height: 26px;
|
||||
padding: 0 1px;
|
||||
|
|
@ -126,7 +126,7 @@
|
|||
width: 100%;
|
||||
|
||||
.chip-inner-container {
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
height: 26px;
|
||||
padding: 0 4px;
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
cursor: pointer;
|
||||
padding: 5px;
|
||||
background: light-dark(@dark-blue-10, @golden-10);
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
|
||||
.label {
|
||||
font-style: normal;
|
||||
|
|
@ -129,7 +129,7 @@
|
|||
cursor: pointer;
|
||||
padding: 5px;
|
||||
background: light-dark(@dark-blue-10, @golden-10);
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
|
||||
.label {
|
||||
font-style: normal;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
.activity-marker {
|
||||
font-size: 0.5rem;
|
||||
flex: none;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
|
||||
.activity-selected-marker {
|
||||
font-size: var(--font-size-14);
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
color: light-dark(@dark, @beige);
|
||||
background-image: url(../assets/parchments/dh-parchment-dark.png);
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
}
|
||||
|
||||
.refreshable-container {
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
color: light-dark(@dark, @beige);
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
h1 {
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
font-family: var(--dh-font-subtitle);
|
||||
font-size: var(--font-size-24);
|
||||
color: @color-text-emphatic;
|
||||
font: 700 var(--font-size-24) var(--dh-font-subtitle);
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
header {
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
button {
|
||||
--button-text-color: var(--color-text-primary);
|
||||
--button-text-color: @color-text-primary;
|
||||
--button-size: 1.5em;
|
||||
padding: 0 var(--spacer-4);
|
||||
img {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
img {
|
||||
width: 136px;
|
||||
height: 136px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
opacity: 0.4;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
@import './attribution/sheet.less';
|
||||
@import './level-up/navigation-container.less';
|
||||
@import './level-up/selections-container.less';
|
||||
@import './level-up/sheet.less';
|
||||
@import './level-up/summary-container.less';
|
||||
@import './level-up/tiers-container.less';
|
||||
@import './level-up/footer.less';
|
||||
@import './level-up/index.less';
|
||||
|
||||
@import './resource-dice/sheet.less';
|
||||
|
||||
|
|
@ -29,8 +24,6 @@
|
|||
|
||||
@import './multiclass-choice/sheet.less';
|
||||
|
||||
@import './reroll-dialog/sheet.less';
|
||||
|
||||
@import './tag-team-dialog/initialization.less';
|
||||
@import './tag-team-dialog/sheet.less';
|
||||
|
||||
|
|
|
|||
6
styles/less/dialog/level-up/index.less
Normal file
6
styles/less/dialog/level-up/index.less
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
@import './navigation-container.less';
|
||||
@import './selections-container.less';
|
||||
@import './summary-container.less';
|
||||
@import './tiers-container.less';
|
||||
@import './footer.less';
|
||||
@import './sheet.less';
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
a,
|
||||
span {
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,7 @@
|
|||
|
||||
.daggerheart.levelup {
|
||||
.levelup-selections-container {
|
||||
overflow: auto;
|
||||
padding: 10px 0;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
max-height: 500px;
|
||||
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
|
||||
|
||||
.achievement-experience-cards {
|
||||
display: flex;
|
||||
|
|
@ -45,20 +40,22 @@
|
|||
|
||||
.levelup-card-selection {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 40px;
|
||||
height: 190px;
|
||||
align-items: stretch;
|
||||
|
||||
.card-preview-container {
|
||||
height: 100%;
|
||||
height: 190px;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.levelup-domains-selection-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-template-rows: repeat(2, minmax(0, 1fr));
|
||||
height: 100%;
|
||||
gap: 4px;
|
||||
|
||||
.levelup-domain-selection-container {
|
||||
display: flex;
|
||||
|
|
@ -66,6 +63,8 @@
|
|||
align-items: center;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
width: 93px;
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
|
|
@ -74,16 +73,20 @@
|
|||
|
||||
.levelup-domain-label {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
text-align: center;
|
||||
top: 4px;
|
||||
background: grey;
|
||||
padding: 0 12px;
|
||||
border-radius: 6px;
|
||||
padding: 2px 12px;
|
||||
z-index: 2;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 124px;
|
||||
object-fit: cover;
|
||||
width: auto;
|
||||
height: auto;
|
||||
|
||||
&.svg {
|
||||
filter: @beige-filter;
|
||||
|
|
@ -92,17 +95,18 @@
|
|||
|
||||
.levelup-domain-selected {
|
||||
position: absolute;
|
||||
height: 54px;
|
||||
width: 54px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid;
|
||||
font-size: var(--font-size-48);
|
||||
border: 2px solid @golden;
|
||||
font-size: var(--font-size-24);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||
color: var(--color-dark-5);
|
||||
top: calc(50% - 29px);
|
||||
background: @dark-golden;
|
||||
color: @golden;
|
||||
top: 10px;
|
||||
z-index: 2;
|
||||
|
||||
i {
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@
|
|||
});
|
||||
|
||||
.daggerheart.levelup {
|
||||
.window-content {
|
||||
max-height: 960px;
|
||||
.tab.active {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
.with-scroll-shadows();
|
||||
}
|
||||
|
||||
div[data-application-part='form'] {
|
||||
|
|
@ -22,15 +23,13 @@
|
|||
gap: 8px;
|
||||
}
|
||||
|
||||
section {
|
||||
.section-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: 20px 8px;
|
||||
margin-top: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.section-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: 20px 8px;
|
||||
margin-top: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.levelup-footer {
|
||||
|
|
|
|||
|
|
@ -17,10 +17,8 @@
|
|||
.levelup-summary-container {
|
||||
overflow: auto;
|
||||
padding: 10px 0;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
max-height: 700px;
|
||||
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
|
||||
.with-scroll-shadows();
|
||||
|
||||
.level-achievements-container,
|
||||
.level-advancements-container {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
width: 120px;
|
||||
height: 120px;
|
||||
background: light-dark(@dark-blue-10, @golden-10);
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
|
||||
&.selected {
|
||||
background: light-dark(@dark-blue-40, @golden-40);
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
font-style: italic;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
padding: 4px 4px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,125 +0,0 @@
|
|||
.daggerheart.dialog.dh-style.views.reroll-dialog {
|
||||
.window-content {
|
||||
max-width: 648px;
|
||||
}
|
||||
|
||||
.reroll-outer-container {
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dices-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dice-outer-container {
|
||||
width: 300px;
|
||||
|
||||
legend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
i {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.dice-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||
|
||||
.result-container {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.375rem;
|
||||
opacity: 0.8;
|
||||
|
||||
&.selected {
|
||||
opacity: 1;
|
||||
border: 1px solid;
|
||||
border-radius: 6px;
|
||||
border-color: light-dark(@dark-blue, @golden);
|
||||
filter: drop-shadow(0 0 3px @golden);
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
mask: var(--svg-die) no-repeat center;
|
||||
mask-size: contain;
|
||||
background: linear-gradient(139.01deg, #efe6d8 3.51%, #372e1f 96.49%);
|
||||
}
|
||||
|
||||
&.d4:before {
|
||||
--svg-die: url(../assets/icons/dice/default/d4.svg);
|
||||
}
|
||||
&.d6:before {
|
||||
--svg-die: url(../assets/icons/dice/default/d6.svg);
|
||||
}
|
||||
&.d8:before {
|
||||
--svg-die: url(../assets/icons/dice/default/d8.svg);
|
||||
}
|
||||
&.d10:before {
|
||||
--svg-die: url(../assets/icons/dice/default/d10.svg);
|
||||
}
|
||||
&.d12:before {
|
||||
--svg-die: url('../assets/icons/dice/default/d12.svg');
|
||||
}
|
||||
&.d20:before {
|
||||
--svg-die: url(../assets/icons/dice/default/d20.svg);
|
||||
}
|
||||
|
||||
.to-reroll-result {
|
||||
position: absolute;
|
||||
bottom: -7px;
|
||||
gap: 2px;
|
||||
border: 1px solid;
|
||||
border-radius: 6px;
|
||||
background-image: url(../assets/parchments/dh-parchment-dark.png);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2px 6px;
|
||||
|
||||
input {
|
||||
margin: 0;
|
||||
height: 12px;
|
||||
line-height: 0px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
line-height: 12px;
|
||||
font-size: var(--font-size-12);
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: var(--font-size-10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
@import '../../utils/mixin.less';
|
||||
|
||||
.theme-light .daggerheart.dialog.dh-style.views.tag-team-dialog {
|
||||
.initialization-container .members-container .member-container {
|
||||
.member-name {
|
||||
|
|
@ -7,39 +9,75 @@
|
|||
}
|
||||
|
||||
.daggerheart.dialog.dh-style.views.tag-team-dialog {
|
||||
.initialization-container {
|
||||
.initialization-container.active {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacer-4);
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.members-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
|
||||
// Force 3 columns for 5 -> 6 players
|
||||
&:has(> :nth-child(5)):not(:has(> :nth-child(7))) {
|
||||
padding-left: 10%;
|
||||
padding-right: 10%;
|
||||
}
|
||||
|
||||
.member-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
border: 1px solid @color-border;
|
||||
overflow: hidden;
|
||||
height: 11.5rem;
|
||||
width: 122px;
|
||||
|
||||
&.inactive {
|
||||
opacity: 0.4;
|
||||
border-color: light-dark(@dark-blue-40, @golden-40);
|
||||
img {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
.member-name {
|
||||
--shadow-color: light-dark(white, black);
|
||||
position: absolute;
|
||||
padding: 0 2px;
|
||||
border: 1px solid;
|
||||
border-radius: 6px;
|
||||
margin-top: 4px;
|
||||
color: light-dark(@dark, @beige);
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
min-height: 4rem;
|
||||
padding: 5rem 4px var(--spacer-8) 4px;
|
||||
text-align: center;
|
||||
|
||||
color: @color-text-primary;
|
||||
text-shadow: 1px 1px 2px var(--shadow-color), 0 0 10px var(--shadow-color);
|
||||
.smooth-gradient-ease-in-out(background-image, to bottom, var(--shadow-color), 100%);
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 6px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
object-fit: cover;
|
||||
object-position: top center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.leader-mark {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
text-shadow: var(--shadow-text-stroke), 0 0 20px black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
.daggerheart.dialog.dh-style.views.tag-team-dialog {
|
||||
.daggerheart.dialog.dh-style.views.tag-team-dialog .window-content {
|
||||
h1 {
|
||||
color: @color-text-emphatic;
|
||||
font: 700 var(--font-size-24) var(--dh-font-subtitle);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.team-container {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
|
@ -36,7 +42,7 @@
|
|||
img {
|
||||
height: 64px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
}
|
||||
|
||||
.member-name {
|
||||
|
|
@ -58,7 +64,7 @@
|
|||
.roll-title {
|
||||
font-size: var(--font-size-20);
|
||||
font-weight: bold;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -66,7 +72,7 @@
|
|||
|
||||
&::before,
|
||||
&::after {
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 2px;
|
||||
|
|
@ -188,6 +194,7 @@
|
|||
|
||||
.roll-selection-container {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
.select-roll-button {
|
||||
margin-top: 8px;
|
||||
|
|
@ -196,7 +203,7 @@
|
|||
justify-content: center;
|
||||
|
||||
i {
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
font-size: 48px;
|
||||
|
||||
&.inactive {
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@
|
|||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
color: @color-text-emphatic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +81,7 @@
|
|||
cursor: pointer;
|
||||
padding: 5px;
|
||||
background: light-dark(@dark-blue-10, @golden-10);
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
|
||||
.label {
|
||||
font-style: normal;
|
||||
|
|
|
|||
|
|
@ -2,24 +2,22 @@
|
|||
@import '../utils/fonts.less';
|
||||
|
||||
.dh-style {
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
|
||||
input[type='text'],
|
||||
input[type='number'],
|
||||
textarea,
|
||||
file-picker,
|
||||
.input[contenteditable] {
|
||||
background: light-dark(transparent, transparent);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 30px @soft-shadow;
|
||||
backdrop-filter: blur(9.5px);
|
||||
-webkit-backdrop-filter: blur(9.5px);
|
||||
outline: 2px solid transparent;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
border: 1px solid light-dark(@dark, @beige);
|
||||
color: @input-color-text;
|
||||
border: 1px solid @input-color-border;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&::placeholder {
|
||||
color: light-dark(@dark-40, @beige-50);
|
||||
color: @color-text-subtle;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
|
|
@ -30,7 +28,7 @@
|
|||
&:focus[type='number'] {
|
||||
background: light-dark(@soft-shadow, @semi-transparent-dark-blue);
|
||||
box-shadow: none;
|
||||
outline: 2px solid light-dark(@dark, @beige);
|
||||
outline: 2px solid @input-color-border;
|
||||
}
|
||||
|
||||
&:disabled[type='text'],
|
||||
|
|
@ -47,7 +45,7 @@
|
|||
.input[contenteditable] {
|
||||
cursor: var(--cursor-text);
|
||||
&:empty:before {
|
||||
color: light-dark(@dark-40, @beige-50);
|
||||
color: @color-text-subtle;
|
||||
content: attr(placeholder);
|
||||
}
|
||||
}
|
||||
|
|
@ -96,11 +94,9 @@
|
|||
|
||||
textarea {
|
||||
color: light-dark(@dark, @beige);
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
}
|
||||
|
||||
button:where(:not(.plain)) {
|
||||
button:where(:not(.plain, color-picker *, file-picker *)) {
|
||||
background: light-dark(transparent, @golden);
|
||||
border: 1px solid light-dark(@dark-blue, @dark-blue);
|
||||
color: light-dark(@dark-blue, @dark-blue);
|
||||
|
|
@ -109,7 +105,7 @@
|
|||
|
||||
&:hover {
|
||||
background: light-dark(@light-black, @dark-blue);
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
}
|
||||
|
||||
&.glow {
|
||||
|
|
@ -130,7 +126,7 @@
|
|||
|
||||
&.reverted {
|
||||
background: light-dark(@dark-blue-10, @golden-10);
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
border: 1px solid light-dark(@dark, transparent);
|
||||
&:hover {
|
||||
background: light-dark(transparent, @golden);
|
||||
|
|
@ -177,7 +173,7 @@
|
|||
height: inherit;
|
||||
.tag {
|
||||
padding: 0.3rem 0.5rem;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
background-color: light-dark(@dark-blue-10, @golden-40);
|
||||
border-radius: 3px;
|
||||
transition: 0.13s ease-out;
|
||||
|
|
@ -254,11 +250,20 @@
|
|||
text-shadow: 0 0 1px currentColor, 0 0 1px currentColor, 0 0 8px light-dark(@dark-blue, @golden);
|
||||
}
|
||||
|
||||
file-picker, color-picker {
|
||||
> input[type=text] {
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
backdrop-filter: unset;
|
||||
}
|
||||
}
|
||||
|
||||
fieldset {
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
border-radius: 6px;
|
||||
border-color: light-dark(@dark-blue, @golden);
|
||||
border-color: @color-fieldset-border;
|
||||
|
||||
&.glassy {
|
||||
background-color: light-dark(@dark-blue-10, @golden-10);
|
||||
|
|
@ -267,7 +272,7 @@
|
|||
legend {
|
||||
padding: 2px 12px;
|
||||
border-radius: 3px;
|
||||
background-color: light-dark(@dark-blue, @golden);
|
||||
background-color: @color-fieldset-border;
|
||||
color: light-dark(@beige, @dark-blue);
|
||||
margin-bottom: var(--spacer-4);
|
||||
}
|
||||
|
|
@ -278,7 +283,7 @@
|
|||
}
|
||||
|
||||
&.fit-height {
|
||||
height: 95%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&.flex {
|
||||
|
|
@ -346,7 +351,7 @@
|
|||
|
||||
legend {
|
||||
font-weight: bold;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
|
||||
&.with-icon {
|
||||
display: flex;
|
||||
|
|
@ -358,18 +363,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
input[type='text'],
|
||||
input[type='number'] {
|
||||
color: light-dark(@dark, @beige);
|
||||
transition: all 0.3s ease;
|
||||
outline: 2px solid transparent;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
outline: 2px solid light-dark(@dark, @beige);
|
||||
}
|
||||
}
|
||||
|
||||
&[disabled],
|
||||
&.child-disabled .form-group,
|
||||
select[disabled],
|
||||
|
|
@ -507,7 +500,7 @@
|
|||
display: block;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-bottom: 1px solid @color-border;
|
||||
mask-image: linear-gradient(270deg, transparent 0%, black 50%, transparent 100%);
|
||||
}
|
||||
|
||||
|
|
@ -515,7 +508,7 @@
|
|||
display: block;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-bottom: 1px solid @color-border;
|
||||
mask-image: linear-gradient(270deg, transparent 0%, black 100%);
|
||||
|
||||
&.invert {
|
||||
|
|
@ -564,7 +557,7 @@
|
|||
border: 0;
|
||||
|
||||
&:hover {
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
|
|
@ -599,59 +592,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.application.setting.dh-style {
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
margin: 8px 0 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.25rem 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
label {
|
||||
font-size: var(--font-size-14);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.form-fields {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&.setting-two-values {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.25rem 0.5rem;
|
||||
|
||||
.form-group {
|
||||
justify-content: end;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.hint {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.system-daggerheart {
|
||||
.tagify {
|
||||
background: light-dark(transparent, transparent);
|
||||
|
|
@ -804,6 +744,7 @@
|
|||
|
||||
.preview-image-container {
|
||||
width: 100%;
|
||||
min-height: 0;
|
||||
flex-grow: 1;
|
||||
object-fit: cover;
|
||||
border-radius: 4px 4px 0 0;
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@
|
|||
.tab.features {
|
||||
padding: 0 10px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
.feature-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
legend {
|
||||
font-weight: bold;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
font-size: var(--font-size-12);
|
||||
}
|
||||
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
button {
|
||||
background: light-dark(@light-black, @dark-blue);
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
border: 1px solid light-dark(@dark-blue, @dark-blue);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@
|
|||
}
|
||||
|
||||
.daggerheart.dh-style {
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
}
|
||||
|
||||
.hint {
|
||||
flex: 0 0 100%;
|
||||
margin: 0;
|
||||
|
|
@ -33,7 +38,7 @@
|
|||
}
|
||||
|
||||
&:before {
|
||||
font-family: 'Font Awesome 6 Pro';
|
||||
font-family: var(--font-awesome);
|
||||
content: '\f110';
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@
|
|||
position: relative;
|
||||
height: 120px;
|
||||
width: 98px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@
|
|||
background-color: transparent;
|
||||
}
|
||||
.editor-content {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||
h1 {
|
||||
font-size: var(--font-size-32);
|
||||
}
|
||||
|
|
@ -42,6 +40,11 @@
|
|||
ul {
|
||||
list-style: disc;
|
||||
}
|
||||
}
|
||||
// Fixes centering and makes it not render over scrollbar
|
||||
&:hover button.toggle:enabled {
|
||||
display: flex;
|
||||
right: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,16 +50,16 @@
|
|||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
padding: 5px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
z-index: 1;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
color: @color-text-emphatic;
|
||||
width: fit-content;
|
||||
|
||||
.slot {
|
||||
width: 15px;
|
||||
height: 10px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
background: light-dark(@dark-blue-10, @golden-10);
|
||||
border-radius: 3px;
|
||||
transition: all 0.3s ease;
|
||||
|
|
@ -148,7 +148,7 @@
|
|||
appearance: none;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border: 1px solid @color-border;
|
||||
border-radius: 6px;
|
||||
z-index: 1;
|
||||
background: @dark-blue;
|
||||
|
|
|
|||
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