mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
Fix conflict
This commit is contained in:
commit
22fa89b395
102 changed files with 5478 additions and 2183 deletions
7
.github/workflows/deploy.yml
vendored
7
.github/workflows/deploy.yml
vendored
|
|
@ -16,6 +16,9 @@ jobs:
|
|||
- name: Build Packs
|
||||
run: npm run pullYMLtoLDB
|
||||
|
||||
- name: Build daggerheart.js
|
||||
run: npm run build
|
||||
|
||||
# get part of the tag after the `v`
|
||||
- name: Extract tag version number
|
||||
id: get_version
|
||||
|
|
@ -34,7 +37,7 @@ jobs:
|
|||
download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/system.zip
|
||||
|
||||
# Create a zip file with all files required by the module to add to the release
|
||||
- run: zip -r ./system.zip system.json README.md LICENSE daggerheart.mjs templates/ styles/daggerheart.css packs/ lang/
|
||||
- run: zip -r ./system.zip system.json README.md LICENSE build/daggerheart.js assets/ templates/ styles/daggerheart.css packs/ lang/
|
||||
|
||||
# Create a release for this specific version
|
||||
- name: Update Release with Files
|
||||
|
|
@ -46,6 +49,6 @@ jobs:
|
|||
draft: ${{ github.event.release.unpublished }}
|
||||
prerelease: ${{ github.event.release.prerelease }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
artifacts: './module.json, ./module.zip'
|
||||
artifacts: './system.json, ./system.zip'
|
||||
tag: ${{ github.event.release.tag_name }}
|
||||
body: ${{ github.event.release.body }}
|
||||
129
daggerheart.mjs
129
daggerheart.mjs
|
|
@ -4,18 +4,23 @@ import * as models from './module/data/_module.mjs';
|
|||
import * as documents from './module/documents/_module.mjs';
|
||||
import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs';
|
||||
import DhCombatTracker from './module/ui/combatTracker.mjs';
|
||||
import { GMUpdateEvent, handleSocketEvent, socketEvent } from './module/helpers/socket.mjs';
|
||||
import { handleSocketEvent, registerSocketHooks } from './module/helpers/socket.mjs';
|
||||
import { registerDHSettings } from './module/applications/settings.mjs';
|
||||
import DhpChatLog from './module/ui/chatLog.mjs';
|
||||
import DhpRuler from './module/ui/ruler.mjs';
|
||||
import DhpTokenRuler from './module/ui/tokenRuler.mjs';
|
||||
import { dualityRollEnricher } from './module/enrichers/DualityRollEnricher.mjs';
|
||||
import { DhDualityRollEnricher, DhTemplateEnricher } from './module/enrichers/_module.mjs';
|
||||
import { getCommandTarget, rollCommandToJSON, setDiceSoNiceForDualityRoll } from './module/helpers/utils.mjs';
|
||||
import { abilities } from './module/config/actorConfig.mjs';
|
||||
import Resources from './module/applications/resources.mjs';
|
||||
import { NarrativeCountdowns, registerCountdownApplicationHooks } from './module/applications/countdowns.mjs';
|
||||
import DHDualityRoll from './module/data/chat-message/dualityRoll.mjs';
|
||||
import { DualityRollColor } from './module/data/settings/Appearance.mjs';
|
||||
import { DHRoll, DualityRoll, D20Roll, DamageRoll, DualityDie } from './module/applications/roll.mjs'
|
||||
import { DhMeasuredTemplate } from './module/placeables/_module.mjs';
|
||||
import { renderDualityButton } from './module/enrichers/DualityRollEnricher.mjs';
|
||||
import { renderMeasuredTemplate } from './module/enrichers/TemplateEnricher.mjs';
|
||||
import { registerCountdownHooks } from './module/data/countdowns.mjs';
|
||||
|
||||
globalThis.SYSTEM = SYSTEM;
|
||||
|
||||
|
|
@ -28,10 +33,18 @@ Hooks.once('init', () => {
|
|||
documents
|
||||
};
|
||||
|
||||
CONFIG.TextEditor.enrichers.push({
|
||||
pattern: /\[\[\/dr\s?(.*?)\]\]/g,
|
||||
enricher: dualityRollEnricher
|
||||
});
|
||||
CONFIG.TextEditor.enrichers.push(
|
||||
...[
|
||||
{
|
||||
pattern: /\[\[\/dr\s?(.*?)\]\]/g,
|
||||
enricher: DhDualityRollEnricher
|
||||
},
|
||||
{
|
||||
pattern: /^@Template\[(.*)\]$/g,
|
||||
enricher: DhTemplateEnricher
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
CONFIG.statusEffects = Object.values(SYSTEM.GENERAL.conditions).map(x => ({
|
||||
...x,
|
||||
|
|
@ -47,6 +60,7 @@ Hooks.once('init', () => {
|
|||
};
|
||||
|
||||
CONFIG.Dice.rolls = [...CONFIG.Dice.rolls, ...[DHRoll, DualityRoll, D20Roll, DamageRoll]];
|
||||
CONFIG.MeasuredTemplate.objectClass = DhMeasuredTemplate;
|
||||
|
||||
CONFIG.Item.documentClass = documents.DhpItem;
|
||||
|
||||
|
|
@ -75,14 +89,19 @@ Hooks.once('init', () => {
|
|||
Actors.registerSheet(SYSTEM.id, applications.DhpEnvironment, { types: ['environment'], makeDefault: true });
|
||||
|
||||
CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect;
|
||||
DocumentSheetConfig.unregisterSheet(
|
||||
foundry.applications.apps.DocumentSheetConfig.unregisterSheet(
|
||||
CONFIG.ActiveEffect.documentClass,
|
||||
'core',
|
||||
foundry.applications.sheets.ActiveEffectConfig
|
||||
);
|
||||
DocumentSheetConfig.registerSheet(CONFIG.ActiveEffect.documentClass, SYSTEM.id, applications.DhActiveEffectConfig, {
|
||||
makeDefault: true
|
||||
});
|
||||
foundry.applications.apps.DocumentSheetConfig.registerSheet(
|
||||
CONFIG.ActiveEffect.documentClass,
|
||||
SYSTEM.id,
|
||||
applications.DhActiveEffectConfig,
|
||||
{
|
||||
makeDefault: true
|
||||
}
|
||||
);
|
||||
|
||||
CONFIG.Combat.dataModels = {
|
||||
base: models.DhCombat
|
||||
|
|
@ -117,78 +136,50 @@ Hooks.once('init', () => {
|
|||
|
||||
Hooks.on('ready', () => {
|
||||
ui.resources = new CONFIG.ui.resources();
|
||||
if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.DisplayFear) !== 'hide')
|
||||
if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).displayFear !== 'hide')
|
||||
ui.resources.render({ force: true });
|
||||
|
||||
document.body.classList.toggle(
|
||||
'theme-colorful',
|
||||
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).dualityColorScheme ===
|
||||
DualityRollColor.colorful.value
|
||||
);
|
||||
|
||||
registerCountdownHooks();
|
||||
registerSocketHooks();
|
||||
registerCountdownApplicationHooks();
|
||||
});
|
||||
|
||||
Hooks.once('dicesoniceready', () => {});
|
||||
|
||||
Hooks.on(socketEvent.GMUpdate, async (action, uuid, update) => {
|
||||
if (game.user.isGM) {
|
||||
const document = uuid ? await fromUuid(uuid) : null;
|
||||
switch (action) {
|
||||
case GMUpdateEvent.UpdateDocument:
|
||||
if (document && update) {
|
||||
await document.update(update);
|
||||
}
|
||||
break;
|
||||
case GMUpdateEvent.UpdateFear:
|
||||
if (game.user.isGM) {
|
||||
await game.settings.set(
|
||||
SYSTEM.id,
|
||||
SYSTEM.SETTINGS.gameSettings.Resources.Fear,
|
||||
Math.max(Math.min(update, 6), 0)
|
||||
);
|
||||
Hooks.callAll(socketEvent.DhpFearUpdate);
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, { action: socketEvent.DhpFearUpdate });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const renderDualityButton = async event => {
|
||||
const button = event.currentTarget,
|
||||
traitValue = button.dataset.trait?.toLowerCase(),
|
||||
target = getCommandTarget();
|
||||
if (!target) return;
|
||||
|
||||
const config = {
|
||||
event: event,
|
||||
title: button.dataset.title,
|
||||
roll: {
|
||||
modifier: traitValue ? target.system.traits[traitValue].value : null,
|
||||
label: button.dataset.label,
|
||||
type: button.dataset.actionType ?? null // Need check
|
||||
},
|
||||
chatMessage: {
|
||||
template: 'systems/daggerheart/templates/chat/duality-roll.hbs'
|
||||
}
|
||||
};
|
||||
await target.diceRoll(config);
|
||||
};
|
||||
|
||||
Hooks.on('renderChatMessageHTML', (_, element) => {
|
||||
element
|
||||
.querySelectorAll('.duality-roll-button')
|
||||
.forEach(element => element.addEventListener('click', renderDualityButton));
|
||||
|
||||
element
|
||||
.querySelectorAll('.measured-template-button')
|
||||
.forEach(element => element.addEventListener('click', renderMeasuredTemplate));
|
||||
});
|
||||
|
||||
Hooks.on('renderJournalEntryPageProseMirrorSheet', (_, element) => {
|
||||
element
|
||||
.querySelectorAll('.duality-roll-button')
|
||||
.forEach(element => element.addEventListener('click', renderDualityButton));
|
||||
|
||||
element
|
||||
.querySelectorAll('.measured-template-button')
|
||||
.forEach(element => element.addEventListener('click', renderMeasuredTemplate));
|
||||
});
|
||||
|
||||
Hooks.on('renderHandlebarsApplication', (_, element) => {
|
||||
element
|
||||
.querySelectorAll('.duality-roll-button')
|
||||
.forEach(element => element.addEventListener('click', renderDualityButton));
|
||||
|
||||
element
|
||||
.querySelectorAll('.measured-template-button')
|
||||
.forEach(element => element.addEventListener('click', renderMeasuredTemplate));
|
||||
});
|
||||
|
||||
Hooks.on('chatMessage', (_, message) => {
|
||||
|
|
@ -266,6 +257,29 @@ Hooks.on('chatMessage', (_, message) => {
|
|||
}
|
||||
});
|
||||
|
||||
Hooks.on('renderJournalDirectory', async (tab, html, _, options) => {
|
||||
if (tab.id === 'journal') {
|
||||
if (options.parts && !options.parts.includes('footer')) return;
|
||||
|
||||
const buttons = tab.element.querySelector('.directory-footer.action-buttons');
|
||||
const title = game.i18n.format('DAGGERHEART.Countdown.Title', {
|
||||
type: game.i18n.localize('DAGGERHEART.Countdown.Types.narrative')
|
||||
});
|
||||
buttons.insertAdjacentHTML(
|
||||
'afterbegin',
|
||||
`
|
||||
<button id="narrative-countdown-button">
|
||||
<i class="fa-solid fa-stopwatch"></i>
|
||||
<span style="font-weight: 400; font-family: var(--font-sans);">${title}</span>
|
||||
</button>`
|
||||
);
|
||||
|
||||
buttons.querySelector('#narrative-countdown-button').onclick = async () => {
|
||||
new NarrativeCountdowns().open();
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const preloadHandlebarsTemplates = async function () {
|
||||
return foundry.applications.handlebars.loadTemplates([
|
||||
'systems/daggerheart/templates/sheets/parts/attributes.hbs',
|
||||
|
|
@ -299,6 +313,7 @@ const preloadHandlebarsTemplates = async function () {
|
|||
'systems/daggerheart/templates/views/actionTypes/roll.hbs',
|
||||
'systems/daggerheart/templates/views/actionTypes/cost.hbs',
|
||||
'systems/daggerheart/templates/views/actionTypes/range-target.hbs',
|
||||
'systems/daggerheart/templates/views/actionTypes/effect.hbs'
|
||||
'systems/daggerheart/templates/views/actionTypes/effect.hbs',
|
||||
'systems/daggerheart/templates/settings/components/settings-item-line.hbs'
|
||||
]);
|
||||
};
|
||||
|
|
|
|||
220
lang/en.json
220
lang/en.json
|
|
@ -24,10 +24,7 @@
|
|||
"Automation": {
|
||||
"Name": "Automation Settings",
|
||||
"Label": "Configure Automation",
|
||||
"Hint": "Various settings automating resource management and more",
|
||||
"HopeLabel": "Hope",
|
||||
"FearLabel": "Fear",
|
||||
"ActionPointsLabel": "Action Points"
|
||||
"Hint": "Various settings automating resource management and more"
|
||||
},
|
||||
"Homebrew": {
|
||||
"Name": "Homebrew Settings",
|
||||
|
|
@ -38,13 +35,7 @@
|
|||
"Range": {
|
||||
"Name": "Range Settings",
|
||||
"Label": "Configure Range Handling",
|
||||
"Hint": "System ruler setup for displaying ranges in Daggerheart",
|
||||
"EnabledLabel": "Enabled",
|
||||
"MeleeLabel": "Melee",
|
||||
"VeryCloseLabel": "Very Close",
|
||||
"CloseLabel": "Close",
|
||||
"FarLabel": "Far",
|
||||
"VeryFarLabel": "Very Far"
|
||||
"Hint": "System ruler setup for displaying ranges in Daggerheart"
|
||||
},
|
||||
"Appearance": {
|
||||
"title": "Appearance Settings",
|
||||
|
|
@ -69,18 +60,45 @@
|
|||
"actionTokens": "Action Tokens"
|
||||
}
|
||||
},
|
||||
"Automation": {
|
||||
"Hope": {
|
||||
"Name": "Hope",
|
||||
"Hint": "Automatically increase a character's hope on a hope duality roll result."
|
||||
"Appearance": {
|
||||
"FIELDS": {
|
||||
"displayFear": { "label": "Fear Display" }
|
||||
},
|
||||
"FearDisplay": {
|
||||
"Token": "Tokens",
|
||||
"Bar": "Bar",
|
||||
"Hide": "Hide"
|
||||
}
|
||||
},
|
||||
"Automation": {
|
||||
"Fear": {
|
||||
"Name": "Fear",
|
||||
"Hint": "Automatically increase the GM's fear pool on a fear duality roll result."
|
||||
},
|
||||
"ActionPoints": {
|
||||
"Name": "Action Points",
|
||||
"Hint": "Automatically give and take Action Points as combatants take their turns."
|
||||
"FIELDS": {
|
||||
"hope": {
|
||||
"label": "Hope",
|
||||
"hint": "Automatically increase a character's hope on a hope duality roll result."
|
||||
},
|
||||
"actionPoints": {
|
||||
"label": "Action Points",
|
||||
"hint": "Automatically give and take Action Points as combatants take their turns."
|
||||
},
|
||||
"countdowns": {
|
||||
"label": "Countdowns",
|
||||
"hint": "Automatically progress non-custom countdowns"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Homebrew": {
|
||||
"NewDowntimeMove": "Downtime Move",
|
||||
"DowntimeMoves": "Downtime Moves",
|
||||
"NrChoices": "# Moves Per Rest",
|
||||
"ResetMovesTitle": "Reset {type} Downtime Moves",
|
||||
"ResetMovesText": "Are you sure you want to reset?",
|
||||
"FIELDS": {
|
||||
"maxFear": { "label": "Max Fear" },
|
||||
"traitArray": { "label": "Initial Trait Modifiers" }
|
||||
}
|
||||
},
|
||||
"Resources": {
|
||||
|
|
@ -113,6 +131,10 @@
|
|||
"hint": "Give each player action tokens to use in combat"
|
||||
},
|
||||
"FIELDS": {
|
||||
"actionTokens": {
|
||||
"enabled": { "label": "Enabled" },
|
||||
"tokens": { "label": "Tokens" }
|
||||
},
|
||||
"useCoins": {
|
||||
"label": "Use Coins",
|
||||
"hint": "test"
|
||||
|
|
@ -163,7 +185,9 @@
|
|||
"OK": "OK",
|
||||
"Cancel": "Cancel",
|
||||
"Or": "Or",
|
||||
"Enabled": "Enabled",
|
||||
"Description": "Description",
|
||||
"Modifier": "Modifier",
|
||||
"Features": "Features",
|
||||
"proficiency": "Proficiency",
|
||||
"unarmored": "Unarmored",
|
||||
|
|
@ -352,39 +376,39 @@
|
|||
}
|
||||
},
|
||||
"Domains": {
|
||||
"Arcana": {
|
||||
"arcana": {
|
||||
"label": "Arcana",
|
||||
"Description": "This is the domain of the innate or instinctual use of magic. Those who walk this path tap into the raw, enigmatic forces of the realms to manipulate both the elements and their own energy. Arcana offers wielders a volatile power, but it is incredibly potent when correctly channeled."
|
||||
},
|
||||
"Blade": {
|
||||
"blade": {
|
||||
"label": "Blade",
|
||||
"Description": "This is the domain of those who dedicate their lives to the mastery of weapons. Whether by blade, bow, or perhaps a more specialized arm, those who follow this path have the skill to cut short the lives of others. Blade requires study and dedication from its followers, in exchange for inexorable power over death."
|
||||
},
|
||||
"Bone": {
|
||||
"bone": {
|
||||
"label": "Bone",
|
||||
"Description": "This is the domain of mastery of swiftness and tactical mastery. Practitioners of this domain have an uncanny control over their own physical abilities, and an eye for predicting the behaviors of others in combat. Bone grants its adherents unparalleled understanding of bodies and their movements in exchange for diligent training."
|
||||
},
|
||||
"Codex": {
|
||||
"codex": {
|
||||
"label": "Codex",
|
||||
"Description": "This is the domain of intensive magical study. Those who seek magical knowledge turn to the recipes of power recorded in books, on scrolls, etched into walls, or tattooed on bodies. Codex offers a commanding and versatile understanding of magic to those devotees who are willing to seek beyond the common knowledge."
|
||||
},
|
||||
"Grace": {
|
||||
"grace": {
|
||||
"label": "Grace",
|
||||
"Description": "This is the domain of charisma. Through rapturous storytelling, clever charm, or a shroud of lies, those who channel this power define the realities of their adversaries, bending perception to their will. Grace offers its wielders raw magnetism and mastery over language."
|
||||
},
|
||||
"Midnight": {
|
||||
"midnight": {
|
||||
"label": "Midnight",
|
||||
"Description": "This is the domain of shadows and secrecy. Whether by clever tricks, or cloak of night those who channel these forces are practiced in that art of obscurity and there is nothing hidden they cannot reach. Midnight offers practitioners the incredible power to control and create enigmas."
|
||||
},
|
||||
"Sage": {
|
||||
"sage": {
|
||||
"label": "Sage",
|
||||
"Description": "This is the domain of the natural world. Those who walk this path tap into the unfettered power of the earth and its creatures to unleash raw magic. Sage grants its adherents the vitality of a blooming flower and ferocity of a hungry predator."
|
||||
},
|
||||
"Splendor": {
|
||||
"splendor": {
|
||||
"label": "Splendor",
|
||||
"Description": "This is the domain of life. Through this magic, followers gain the ability to heal, though such power also grants the wielder some control over death. Splendor offers its disciples the magnificent ability to both give and end life."
|
||||
},
|
||||
"Valor": {
|
||||
"valor": {
|
||||
"label": "Valor",
|
||||
"Description": "This is the domain of protection. Whether through attack or defense, those who choose this discipline channel formidable strength to protect their allies in battle. Valor offers great power to those who raise their shield in defense of others."
|
||||
}
|
||||
|
|
@ -402,6 +426,45 @@
|
|||
"requestingSpotlight": "Requesting The Spotlight",
|
||||
"combatStarted": "Active"
|
||||
},
|
||||
"CharacterCreation": {
|
||||
"Title": "{actor} - Character Setup",
|
||||
"TraitIncreases": "Trait Increases",
|
||||
"SuggestedTraits": "Suggested Traits",
|
||||
"InitialExperiences": "Initial Experiences",
|
||||
"Heritage": "Heritage",
|
||||
"SelectAncestry": "Select Ancestry",
|
||||
"SelectCommunity": "Select Community",
|
||||
"SelectClass": "Select Class",
|
||||
"SelectSubclass": "Select Subclass",
|
||||
"SelectArmor": "Select Armor",
|
||||
"SelectPrimaryWeapon": "Select Primary Weapon",
|
||||
"SelectSecondaryWeapon": "Select Secondary Weapon",
|
||||
"SuggestedArmor": "Suggested Armor",
|
||||
"SuggestedWeapons": "Suggested Weapon",
|
||||
"SuggestedPrimaryWeapon": "Suggested Primary Weapon",
|
||||
"SuggestedSecondaryWeapon": "Suggested Secondary Weapon",
|
||||
"StartingItems": "Starting Items",
|
||||
"Choice": "Choice",
|
||||
"NewExperience": "New Experience..",
|
||||
"FinishCreation": "Finish Character Setup",
|
||||
"Tabs": {
|
||||
"Optional": "Optional",
|
||||
"Setup": "Setup",
|
||||
"Equipment": "Equipment",
|
||||
"Story": "Story"
|
||||
},
|
||||
"Notifications": {
|
||||
"SubclassNotInClass": "This subclass does not belong to your selected class.",
|
||||
"MissingClass": "You don't have a class selected yet.",
|
||||
"WrongDomain": "The card isn't from one of your class domains.",
|
||||
"CardTooHighLevel": "The card is too high level!",
|
||||
"DuplicateCard": "You cannot select the same card more than once.",
|
||||
"NotPrimary": "The weapon is not a primary weapon!",
|
||||
"NotSecondary": "The weapon is not a secondary weapon!",
|
||||
"ItemTooHighTier": "The item must be from Tier1",
|
||||
"PrimaryIsTwoHanded": "Cannot select a secondary weapon with a two-handed primary!"
|
||||
}
|
||||
},
|
||||
"LevelUp": {
|
||||
"Options": {
|
||||
"trait": "Gain a +1 bonus to two unmarked character traits and mark them.",
|
||||
|
|
@ -445,29 +508,51 @@
|
|||
}
|
||||
},
|
||||
"Downtime": {
|
||||
"TendToWounds": {
|
||||
"Name": "Tend to Wounds",
|
||||
"Description": "Describe how you patch yourself up and remove all marked Hit Points. You may also do this on an ally instead."
|
||||
"DowntimeHeader": "Downtime Moves ({current}/{max})",
|
||||
"ShortRest": {
|
||||
"title": "Short Rest",
|
||||
"TendToWounds": {
|
||||
"Name": "Tend to Wounds",
|
||||
"Description": "Describe how you hastily patch yourself up, then clear a number of Hit Points equal to 1d4 + your tier. You can do this to an ally instead."
|
||||
},
|
||||
"ClearStress": {
|
||||
"Name": "Clear Stress",
|
||||
"Description": "Describe how you blow off steam or pull yourself together, then clear a number of Stress equal to 1d4 + your tier."
|
||||
},
|
||||
"RepairArmor": {
|
||||
"Name": "Repair Armor",
|
||||
"Description": "Describe how you quickly repair your armor, then clear a number of Armor Slots equal to 1d4 + your tier. You can do this to an ally's armor instead."
|
||||
},
|
||||
"Prepare": {
|
||||
"Name": "Prepare",
|
||||
"Description": "Describe how you prepare yourself for the path ahead, then gain a Hope. If you choose to Prepare with one or more members of your party, you each gain 2 Hope."
|
||||
}
|
||||
},
|
||||
"ClearStress": {
|
||||
"Name": "Clear Stress",
|
||||
"Description": "Describe how you blow off steam or pull yourself together, and clear all marked Stress."
|
||||
"LongRest": {
|
||||
"title": "Long Rest",
|
||||
"TendToWounds": {
|
||||
"Name": "Tend to Wounds",
|
||||
"Description": "Describe how you patch yourself up and remove all marked Hit Points. You may also do this on an ally instead."
|
||||
},
|
||||
"ClearStress": {
|
||||
"Name": "Clear Stress",
|
||||
"Description": "Describe how you blow off steam or pull yourself together, and clear all marked Stress."
|
||||
},
|
||||
"RepairArmor": {
|
||||
"Name": "Repair Armor",
|
||||
"Description": "Describe how you spend time repairing your armor and clear all of its Armor Slots. You may also do this to an ally's armor instead."
|
||||
},
|
||||
"Prepare": {
|
||||
"Name": "Prepare",
|
||||
"Description": "Describe how you are preparing for the next day's adventure, then gain a Hope. If you choose to Prepare with one or more members of your party, you may each take two Hope."
|
||||
},
|
||||
"WorkOnAProject": {
|
||||
"Name": "Work on a Project",
|
||||
"Description": "Establish or continue work on a project."
|
||||
}
|
||||
},
|
||||
"RepairArmor": {
|
||||
"Name": "Repair Armor",
|
||||
"Description": "Describe how you spend time repairing your armor and clear all of its Armor Slots. You may also do this to an ally’s armor instead."
|
||||
},
|
||||
"Prepare": {
|
||||
"Name": "Prepare",
|
||||
"Description": "Describe how you are preparing for the next day’s adventure, then gain a Hope. If you choose to Prepare with one or more members of your party, you may each take two Hope."
|
||||
},
|
||||
"WorkOnAProject": {
|
||||
"Name": "Work on a Project",
|
||||
"Description": "Establish or continue work on a project. The GM might ask for a roll to determine how much to tick down on the completion track."
|
||||
},
|
||||
"Custom": {
|
||||
"NamePlaceholder": "Custom Activity...",
|
||||
"Placeholder": "A custom downtime activity description..."
|
||||
"Notifications": {
|
||||
"NoMoreMoves": "You cannot select any more downtime moves"
|
||||
}
|
||||
},
|
||||
"DeathMoves": {
|
||||
|
|
@ -960,6 +1045,45 @@
|
|||
"Title": "Downtime"
|
||||
}
|
||||
},
|
||||
"Countdown": {
|
||||
"FIELDS": {
|
||||
"countdowns": {
|
||||
"element": {
|
||||
"name": { "label": "Name" },
|
||||
"progress": {
|
||||
"current": { "label": "Current" },
|
||||
"max": { "label": "Max" },
|
||||
"type": {
|
||||
"value": { "label": "Value" },
|
||||
"label": { "label": "Label", "hint": "Used for custom" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Type": {
|
||||
"Spotlight": "Spotlight",
|
||||
"Custom": "Custom",
|
||||
"CharacterAttack": "Character Attack"
|
||||
},
|
||||
"NewCountdown": "New Countdown",
|
||||
"AddCountdown": "Add Countdown",
|
||||
"RemoveCountdownTitle": "Remove Countdown",
|
||||
"RemoveCountdownText": "Are you sure you want to remove the countdown: {name}?",
|
||||
"OpenOwnership": "Edit Player Ownership",
|
||||
"Title": "{type} Countdowns",
|
||||
"Types": {
|
||||
"narrative": "Narrative",
|
||||
"encounter": "Encounter"
|
||||
},
|
||||
"Notifications": {
|
||||
"LimitedOwnershipMaximise": "You don't have permission to enter edit view"
|
||||
}
|
||||
},
|
||||
"OwnershipSelection": {
|
||||
"Title": "Ownership Selection - {name}",
|
||||
"Default": "Default Ownership"
|
||||
},
|
||||
"Sheets": {
|
||||
"PC": {
|
||||
"Name": "Name",
|
||||
|
|
|
|||
|
|
@ -13,5 +13,3 @@ export { default as DhpArmor } from './sheets/items/armor.mjs';
|
|||
export { default as DhpChatMessage } from './chatMessage.mjs';
|
||||
export { default as DhpEnvironment } from './sheets/environment.mjs';
|
||||
export { default as DhActiveEffectConfig } from './sheets/activeEffectConfig.mjs';
|
||||
|
||||
export * as pseudoDocumentSheet from './sheets/pseudo-documents/_module.mjs';
|
||||
|
|
|
|||
508
module/applications/characterCreation.mjs
Normal file
508
module/applications/characterCreation.mjs
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
import { abilities } from '../config/actorConfig.mjs';
|
||||
import { burden } from '../config/generalConfig.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhCharacterCreation extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(character) {
|
||||
super({});
|
||||
|
||||
this.character = character;
|
||||
|
||||
this.setup = {
|
||||
traits: this.character.system.traits,
|
||||
ancestry: this.character.system.ancestry ?? {},
|
||||
community: this.character.system.community ?? {},
|
||||
class: this.character.system.class?.value ?? {},
|
||||
subclass: this.character.system.class?.subclass ?? {},
|
||||
experiences: {
|
||||
[foundry.utils.randomID()]: { description: '', value: 2 },
|
||||
[foundry.utils.randomID()]: { description: '', value: 2 }
|
||||
},
|
||||
domainCards: {
|
||||
[foundry.utils.randomID()]: {},
|
||||
[foundry.utils.randomID()]: {}
|
||||
},
|
||||
visibility: 1
|
||||
};
|
||||
|
||||
this.equipment = {
|
||||
armor: {},
|
||||
primaryWeapon: {},
|
||||
secondaryWeapon: {},
|
||||
inventory: {
|
||||
take: {},
|
||||
choiceA: {},
|
||||
choiceB: {}
|
||||
}
|
||||
};
|
||||
|
||||
this._dragDrop = this._createDragDropHandlers();
|
||||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.format('DAGGERHEART.CharacterCreation.Title', { actor: this.character.name });
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'character-creation'],
|
||||
position: { width: 800, height: 'auto' },
|
||||
actions: {
|
||||
viewCompendium: this.viewCompendium,
|
||||
viewItem: this.viewItem,
|
||||
useSuggestedTraits: this.useSuggestedTraits,
|
||||
equipmentChoice: this.equipmentChoice,
|
||||
finish: this.finish
|
||||
},
|
||||
form: {
|
||||
handler: this.updateForm,
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false
|
||||
},
|
||||
dragDrop: [
|
||||
{ dragSelector: null, dropSelector: '.ancestry-card' },
|
||||
{ dragSelector: null, dropSelector: '.community-card' },
|
||||
{ dragSelector: null, dropSelector: '.class-card' },
|
||||
{ dragSelector: null, dropSelector: '.subclass-card' },
|
||||
{ dragSelector: null, dropSelector: '.domain-card' },
|
||||
{ dragSelector: null, dropSelector: '.armor-card' },
|
||||
{ dragSelector: null, dropSelector: '.primary-weapon-card' },
|
||||
{ dragSelector: null, dropSelector: '.secondary-weapon-card' },
|
||||
{ dragSelector: '.suggestion-inner-container', dropSelector: '.selections-container' }
|
||||
]
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
tabs: { template: 'systems/daggerheart/templates/views/characterCreation/tabs.hbs' },
|
||||
setup: { template: 'systems/daggerheart/templates/views/characterCreation/tabs/setup.hbs' },
|
||||
equipment: { template: 'systems/daggerheart/templates/views/characterCreation/tabs/equipment.hbs' },
|
||||
// story: { template: 'systems/daggerheart/templates/views/characterCreation/tabs/story.hbs' },
|
||||
footer: { template: 'systems/daggerheart/templates/views/characterCreation/footer.hbs' }
|
||||
};
|
||||
|
||||
static TABS = {
|
||||
setup: {
|
||||
active: true,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'setup',
|
||||
label: 'DAGGERHEART.CharacterCreation.Tabs.Setup'
|
||||
},
|
||||
equipment: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'equipment',
|
||||
label: 'DAGGERHEART.CharacterCreation.Tabs.Equipment',
|
||||
optional: true
|
||||
}
|
||||
// story: {
|
||||
// active: false,
|
||||
// cssClass: '',
|
||||
// group: 'primary',
|
||||
// id: 'story',
|
||||
// label: 'DAGGERHEART.CharacterCreation.Tabs.Story',
|
||||
// optional: true
|
||||
// }
|
||||
};
|
||||
|
||||
_getTabs(tabs) {
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active;
|
||||
v.cssClass = v.active ? 'active' : '';
|
||||
|
||||
switch (v.id) {
|
||||
case 'setup':
|
||||
const classFinished = this.setup.class.uuid && this.setup.subclass.uuid;
|
||||
const heritageFinished = this.setup.ancestry.uuid && this.setup.community.uuid;
|
||||
const traitsFinished = Object.values(this.setup.traits).every(x => x.value !== null);
|
||||
const experiencesFinished = Object.values(this.setup.experiences).every(x => x.description);
|
||||
const domainCardsFinished = Object.values(this.setup.domainCards).every(x => x.uuid);
|
||||
v.finished =
|
||||
classFinished &&
|
||||
heritageFinished &&
|
||||
traitsFinished &&
|
||||
experiencesFinished &&
|
||||
domainCardsFinished;
|
||||
break;
|
||||
case 'equipment':
|
||||
const armorFinished = this.equipment.armor?.uuid;
|
||||
const primaryFinished = this.equipment.primaryWeapon?.uuid;
|
||||
const secondaryFinished =
|
||||
this.equipment.secondaryWeapon?.uuid ||
|
||||
(primaryFinished && this.equipment.primaryWeapon.system.burden == burden.twoHanded.value);
|
||||
const choiceAFinished = this.equipment.inventory.choiceA?.uuid;
|
||||
const choiceBFinished = this.equipment.inventory.choiceB?.uuid;
|
||||
|
||||
v.finished =
|
||||
armorFinished && primaryFinished && secondaryFinished && choiceAFinished && choiceBFinished;
|
||||
}
|
||||
}
|
||||
|
||||
tabs.equipment.cssClass = tabs.setup.finished ? tabs.equipment.cssClass : 'disabled';
|
||||
// tabs.story.cssClass = tabs.setup.finished ? tabs.story.cssClass : 'disabled';
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
changeTab(tab, group, options) {
|
||||
super.changeTab(tab, group, options);
|
||||
|
||||
for (var listTab of Object.keys(this.constructor.TABS)) {
|
||||
const marker = options.navElement.querySelector(`a[data-action="tab"].${listTab} .finish-marker`);
|
||||
if (listTab === tab) {
|
||||
marker.classList.add('active');
|
||||
} else {
|
||||
marker.classList.remove('active');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
this._dragDrop.forEach(d => d.bind(htmlElement));
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.tabs = this._getTabs(this.constructor.TABS);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
async _preparePartContext(partId, context) {
|
||||
switch (partId) {
|
||||
case 'setup':
|
||||
const availableTraitModifiers = game.settings
|
||||
.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew)
|
||||
.traitArray.map(trait => ({ key: trait, name: trait }));
|
||||
for (let trait of Object.values(this.setup.traits).filter(x => x.value !== null)) {
|
||||
const index = availableTraitModifiers.findIndex(x => x.key === trait.value);
|
||||
if (index !== -1) {
|
||||
availableTraitModifiers.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
context.suggestedTraits = this.setup.class.system
|
||||
? Object.keys(this.setup.class.system.characterGuide.suggestedTraits).map(traitKey => {
|
||||
const trait = this.setup.class.system.characterGuide.suggestedTraits[traitKey];
|
||||
return `${game.i18n.localize(`DAGGERHEART.Abilities.${traitKey}.short`)} ${trait > 0 ? `+${trait}` : trait}`;
|
||||
})
|
||||
: [];
|
||||
context.traits = {
|
||||
values: Object.keys(this.setup.traits).map(traitKey => {
|
||||
const trait = this.setup.traits[traitKey];
|
||||
const options = [...availableTraitModifiers];
|
||||
if (trait.value !== null && !options.some(x => x.key === trait.value))
|
||||
options.push({ key: trait.value, name: trait.value });
|
||||
|
||||
return {
|
||||
...trait,
|
||||
key: traitKey,
|
||||
name: game.i18n.localize(abilities[traitKey].label),
|
||||
options: options
|
||||
};
|
||||
})
|
||||
};
|
||||
context.traits.nrTotal = Object.keys(context.traits.values).length;
|
||||
context.traits.nrSelected = Object.values(context.traits.values).reduce(
|
||||
(acc, trait) => acc + (trait.value !== null ? 1 : 0),
|
||||
0
|
||||
);
|
||||
|
||||
context.experience = {
|
||||
values: this.setup.experiences,
|
||||
nrTotal: Object.keys(this.setup.experiences).length,
|
||||
nrSelected: Object.values(this.setup.experiences).reduce(
|
||||
(acc, exp) => acc + (exp.description ? 1 : 0),
|
||||
0
|
||||
)
|
||||
};
|
||||
|
||||
context.ancestry = { ...this.setup.ancestry, compendium: 'ancestries' };
|
||||
context.community = { ...this.setup.community, compendium: 'communities' };
|
||||
context.class = { ...this.setup.class, compendium: 'classes' };
|
||||
context.subclass = { ...this.setup.subclass, compendium: 'subclasses' };
|
||||
context.domainCards = Object.keys(this.setup.domainCards).reduce((acc, x) => {
|
||||
acc[x] = { ...this.setup.domainCards[x], compendium: 'domains' };
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
context.visibility = this.setup.visibility;
|
||||
break;
|
||||
case 'equipment':
|
||||
const suggestions = await this.getEquipmentSuggestions(
|
||||
this.equipment.inventory.choiceA,
|
||||
this.equipment.inventory.choiceB
|
||||
);
|
||||
context.armor = {
|
||||
...this.equipment.armor,
|
||||
suggestion: { ...suggestions.armor, taken: suggestions.armor?.uuid === this.equipment.armor?.uuid },
|
||||
compendium: 'armors'
|
||||
};
|
||||
context.primaryWeapon = {
|
||||
...this.equipment.primaryWeapon,
|
||||
suggestion: {
|
||||
...suggestions.primaryWeapon,
|
||||
taken: suggestions.primaryWeapon?.uuid === this.equipment.primaryWeapon?.uuid
|
||||
},
|
||||
compendium: 'weapons'
|
||||
};
|
||||
context.secondaryWeapon = {
|
||||
...this.equipment.secondaryWeapon,
|
||||
suggestion: {
|
||||
...suggestions.secondaryWeapon,
|
||||
taken: suggestions.secondaryWeapon?.uuid === this.equipment.secondaryWeapon?.uuid
|
||||
},
|
||||
disabled: this.equipment.primaryWeapon?.system?.burden === burden.twoHanded.value,
|
||||
compendium: 'weapons'
|
||||
};
|
||||
context.inventory = {
|
||||
take: suggestions.inventory.take,
|
||||
choiceA: { suggestions: suggestions.inventory.choiceA, compendium: 'consumables' },
|
||||
choiceB: { suggestions: suggestions.inventory.choiceB, compendium: 'general-items' }
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateForm(event, _, formData) {
|
||||
this.setup = foundry.utils.mergeObject(this.setup, formData.object);
|
||||
|
||||
this.setup.visibility = this.getUpdateVisibility();
|
||||
this.render();
|
||||
}
|
||||
|
||||
getUpdateVisibility() {
|
||||
switch (this.setup.visibility) {
|
||||
case 5:
|
||||
return 5;
|
||||
case 4:
|
||||
return Object.values(this.setup.experiences).every(x => x.description) ? 5 : 4;
|
||||
case 3:
|
||||
return Object.values(this.setup.traits).every(x => x.value !== null) ? 4 : 3;
|
||||
case 2:
|
||||
return this.setup.ancestry.uuid && this.setup.community.uuid ? 3 : 2;
|
||||
case 1:
|
||||
return this.setup.class.uuid && this.setup.subclass.uuid ? 2 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
async getEquipmentSuggestions(choiceA, choiceB) {
|
||||
if (!this.setup.class.uuid) return { inventory: { take: [] } };
|
||||
|
||||
const { inventory, characterGuide } = this.setup.class.system;
|
||||
return {
|
||||
armor: characterGuide.suggestedArmor ?? null,
|
||||
primaryWeapon: characterGuide.suggestedPrimaryWeapon ?? null,
|
||||
secondaryWeapon: characterGuide.suggestedSecondaryWeapon
|
||||
? { ...characterGuide.suggestedSecondaryWeapon, uuid: characterGuide.suggestedSecondaryWeapon.uuid }
|
||||
: null,
|
||||
inventory: {
|
||||
take: inventory.take ?? [],
|
||||
choiceA:
|
||||
inventory.choiceA?.map(x => ({ ...x, uuid: x.uuid, selected: x.uuid === choiceA?.uuid })) ?? [],
|
||||
choiceB: inventory.choiceB?.map(x => ({ ...x, uuid: x.uuid, selected: x.uuid === choiceB?.uuid })) ?? []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_createDragDropHandlers() {
|
||||
return this.options.dragDrop.map(d => {
|
||||
d.callbacks = {
|
||||
dragstart: this._onDragStart.bind(this),
|
||||
drop: this._onDrop.bind(this)
|
||||
};
|
||||
return new foundry.applications.ux.DragDrop.implementation(d);
|
||||
});
|
||||
}
|
||||
|
||||
static async viewCompendium(_, target) {
|
||||
(await game.packs.get(`daggerheart.${target.dataset.compendium}`))?.render(true);
|
||||
}
|
||||
|
||||
static async viewItem(_, target) {
|
||||
(await foundry.utils.fromUuid(target.dataset.uuid)).sheet.render(true);
|
||||
}
|
||||
|
||||
static useSuggestedTraits() {
|
||||
this.setup.traits = Object.keys(this.setup.traits).reduce((acc, traitKey) => {
|
||||
acc[traitKey] = {
|
||||
...this.setup.traits[traitKey],
|
||||
value: this.setup.class.system.characterGuide.suggestedTraits[traitKey]
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
this.setup.visibility = this.getUpdateVisibility();
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async equipmentChoice(_, target) {
|
||||
this.equipment.inventory[target.dataset.path] = await foundry.utils.fromUuid(target.dataset.uuid);
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async finish() {
|
||||
const embeddedAncestries = await this.character.createEmbeddedDocuments('Item', [this.setup.ancestry]);
|
||||
const embeddedCommunities = await this.character.createEmbeddedDocuments('Item', [this.setup.community]);
|
||||
await this.character.createEmbeddedDocuments('Item', [this.setup.class]);
|
||||
await this.character.createEmbeddedDocuments('Item', [this.setup.subclass]);
|
||||
await this.character.createEmbeddedDocuments('Item', Object.values(this.setup.domainCards));
|
||||
|
||||
if (this.equipment.armor.uuid)
|
||||
await this.character.createEmbeddedDocuments('Item', [
|
||||
{ ...this.equipment.armor, system: { ...this.equipment.armor.system, equipped: true } }
|
||||
]);
|
||||
if (this.equipment.primaryWeapon.uuid)
|
||||
await this.character.createEmbeddedDocuments('Item', [
|
||||
{ ...this.equipment.primaryWeapon, system: { ...this.equipment.primaryWeapon.system, equipped: true } }
|
||||
]);
|
||||
if (this.equipment.secondaryWeapon.uuid)
|
||||
await this.character.createEmbeddedDocuments('Item', [
|
||||
{
|
||||
...this.equipment.secondaryWeapon,
|
||||
system: { ...this.equipment.secondaryWeapon.system, equipped: true }
|
||||
}
|
||||
]);
|
||||
if (this.equipment.inventory.choiceA.uuid)
|
||||
await this.character.createEmbeddedDocuments('Item', [this.equipment.inventory.choiceA]);
|
||||
if (this.equipment.inventory.choiceB.uuid)
|
||||
await this.character.createEmbeddedDocuments('Item', [this.equipment.inventory.choiceB]);
|
||||
await this.character.createEmbeddedDocuments('Item', this.setup.class.system.inventory.take);
|
||||
|
||||
await this.character.update({
|
||||
system: {
|
||||
traits: this.setup.traits,
|
||||
experiences: this.setup.experiences,
|
||||
ancestry: embeddedAncestries[0].uuid,
|
||||
community: embeddedCommunities[0].uuid
|
||||
}
|
||||
});
|
||||
|
||||
this.close();
|
||||
}
|
||||
|
||||
async _onDragStart(event) {
|
||||
const target = event.currentTarget;
|
||||
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(target.dataset));
|
||||
event.dataTransfer.setDragImage(target, 60, 0);
|
||||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
const data = TextEditor.getDragEventData(event);
|
||||
const item = await foundry.utils.fromUuid(data.uuid);
|
||||
if (item.type === 'ancestry' && event.target.closest('.ancestry-card')) {
|
||||
this.setup.ancestry = {
|
||||
...item,
|
||||
effects: Array.from(item.effects).map(x => x.toObject()),
|
||||
uuid: item.uuid
|
||||
};
|
||||
} else if (item.type === 'community' && event.target.closest('.community-card')) {
|
||||
this.setup.community = {
|
||||
...item,
|
||||
effects: Array.from(item.effects).map(x => x.toObject()),
|
||||
uuid: item.uuid
|
||||
};
|
||||
} else if (item.type === 'class' && event.target.closest('.class-card')) {
|
||||
this.setup.class = { ...item, effects: Array.from(item.effects).map(x => x.toObject()), uuid: item.uuid };
|
||||
this.setup.subclass = {};
|
||||
this.setup.domainCards = {
|
||||
[foundry.utils.randomID()]: {},
|
||||
[foundry.utils.randomID()]: {}
|
||||
};
|
||||
} else if (item.type === 'subclass' && event.target.closest('.subclass-card')) {
|
||||
if (this.setup.class.system.subclasses.every(subclass => subclass.uuid !== item.uuid)) {
|
||||
ui.notifications.error(
|
||||
game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.SubclassNotInClass')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setup.subclass = {
|
||||
...item,
|
||||
effects: Array.from(item.effects).map(x => x.toObject()),
|
||||
uuid: item.uuid
|
||||
};
|
||||
} else if (item.type === 'domainCard' && event.target.closest('.domain-card')) {
|
||||
if (!this.setup.class.uuid) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.MissingClass'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.setup.class.system.domains.includes(item.system.domain)) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.WrongDomain'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.system.level > 1) {
|
||||
ui.notifications.error(
|
||||
game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.CardTooHighLevel')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Object.values(this.setup.domainCards).some(card => card.uuid === item.uuid)) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.DuplicateCard'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.setup.domainCards[event.target.closest('.domain-card').dataset.card] = { ...item, uuid: item.uuid };
|
||||
} else if (item.type === 'armor' && event.target.closest('.armor-card')) {
|
||||
if (item.system.tier > 1) {
|
||||
ui.notifications.error(
|
||||
game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.ItemTooHighTier')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.equipment.armor = { ...item, uuid: item.uuid };
|
||||
} else if (item.type === 'weapon' && event.target.closest('.primary-weapon-card')) {
|
||||
if (item.system.secondary) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.NotPrimary'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.system.tier > 1) {
|
||||
ui.notifications.error(
|
||||
game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.ItemTooHighTier')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.equipment.primaryWeapon = { ...item, uuid: item.uuid };
|
||||
} else if (item.type === 'weapon' && event.target.closest('.secondary-weapon-card')) {
|
||||
if (this.equipment.primaryWeapon?.system?.burden === burden.twoHanded.value) {
|
||||
ui.notifications.error(
|
||||
game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.PrimaryIsTwoHanded')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item.system.secondary) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.NotSecondary'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.system.tier > 1) {
|
||||
ui.notifications.error(
|
||||
game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.ItemTooHighTier')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.equipment.secondaryWeapon = { ...item, uuid: item.uuid };
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setup.visibility = this.getUpdateVisibility();
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,8 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
|
||||
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
||||
const html = await super.renderHTML();
|
||||
console.log(this.system)
|
||||
if (
|
||||
this.type === 'dualityRoll'
|
||||
) {
|
||||
|
||||
if (this.type === 'dualityRoll') {
|
||||
html.classList.add('duality');
|
||||
/* const dualityResult = this.system.dualityResult; */
|
||||
switch (this.system.roll.result.duality) {
|
||||
|
|
|
|||
339
module/applications/countdowns.mjs
Normal file
339
module/applications/countdowns.mjs
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
import { countdownTypes } from '../config/generalConfig.mjs';
|
||||
import { GMUpdateEvent, RefreshType, socketEvent } from '../helpers/socket.mjs';
|
||||
import OwnershipSelection from './ownershipSelection.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(basePath) {
|
||||
super({});
|
||||
|
||||
this.basePath = basePath;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.format('DAGGERHEART.Countdown.Title', {
|
||||
type: game.i18n.localize(`DAGGERHEART.Countdown.Types.${this.basePath}`)
|
||||
});
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['daggerheart', 'dh-style', 'countdown'],
|
||||
tag: 'form',
|
||||
position: { width: 740, height: 700 },
|
||||
window: {
|
||||
frame: true,
|
||||
title: 'Countdowns',
|
||||
resizable: true,
|
||||
minimizable: true
|
||||
},
|
||||
actions: {
|
||||
addCountdown: this.addCountdown,
|
||||
removeCountdown: this.removeCountdown,
|
||||
editImage: this.onEditImage,
|
||||
openOwnership: this.openOwnership,
|
||||
openCountdownOwnership: this.openCountdownOwnership
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
countdowns: {
|
||||
template: 'systems/daggerheart/templates/views/countdowns.hbs',
|
||||
scrollable: ['.expanded-view']
|
||||
}
|
||||
};
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelectorAll('.mini-countdown-container').forEach(element => {
|
||||
element.addEventListener('click', event => this.updateCountdownValue.bind(this)(event, true));
|
||||
element.addEventListener('contextmenu', event => this.updateCountdownValue.bind(this)(event, false));
|
||||
});
|
||||
}
|
||||
|
||||
async _onFirstRender(context, options) {
|
||||
super._onFirstRender(context, options);
|
||||
|
||||
this.element.querySelector('.expanded-view').classList.toggle('hidden');
|
||||
this.element.querySelector('.minimized-view').classList.toggle('hidden');
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
const countdownData = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||
|
||||
context.isGM = game.user.isGM;
|
||||
context.base = this.basePath;
|
||||
|
||||
context.canCreate = countdownData.playerOwnership[game.user.id].value === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
|
||||
context.source = {
|
||||
...countdownData,
|
||||
countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => {
|
||||
const countdown = countdownData.countdowns[key];
|
||||
|
||||
const ownershipValue = countdown.playerOwnership[game.user.id].value;
|
||||
if (ownershipValue > CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) {
|
||||
acc[key] = { ...countdown, canEdit: ownershipValue === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER };
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
context.systemFields = countdownData.schema.fields;
|
||||
context.countdownFields = context.systemFields.countdowns.element.fields;
|
||||
context.minimized = this.minimized || _options.isFirstRender;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateData(event, _, formData) {
|
||||
const data = foundry.utils.expandObject(formData.object);
|
||||
const newSetting = foundry.utils.mergeObject(
|
||||
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns).toObject(),
|
||||
data
|
||||
);
|
||||
|
||||
if (game.user.isGM) {
|
||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, newSetting);
|
||||
this.render();
|
||||
} else {
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: {
|
||||
action: GMUpdateEvent.UpdateSetting,
|
||||
uuid: SYSTEM.SETTINGS.gameSettings.Countdowns,
|
||||
update: newSetting
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async minimize() {
|
||||
await super.minimize();
|
||||
|
||||
this.element.querySelector('.expanded-view').classList.toggle('hidden');
|
||||
this.element.querySelector('.minimized-view').classList.toggle('hidden');
|
||||
}
|
||||
|
||||
async maximize() {
|
||||
if (this.minimized) {
|
||||
const settings = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||
if (settings.playerOwnership[game.user.id].value <= CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED) {
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.Countdown.Notifications.LimitedOwnership'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.element.querySelector('.expanded-view').classList.toggle('hidden');
|
||||
this.element.querySelector('.minimized-view').classList.toggle('hidden');
|
||||
}
|
||||
|
||||
await super.maximize();
|
||||
}
|
||||
|
||||
async updateSetting(update) {
|
||||
if (game.user.isGM) {
|
||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, update);
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
action: socketEvent.Refresh,
|
||||
data: {
|
||||
refreshType: RefreshType.Countdown,
|
||||
application: `${this.basePath}-countdowns`
|
||||
}
|
||||
});
|
||||
|
||||
this.render();
|
||||
} else {
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: {
|
||||
action: GMUpdateEvent.UpdateSetting,
|
||||
uuid: SYSTEM.SETTINGS.gameSettings.Countdowns,
|
||||
update: update,
|
||||
refresh: { refreshType: RefreshType.Countdown, application: `${this.basePath}-countdowns` }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static onEditImage(_, target) {
|
||||
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||
const current = setting.countdowns[target.dataset.countdown].img;
|
||||
const fp = new FilePicker({
|
||||
current,
|
||||
type: 'image',
|
||||
callback: async path => this.updateImage.bind(this)(path, target.dataset.countdown),
|
||||
top: this.position.top + 40,
|
||||
left: this.position.left + 10
|
||||
});
|
||||
return fp.browse();
|
||||
}
|
||||
|
||||
async updateImage(path, countdown) {
|
||||
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||
await setting.updateSource({
|
||||
[`${this.basePath}.countdowns.${countdown}.img`]: path
|
||||
});
|
||||
|
||||
await this.updateSetting(setting);
|
||||
}
|
||||
|
||||
static openOwnership(_, target) {
|
||||
new Promise((resolve, reject) => {
|
||||
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
|
||||
const ownership = { default: setting.ownership.default, players: setting.playerOwnership };
|
||||
new OwnershipSelection(resolve, reject, this.title, ownership).render(true);
|
||||
}).then(async ownership => {
|
||||
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||
await setting.updateSource({
|
||||
[`${this.basePath}.ownership`]: ownership
|
||||
});
|
||||
|
||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, setting.toObject());
|
||||
this.render();
|
||||
});
|
||||
}
|
||||
|
||||
static openCountdownOwnership(_, target) {
|
||||
const countdownId = target.dataset.countdown;
|
||||
new Promise((resolve, reject) => {
|
||||
const countdown = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath]
|
||||
.countdowns[countdownId];
|
||||
const ownership = { default: countdown.ownership.default, players: countdown.playerOwnership };
|
||||
new OwnershipSelection(resolve, reject, countdown.name, ownership).render(true);
|
||||
}).then(async ownership => {
|
||||
const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||
await setting.updateSource({
|
||||
[`${this.basePath}.countdowns.${countdownId}.ownership`]: ownership
|
||||
});
|
||||
|
||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, setting);
|
||||
this.render();
|
||||
});
|
||||
}
|
||||
|
||||
async updateCountdownValue(event, increase) {
|
||||
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||
const countdown = countdownSetting[this.basePath].countdowns[event.currentTarget.dataset.countdown];
|
||||
|
||||
if (countdown.playerOwnership[game.user.id] < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentValue = countdown.progress.current;
|
||||
|
||||
if (increase && currentValue === countdown.progress.max) return;
|
||||
if (!increase && currentValue === 0) return;
|
||||
|
||||
await countdownSetting.updateSource({
|
||||
[`${this.basePath}.countdowns.${event.currentTarget.dataset.countdown}.progress.current`]: increase
|
||||
? currentValue + 1
|
||||
: currentValue - 1
|
||||
});
|
||||
|
||||
await this.updateSetting(countdownSetting.toObject());
|
||||
}
|
||||
|
||||
static async addCountdown() {
|
||||
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||
await countdownSetting.updateSource({
|
||||
[`${this.basePath}.countdowns.${foundry.utils.randomID()}`]: {
|
||||
name: game.i18n.localize('DAGGERHEART.Countdown.NewCountdown'),
|
||||
ownership: game.user.isGM
|
||||
? {}
|
||||
: {
|
||||
players: {
|
||||
[game.user.id]: { type: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await this.updateSetting(countdownSetting.toObject());
|
||||
}
|
||||
|
||||
static async removeCountdown(_, target) {
|
||||
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||
const countdownName = countdownSetting[this.basePath].countdowns[target.dataset.countdown].name;
|
||||
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: game.i18n.localize('DAGGERHEART.Countdown.RemoveCountdownTitle')
|
||||
},
|
||||
content: game.i18n.format('DAGGERHEART.Countdown.RemoveCountdownText', { name: countdownName })
|
||||
});
|
||||
if (!confirmed) return;
|
||||
|
||||
await countdownSetting.updateSource({ [`${this.basePath}.countdowns.-=${target.dataset.countdown}`]: null });
|
||||
|
||||
await this.updateSetting(countdownSetting.toObject());
|
||||
}
|
||||
|
||||
async open() {
|
||||
await this.render(true);
|
||||
if (
|
||||
Object.keys(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath].countdowns)
|
||||
.length > 0
|
||||
) {
|
||||
this.minimize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class NarrativeCountdowns extends Countdowns {
|
||||
constructor() {
|
||||
super('narrative');
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: 'narrative-countdowns'
|
||||
};
|
||||
}
|
||||
|
||||
export class EncounterCountdowns extends Countdowns {
|
||||
constructor() {
|
||||
super('encounter');
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: 'encounter-countdowns'
|
||||
};
|
||||
}
|
||||
|
||||
export const registerCountdownApplicationHooks = () => {
|
||||
const updateCountdowns = async shouldProgress => {
|
||||
if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).countdowns) {
|
||||
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
|
||||
for (let countdownCategoryKey in countdownSetting) {
|
||||
const countdownCategory = countdownSetting[countdownCategoryKey];
|
||||
for (let countdownKey in countdownCategory.countdowns) {
|
||||
const countdown = countdownCategory.countdowns[countdownKey];
|
||||
|
||||
if (shouldProgress(countdown)) {
|
||||
await countdownSetting.updateSource({
|
||||
[`${countdownCategoryKey}.countdowns.${countdownKey}.progress.current`]:
|
||||
countdown.progress.current - 1
|
||||
});
|
||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, countdownSetting);
|
||||
foundry.applications.instances.get(`${countdownCategoryKey}-countdowns`)?.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Hooks.on(SYSTEM.HOOKS.characterAttack, async () => {
|
||||
updateCountdowns(countdown => {
|
||||
return (
|
||||
countdown.progress.type.value === countdownTypes.characterAttack.id && countdown.progress.current > 0
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Hooks.on(SYSTEM.HOOKS.spotlight, async () => {
|
||||
updateCountdowns(countdown => {
|
||||
return countdown.progress.type.value === countdownTypes.spotlight.id && countdown.progress.current > 0;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { actionsTypes } from '../data/_module.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
|
|
@ -5,25 +7,25 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
super({});
|
||||
|
||||
this.actor = actor;
|
||||
this.selectedActivity = null;
|
||||
this.shortrest = shortrest;
|
||||
|
||||
this.customActivity = SYSTEM.GENERAL.downtime.custom;
|
||||
const options = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).restMoves;
|
||||
this.moveData = shortrest ? options.shortRest : options.longRest;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return `${this.actor.name} - ${this.shortrest ? 'Short Rest' : 'Long Rest'}`;
|
||||
return '';
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'views', 'downtime'],
|
||||
position: { width: 800, height: 'auto' },
|
||||
position: { width: 680, height: 'auto' },
|
||||
actions: {
|
||||
selectActivity: this.selectActivity,
|
||||
selectMove: this.selectMove,
|
||||
takeDowntime: this.takeDowntime
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true }
|
||||
form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
|
|
@ -33,51 +35,63 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
}
|
||||
};
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement
|
||||
.querySelectorAll('.activity-image')
|
||||
.forEach(element => element.addEventListener('contextmenu', this.deselectMove.bind(this)));
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.selectedActivity = this.selectedActivity;
|
||||
context.options = this.shortrest ? SYSTEM.GENERAL.downtime.shortRest : SYSTEM.GENERAL.downtime.longRest;
|
||||
context.customActivity = this.customActivity;
|
||||
|
||||
context.disabledDowntime =
|
||||
!this.selectedActivity ||
|
||||
(this.selectedActivity.id === this.customActivity.id &&
|
||||
(!this.customActivity.name || !this.customActivity.description));
|
||||
context.moveData = this.moveData;
|
||||
context.nrCurrentChoices = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
|
||||
context.disabledDowntime = context.nrCurrentChoices < context.moveData.nrChoices;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static selectActivity(_, button) {
|
||||
const activity = button.dataset.activity;
|
||||
this.selectedActivity =
|
||||
activity === this.customActivity.id
|
||||
? this.customActivity
|
||||
: this.shortrest
|
||||
? SYSTEM.GENERAL.downtime.shortRest[activity]
|
||||
: SYSTEM.GENERAL.downtime.longRest[activity];
|
||||
static selectMove(_, button) {
|
||||
const nrSelected = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
|
||||
if (nrSelected === this.moveData.nrChoices) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Downtime.Notifications.NoMoreMoves'));
|
||||
return;
|
||||
}
|
||||
|
||||
const move = button.dataset.move;
|
||||
this.moveData.moves[move].selected = this.moveData.moves[move].selected
|
||||
? this.moveData.moves[move].selected + 1
|
||||
: 1;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
deselectMove(event) {
|
||||
const move = event.currentTarget.dataset.move;
|
||||
this.moveData.moves[move].selected = this.moveData.moves[move].selected
|
||||
? this.moveData.moves[move].selected - 1
|
||||
: 0;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async takeDowntime() {
|
||||
const refreshedFeatures = this.shortrest
|
||||
? this.actor.system.refreshableFeatures.shortRest
|
||||
: [...this.actor.system.refreshableFeatures.shortRest, ...this.actor.system.refreshableFeatures.longRest];
|
||||
for (var feature of refreshedFeatures) {
|
||||
await feature.system.refresh();
|
||||
}
|
||||
const moves = Object.values(this.moveData.moves).filter(x => x.selected);
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const msg = new cls({
|
||||
user: game.user.id,
|
||||
system: {
|
||||
moves: moves,
|
||||
actor: this.actor.uuid
|
||||
},
|
||||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/chat/downtime.hbs',
|
||||
{
|
||||
player: this.actor.name,
|
||||
title: game.i18n.localize(this.selectedActivity.name),
|
||||
img: this.selectedActivity.img,
|
||||
description: game.i18n.localize(this.selectedActivity.description),
|
||||
refreshedFeatures: refreshedFeatures
|
||||
title: `${this.actor.name} - ${game.i18n.localize(`DAGGERHEART.Downtime.${this.shortRest ? 'ShortRest' : 'LongRest'}.title`)}`,
|
||||
moves: moves
|
||||
}
|
||||
)
|
||||
});
|
||||
|
|
|
|||
72
module/applications/ownershipSelection.mjs
Normal file
72
module/applications/ownershipSelection.mjs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class OwnershipSelection extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(resolve, reject, name, ownership) {
|
||||
super({});
|
||||
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
this.name = name;
|
||||
this.ownership = ownership;
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'views', 'ownership-selection'],
|
||||
position: {
|
||||
width: 600,
|
||||
height: 'auto'
|
||||
},
|
||||
form: { handler: this.updateData }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
selection: {
|
||||
template: 'systems/daggerheart/templates/views/ownershipSelection.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
get title() {
|
||||
return game.i18n.format('DAGGERHEART.OwnershipSelection.Title', { name: this.name });
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.ownershipOptions = Object.keys(CONST.DOCUMENT_OWNERSHIP_LEVELS).map(level => ({
|
||||
value: CONST.DOCUMENT_OWNERSHIP_LEVELS[level],
|
||||
label: game.i18n.localize(`OWNERSHIP.${level}`)
|
||||
}));
|
||||
context.ownership = {
|
||||
default: this.ownership.default,
|
||||
players: Object.keys(this.ownership.players).reduce((acc, x) => {
|
||||
const user = game.users.get(x);
|
||||
if (!user.isGM) {
|
||||
acc[x] = {
|
||||
img: user.character?.img ?? 'icons/svg/cowled.svg',
|
||||
name: user.name,
|
||||
ownership: this.ownership.players[x].value
|
||||
};
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateData(event, _, formData) {
|
||||
const { ownership } = foundry.utils.expandObject(formData.object);
|
||||
|
||||
this.resolve(ownership);
|
||||
this.close(true);
|
||||
}
|
||||
|
||||
async close(fromSave) {
|
||||
if (!fromSave) {
|
||||
this.reject();
|
||||
}
|
||||
|
||||
await super.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ export default class Resources extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
}
|
||||
|
||||
get maxFear() {
|
||||
return game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.MaxFear);
|
||||
return game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).maxFear;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
|
@ -59,7 +59,7 @@ export default class Resources extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
|
||||
/** @override */
|
||||
async _prepareContext(_options) {
|
||||
const display = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.DisplayFear),
|
||||
const display = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).displayFear,
|
||||
current = this.currentFear,
|
||||
max = this.maxFear,
|
||||
percent = (current / max) * 100,
|
||||
|
|
|
|||
|
|
@ -1,178 +1,122 @@
|
|||
import { DualityRollColor } from '../config/settingsConfig.mjs';
|
||||
import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs';
|
||||
import DhAppearance from '../data/settings/Appearance.mjs';
|
||||
import DHAppearanceSettings from './settings/appearanceSettings.mjs';
|
||||
import DhVariantRules from '../data/settings/VariantRules.mjs';
|
||||
import DHVariantRuleSettings from './settings/variantRuleSettings.mjs';
|
||||
|
||||
class DhpAutomationSettings extends FormApplication {
|
||||
constructor(object = {}, options = {}) {
|
||||
super(object, options);
|
||||
}
|
||||
|
||||
static get defaultOptions() {
|
||||
const defaults = super.defaultOptions;
|
||||
const overrides = {
|
||||
height: 'auto',
|
||||
width: 400,
|
||||
id: 'daggerheart-automation-settings',
|
||||
template: 'systems/daggerheart/templates/views/automation-settings.hbs',
|
||||
closeOnSubmit: true,
|
||||
submitOnChange: false,
|
||||
classes: ['daggerheart', 'views', 'settings']
|
||||
};
|
||||
|
||||
const mergedOptions = foundry.utils.mergeObject(defaults, overrides);
|
||||
|
||||
return mergedOptions;
|
||||
}
|
||||
|
||||
async getData() {
|
||||
const context = super.getData();
|
||||
context.settings = SYSTEM.SETTINGS.gameSettings.Automation;
|
||||
context.hope = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope);
|
||||
context.actionPoints = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.ActionPoints);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
}
|
||||
|
||||
async _updateObject(_, formData) {
|
||||
const data = foundry.utils.expandObject(formData);
|
||||
const updateSettingsKeys = Object.keys(data);
|
||||
for (var i = 0; i < updateSettingsKeys.length; i++) {
|
||||
await game.settings.set(SYSTEM.id, updateSettingsKeys[i], data[updateSettingsKeys[i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DhpHomebrewSettings extends FormApplication {
|
||||
constructor(object = {}, options = {}) {
|
||||
super(object, options);
|
||||
}
|
||||
|
||||
static get defaultOptions() {
|
||||
const defaults = super.defaultOptions;
|
||||
const overrides = {
|
||||
height: 'auto',
|
||||
width: 400,
|
||||
id: 'daggerheart-homebrew-settings',
|
||||
template: 'systems/daggerheart/templates/views/homebrew-settings.hbs',
|
||||
closeOnSubmit: true,
|
||||
submitOnChange: false,
|
||||
classes: ['daggerheart', 'views', 'settings']
|
||||
};
|
||||
|
||||
const mergedOptions = foundry.utils.mergeObject(defaults, overrides);
|
||||
|
||||
return mergedOptions;
|
||||
}
|
||||
|
||||
async getData() {
|
||||
const context = super.getData();
|
||||
context.settings = SYSTEM.SETTINGS.gameSettings.General;
|
||||
context.abilityArray = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.AbilityArray);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
}
|
||||
|
||||
async _updateObject(_, formData) {
|
||||
const data = foundry.utils.expandObject(formData);
|
||||
const updateSettingsKeys = Object.keys(data);
|
||||
for (var i = 0; i < updateSettingsKeys.length; i++) {
|
||||
await game.settings.set(SYSTEM.id, updateSettingsKeys[i], data[updateSettingsKeys[i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DhpRangeSettings extends FormApplication {
|
||||
constructor(object = {}, options = {}) {
|
||||
super(object, options);
|
||||
|
||||
this.range = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.RangeMeasurement);
|
||||
}
|
||||
|
||||
static get defaultOptions() {
|
||||
const defaults = super.defaultOptions;
|
||||
const overrides = {
|
||||
height: 'auto',
|
||||
width: 400,
|
||||
id: 'daggerheart-range-settings',
|
||||
template: 'systems/daggerheart/templates/views/range-settings.hbs',
|
||||
closeOnSubmit: false,
|
||||
submitOnChange: true,
|
||||
classes: ['daggerheart', 'views', 'settings']
|
||||
};
|
||||
|
||||
const mergedOptions = foundry.utils.mergeObject(defaults, overrides);
|
||||
|
||||
return mergedOptions;
|
||||
}
|
||||
|
||||
async getData() {
|
||||
const context = super.getData();
|
||||
context.settings = SYSTEM.SETTINGS.gameSettings.General;
|
||||
context.range = this.range;
|
||||
context.disabled =
|
||||
context.range.enabled &&
|
||||
[
|
||||
context.range.melee,
|
||||
context.range.veryClose,
|
||||
context.range.close,
|
||||
context.range.far,
|
||||
context.range.veryFar
|
||||
].some(x => x === null || x === false);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
html.find('.range-reset').click(this.reset.bind(this));
|
||||
html.find('.save').click(this.save.bind(this));
|
||||
html.find('.close').click(this.close.bind(this));
|
||||
}
|
||||
|
||||
async _updateObject(_, formData) {
|
||||
const data = foundry.utils.expandObject(formData, { disabled: true });
|
||||
this.range = foundry.utils.mergeObject(this.range, data);
|
||||
this.render(true);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.range = {
|
||||
enabled: false,
|
||||
melee: 5,
|
||||
veryClose: 15,
|
||||
close: 30,
|
||||
far: 60,
|
||||
veryFar: 120
|
||||
};
|
||||
this.render(true);
|
||||
}
|
||||
|
||||
async save() {
|
||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.RangeMeasurement, this.range);
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
import DhCountdowns from '../data/countdowns.mjs';
|
||||
import {
|
||||
DhAppearance,
|
||||
DhAutomation,
|
||||
DhHomebrew,
|
||||
DhRangeMeasurement,
|
||||
DhVariantRules
|
||||
} from '../data/settings/_module.mjs';
|
||||
import {
|
||||
DhAppearanceSettings,
|
||||
DhAutomationSettings,
|
||||
DhHomebrewSettings,
|
||||
DhRangeMeasurementSettings,
|
||||
DhVariantRuleSettings
|
||||
} from './settings/_module.mjs';
|
||||
|
||||
export const registerDHSettings = () => {
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.AbilityArray, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.General.AbilityArray.Name'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.General.AbilityArray.Hint'),
|
||||
registerMenuSettings();
|
||||
registerMenus();
|
||||
registerNonConfigSettings();
|
||||
};
|
||||
|
||||
const registerMenuSettings = () => {
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.variantRules, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: String,
|
||||
default: '[2,1,1,0,0,-1]'
|
||||
type: DhVariantRules
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: DhAutomation
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: DhHomebrew,
|
||||
onChange: value => {
|
||||
if (value.maxFear) {
|
||||
if (ui.resources) ui.resources.render({ force: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, {
|
||||
scope: 'client',
|
||||
config: false,
|
||||
type: DhAppearance,
|
||||
onChange: value => {
|
||||
if (value.displayFear) {
|
||||
if (ui.resources) {
|
||||
if (value.displayFear === 'hide') ui.resources.close({ allowed: true });
|
||||
else ui.resources.render({ force: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement, {
|
||||
scope: 'client',
|
||||
config: false,
|
||||
type: DhRangeMeasurement
|
||||
});
|
||||
};
|
||||
|
||||
const registerMenus = () => {
|
||||
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Automation.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name'),
|
||||
label: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Hint'),
|
||||
icon: SYSTEM.SETTINGS.menu.Automation.Icon,
|
||||
type: DhAutomationSettings,
|
||||
restricted: true
|
||||
});
|
||||
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Homebrew.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Name'),
|
||||
label: game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Hint'),
|
||||
icon: SYSTEM.SETTINGS.menu.Homebrew.Icon,
|
||||
type: DhHomebrewSettings,
|
||||
restricted: true
|
||||
});
|
||||
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Range.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Menu.Range.Name'),
|
||||
label: game.i18n.localize('DAGGERHEART.Settings.Menu.Range.Label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Range.Hint'),
|
||||
icon: SYSTEM.SETTINGS.menu.Range.Icon,
|
||||
type: DhRangeMeasurementSettings,
|
||||
restricted: true
|
||||
});
|
||||
|
||||
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Menu.Appearance.title'),
|
||||
label: game.i18n.localize('DAGGERHEART.Settings.Menu.Appearance.label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Appearance.hint'),
|
||||
icon: 'fa-solid fa-palette',
|
||||
type: DhAppearanceSettings,
|
||||
restricted: false
|
||||
});
|
||||
|
||||
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.VariantRules.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.title'),
|
||||
label: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.hint'),
|
||||
icon: SYSTEM.SETTINGS.menu.VariantRules.Icon,
|
||||
type: DhVariantRuleSettings,
|
||||
restricted: false
|
||||
});
|
||||
};
|
||||
|
||||
const registerNonConfigSettings = () => {
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: DhLevelTiers,
|
||||
default: defaultLevelTiers
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear, {
|
||||
|
|
@ -188,143 +132,9 @@ export const registerDHSettings = () => {
|
|||
}
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.MaxFear, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Resources.MaxFear.Name'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Resources.MaxFear.Hint'),
|
||||
scope: 'world',
|
||||
config: true,
|
||||
type: Number,
|
||||
default: 12,
|
||||
onChange: () => {
|
||||
if (ui.resources) ui.resources.render({ force: true });
|
||||
}
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.DisplayFear, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Resources.DisplayFear.Name'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Resources.DisplayFear.Hint'),
|
||||
scope: 'client',
|
||||
config: true,
|
||||
type: String,
|
||||
choices: {
|
||||
token: 'Tokens',
|
||||
bar: 'Bar',
|
||||
hide: 'Hide'
|
||||
},
|
||||
default: 'token',
|
||||
onChange: value => {
|
||||
if (ui.resources) {
|
||||
if (value === 'hide') ui.resources.close({ allowed: true });
|
||||
else ui.resources.render({ force: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Automation.Hope.Name'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Automation.Hope.Hint'),
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.ActionPoints, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Automation.ActionPoints.Name'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Automation.ActionPoints.Hint'),
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: Boolean,
|
||||
default: true
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.RangeMeasurement, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.General.RangeMeasurement.Name'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.General.RangeMeasurement.Hint'),
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: Object,
|
||||
default: {
|
||||
enabled: true,
|
||||
melee: 5,
|
||||
veryClose: 15,
|
||||
close: 30,
|
||||
far: 60,
|
||||
veryFar: 120
|
||||
}
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.variantRules, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: DhVariantRules,
|
||||
default: DhVariantRules.defaultSchema
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, {
|
||||
scope: 'client',
|
||||
config: false,
|
||||
type: DhAppearance,
|
||||
default: DhAppearance.defaultSchema
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.DualityRollColor, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Name'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Hint'),
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: Number,
|
||||
choices: Object.values(DualityRollColor),
|
||||
default: DualityRollColor.colorful.value
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: DhLevelTiers,
|
||||
default: defaultLevelTiers
|
||||
});
|
||||
|
||||
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Automation.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name'),
|
||||
label: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Hint'),
|
||||
icon: SYSTEM.SETTINGS.menu.Automation.Icon,
|
||||
type: DhpAutomationSettings,
|
||||
restricted: true
|
||||
});
|
||||
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Homebrew.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Name'),
|
||||
label: game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Hint'),
|
||||
icon: SYSTEM.SETTINGS.menu.Homebrew.Icon,
|
||||
type: DhpHomebrewSettings,
|
||||
restricted: true
|
||||
});
|
||||
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Range.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Menu.Range.Name'),
|
||||
label: game.i18n.localize('DAGGERHEART.Settings.Menu.Range.Label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Range.Hint'),
|
||||
icon: SYSTEM.SETTINGS.menu.Range.Icon,
|
||||
type: DhpRangeSettings,
|
||||
restricted: true
|
||||
});
|
||||
|
||||
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Menu.Appearance.title'),
|
||||
label: game.i18n.localize('DAGGERHEART.Settings.Menu.Appearance.label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Appearance.hint'),
|
||||
icon: 'fa-solid fa-palette',
|
||||
type: DHAppearanceSettings,
|
||||
restricted: false
|
||||
});
|
||||
|
||||
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.VariantRules.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.title'),
|
||||
label: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.hint'),
|
||||
icon: SYSTEM.SETTINGS.menu.VariantRules.Icon,
|
||||
type: DHVariantRuleSettings,
|
||||
restricted: false
|
||||
type: DhCountdowns
|
||||
});
|
||||
};
|
||||
|
|
|
|||
13
module/applications/settings/_module.mjs
Normal file
13
module/applications/settings/_module.mjs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import DhAppearanceSettings from './appearanceSettings.mjs';
|
||||
import DhAutomationSettings from './automationSettings.mjs';
|
||||
import DhHomebrewSettings from './homebrewSettings.mjs';
|
||||
import DhRangeMeasurementSettings from './rangeMeasurementSettings.mjs';
|
||||
import DhVariantRuleSettings from './variantRuleSettings.mjs';
|
||||
|
||||
export {
|
||||
DhAppearanceSettings,
|
||||
DhAutomationSettings,
|
||||
DhHomebrewSettings,
|
||||
DhRangeMeasurementSettings,
|
||||
DhVariantRuleSettings
|
||||
};
|
||||
|
|
@ -54,20 +54,11 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
|||
|
||||
static async save() {
|
||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, this.settings.toObject());
|
||||
/* const reload = await foundry.applications.api.DialogV2.confirm({
|
||||
id: 'reload-world-confirm',
|
||||
modal: true,
|
||||
rejectClose: false,
|
||||
window: { title: 'SETTINGS.ReloadPromptTitle' },
|
||||
position: { width: 400 },
|
||||
content: `<p>${game.i18n.localize('SETTINGS.ReloadPromptBody')}</p>`
|
||||
});
|
||||
|
||||
if (reload) {
|
||||
await game.socket.emit('reload');
|
||||
foundry.utils.debouncedReload();
|
||||
} */
|
||||
document.body.classList.toggle('theme-colorful', game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).dualityColorScheme === DualityRollColor.colorful.value);
|
||||
document.body.classList.toggle(
|
||||
'theme-colorful',
|
||||
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).dualityColorScheme ===
|
||||
DualityRollColor.colorful.value
|
||||
);
|
||||
|
||||
this.close();
|
||||
}
|
||||
|
|
|
|||
59
module/applications/settings/automationSettings.mjs
Normal file
59
module/applications/settings/automationSettings.mjs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { DhAutomation } from '../../data/settings/_module.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhAutomationSettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor() {
|
||||
super({});
|
||||
|
||||
this.settings = new DhAutomation(
|
||||
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).toObject()
|
||||
);
|
||||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name');
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
id: 'daggerheart-automation-settings',
|
||||
classes: ['daggerheart', 'setting', 'dh-style'],
|
||||
position: { width: '600', height: 'auto' },
|
||||
actions: {
|
||||
reset: this.reset,
|
||||
save: this.save
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: 'systems/daggerheart/templates/settings/automation-settings.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.settingFields = this.settings;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateData(event, element, formData) {
|
||||
const updatedSettings = foundry.utils.expandObject(formData.object);
|
||||
|
||||
await this.settings.updateSource(updatedSettings);
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async reset() {
|
||||
this.settings = new DhAutomation();
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async save() {
|
||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation, this.settings.toObject());
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
144
module/applications/settings/components/settingsActionsView.mjs
Normal file
144
module/applications/settings/components/settingsActionsView.mjs
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import { actionsTypes } from '../../../data/_module.mjs';
|
||||
import DHActionConfig from '../../config/Action.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhSettingsActionView extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(resolve, reject, title, name, img, description, actions) {
|
||||
super({});
|
||||
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
this.viewTitle = title;
|
||||
this.name = name;
|
||||
this.img = img;
|
||||
this.description = description;
|
||||
this.actions = actions;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return this.viewTitle;
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'setting', 'dh-style'],
|
||||
position: { width: '400', height: 'auto' },
|
||||
actions: {
|
||||
editImage: this.onEditImage,
|
||||
addItem: this.addItem,
|
||||
editItem: this.editItem,
|
||||
removeItem: this.removeItem,
|
||||
resetMoves: this.resetMoves,
|
||||
saveForm: this.saveForm
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
header: { template: 'systems/daggerheart/templates/settings/components/action-view-header.hbs' },
|
||||
main: {
|
||||
template: 'systems/daggerheart/templates/settings/components/action-view.hbs'
|
||||
},
|
||||
footer: { template: 'systems/daggerheart/templates/settings/components/action-view-footer.hbs' }
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.name = this.name;
|
||||
context.img = this.img;
|
||||
context.description = this.description;
|
||||
context.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML(context.description);
|
||||
context.actions = this.actions;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateData(event, element, formData) {
|
||||
const { name, img, description } = foundry.utils.expandObject(formData.object);
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async saveForm(event) {
|
||||
this.resolve({
|
||||
name: this.name,
|
||||
img: this.img,
|
||||
description: this.description,
|
||||
actions: this.actions
|
||||
});
|
||||
this.close(true);
|
||||
}
|
||||
|
||||
static close(fromSave) {
|
||||
if (!fromSave) {
|
||||
this.reject();
|
||||
}
|
||||
|
||||
super.close();
|
||||
}
|
||||
|
||||
static onEditImage() {
|
||||
const fp = new FilePicker({
|
||||
current: this.img,
|
||||
type: 'image',
|
||||
callback: async path => {
|
||||
this.img = path;
|
||||
this.render();
|
||||
},
|
||||
top: this.position.top + 40,
|
||||
left: this.position.left + 10
|
||||
});
|
||||
return fp.browse();
|
||||
}
|
||||
|
||||
async selectActionType() {
|
||||
const content = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/views/actionType.hbs',
|
||||
{ types: SYSTEM.ACTIONS.actionTypes }
|
||||
),
|
||||
title = 'Select Action Type',
|
||||
type = 'form',
|
||||
data = {};
|
||||
return Dialog.prompt({
|
||||
title,
|
||||
label: title,
|
||||
content,
|
||||
type,
|
||||
callback: html => {
|
||||
const form = html[0].querySelector('form'),
|
||||
fd = new foundry.applications.ux.FormDataExtended(form);
|
||||
foundry.utils.mergeObject(data, fd.object, { inplace: true });
|
||||
return data;
|
||||
},
|
||||
rejectClose: false
|
||||
});
|
||||
}
|
||||
|
||||
static async addItem() {
|
||||
const actionType = await this.selectActionType();
|
||||
const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack,
|
||||
action = new cls({
|
||||
_id: foundry.utils.randomID(),
|
||||
type: actionType.type,
|
||||
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name),
|
||||
...cls.getSourceConfig(this.document)
|
||||
});
|
||||
|
||||
this.actions.push(action);
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async editItem(_, button) {
|
||||
await new DHActionConfig(this.actions[button.dataset.id]).render(true);
|
||||
}
|
||||
|
||||
static removeItem(event, button) {
|
||||
this.actions = this.actions.filter((_, index) => index !== Number.parseInt(button.dataset.id));
|
||||
this.render();
|
||||
}
|
||||
|
||||
static resetMoves() {}
|
||||
}
|
||||
157
module/applications/settings/homebrewSettings.mjs
Normal file
157
module/applications/settings/homebrewSettings.mjs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
import { DhHomebrew } from '../../data/settings/_module.mjs';
|
||||
import DhSettingsActionView from './components/settingsActionsView.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhHomebrewSettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor() {
|
||||
super({});
|
||||
|
||||
this.settings = new DhHomebrew(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).toObject());
|
||||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Name');
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
id: 'daggerheart-homebrew-settings',
|
||||
classes: ['daggerheart', 'setting', 'dh-style'],
|
||||
position: { width: '600', height: 'auto' },
|
||||
actions: {
|
||||
addItem: this.addItem,
|
||||
editItem: this.editItem,
|
||||
removeItem: this.removeItem,
|
||||
resetMoves: this.resetMoves,
|
||||
save: this.save
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: 'systems/daggerheart/templates/settings/homebrew-settings.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.settingFields = this.settings;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateData(event, element, formData) {
|
||||
const updatedSettings = foundry.utils.expandObject(formData.object);
|
||||
|
||||
await this.settings.updateSource({
|
||||
...updatedSettings,
|
||||
traitArray: Object.values(updatedSettings.traitArray)
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async addItem(_, target) {
|
||||
await this.settings.updateSource({
|
||||
[`restMoves.${target.dataset.type}.moves.${foundry.utils.randomID()}`]: {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Homebrew.NewDowntimeMove'),
|
||||
img: 'icons/magic/life/cross-worn-green.webp',
|
||||
description: '',
|
||||
actions: []
|
||||
}
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async editItem(_, target) {
|
||||
const move = this.settings.restMoves[target.dataset.type].moves[target.dataset.id];
|
||||
new Promise((resolve, reject) => {
|
||||
new DhSettingsActionView(
|
||||
resolve,
|
||||
reject,
|
||||
game.i18n.localize('DAGGERHEART.Settings.Homebrew.DowntimeMoves'),
|
||||
move.name,
|
||||
move.img,
|
||||
move.description,
|
||||
move.actions
|
||||
).render(true);
|
||||
}).then(data => this.updateAction.bind(this)(data, target.dataset.type, target.dataset.id));
|
||||
}
|
||||
|
||||
async updateAction(data, type, id) {
|
||||
await this.settings.updateSource({
|
||||
[`restMoves.${type}.moves.${id}`]: {
|
||||
name: data.name,
|
||||
img: data.img,
|
||||
description: data.description
|
||||
}
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async removeItem(_, target) {
|
||||
await this.settings.updateSource({
|
||||
[`restMoves.${target.dataset.type}.moves.-=${target.dataset.id}`]: null
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async resetMoves(_, target) {
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: game.i18n.format('DAGGERHEART.Settings.Homebrew.ResetMovesTitle', {
|
||||
type: game.i18n.localize(
|
||||
`DAGGERHEART.Downtime.${target.dataset.type === 'shortRest' ? 'ShortRest' : 'LongRest'}.title`
|
||||
)
|
||||
})
|
||||
},
|
||||
content: game.i18n.localize('DAGGERHEART.Settings.Homebrew.ResetMovesText')
|
||||
});
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
const fields = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).schema.fields;
|
||||
|
||||
const removeUpdate = Object.keys(this.settings.restMoves[target.dataset.type].moves).reduce((acc, key) => {
|
||||
acc[`-=${key}`] = null;
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const updateBase =
|
||||
target.dataset.type === 'shortRest'
|
||||
? fields.restMoves.fields.shortRest.fields
|
||||
: fields.restMoves.fields.longRest.fields;
|
||||
const update = {
|
||||
nrChoices: updateBase.nrChoices.initial,
|
||||
moves: Object.keys(updateBase.moves.initial).reduce((acc, key) => {
|
||||
const move = updateBase.moves.initial[key];
|
||||
acc[key] = {
|
||||
...move,
|
||||
name: game.i18n.localize(move.name),
|
||||
description: game.i18n.localize(move.description)
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
|
||||
await this.settings.updateSource({
|
||||
[`restMoves.${target.dataset.type}`]: {
|
||||
...update,
|
||||
moves: {
|
||||
...removeUpdate,
|
||||
...update.moves
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async save() {
|
||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
59
module/applications/settings/rangeMeasurementSettings.mjs
Normal file
59
module/applications/settings/rangeMeasurementSettings.mjs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { DhRangeMeasurement } from '../../data/settings/_module.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhRangeMeasurementSettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor() {
|
||||
super({});
|
||||
|
||||
this.settings = new DhRangeMeasurement(
|
||||
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement).toObject()
|
||||
);
|
||||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name');
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
id: 'daggerheart-automation-settings',
|
||||
classes: ['daggerheart', 'setting', 'dh-style'],
|
||||
position: { width: '600', height: 'auto' },
|
||||
actions: {
|
||||
reset: this.reset,
|
||||
save: this.save
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: 'systems/daggerheart/templates/settings/range-measurement-settings.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.settingFields = this.settings;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateData(event, element, formData) {
|
||||
const updatedSettings = foundry.utils.expandObject(formData.object);
|
||||
|
||||
await this.settings.updateSource(updatedSettings);
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async reset() {
|
||||
this.settings = new DhRangeMeasurement();
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async save() {
|
||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement, this.settings.toObject());
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
export default class DhActiveEffectConfig extends ActiveEffectConfig {
|
||||
export default class DhActiveEffectConfig extends foundry.applications.sheets.ActiveEffectConfig {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['daggerheart', 'sheet', 'dh-style']
|
||||
};
|
||||
|
|
|
|||
|
|
@ -165,18 +165,18 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
context.config = SYSTEM;
|
||||
|
||||
const selectedAttributes = Object.values(this.document.system.traits).map(x => x.base);
|
||||
context.abilityScoreArray = JSON.parse(
|
||||
await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.AbilityArray)
|
||||
).reduce((acc, x) => {
|
||||
const selectedIndex = selectedAttributes.indexOf(x);
|
||||
if (selectedIndex !== -1) {
|
||||
selectedAttributes.splice(selectedIndex, 1);
|
||||
} else {
|
||||
acc.push({ name: x, value: x });
|
||||
}
|
||||
context.abilityScoreArray = await game.settings
|
||||
.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew)
|
||||
.traitArray.reduce((acc, x) => {
|
||||
const selectedIndex = selectedAttributes.indexOf(x);
|
||||
if (selectedIndex !== -1) {
|
||||
selectedAttributes.splice(selectedIndex, 1);
|
||||
} else {
|
||||
acc.push({ name: x, value: x });
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
return acc;
|
||||
}, []);
|
||||
if (!context.abilityScoreArray.includes(0)) context.abilityScoreArray.push({ name: 0, value: 0 });
|
||||
context.abilityScoresFinished = context.abilityScoreArray.every(x => x.value === 0);
|
||||
|
||||
|
|
@ -370,7 +370,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
|||
static async attackRoll(event, button) {
|
||||
const weapon = await fromUuid(button.dataset.weapon);
|
||||
if (!weapon) return;
|
||||
weapon.use(event);
|
||||
|
||||
const wasUsed = await weapon.use(event);
|
||||
if (wasUsed) {
|
||||
Hooks.callAll(SYSTEM.HOOKS.characterAttack, {});
|
||||
}
|
||||
}
|
||||
|
||||
static openLevelUp() {
|
||||
|
|
|
|||
|
|
@ -221,46 +221,53 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
|
|||
async _onDrop(event) {
|
||||
const data = TextEditor.getDragEventData(event);
|
||||
const item = await fromUuid(data.uuid);
|
||||
const target = event.target.closest('fieldset.drop-section');
|
||||
if (item.type === 'subclass') {
|
||||
await this.document.update({
|
||||
'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
} else if (item.type === 'weapon') {
|
||||
if (event.currentTarget.classList.contains('primary-weapon-section')) {
|
||||
if (target.classList.contains('primary-weapon-section')) {
|
||||
if (!this.document.system.characterGuide.suggestedPrimaryWeapon && !item.system.secondary)
|
||||
await this.document.update({
|
||||
'system.characterGuide.suggestedPrimaryWeapon': item.uuid
|
||||
});
|
||||
} else if (event.currentTarget.classList.contains('secondary-weapon-section')) {
|
||||
} else if (target.classList.contains('secondary-weapon-section')) {
|
||||
if (!this.document.system.characterGuide.suggestedSecondaryWeapon && item.system.secondary)
|
||||
await this.document.update({
|
||||
'system.characterGuide.suggestedSecondaryWeapon': item.uuid
|
||||
});
|
||||
}
|
||||
} else if (item.type === 'armor') {
|
||||
if (event.currentTarget.classList.contains('armor-section')) {
|
||||
if (target.classList.contains('armor-section')) {
|
||||
if (!this.document.system.characterGuide.suggestedArmor)
|
||||
await this.document.update({
|
||||
'system.characterGuide.suggestedArmor': item.uuid
|
||||
});
|
||||
}
|
||||
} else if (event.currentTarget.classList.contains('choice-a-section')) {
|
||||
} else if (target.classList.contains('choice-a-section')) {
|
||||
if (item.type === 'miscellaneous' || item.type === 'consumable') {
|
||||
if (this.document.system.inventory.choiceA.length < 2)
|
||||
await this.document.update({
|
||||
'system.inventory.choiceA': [...this.document.system.inventory.choiceA, item.uuid]
|
||||
'system.inventory.choiceA': [
|
||||
...this.document.system.inventory.choiceA.map(x => x.uuid),
|
||||
item.uuid
|
||||
]
|
||||
});
|
||||
}
|
||||
} else if (item.type === 'miscellaneous') {
|
||||
if (event.currentTarget.classList.contains('take-section')) {
|
||||
if (target.classList.contains('take-section')) {
|
||||
if (this.document.system.inventory.take.length < 3)
|
||||
await this.document.update({
|
||||
'system.inventory.take': [...this.document.system.inventory.take, item.uuid]
|
||||
'system.inventory.take': [...this.document.system.inventory.take.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
} else if (event.currentTarget.classList.contains('choice-b-section')) {
|
||||
} else if (target.classList.contains('choice-b-section')) {
|
||||
if (this.document.system.inventory.choiceB.length < 2)
|
||||
await this.document.update({
|
||||
'system.inventory.choiceB': [...this.document.system.inventory.choiceB, item.uuid]
|
||||
'system.inventory.choiceB': [
|
||||
...this.document.system.inventory.choiceB.map(x => x.uuid),
|
||||
item.uuid
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
export {default as PseudoDocumentSheet }from "./pseudo-documents-sheet.mjs";
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class PseudoDocumentSheet extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
this.#pseudoDocument = options.document;
|
||||
}
|
||||
|
||||
/**
|
||||
* The UUID of the associated pseudo-document
|
||||
* @type {string}
|
||||
*/
|
||||
get pseudoUuid() {
|
||||
return this.pseudoDocument.uuid;
|
||||
}
|
||||
|
||||
#pseudoDocument;
|
||||
|
||||
/**
|
||||
* The pseudo-document instance this sheet represents
|
||||
* @type {object}
|
||||
*/
|
||||
get pseudoDocument() {
|
||||
return this.#pseudoDocument;
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'sheet'],
|
||||
position: { width: 600 },
|
||||
form: {
|
||||
handler: PseudoDocumentSheet.#onSubmitForm,
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false
|
||||
},
|
||||
dragDrop: [{ dragSelector: null, dropSelector: null }],
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
header: { template: 'systems/daggerheart/templates/sheets/pseudo-documents/header.hbs' },
|
||||
};
|
||||
|
||||
/** @inheritDoc */
|
||||
async _prepareContext(options) {
|
||||
const context = await super._prepareContext(options);
|
||||
const document = this.pseudoDocument;
|
||||
return Object.assign(context, {
|
||||
document,
|
||||
source: document._source,
|
||||
editable: this.isEditable,
|
||||
user: game.user,
|
||||
rootId: this.id,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler
|
||||
* @param {SubmitEvent | Event} event - The originating form submission or input change event
|
||||
* @param {HTMLFormElement} form - The form element that was submitted
|
||||
* @param {foundry.applications.ux.FormDataExtended} formData - Processed data for the submitted form
|
||||
*/
|
||||
static async #onSubmitForm(event, form, formData) {
|
||||
const submitData = foundry.utils.expandObject(formData.object);
|
||||
await this.pseudoDocument.update(submitData);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +1,56 @@
|
|||
export const domains = {
|
||||
arcana: {
|
||||
id: 'arcana',
|
||||
label: 'DAGGERHEART.Domains.Arcana.label',
|
||||
label: 'DAGGERHEART.Domains.arcana.label',
|
||||
src: 'icons/magic/symbols/circled-gem-pink.webp',
|
||||
description: 'DAGGERHEART.Domains.Arcana'
|
||||
},
|
||||
blade: {
|
||||
id: 'blade',
|
||||
label: 'DAGGERHEART.Domains.Blade.label',
|
||||
label: 'DAGGERHEART.Domains.blade.label',
|
||||
src: 'icons/weapons/swords/sword-broad-crystal-paired.webp',
|
||||
description: 'DAGGERHEART.Domains.Blade'
|
||||
},
|
||||
bone: {
|
||||
id: 'bone',
|
||||
label: 'DAGGERHEART.Domains.Bone.label',
|
||||
label: 'DAGGERHEART.Domains.bone.label',
|
||||
src: 'icons/skills/wounds/bone-broken-marrow-red.webp',
|
||||
description: 'DAGGERHEART.Domains.Bone'
|
||||
},
|
||||
codex: {
|
||||
id: 'codex',
|
||||
label: 'DAGGERHEART.Domains.Codex.label',
|
||||
label: 'DAGGERHEART.Domains.codex.label',
|
||||
src: 'icons/sundries/books/book-embossed-jewel-gold-purple.webp',
|
||||
description: 'DAGGERHEART.Domains.Codex'
|
||||
},
|
||||
grace: {
|
||||
id: 'grace',
|
||||
label: 'DAGGERHEART.Domains.Grace.label',
|
||||
label: 'DAGGERHEART.Domains.grace.label',
|
||||
src: 'icons/skills/movement/feet-winged-boots-glowing-yellow.webp',
|
||||
description: 'DAGGERHEART.Domains.Grace'
|
||||
},
|
||||
midnight: {
|
||||
id: 'midnight',
|
||||
label: 'DAGGERHEART.Domains.Midnight.label',
|
||||
label: 'DAGGERHEART.Domains.midnight.label',
|
||||
src: 'icons/environment/settlement/watchtower-castle-night.webp',
|
||||
background: 'systems/daggerheart/assets/backgrounds/MidnightBackground.webp',
|
||||
description: 'DAGGERHEART.Domains.Midnight'
|
||||
},
|
||||
sage: {
|
||||
id: 'sage',
|
||||
label: 'DAGGERHEART.Domains.Sage.label',
|
||||
label: 'DAGGERHEART.Domains.sage.label',
|
||||
src: 'icons/sundries/misc/pipe-wooden-straight-brown.webp',
|
||||
description: 'DAGGERHEART.Domains.Sage'
|
||||
},
|
||||
splendor: {
|
||||
id: 'splendor',
|
||||
label: 'DAGGERHEART.Domains.Splendor.label',
|
||||
label: 'DAGGERHEART.Domains.splendor.label',
|
||||
src: 'icons/magic/control/control-influence-crown-gold.webp',
|
||||
description: 'DAGGERHEART.Domains.Splendor'
|
||||
},
|
||||
valor: {
|
||||
id: 'valor',
|
||||
label: 'DAGGERHEART.Domains.Valor.label',
|
||||
label: 'DAGGERHEART.Domains.valor.label',
|
||||
src: 'icons/magic/control/control-influence-rally-purple.webp',
|
||||
description: 'DAGGERHEART.Domains.Valor'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,42 @@
|
|||
export const range = {
|
||||
self: {
|
||||
id: 'self',
|
||||
short: 's',
|
||||
label: 'DAGGERHEART.Range.self.name',
|
||||
description: 'DAGGERHEART.Range.self.description',
|
||||
distance: 0
|
||||
},
|
||||
melee: {
|
||||
id: 'melee',
|
||||
short: 'm',
|
||||
label: 'DAGGERHEART.Range.melee.name',
|
||||
description: 'DAGGERHEART.Range.melee.description',
|
||||
distance: 1
|
||||
},
|
||||
veryClose: {
|
||||
id: 'veryClose',
|
||||
short: 'vc',
|
||||
label: 'DAGGERHEART.Range.veryClose.name',
|
||||
description: 'DAGGERHEART.Range.veryClose.description',
|
||||
distance: 3
|
||||
},
|
||||
close: {
|
||||
id: 'close',
|
||||
short: 'c',
|
||||
label: 'DAGGERHEART.Range.close.name',
|
||||
description: 'DAGGERHEART.Range.close.description',
|
||||
distance: 10
|
||||
},
|
||||
far: {
|
||||
id: 'far',
|
||||
short: 'f',
|
||||
label: 'DAGGERHEART.Range.far.name',
|
||||
description: 'DAGGERHEART.Range.far.description',
|
||||
distance: 20
|
||||
},
|
||||
veryFar: {
|
||||
id: 'veryFar',
|
||||
short: 'vf',
|
||||
label: 'DAGGERHEART.Range.veryFar.name',
|
||||
description: 'DAGGERHEART.Range.veryFar.description',
|
||||
distance: 30
|
||||
|
|
@ -104,65 +111,99 @@ export const conditions = {
|
|||
}
|
||||
};
|
||||
|
||||
export const downtime = {
|
||||
shortRest: {
|
||||
export const defaultRestOptions = {
|
||||
shortRest: () => ({
|
||||
tendToWounds: {
|
||||
id: 'tendToWounds',
|
||||
name: 'DAGGERHEART.Downtime.TendToWounds.Name',
|
||||
name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.TendToWounds.Name'),
|
||||
img: 'icons/magic/life/cross-worn-green.webp',
|
||||
description: 'DAGGERHEART.Downtime.TendToWounds.Description'
|
||||
description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.TendToWounds.Description'),
|
||||
actions: [
|
||||
{
|
||||
type: 'healing',
|
||||
name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.TendToWounds.Name'),
|
||||
img: 'icons/magic/life/cross-worn-green.webp',
|
||||
actionType: 'action',
|
||||
healing: {
|
||||
type: 'health',
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '1d4 + 1' // should be 1d4 + {tier}. How to use the roll param?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
clearStress: {
|
||||
id: 'clearStress',
|
||||
name: 'DAGGERHEART.Downtime.ClearStress.Name',
|
||||
name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.ClearStress.Name'),
|
||||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||
description: 'DAGGERHEART.Downtime.ClearStress.Description'
|
||||
description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.ClearStress.Description'),
|
||||
actions: [
|
||||
{
|
||||
type: 'healing',
|
||||
name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.ClearStress.Name'),
|
||||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||
actionType: 'action',
|
||||
healing: {
|
||||
type: 'stress',
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '1d4 + 1' // should be 1d4 + {tier}. How to use the roll param?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
repairArmor: {
|
||||
id: 'repairArmor',
|
||||
name: 'DAGGERHEART.Downtime.RepairArmor.Name',
|
||||
name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Name'),
|
||||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||
description: 'DAGGERHEART.Downtime.RepairArmor.Description'
|
||||
description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Description')
|
||||
},
|
||||
prepare: {
|
||||
id: 'prepare',
|
||||
name: 'DAGGERHEART.Downtime.Prepare.Name',
|
||||
name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Name'),
|
||||
img: 'icons/skills/trades/academics-merchant-scribe.webp',
|
||||
description: 'DAGGERHEART.Downtime.Prepare.Description'
|
||||
description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Description')
|
||||
}
|
||||
},
|
||||
longRest: {
|
||||
}),
|
||||
longRest: () => ({
|
||||
tendToWounds: {
|
||||
id: 'tendToWounds',
|
||||
name: 'DAGGERHEART.Downtime.TendToWounds.Name',
|
||||
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Name'),
|
||||
img: 'icons/magic/life/cross-worn-green.webp',
|
||||
description: 'DAGGERHEART.Downtime.TendToWounds.Description'
|
||||
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Description')
|
||||
},
|
||||
clearStress: {
|
||||
id: 'clearStress',
|
||||
name: 'DAGGERHEART.Downtime.ClearStress.Name',
|
||||
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Name'),
|
||||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||
description: 'DAGGERHEART.Downtime.ClearStress.Description'
|
||||
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Description')
|
||||
},
|
||||
repairArmor: {
|
||||
id: 'repairArmor',
|
||||
name: 'DAGGERHEART.Downtime.RepairArmor.Name',
|
||||
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Name'),
|
||||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||
description: 'DAGGERHEART.Downtime.RepairArmor.Description'
|
||||
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Description')
|
||||
},
|
||||
prepare: {
|
||||
id: 'prepare',
|
||||
name: 'DAGGERHEART.Downtime.Prepare.Name',
|
||||
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Name'),
|
||||
img: 'icons/skills/trades/academics-merchant-scribe.webp',
|
||||
description: 'DAGGERHEART.Downtime.Prepare.Description'
|
||||
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Description')
|
||||
},
|
||||
workOnAProject: {
|
||||
id: 'workOnAProject',
|
||||
name: 'DAGGERHEART.Downtime.WorkOnAProject.Name',
|
||||
name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Name'),
|
||||
img: 'icons/skills/social/thumbsup-approval-like.webp',
|
||||
description: 'DAGGERHEART.Downtime.WorkOnAProject.Description'
|
||||
description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Description')
|
||||
}
|
||||
},
|
||||
}),
|
||||
custom: {
|
||||
id: 'customActivity',
|
||||
name: '',
|
||||
|
|
@ -353,6 +394,20 @@ export const abilityCosts = {
|
|||
}
|
||||
};
|
||||
|
||||
export const countdownTypes = {
|
||||
spotlight: {
|
||||
id: 'spotlight',
|
||||
label: 'DAGGERHEART.Countdown.Type.Spotlight'
|
||||
},
|
||||
characterAttack: {
|
||||
id: 'characterAttack',
|
||||
label: 'DAGGERHEART.Countdown.Type.CharacterAttack'
|
||||
},
|
||||
custom: {
|
||||
id: 'custom',
|
||||
label: 'DAGGERHEART.Countdown.Type.Custom'
|
||||
}
|
||||
};
|
||||
export const rollTypes = {
|
||||
weapon: {
|
||||
id: 'weapon',
|
||||
|
|
@ -367,3 +422,9 @@ export const rollTypes = {
|
|||
label: 'DAGGERHEART.RollTypes.ability.name'
|
||||
}
|
||||
};
|
||||
|
||||
export const fearDisplay = {
|
||||
token: { value: 'token', label: 'DAGGERHEART.Settings.Appearance.FearDisplay.Token' },
|
||||
bar: { value: 'bar', label: 'DAGGERHEART.Settings.Appearance.FearDisplay.Bar' },
|
||||
hide: { value: 'hide', label: 'DAGGERHEART.Settings.Appearance.FearDisplay.Hide' }
|
||||
};
|
||||
|
|
|
|||
4
module/config/hooksConfig.mjs
Normal file
4
module/config/hooksConfig.mjs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export const hooks = {
|
||||
characterAttack: 'characterAttackHook',
|
||||
spotlight: 'spotlightHook'
|
||||
};
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import { pseudoDocuments } from "../data/_module.mjs";
|
||||
import { pseudoDocumentSheet } from "../applications/_module.mjs";
|
||||
|
||||
//CONFIG.daggerheart.pseudoDocuments
|
||||
export default {
|
||||
sheetClass: pseudoDocumentSheet.PseudoDocumentSheet,
|
||||
feature: {
|
||||
label: "DAGGERHEART.Feature.Label",
|
||||
documentClass: pseudoDocuments.feature.BaseFeatureData,
|
||||
types: {
|
||||
weapon: {
|
||||
label: "DAGGERHEART.Feature.Weapon.Label",
|
||||
documentClass: pseudoDocuments.feature.WeaponFeature,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -18,23 +18,16 @@ export const menu = {
|
|||
};
|
||||
|
||||
export const gameSettings = {
|
||||
Automation: {
|
||||
Hope: 'AutomationHope',
|
||||
ActionPoints: 'AutomationActionPoints'
|
||||
},
|
||||
Resources: {
|
||||
Fear: 'ResourcesFear',
|
||||
MaxFear: 'ResourcesMaxFear',
|
||||
DisplayFear: 'DisplayFear'
|
||||
},
|
||||
General: {
|
||||
AbilityArray: 'AbilityArray',
|
||||
RangeMeasurement: 'RangeMeasurement'
|
||||
},
|
||||
DualityRollColor: 'DualityRollColor',
|
||||
LevelTiers: 'LevelTiers',
|
||||
Automation: 'Automation',
|
||||
Homebrew: 'Homebrew',
|
||||
RangeMeasurement: 'RangeMeasurement',
|
||||
appearance: 'Appearance',
|
||||
variantRules: 'VariantRules'
|
||||
variantRules: 'VariantRules',
|
||||
Resources: {
|
||||
Fear: 'ResourcesFear'
|
||||
},
|
||||
LevelTiers: 'LevelTiers',
|
||||
Countdowns: 'Countdowns'
|
||||
};
|
||||
|
||||
export const DualityRollColor = {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import * as DOMAIN from './domainConfig.mjs';
|
|||
import * as ACTOR from './actorConfig.mjs';
|
||||
import * as ITEM from './itemConfig.mjs';
|
||||
import * as SETTINGS from './settingsConfig.mjs';
|
||||
import { hooks as HOOKS } from './hooksConfig.mjs';
|
||||
import * as EFFECTS from './effectConfig.mjs';
|
||||
import * as ACTIONS from './actionConfig.mjs';
|
||||
import pseudoDocuments from "./pseudoConfig.mjs";
|
||||
|
||||
export const SYSTEM_ID = 'daggerheart';
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ export const SYSTEM = {
|
|||
ACTOR,
|
||||
ITEM,
|
||||
SETTINGS,
|
||||
HOOKS,
|
||||
EFFECTS,
|
||||
ACTIONS,
|
||||
pseudoDocuments
|
||||
ACTIONS
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
export { default as DhClass } from './item/class.mjs';
|
||||
export { default as DhSubclass } from './item/subclass.mjs';
|
||||
export { default as DhCombat } from './combat.mjs';
|
||||
export { default as DhCombatant } from './combatant.mjs';
|
||||
|
||||
|
|
@ -8,4 +6,3 @@ export * as items from './item/_module.mjs';
|
|||
export { actionsTypes } from './action/_module.mjs';
|
||||
export * as messages from './chat-message/_modules.mjs';
|
||||
export * as fields from './fields/_module.mjs';
|
||||
export * as pseudoDocuments from './pseudo-documents/_module.mjs';
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import BaseDataActor from './base.mjs';
|
|||
|
||||
const attributeField = () =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
value: new foundry.data.fields.NumberField({ initial: null, integer: true }),
|
||||
bonus: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
tierMarked: new foundry.data.fields.BooleanField({ initial: false })
|
||||
});
|
||||
|
|
@ -54,13 +54,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
description: new fields.StringField({}),
|
||||
value: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
bonus: new fields.NumberField({ integer: true, initial: 0 })
|
||||
}),
|
||||
{
|
||||
initial: {
|
||||
[foundry.utils.randomID()]: { description: '', value: 2 },
|
||||
[foundry.utils.randomID()]: { description: '', value: 2 }
|
||||
}
|
||||
}
|
||||
})
|
||||
),
|
||||
gold: new fields.SchemaField({
|
||||
coins: new fields.NumberField({ initial: 0, integer: true }),
|
||||
|
|
@ -93,6 +87,14 @@ export default class DhCharacter extends BaseDataActor {
|
|||
};
|
||||
}
|
||||
|
||||
get tier() {
|
||||
return this.levelData.level.current === 1
|
||||
? 1
|
||||
: Object.values(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers).tiers).find(
|
||||
tier => currentLevel >= tier.levels.start && currentLevel <= tier.levels.end
|
||||
).tier;
|
||||
}
|
||||
|
||||
get ancestry() {
|
||||
return this.parent.items.find(x => x.type === 'ancestry') ?? null;
|
||||
}
|
||||
|
|
@ -140,19 +142,6 @@ export default class DhCharacter extends BaseDataActor {
|
|||
: null;
|
||||
}
|
||||
|
||||
get refreshableFeatures() {
|
||||
return this.parent.items.reduce(
|
||||
(acc, x) => {
|
||||
if (x.type === 'feature' && x.system.refreshData?.type === 'feature' && x.system.refreshData?.type) {
|
||||
acc[x.system.refreshData.type].push(x);
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ shortRest: [], longRest: [] }
|
||||
);
|
||||
}
|
||||
|
||||
static async unequipBeforeEquip(itemToEquip) {
|
||||
const primary = this.primaryWeapon,
|
||||
secondary = this.secondaryWeapon;
|
||||
|
|
@ -235,7 +224,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
|
||||
for (var traitKey in this.traits) {
|
||||
var trait = this.traits[traitKey];
|
||||
trait.total = trait.value + trait.bonus;
|
||||
trait.total = (trait.value ?? 0) + trait.bonus;
|
||||
}
|
||||
|
||||
for (var experienceKey in this.experiences) {
|
||||
|
|
@ -248,6 +237,14 @@ export default class DhCharacter extends BaseDataActor {
|
|||
this.evasion.total = (this.class?.evasion ?? 0) + this.evasion.bonus;
|
||||
this.proficiency.total = this.proficiency.value + this.proficiency.bonus;
|
||||
}
|
||||
|
||||
getRollData() {
|
||||
const data = super.getRollData();
|
||||
return {
|
||||
...data,
|
||||
tier: this.tier
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class DhPCLevelData extends foundry.abstract.DataModel {
|
||||
|
|
|
|||
|
|
@ -18,4 +18,4 @@ export const config = {
|
|||
damageRoll: DHDamageRoll,
|
||||
dualityRoll: DHDualityRoll,
|
||||
applyEffect: DHApplyEffect
|
||||
};
|
||||
};
|
||||
|
|
|
|||
139
module/data/countdowns.mjs
Normal file
139
module/data/countdowns.mjs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
import { countdownTypes } from '../config/generalConfig.mjs';
|
||||
import { RefreshType, socketEvent } from '../helpers/socket.mjs';
|
||||
|
||||
export default class DhCountdowns extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
narrative: new fields.EmbeddedDataField(DhCountdownData),
|
||||
encounter: new fields.EmbeddedDataField(DhCountdownData)
|
||||
};
|
||||
}
|
||||
|
||||
static CountdownCategories = { narrative: 'narrative', combat: 'combat' };
|
||||
}
|
||||
|
||||
class DhCountdownData extends foundry.abstract.DataModel {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.Countdown']; // Nots ure why this won't work. Setting labels manually for now
|
||||
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
countdowns: new fields.TypedObjectField(new fields.EmbeddedDataField(DhCountdown)),
|
||||
ownership: new fields.SchemaField({
|
||||
default: new fields.NumberField({
|
||||
required: true,
|
||||
choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS),
|
||||
initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE
|
||||
}),
|
||||
players: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
type: new fields.NumberField({
|
||||
required: true,
|
||||
choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS),
|
||||
initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
get playerOwnership() {
|
||||
return Array.from(game.users).reduce((acc, user) => {
|
||||
acc[user.id] = {
|
||||
value: user.isGM
|
||||
? CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER
|
||||
: this.ownership.players[user.id] && this.ownership.players[user.id].type !== -1
|
||||
? this.ownership.players[user.id].type
|
||||
: this.ownership.default,
|
||||
isGM: user.isGM
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
}
|
||||
|
||||
class DhCountdown extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
name: new fields.StringField({
|
||||
required: true,
|
||||
label: 'DAGGERHEART.Countdown.FIELDS.countdowns.element.name.label'
|
||||
}),
|
||||
img: new fields.FilePathField({
|
||||
categories: ['IMAGE'],
|
||||
base64: false,
|
||||
initial: 'icons/magic/time/hourglass-yellow-green.webp'
|
||||
}),
|
||||
ownership: new fields.SchemaField({
|
||||
default: new fields.NumberField({
|
||||
required: true,
|
||||
choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS),
|
||||
initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE
|
||||
}),
|
||||
players: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
type: new fields.NumberField({
|
||||
required: true,
|
||||
choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS),
|
||||
initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT
|
||||
})
|
||||
})
|
||||
)
|
||||
}),
|
||||
progress: new fields.SchemaField({
|
||||
current: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
initial: 1,
|
||||
label: 'DAGGERHEART.Countdown.FIELDS.countdowns.element.progress.current.label'
|
||||
}),
|
||||
max: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
initial: 1,
|
||||
label: 'DAGGERHEART.Countdown.FIELDS.countdowns.element.progress.max.label'
|
||||
}),
|
||||
type: new fields.SchemaField({
|
||||
value: new fields.StringField({
|
||||
required: true,
|
||||
choices: countdownTypes,
|
||||
initial: countdownTypes.spotlight.id,
|
||||
label: 'DAGGERHEART.Countdown.FIELDS.countdowns.element.progress.type.value.label'
|
||||
}),
|
||||
label: new fields.StringField({
|
||||
label: 'DAGGERHEART.Countdown.FIELDS.countdowns.element.progress.type.label.label'
|
||||
})
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
get playerOwnership() {
|
||||
return Array.from(game.users).reduce((acc, user) => {
|
||||
acc[user.id] = {
|
||||
value: user.isGM
|
||||
? CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER
|
||||
: this.ownership.players[user.id] && this.ownership.players[user.id].type !== -1
|
||||
? this.ownership.players[user.id].type
|
||||
: this.ownership.default,
|
||||
isGM: user.isGM
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
}
|
||||
|
||||
export const registerCountdownHooks = () => {
|
||||
Hooks.on(socketEvent.Refresh, ({ refreshType, application }) => {
|
||||
if (refreshType === RefreshType.Countdown) {
|
||||
foundry.applications.instances.get(application)?.render();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
export { default as FormulaField } from './formulaField.mjs';
|
||||
export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs';
|
||||
export { default as PseudoDocumentsField } from './pseudoDocumentsField.mjs';
|
||||
export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs';
|
||||
|
|
|
|||
20
module/data/fields/foreignDocumentUUIDArrayField.mjs
Normal file
20
module/data/fields/foreignDocumentUUIDArrayField.mjs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import ForeignDocumentUUIDField from './foreignDocumentUUIDField.mjs';
|
||||
/**
|
||||
* A subclass of {@link foundry.data.fields.ArrayField} that defines an array of foreign document UUID references.
|
||||
*/
|
||||
export default class ForeignDocumentUUIDArrayField extends foundry.data.fields.ArrayField {
|
||||
/**
|
||||
* @param {foundry.data.types.DocumentUUIDFieldOptions} [fieldOption] - Options to configure each individual ForeignDocumentUUIDField.
|
||||
* @param {foundry.data.types.ArrayFieldOptions} [options] - Options to configure the array behavior
|
||||
* @param {foundry.data.types.DataFieldContext} [context] - Optional context for schema processing
|
||||
*/
|
||||
constructor(fieldOption = {}, options = {}, context = {}) {
|
||||
super(new ForeignDocumentUUIDField(fieldOption), options, context);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
initialize(value, model, options = {}) {
|
||||
const v = super.initialize(value, model, options);
|
||||
return () => v.map(entry => (typeof entry === 'function' ? entry() : entry));
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ export default class ForeignDocumentUUIDField extends foundry.data.fields.Docume
|
|||
/**@override */
|
||||
initialize(value, _model, _options = {}) {
|
||||
if (this.idOnly) return value;
|
||||
return (() => {
|
||||
return () => {
|
||||
try {
|
||||
const doc = fromUuidSync(value);
|
||||
return doc;
|
||||
|
|
@ -31,7 +31,7 @@ export default class ForeignDocumentUUIDField extends foundry.data.fields.Docume
|
|||
console.error(error);
|
||||
return value ?? null;
|
||||
}
|
||||
})();
|
||||
};
|
||||
}
|
||||
|
||||
/**@override */
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
import PseudoDocument from '../pseudo-documents/base/pseudoDocument.mjs';
|
||||
|
||||
const { TypedObjectField, TypedSchemaField } = foundry.data.fields;
|
||||
|
||||
/**
|
||||
* @typedef _PseudoDocumentsFieldOptions
|
||||
* @property {Number} [max] - The maximum amount of elements (default: `Infinity`)
|
||||
* @property {String[]} [validTypes] - Allowed pseudo-documents types (default: `[]`)
|
||||
* @property {Function} [validateKey] - callback for validate keys of the object;
|
||||
|
||||
* @typedef {foundry.data.types.DataFieldOptions & _PseudoDocumentsFieldOptions} PseudoDocumentsFieldOptions
|
||||
*/
|
||||
export default class PseudoDocumentsField extends TypedObjectField {
|
||||
/**
|
||||
* @param {PseudoDocument} model - The PseudoDocument of each entry in this collection.
|
||||
* @param {PseudoDocumentsFieldOptions} [options] - Options which configure the behavior of the field
|
||||
* @param {foundry.data.types.DataFieldContext} [context] - Additional context which describes the field
|
||||
*/
|
||||
constructor(model, options = {}, context = {}) {
|
||||
options.validateKey ||= key => foundry.data.validators.isValidId(key);
|
||||
if (!foundry.utils.isSubclass(model, PseudoDocument)) throw new Error('The model must be a PseudoDocument');
|
||||
|
||||
const allTypes = model.TYPES;
|
||||
|
||||
const filteredTypes = options.validTypes
|
||||
? Object.fromEntries(
|
||||
Object.entries(allTypes).filter(([key]) => options.validTypes.includes(key))
|
||||
)
|
||||
: allTypes;
|
||||
|
||||
const field = new TypedSchemaField(filteredTypes);
|
||||
super(field, options, context);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
static get _defaults() {
|
||||
return Object.assign(super._defaults, {
|
||||
max: Infinity,
|
||||
validTypes: []
|
||||
});
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_validateType(value, options = {}) {
|
||||
if (Object.keys(value).length > this.max) throw new Error(`cannot have more than ${this.max} elements`);
|
||||
return super._validateType(value, options);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
initialize(value, model, options = {}) {
|
||||
if (!value) return;
|
||||
value = super.initialize(value, model, options);
|
||||
const collection = new foundry.utils.Collection(Object.values(value).map(d => [d._id, d]));
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,52 +5,49 @@
|
|||
* @property {string} type - The system type that this data model represents.
|
||||
* @property {boolean} hasDescription - Indicates whether items of this type have description field
|
||||
* @property {boolean} isQuantifiable - Indicates whether items of this type have quantity field
|
||||
* @property {Record<string,string>} embedded - Record of document names of pseudo-documents and the path to the collection
|
||||
*/
|
||||
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||
/** @returns {ItemDataModelMetadata}*/
|
||||
static get metadata() {
|
||||
return {
|
||||
label: "Base Item",
|
||||
type: "base",
|
||||
hasDescription: false,
|
||||
isQuantifiable: false,
|
||||
embedded: {},
|
||||
};
|
||||
}
|
||||
/** @returns {ItemDataModelMetadata}*/
|
||||
static get metadata() {
|
||||
return {
|
||||
label: 'Base Item',
|
||||
type: 'base',
|
||||
hasDescription: false,
|
||||
isQuantifiable: false
|
||||
};
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
static defineSchema() {
|
||||
const schema = {};
|
||||
/** @inheritDoc */
|
||||
static defineSchema() {
|
||||
const schema = {};
|
||||
|
||||
if (this.metadata.hasDescription)
|
||||
schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||
if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||
|
||||
if (this.metadata.isQuantifiable)
|
||||
schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true });
|
||||
if (this.metadata.isQuantifiable)
|
||||
schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true });
|
||||
|
||||
return schema;
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient access to the item's actor, if it exists.
|
||||
* @returns {foundry.documents.Actor | null}
|
||||
*/
|
||||
get actor() {
|
||||
return this.parent.actor;
|
||||
}
|
||||
/**
|
||||
* Convenient access to the item's actor, if it exists.
|
||||
* @returns {foundry.documents.Actor | null}
|
||||
*/
|
||||
get actor() {
|
||||
return this.parent.actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
|
||||
* @param {object} [options] - Options which modify the getRollData method.
|
||||
* @returns {object}
|
||||
*/
|
||||
getRollData(options = {}) {
|
||||
const actorRollData = this.actor?.getRollData() ?? {};
|
||||
const data = { ...actorRollData, item: { ...this } };
|
||||
return data;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
|
||||
* @param {object} [options] - Options which modify the getRollData method.
|
||||
* @returns {object}
|
||||
*/
|
||||
getRollData(options = {}) {
|
||||
const actorRollData = this.actor?.getRollData() ?? {};
|
||||
const data = { ...actorRollData, item: { ...this } };
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import BaseDataItem from './base.mjs';
|
||||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
||||
import ActionField from '../fields/actionField.mjs';
|
||||
|
||||
export default class DHClass extends BaseDataItem {
|
||||
|
|
@ -18,23 +19,16 @@ export default class DHClass extends BaseDataItem {
|
|||
return {
|
||||
...super.defineSchema(),
|
||||
domains: new fields.ArrayField(new fields.StringField(), { max: 2 }),
|
||||
classItems: new fields.ArrayField(new ForeignDocumentUUIDField({ type: 'Item' })),
|
||||
classItems: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
|
||||
|
||||
evasion: new fields.NumberField({ initial: 0, integer: true }),
|
||||
hopeFeatures: new foundry.data.fields.ArrayField(new ActionField()),
|
||||
classFeatures: new foundry.data.fields.ArrayField(new ActionField()),
|
||||
subclasses: new fields.ArrayField(
|
||||
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
|
||||
),
|
||||
subclasses: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
|
||||
inventory: new fields.SchemaField({
|
||||
take: new fields.ArrayField(
|
||||
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
|
||||
),
|
||||
choiceA: new fields.ArrayField(
|
||||
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
|
||||
),
|
||||
choiceB: new fields.ArrayField(
|
||||
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
|
||||
)
|
||||
take: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
|
||||
choiceA: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
|
||||
choiceB: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false })
|
||||
}),
|
||||
characterGuide: new fields.SchemaField({
|
||||
suggestedTraits: new fields.SchemaField({
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import BaseDataItem from "./base.mjs";
|
||||
import BaseDataItem from './base.mjs';
|
||||
import ActionField from '../fields/actionField.mjs';
|
||||
|
||||
export default class DHConsumable extends BaseDataItem {
|
||||
/** @inheritDoc */
|
||||
/** @inheritDoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: "TYPES.Item.consumable",
|
||||
type: "consumable",
|
||||
label: 'TYPES.Item.consumable',
|
||||
type: 'consumable',
|
||||
hasDescription: true,
|
||||
isQuantifiable: true,
|
||||
isQuantifiable: true
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ export default class DHMiscellaneous extends BaseDataItem {
|
|||
/** @inheritDoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: "TYPES.Item.miscellaneous",
|
||||
type: "miscellaneous",
|
||||
label: 'TYPES.Item.miscellaneous',
|
||||
type: 'miscellaneous',
|
||||
hasDescription: true,
|
||||
isQuantifiable: true,
|
||||
isQuantifiable: true
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import ActionField from '../fields/actionField.mjs';
|
||||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
||||
import BaseDataItem from './base.mjs';
|
||||
|
||||
const featureSchema = () => {
|
||||
return new foundry.data.fields.SchemaField({
|
||||
name: new foundry.data.fields.StringField({ required: true }),
|
||||
effects: new foundry.data.fields.ArrayField(new ForeignDocumentUUIDField({ type: 'ActiveEffect' })),
|
||||
effects: new ForeignDocumentUUIDArrayField({ type: 'ActiveEffect', required: false }),
|
||||
actions: new foundry.data.fields.ArrayField(new ActionField())
|
||||
});
|
||||
};
|
||||
|
|
@ -64,7 +64,7 @@ export default class DHSubclass extends BaseDataItem {
|
|||
} else if (subclassData) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.SubclassAlreadySelected'));
|
||||
return false;
|
||||
} else if (classData.system.subclasses.every(x => x.uuid !== `Item.${data._id}`)) {
|
||||
} else if (classData.system.subclasses.every(x => x.uuid !== (data.uuid ?? `Item.${data._id}`))) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.SubclassNotInClass'));
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
export { default as base } from './base/pseudoDocument.mjs';
|
||||
export * as feature from './feature/_module.mjs';
|
||||
|
|
@ -1,213 +0,0 @@
|
|||
/**
|
||||
* @typedef {object} PseudoDocumentMetadata
|
||||
* @property {string} name - The document name of this pseudo-document
|
||||
* @property {Record<string, string>} embedded - Record of document names and their collection paths
|
||||
* @property {typeof foundry.applications.api.ApplicationV2} [sheetClass] - The class used to render this pseudo-document
|
||||
* @property {string} defaultArtwork - The default image used for newly created documents
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class Base class for pseudo-documents
|
||||
* @extends {foundry.abstract.DataModel}
|
||||
*/
|
||||
export default class BasePseudoDocument extends foundry.abstract.DataModel {
|
||||
/**
|
||||
* Pseudo-document metadata.
|
||||
* @returns {PseudoDocumentMetadata}
|
||||
*/
|
||||
static get metadata() {
|
||||
return {
|
||||
name: '',
|
||||
embedded: {},
|
||||
defaultArtwork: foundry.documents.Item.DEFAULT_ICON,
|
||||
sheetClass: CONFIG.daggerheart.pseudoDocuments.sheetClass,
|
||||
};
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static LOCALIZATION_PREFIXES = ['DOCUMENT'];
|
||||
|
||||
/** @inheritdoc */
|
||||
static defineSchema() {
|
||||
const { fields } = foundry.data;
|
||||
|
||||
return {
|
||||
_id: new fields.DocumentIdField({ initial: () => foundry.utils.randomID() }),
|
||||
name: new fields.StringField({ required: true, blank: false, textSearch: true }),
|
||||
img: new fields.FilePathField({ categories: ['IMAGE'], initial: this.metadata.defaultArtwork }),
|
||||
description: new fields.HTMLField({ textSearch: true })
|
||||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Instance Properties */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* The id of this pseudo-document.
|
||||
* @type {string}
|
||||
*/
|
||||
get id() {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* The uuid of this document.
|
||||
* @type {string}
|
||||
*/
|
||||
get uuid() {
|
||||
let parent = this.parent;
|
||||
while (!(parent instanceof BasePseudoDocument) && !(parent instanceof foundry.abstract.Document))
|
||||
parent = parent.parent;
|
||||
return [parent.uuid, this.constructor.metadata.name, this.id].join('.');
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* The parent document of this pseudo-document.
|
||||
* @type {foundry.abstract.Document}
|
||||
*/
|
||||
get document() {
|
||||
let parent = this;
|
||||
while (!(parent instanceof foundry.abstract.Document)) parent = parent.parent;
|
||||
return parent;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Item to which this PseudoDocument belongs, if applicable.
|
||||
* @type {foundry.documents.Item|null}
|
||||
*/
|
||||
get item() {
|
||||
return this.parent?.parent instanceof Item ? this.parent.parent : null;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Actor to which this PseudoDocument's item belongs, if the item is embedded.
|
||||
* @type {foundry.documents.Actor|null}
|
||||
*/
|
||||
get actor() {
|
||||
return this.item?.parent ?? null;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* The property path to this pseudo document relative to its parent document.
|
||||
* @type {string}
|
||||
*/
|
||||
get fieldPath() {
|
||||
const fp = this.schema.fieldPath;
|
||||
let path = fp.slice(0, fp.lastIndexOf('element') - 1);
|
||||
|
||||
if (this.parent instanceof BasePseudoDocument) {
|
||||
path = [this.parent.fieldPath, this.parent.id, path].join('.');
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Embedded Document Methods */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Retrieve an embedded pseudo-document.
|
||||
* @param {string} embeddedName The document name of the embedded pseudo-document.
|
||||
* @param {string} id The id of the embedded pseudo-document.
|
||||
* @param {object} [options] Retrieval options.
|
||||
* @param {boolean} [options.strinct] Throw an error if the embedded pseudo-document does not exist?
|
||||
* @returns {PseudoDocument|null}
|
||||
*/
|
||||
getEmbeddedDocument(embeddedName, id, { strict = false } = {}) {
|
||||
const embeds = this.constructor.metadata.embedded ?? {};
|
||||
if (embeddedName in embeds) {
|
||||
return foundry.utils.getProperty(this, embeds[embeddedName]).get(id, { strict }) ?? null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* CRUD Operations */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Does this pseudo-document exist in the document's source?
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isSource() {
|
||||
const source = foundry.utils.getProperty(this.document._source, this.fieldPath);
|
||||
if (foundry.utils.getType(source) !== 'Object') {
|
||||
throw new Error('Source is not an object!');
|
||||
}
|
||||
return this.id in source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of this pseudo-document.
|
||||
* @param {object} [data] The data used for the creation.
|
||||
* @param {object} operation The context of the update operation.
|
||||
* @param {foundry.abstract.Document} operation.parent The parent of this document.
|
||||
* @returns {Promise<foundry.abstract.Document>} A promise that resolves to the updated document.
|
||||
*/
|
||||
static async create(data = {}, { parent, ...operation } = {}) {
|
||||
if (!parent) {
|
||||
throw new Error('A parent document must be specified for the creation of a pseudo-document!');
|
||||
}
|
||||
const id =
|
||||
operation.keepId && foundry.data.validators.isValidId(data._id) ? data._id : foundry.utils.randomID();
|
||||
|
||||
const fieldPath = parent.system.constructor.metadata.embedded?.[this.metadata.name];
|
||||
if (!fieldPath) {
|
||||
throw new Error(
|
||||
`A ${parent.documentName} of type '${parent.type}' does not support ${this.metadata.name}!`
|
||||
);
|
||||
}
|
||||
|
||||
const update = { [`system.${fieldPath}.${id}`]: { ...data, _id: id } };
|
||||
const updatedParent = await parent.update(update, operation);
|
||||
return foundry.utils.getProperty(updatedParent, `system.${fieldPath}.${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this pseudo-document.
|
||||
* @param {object} [operation] The context of the operation.
|
||||
* @returns {Promise<foundry.abstract.Document>} A promise that resolves to the updated document.
|
||||
*/
|
||||
async delete(operation = {}) {
|
||||
if (!this.isSource) throw new Error('You cannot delete a non-source pseudo-document!');
|
||||
const update = { [`${this.fieldPath}.-=${this.id}`]: null };
|
||||
return this.document.update(update, operation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate this pseudo-document.
|
||||
* @returns {Promise<foundry.abstract.Document>} A promise that resolves to the updated document.
|
||||
*/
|
||||
async duplicate() {
|
||||
if (!this.isSource) throw new Error('You cannot duplicate a non-source pseudo-document!');
|
||||
const docData = foundry.utils.mergeObject(this.toObject(), {
|
||||
name: game.i18n.format('DOCUMENT.CopyOf', { name: this.name })
|
||||
});
|
||||
return this.constructor.create(docData, { parent: this.document });
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this pseudo-document.
|
||||
* @param {object} [change] The change to perform.
|
||||
* @param {object} [operation] The context of the operation.
|
||||
* @returns {Promise<foundry.abstract.Document>} A promise that resolves to the updated document.
|
||||
*/
|
||||
async update(change = {}, operation = {}) {
|
||||
if (!this.isSource) throw new Error('You cannot update a non-source pseudo-document!');
|
||||
const path = [this.fieldPath, this.id].join('.');
|
||||
const update = { [path]: change };
|
||||
return this.document.update(update, operation);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
import BasePseudoDocument from './base.mjs';
|
||||
import SheetManagementMixin from './sheetManagementMixin.mjs';
|
||||
|
||||
/** @extends BasePseudoDocument */
|
||||
export default class PseudoDocument extends SheetManagementMixin(BasePseudoDocument) {
|
||||
static get TYPES() {
|
||||
const { types } = CONFIG.daggerheart.pseudoDocuments[this.metadata.name];
|
||||
const typeEntries = Object.entries(types).map(([key, { documentClass }]) => [key, documentClass]);
|
||||
return (this._TYPES ??= Object.freeze(Object.fromEntries(typeEntries)));
|
||||
}
|
||||
|
||||
static _TYPES;
|
||||
|
||||
/**
|
||||
* The type of this shape.
|
||||
* @type {string}
|
||||
*/
|
||||
static TYPE = '';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
static getTypesChoices(validTypes) {
|
||||
const { types } = CONFIG.daggerheart.pseudoDocuments[model.metadata.name];
|
||||
const typeEntries = Object.entries(types)
|
||||
.map(([key, { label }]) => [key, label])
|
||||
.filter(([key]) => !validTypes || validTypes.includes(key));
|
||||
|
||||
return Object.entries(typeEntries);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
static defineSchema() {
|
||||
const { fields } = foundry.data;
|
||||
|
||||
return Object.assign(super.defineSchema(), {
|
||||
type: new fields.StringField({
|
||||
required: true,
|
||||
blank: false,
|
||||
initial: this.TYPE,
|
||||
validate: value => value === this.TYPE,
|
||||
validationError: `must be equal to "${this.TYPE}"`
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
static async create(data = {}, { parent, ...operation } = {}) {
|
||||
data = foundry.utils.deepClone(data);
|
||||
if (!data.type) data.type = Object.keys(this.TYPES)[0];
|
||||
if (!(data.type in this.TYPES)) {
|
||||
throw new Error(
|
||||
`The '${data.type}' type is not a valid type for a '${this.metadata.documentName}' pseudo-document!`
|
||||
);
|
||||
}
|
||||
return super.create(data, { parent, ...operation });
|
||||
}
|
||||
}
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
import BasePseudoDocument from './base.mjs';
|
||||
const { ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
/**
|
||||
* A mixin that adds sheet management capabilities to pseudo-documents
|
||||
* @template {typeof BasePseudoDocument} T
|
||||
* @param {T} Base
|
||||
* @returns {T & typeof PseudoDocumentWithSheets}
|
||||
*/
|
||||
export default function SheetManagementMixin(Base) {
|
||||
class PseudoDocumentWithSheets extends Base {
|
||||
/**
|
||||
* Reference to the sheet of this pseudo-document.
|
||||
* @type {ApplicationV2|null}
|
||||
*/
|
||||
get sheet() {
|
||||
if (this._sheet) return this._sheet;
|
||||
const cls = this.constructor.metadata.sheetClass ?? ApplicationV2;
|
||||
|
||||
if (!foundry.utils.isSubclass(cls, ApplicationV2)) {
|
||||
return void ui.notifications.error(
|
||||
'Daggerheart | Error on PseudoDocument | sheetClass must be ApplicationV2'
|
||||
);
|
||||
}
|
||||
|
||||
const sheet = new cls({ document: this });
|
||||
this._sheet = sheet;
|
||||
return sheet;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Static Properties */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set of apps what should be re-render.
|
||||
* @type {Set<ApplicationV2>}
|
||||
* @internal
|
||||
*/
|
||||
_apps = new Set();
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Existing sheets of a specific type for a specific document.
|
||||
* @type {ApplicationV2 | null}
|
||||
*/
|
||||
_sheet = null;
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Display Methods */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Render all the Application instances which are connected to this PseudoDocument.
|
||||
* @param {ApplicationRenderOptions} [options] Rendering options.
|
||||
*/
|
||||
render(options) {
|
||||
for (const app of this._apps ?? []) {
|
||||
app.render({ window: { title: app.title }, ...options });
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Register an application to respond to updates to a certain document.
|
||||
* @param {ApplicationV2} app Application to update.
|
||||
* @internal
|
||||
*/
|
||||
_registerApp(app) {
|
||||
this._apps.add(app);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Remove an application from the render registry.
|
||||
* @param {ApplicationV2} app Application to stop watching.
|
||||
*/
|
||||
_unregisterApp(app) {
|
||||
this._apps.delete(app);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Drag and Drop */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Serialize salient information for this PseudoDocument when dragging it.
|
||||
* @returns {object} An object of drag data.
|
||||
*/
|
||||
toDragData() {
|
||||
const dragData = { type: this.documentName, data: this.toObject() };
|
||||
if (this.id) dragData.uuid = this.uuid;
|
||||
return dragData;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Dialog Methods */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Spawn a dialog for creating a new PseudoDocument.
|
||||
* @param {object} [data] Data to pre-populate the document with.
|
||||
* @param {object} context
|
||||
* @param {foundry.documents.Item} context.parent A parent for the document.
|
||||
* @param {string[]|null} [context.types] A list of types to restrict the choices to, or null for no restriction.
|
||||
* @returns {Promise<BasePseudoDocument|null>}
|
||||
*/
|
||||
static async createDialog(data = {}, { parent, types = null, ...options } = {}) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Present a Dialog form to confirm deletion of this PseudoDocument.
|
||||
* @param {object} [options] - Additional options passed to `DialogV2.confirm`;
|
||||
* @returns {Promise<foundry.abstract.Document>} A Promise which resolves to the deleted PseudoDocument.
|
||||
*/
|
||||
async deleteDialog(options = {}) {
|
||||
const type = game.i18n.localize(this.constructor.metadata.label);
|
||||
const content = options.content ?? `<p>
|
||||
<strong>${game.i18n.localize("AreYouSure")}</strong>
|
||||
${game.i18n.format("SIDEBAR.DeleteWarning", { type })}
|
||||
</p>`;
|
||||
|
||||
return foundry.applications.api.DialogV2.confirm({
|
||||
content,
|
||||
yes: { callback: () => this.delete(operation) },
|
||||
window: {
|
||||
icon: "fa-solid fa-trash",
|
||||
title: `${game.i18n.format("DOCUMENT.Delete", { type })}: ${this.name}`
|
||||
},
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default new name for a Document
|
||||
* @param {object} collection - Collection of Documents
|
||||
* @returns {string}
|
||||
*/
|
||||
static defaultName(collection) {
|
||||
const documentName = this.metadata.name;
|
||||
const takenNames = new Set();
|
||||
for (const document of collection) takenNames.add(document.name);
|
||||
|
||||
const config = CONFIG.daggerheart.pseudoDocuments[documentName];
|
||||
const baseName = game.i18n.localize(config.label);
|
||||
let name = baseName;
|
||||
let index = 1;
|
||||
while (takenNames.has(name)) name = `${baseName} (${++index})`;
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
return PseudoDocumentWithSheets;
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
export { default as BaseFeatureData } from './baseFeatureData.mjs';
|
||||
export { default as WeaponFeature } from './weaponFeature.mjs';
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import PseudoDocument from '../base/pseudoDocument.mjs';
|
||||
|
||||
export default class BaseFeatureData extends PseudoDocument {
|
||||
/**@inheritdoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(
|
||||
super.metadata,
|
||||
{
|
||||
name: 'feature',
|
||||
embedded: {},
|
||||
//sheetClass: null //TODO: define feature-sheet
|
||||
},
|
||||
{ inplace: false }
|
||||
);
|
||||
}
|
||||
|
||||
static defineSchema() {
|
||||
const { fields } = foundry.data;
|
||||
const schema = super.defineSchema();
|
||||
return Object.assign(schema, {
|
||||
subtype: new fields.StringField({ initial: 'test' })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import BaseFeatureData from './baseFeatureData.mjs';
|
||||
|
||||
export default class WeaponFeature extends BaseFeatureData {
|
||||
/**@override */
|
||||
static TYPE = 'weapon';
|
||||
}
|
||||
|
|
@ -1,7 +1,15 @@
|
|||
import { fearDisplay } from '../../config/generalConfig.mjs';
|
||||
|
||||
export default class DhAppearance extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
displayFear: new fields.StringField({
|
||||
required: true,
|
||||
choices: fearDisplay,
|
||||
initial: fearDisplay.token.value,
|
||||
label: 'DAGGERHEART.Settings.Appearance.FIELDS.displayFear.label'
|
||||
}),
|
||||
dualityColorScheme: new fields.StringField({
|
||||
required: true,
|
||||
choices: DualityRollColor,
|
||||
|
|
@ -35,8 +43,6 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
|||
})
|
||||
};
|
||||
}
|
||||
|
||||
static defaultSchema = {};
|
||||
}
|
||||
|
||||
export const DualityRollColor = {
|
||||
|
|
|
|||
12
module/data/settings/Automation.mjs
Normal file
12
module/data/settings/Automation.mjs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export default class DhAutomation extends foundry.abstract.DataModel {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.Settings.Automation']; // Doesn't work for some reason
|
||||
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
hope: new fields.BooleanField({ required: true, initial: false }),
|
||||
actionPoints: new fields.BooleanField({ required: true, initial: false }),
|
||||
countdowns: new fields.BooleanField({ requireD: true, initial: false })
|
||||
};
|
||||
}
|
||||
}
|
||||
55
module/data/settings/Homebrew.mjs
Normal file
55
module/data/settings/Homebrew.mjs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { defaultRestOptions } from '../../config/generalConfig.mjs';
|
||||
|
||||
export default class DhHomebrew extends foundry.abstract.DataModel {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.Settings.Homebrew']; // Doesn't work for some reason
|
||||
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
maxFear: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
min: 0,
|
||||
initial: 12,
|
||||
label: 'DAGGERHEART.Settings.Homebrew.FIELDS.maxFear.label'
|
||||
}),
|
||||
traitArray: new fields.ArrayField(new fields.NumberField({ required: true, integer: true }), {
|
||||
initial: () => [2, 1, 1, 0, 0, -1]
|
||||
}),
|
||||
restMoves: new fields.SchemaField({
|
||||
longRest: new fields.SchemaField({
|
||||
nrChoices: new fields.NumberField({ required: true, integer: true, min: 1, initial: 2 }),
|
||||
moves: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true }),
|
||||
img: new fields.FilePathField({
|
||||
initial: 'icons/magic/life/cross-worn-green.webp',
|
||||
categories: ['IMAGE'],
|
||||
base64: false
|
||||
}),
|
||||
description: new fields.HTMLField(),
|
||||
actions: new fields.ArrayField(new fields.ObjectField())
|
||||
}),
|
||||
{ initial: defaultRestOptions.longRest() }
|
||||
)
|
||||
}),
|
||||
shortRest: new fields.SchemaField({
|
||||
nrChoices: new fields.NumberField({ required: true, integer: true, min: 1, initial: 2 }),
|
||||
moves: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true }),
|
||||
img: new fields.FilePathField({
|
||||
initial: 'icons/magic/life/cross-worn-green.webp',
|
||||
categories: ['IMAGE'],
|
||||
base64: false
|
||||
}),
|
||||
description: new fields.HTMLField(),
|
||||
actions: new fields.ArrayField(new fields.ObjectField())
|
||||
}),
|
||||
{ initial: defaultRestOptions.shortRest() }
|
||||
)
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
17
module/data/settings/RangeMeasurement.mjs
Normal file
17
module/data/settings/RangeMeasurement.mjs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
export default class DhRangeMeasurement extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
enabled: new fields.BooleanField({ required: true, initial: false, label: 'DAGGERHEART.General.Enabled' }),
|
||||
melee: new fields.NumberField({ required: true, initial: 5, label: 'DAGGERHEART.Range.melee.name' }),
|
||||
veryClose: new fields.NumberField({
|
||||
required: true,
|
||||
initial: 15,
|
||||
label: 'DAGGERHEART.Range.veryClose.name'
|
||||
}),
|
||||
close: new fields.NumberField({ required: true, initial: 30, label: 'DAGGERHEART.Range.close.name' }),
|
||||
far: new fields.NumberField({ required: true, initial: 60, label: 'DAGGERHEART.Range.far.name' }),
|
||||
veryFar: new fields.NumberField({ required: true, initial: 120, label: 'DAGGERHEART.Range.veryFar.name' })
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -5,12 +5,22 @@ export default class DhVariantRules extends foundry.abstract.DataModel {
|
|||
const fields = foundry.data.fields;
|
||||
return {
|
||||
actionTokens: new fields.SchemaField({
|
||||
enabled: new fields.BooleanField({ required: true, initial: false }),
|
||||
tokens: new fields.NumberField({ required: true, integer: true, initial: 3 })
|
||||
enabled: new fields.BooleanField({
|
||||
required: true,
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.Settings.VariantRules.FIELDS.actionTokens.enabled.label'
|
||||
}),
|
||||
tokens: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
initial: 3,
|
||||
label: 'DAGGERHEART.Settings.VariantRules.FIELDS.actionTokens.tokens.label'
|
||||
})
|
||||
}),
|
||||
useCoins: new fields.BooleanField({ initial: false })
|
||||
useCoins: new fields.BooleanField({
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.Settings.VariantRules.FIELDS.useCoins.label'
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
static defaultSchema = {};
|
||||
}
|
||||
|
|
|
|||
7
module/data/settings/_module.mjs
Normal file
7
module/data/settings/_module.mjs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import DhAppearance from './Appearance.mjs';
|
||||
import DhAutomation from './Automation.mjs';
|
||||
import DhHomebrew from './Homebrew.mjs';
|
||||
import DhRangeMeasurement from './RangeMeasurement.mjs';
|
||||
import DhVariantRules from './VariantRules.mjs';
|
||||
|
||||
export { DhAppearance, DhAutomation, DhHomebrew, DhRangeMeasurement, DhVariantRules };
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
export default class SelectDialog extends Dialog {
|
||||
constructor(data, options) {
|
||||
super(options);
|
||||
|
||||
this.data = {
|
||||
title: data.title,
|
||||
buttons: data.buttons,
|
||||
content: foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/dialog/item-select.hbs',
|
||||
{
|
||||
items: data.choices
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
this.actor = data.actor;
|
||||
this.actionCostMax = data.actionCostMax;
|
||||
this.nrChoices = data.nrChoices;
|
||||
this.validate = data.validate;
|
||||
}
|
||||
|
||||
async getData(options = {}) {
|
||||
let buttons = Object.keys(this.data.buttons).reduce((obj, key) => {
|
||||
let b = this.data.buttons[key];
|
||||
b.cssClass = (this.data.default === key ? [key, 'default', 'bright'] : [key]).join(' ');
|
||||
if (b.condition !== false) obj[key] = b;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
const content = await this.data.content;
|
||||
|
||||
return {
|
||||
content: content,
|
||||
buttons: buttons
|
||||
};
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
$(html).find('.item-button').click(this.selectChoice);
|
||||
}
|
||||
|
||||
selectChoice = async event => {
|
||||
if (this.validate) {
|
||||
if (!this.validate(event.currentTarget.dataset.validateProp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
event.currentTarget.classList.toggle('checked');
|
||||
$(event.currentTarget).find('i')[0].classList.toggle('checked');
|
||||
|
||||
const buttons = $(this.element[0]).find('button.checked');
|
||||
if (buttons.length === this.nrChoices) {
|
||||
$(event.currentTarget).closest('.window-content').find('.confirm')[0].disabled = false;
|
||||
} else {
|
||||
$(event.currentTarget).closest('.window-content').find('.confirm')[0].disabled = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} data
|
||||
* choices, actor, title, cancelMessage, nrChoices, validate
|
||||
* @returns
|
||||
*/
|
||||
static async selectItem(data) {
|
||||
return this.wait({
|
||||
title: data.title ?? 'Selection',
|
||||
buttons: {
|
||||
no: {
|
||||
icon: '<i class="fas fa-times"></i>',
|
||||
label: game.i18n.localize('DAGGERHEART.General.Cancel'),
|
||||
callback: _ => {
|
||||
if (data.cancelMessage) {
|
||||
ChatMessage.create({ content: data.cancelMessage });
|
||||
}
|
||||
return [];
|
||||
}
|
||||
},
|
||||
confirm: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: game.i18n.localize('DAGGERHEART.General.OK'),
|
||||
callback: html => {
|
||||
const buttons = $(html).find('button.checked');
|
||||
return buttons.map(key => Number.parseInt(buttons[key].dataset.index)).toArray();
|
||||
},
|
||||
disabled: true
|
||||
}
|
||||
},
|
||||
choices: data.choices,
|
||||
actor: data.actor,
|
||||
nrChoices: data.nrChoices ?? 1,
|
||||
validate: data.validate
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -324,7 +324,7 @@ export default class DhpActor extends Actor {
|
|||
);
|
||||
|
||||
if (this.type === 'character') {
|
||||
const automateHope = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope);
|
||||
const automateHope = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).hope;
|
||||
|
||||
if (automateHope && result.hopeUsed) {
|
||||
await this.update({
|
||||
|
|
@ -354,7 +354,7 @@ export default class DhpActor extends Actor {
|
|||
hope = roll.dice[0].results[0].result;
|
||||
fear = roll.dice[1].results[0].result;
|
||||
if (
|
||||
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope) &&
|
||||
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).hope &&
|
||||
config.roll.type === 'action'
|
||||
) {
|
||||
if (hope > fear) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { abilities } from '../config/actorConfig.mjs';
|
||||
import { rollCommandToJSON } from '../helpers/utils.mjs';
|
||||
import { getCommandTarget, rollCommandToJSON } from '../helpers/utils.mjs';
|
||||
|
||||
export function dualityRollEnricher(match, _options) {
|
||||
export default function DhDualityRollEnricher(match, _options) {
|
||||
const roll = rollCommandToJSON(match[1]);
|
||||
if (!roll) return match[0];
|
||||
|
||||
|
|
@ -39,3 +39,24 @@ export function getDualityMessage(roll) {
|
|||
|
||||
return dualityElement;
|
||||
}
|
||||
|
||||
export const renderDualityButton = async event => {
|
||||
const button = event.currentTarget,
|
||||
traitValue = button.dataset.trait?.toLowerCase(),
|
||||
target = getCommandTarget();
|
||||
if (!target) return;
|
||||
|
||||
const config = {
|
||||
event: event,
|
||||
title: button.dataset.title,
|
||||
roll: {
|
||||
modifier: traitValue ? target.system.traits[traitValue].value : null,
|
||||
label: button.dataset.label,
|
||||
type: button.dataset.actionType ?? null // Need check
|
||||
},
|
||||
chatMessage: {
|
||||
template: 'systems/daggerheart/templates/chat/duality-roll.hbs'
|
||||
}
|
||||
};
|
||||
await target.diceRoll(config);
|
||||
};
|
||||
|
|
|
|||
60
module/enrichers/TemplateEnricher.mjs
Normal file
60
module/enrichers/TemplateEnricher.mjs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import { range as configRange } from '../config/generalConfig.mjs';
|
||||
|
||||
export default function DhTemplateEnricher(match, _options) {
|
||||
const parts = match[1].split('|').map(x => x.trim());
|
||||
|
||||
let type = null,
|
||||
range = null;
|
||||
|
||||
parts.forEach(part => {
|
||||
const split = part.split(':').map(x => x.toLowerCase().trim());
|
||||
if (split.length === 2) {
|
||||
switch (split[0]) {
|
||||
case 'type':
|
||||
const matchedType = Object.values(CONST.MEASURED_TEMPLATE_TYPES).find(
|
||||
x => x.toLowerCase() === split[1]
|
||||
);
|
||||
type = matchedType;
|
||||
break;
|
||||
case 'range':
|
||||
const matchedRange = Object.values(configRange).find(
|
||||
x => x.id.toLowerCase() === split[1] || x.short === split[1]
|
||||
);
|
||||
range = matchedRange?.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!type || !range) return match[0];
|
||||
|
||||
const templateElement = document.createElement('span');
|
||||
templateElement.innerHTML = `
|
||||
<button class="measured-template-button" data-type="${type}" data-range="${range}">
|
||||
${game.i18n.localize(`TEMPLATE.TYPES.${type}`)} - ${game.i18n.localize(`DAGGERHEART.Range.${range}.name`)}
|
||||
</button>
|
||||
`;
|
||||
|
||||
return templateElement;
|
||||
}
|
||||
|
||||
export const renderMeasuredTemplate = async event => {
|
||||
const button = event.currentTarget,
|
||||
type = button.dataset.type,
|
||||
range = button.dataset.range;
|
||||
|
||||
if (!type || !range || !game.canvas.scene) return;
|
||||
|
||||
const distance = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement)[range];
|
||||
const { width, height } = game.canvas.scene.dimensions;
|
||||
canvas.scene.createEmbeddedDocuments('MeasuredTemplate', [
|
||||
{
|
||||
x: width / 2,
|
||||
y: height / 2,
|
||||
t: type,
|
||||
distance: distance,
|
||||
width: type === CONST.MEASURED_TEMPLATE_TYPES.RAY ? 5 : undefined,
|
||||
angle: type === CONST.MEASURED_TEMPLATE_TYPES.CONE ? CONFIG.MeasuredTemplate.defaults.angle : undefined
|
||||
}
|
||||
]);
|
||||
};
|
||||
4
module/enrichers/_module.mjs
Normal file
4
module/enrichers/_module.mjs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import DhDualityRollEnricher from './DualityRollEnricher.mjs';
|
||||
import DhTemplateEnricher from './TemplateEnricher.mjs';
|
||||
|
||||
export { DhDualityRollEnricher, DhTemplateEnricher };
|
||||
|
|
@ -1,20 +1,68 @@
|
|||
export function handleSocketEvent({ action = null, data = {} } = {}) {
|
||||
switch (action) {
|
||||
case socketEvent.GMUpdate:
|
||||
Hooks.callAll(socketEvent.GMUpdate, data.action, data.uuid, data.update);
|
||||
Hooks.callAll(socketEvent.GMUpdate, data);
|
||||
break;
|
||||
case socketEvent.DhpFearUpdate:
|
||||
Hooks.callAll(socketEvent.DhpFearUpdate);
|
||||
break;
|
||||
case socketEvent.Refresh:
|
||||
Hooks.call(socketEvent.Refresh, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export const socketEvent = {
|
||||
GMUpdate: 'DhpGMUpdate',
|
||||
DhpFearUpdate: 'DhpFearUpdate'
|
||||
GMUpdate: 'DhGMUpdate',
|
||||
Refresh: 'DhRefresh',
|
||||
DhpFearUpdate: 'DhFearUpdate'
|
||||
};
|
||||
|
||||
export const GMUpdateEvent = {
|
||||
UpdateDocument: 'DhpGMUpdateDocument',
|
||||
UpdateFear: 'DhpUpdateFear'
|
||||
UpdateDocument: 'DhGMUpdateDocument',
|
||||
UpdateSetting: 'DhGMUpdateSetting',
|
||||
UpdateFear: 'DhGMUpdateFear'
|
||||
};
|
||||
|
||||
export const RefreshType = {
|
||||
Countdown: 'DhCoundownRefresh'
|
||||
};
|
||||
|
||||
export const registerSocketHooks = () => {
|
||||
Hooks.on(socketEvent.GMUpdate, async data => {
|
||||
if (game.user.isGM) {
|
||||
const document = data.uuid ? await fromUuid(data.uuid) : null;
|
||||
switch (data.action) {
|
||||
case GMUpdateEvent.UpdateDocument:
|
||||
if (document && data.update) {
|
||||
await document.update(data.update);
|
||||
}
|
||||
break;
|
||||
case GMUpdateEvent.UpdateSetting:
|
||||
if (game.user.isGM) {
|
||||
await game.settings.set(SYSTEM.id, data.uuid, data.update);
|
||||
}
|
||||
break;
|
||||
case GMUpdateEvent.UpdateFear:
|
||||
if (game.user.isGM) {
|
||||
await game.settings.set(
|
||||
SYSTEM.id,
|
||||
SYSTEM.SETTINGS.gameSettings.Resources.Fear,
|
||||
Math.max(Math.min(data.update, 6), 0)
|
||||
);
|
||||
Hooks.callAll(socketEvent.DhpFearUpdate);
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, { action: socketEvent.DhpFearUpdate });
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (data.refresh) {
|
||||
await game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
action: socketEvent.Refresh,
|
||||
data: data.refresh
|
||||
});
|
||||
Hooks.call(socketEvent.Refresh, data.refresh);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
|||
3
module/placeables/_module.mjs
Normal file
3
module/placeables/_module.mjs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import DhMeasuredTemplate from './measuredTemplate.mjs';
|
||||
|
||||
export { DhMeasuredTemplate };
|
||||
35
module/placeables/measuredTemplate.mjs
Normal file
35
module/placeables/measuredTemplate.mjs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
export default class DhMeasuredTemplate extends foundry.canvas.placeables.MeasuredTemplate {
|
||||
_refreshRulerText() {
|
||||
super._refreshRulerText();
|
||||
|
||||
const rangeMeasurementSettings = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement);
|
||||
if (rangeMeasurementSettings.enabled) {
|
||||
const splitRulerText = this.ruler.text.split(' ');
|
||||
if (splitRulerText.length > 0) {
|
||||
const rulerValue = Number(splitRulerText[0]);
|
||||
const vagueLabel = this.constructor.getDistanceLabel(rulerValue, rangeMeasurementSettings);
|
||||
this.ruler.text = vagueLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static getDistanceLabel(distance, settings) {
|
||||
if (distance <= settings.melee) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.melee.name');
|
||||
}
|
||||
if (distance <= settings.veryClose) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.veryClose.name');
|
||||
}
|
||||
if (distance <= settings.close) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.close.name');
|
||||
}
|
||||
if (distance <= settings.far) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.far.name');
|
||||
}
|
||||
if (distance <= settings.veryFar) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.veryFar.name');
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { actionsTypes } from '../data/_module.mjs';
|
||||
|
||||
export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog {
|
||||
constructor() {
|
||||
super();
|
||||
|
|
@ -44,6 +46,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
html.querySelectorAll('.ability-use-button').forEach(element =>
|
||||
element.addEventListener('click', event => this.abilityUseButton.bind(this)(event, data.message))
|
||||
);
|
||||
html.querySelectorAll('.action-use-button').forEach(element =>
|
||||
element.addEventListener('click', event => this.actionUseButton.bind(this)(event, data.message))
|
||||
);
|
||||
};
|
||||
|
||||
setupHooks() {
|
||||
|
|
@ -173,4 +178,16 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
const actor = game.actors.get(message.system.source.actor);
|
||||
await actor.useAction(action);
|
||||
};
|
||||
|
||||
actionUseButton = async (_, message) => {
|
||||
const parent = await foundry.utils.fromUuid(message.system.actor);
|
||||
const testAction = Object.values(message.system.moves)[0].actions[0];
|
||||
const cls = actionsTypes[testAction.type];
|
||||
const action = new cls(
|
||||
{ ...testAction, _id: foundry.utils.randomID(), name: game.i18n.localize(testAction.name) },
|
||||
{ parent: parent }
|
||||
);
|
||||
|
||||
action.use();
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import { EncounterCountdowns } from '../applications/countdowns.mjs';
|
||||
|
||||
export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
||||
static DEFAULT_OPTIONS = {
|
||||
actions: {
|
||||
requestSpotlight: this.requestSpotlight,
|
||||
toggleSpotlight: this.toggleSpotlight,
|
||||
setActionTokens: this.setActionTokens
|
||||
setActionTokens: this.setActionTokens,
|
||||
openCountdowns: this.openCountdowns
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -83,6 +86,8 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
|||
.map(x => x.id)
|
||||
.indexOf(combatantId);
|
||||
|
||||
if (this.viewed.turn !== toggleTurn) Hooks.callAll(SYSTEM.HOOKS.spotlight, {});
|
||||
|
||||
await this.viewed.update({ turn: this.viewed.turn === toggleTurn ? null : toggleTurn });
|
||||
await combatant.update({ 'system.spotlight.requesting': false });
|
||||
}
|
||||
|
|
@ -97,4 +102,8 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
|||
await combatant.update({ 'system.actionTokens': newIndex });
|
||||
this.render();
|
||||
}
|
||||
|
||||
static openCountdowns() {
|
||||
new EncounterCountdowns().open();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,18 @@
|
|||
import DhMeasuredTemplate from '../placeables/measuredTemplate.mjs';
|
||||
|
||||
export default class DhpRuler extends foundry.canvas.interaction.Ruler {
|
||||
_getWaypointLabelContext(waypoint, state) {
|
||||
const context = super._getWaypointLabelContext(waypoint, state);
|
||||
if (!context) return;
|
||||
|
||||
const range = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.RangeMeasurement);
|
||||
const range = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement);
|
||||
|
||||
if (range.enabled) {
|
||||
const distance = this.#getRangeLabel(waypoint.measurement.distance.toNearest(0.01), range);
|
||||
const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range);
|
||||
context.cost = { total: distance, units: null };
|
||||
context.distance = { total: distance, units: null };
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
#getRangeLabel(distance, settings) {
|
||||
if (distance <= settings.melee) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.Melee.Name');
|
||||
}
|
||||
if (distance <= settings.veryClose) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.VeryClose.Name');
|
||||
}
|
||||
if (distance <= settings.close) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.Close.Name');
|
||||
}
|
||||
if (distance <= settings.far) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.Far.Name');
|
||||
}
|
||||
if (distance <= settings.veryFar) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.VeryFar.Name');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,18 @@
|
|||
import DhMeasuredTemplate from '../placeables/measuredTemplate.mjs';
|
||||
|
||||
export default class DhpTokenRuler extends foundry.canvas.placeables.tokens.TokenRuler {
|
||||
_getWaypointLabelContext(waypoint, state) {
|
||||
const context = super._getWaypointLabelContext(waypoint, state);
|
||||
if (!context) return;
|
||||
|
||||
const range = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.RangeMeasurement);
|
||||
const range = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement);
|
||||
|
||||
if (range.enabled) {
|
||||
const distance = this.#getRangeLabel(waypoint.measurement.distance.toNearest(0.01), range);
|
||||
const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range);
|
||||
context.cost = { total: distance, units: null };
|
||||
context.distance = { total: distance, units: null };
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
#getRangeLabel(distance, settings) {
|
||||
if (distance <= settings.melee) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.Melee.Name');
|
||||
}
|
||||
if (distance <= settings.veryClose) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.VeryClose.Name');
|
||||
}
|
||||
if (distance <= settings.close) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.Close.Name');
|
||||
}
|
||||
if (distance <= settings.far) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.Far.Name');
|
||||
}
|
||||
if (distance <= settings.veryFar) {
|
||||
return game.i18n.localize('DAGGERHEART.Range.VeryFar.Name');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1718
package-lock.json
generated
1718
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -8,6 +8,9 @@
|
|||
"scripts": {
|
||||
"start": "concurrently \"rollup -c --watch\" \"node ../../../../FoundryDev/main.js --dataPath=../../../ --noupnp\" \"gulp\"",
|
||||
"start-test": "node ./resources/app/main.js --dataPath=./ && rollup -c --watch && gulp",
|
||||
"build": "npm run rollup && npm run gulp",
|
||||
"rollup": "rollup -c",
|
||||
"gulp": "gulp less",
|
||||
"pushLDBtoYML": "node ./tools/pushLDBtoYML.mjs",
|
||||
"pullYMLtoLDB": "node ./tools/pullYMLtoLDB.mjs",
|
||||
"createSymlink": "node ./tools/create-symlink.mjs"
|
||||
|
|
|
|||
|
|
@ -134,6 +134,12 @@ div.daggerheart.views.multiclass {
|
|||
}
|
||||
|
||||
.downtime-container {
|
||||
.downtime-header {
|
||||
margin: 0;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.activity-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -150,12 +156,32 @@ div.daggerheart.views.multiclass {
|
|||
}
|
||||
|
||||
.activity-image {
|
||||
width: 120px;
|
||||
width: 80px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-right: 8px;
|
||||
border: 2px solid black;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
|
||||
.activity-select-label {
|
||||
position: absolute;
|
||||
top: -9px;
|
||||
font-size: 14px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
color: light-dark(@beige, @dark);
|
||||
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||
padding: 0 8px;
|
||||
line-height: 1;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.selected {
|
||||
filter: drop-shadow(0 0 6px gold);
|
||||
|
|
|
|||
412
styles/characterCreation.less
Normal file
412
styles/characterCreation.less
Normal file
|
|
@ -0,0 +1,412 @@
|
|||
.theme-light .daggerheart.dh-style.dialog.character-creation {
|
||||
.tab-navigation nav a .descriptor {
|
||||
background: red;
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
}
|
||||
.main-selections-container {
|
||||
.traits-container .suggested-traits-container .suggested-trait-container,
|
||||
.creation-action-footer .footer-section nav a .descriptor,
|
||||
.equipment-selection .simple-equipment-container .simple-equipment label {
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.daggerheart.dh-style.dialog.character-creation {
|
||||
.window-content {
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.tab-navigation {
|
||||
nav {
|
||||
flex: 1;
|
||||
|
||||
a {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.nav-section-text {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.finish-marker {
|
||||
position: absolute;
|
||||
align-self: center;
|
||||
top: -8px;
|
||||
padding: 4px;
|
||||
border: 1px solid;
|
||||
border-radius: 50%;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--color-cool-4);
|
||||
content: '';
|
||||
|
||||
&.active {
|
||||
background-color: var(--color-warm-2);
|
||||
}
|
||||
}
|
||||
|
||||
.descriptor {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
font-size: 12px;
|
||||
border-radius: 8px;
|
||||
width: 56px;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
color: light-dark(@beige, @dark);
|
||||
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-selections-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
.selections-container {
|
||||
width: 140px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
|
||||
.card-preview-container {
|
||||
border-color: light-dark(@dark-blue, @golden);
|
||||
}
|
||||
}
|
||||
|
||||
.selections-outer-container {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
height: 210px;
|
||||
}
|
||||
|
||||
.section-container {
|
||||
border-radius: 8px;
|
||||
border-color: light-dark(@dark-blue, @golden);
|
||||
|
||||
legend {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.section-inner-container {
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
border-color: light-dark(@dark-blue, @golden);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
legend {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
height: 16px;
|
||||
width: 110px;
|
||||
min-height: unset;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
color: light-dark(@beige, @beige);
|
||||
background-color: light-dark(var(--color-warm-3), var(--color-warm-3));
|
||||
|
||||
&:hover {
|
||||
background-color: light-dark(var(--color-warm-2), var(--color-warm-2));
|
||||
filter: drop-shadow(0 0 3px light-dark(var(--color-warm-2), var(--color-warm-2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.traits-container {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
.suggested-traits-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 176px;
|
||||
gap: 4px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.suggested-trait-container {
|
||||
width: 56px;
|
||||
white-space: nowrap;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
color: light-dark(@beige, @dark);
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
}
|
||||
}
|
||||
|
||||
.traits-inner-container {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
gap: 8px;
|
||||
|
||||
.trait-container {
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.experiences-inner-container {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
text-align: center;
|
||||
|
||||
.experience-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.experience-description {
|
||||
border-color: light-dark(@dark-blue, @golden);
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.experience-value {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 22px;
|
||||
border-left: 1px solid light-dark(@dark-blue, @golden);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.creation-action-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
|
||||
.footer-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
|
||||
nav {
|
||||
flex: 1;
|
||||
gap: 8px;
|
||||
border: 0;
|
||||
|
||||
a {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
|
||||
.nav-section-text {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.finish-marker {
|
||||
position: absolute;
|
||||
align-self: center;
|
||||
top: -10px;
|
||||
padding: 4px;
|
||||
border: 1px solid;
|
||||
border-radius: 50%;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--color-cool-4);
|
||||
content: '';
|
||||
|
||||
&.finished {
|
||||
background-color: var(--color-warm-2);
|
||||
}
|
||||
}
|
||||
|
||||
.descriptor {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
font-size: 12px;
|
||||
border-radius: 8px;
|
||||
width: 56px;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
color: light-dark(@beige, @dark);
|
||||
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-equipment-selection {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 16px;
|
||||
|
||||
&.triple {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.equipment-selection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border: 2px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 8px;
|
||||
|
||||
legend {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
padding: 0 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.equipment-subsection {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.equipment-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.simple-equipment-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
gap: 8px;
|
||||
height: 100%;
|
||||
|
||||
.simple-equipment {
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
&.selectable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.inactive {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
label {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
color: light-dark(@beige, @dark);
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.suggestion-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: min-content;
|
||||
border: 2px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 8px;
|
||||
|
||||
legend {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.suggestion-inner-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 6px;
|
||||
cursor: grab;
|
||||
|
||||
&.taken {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
label {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.creation-action-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
.chat-message {
|
||||
.duality-modifiers, .duality-result, .dice-title {
|
||||
.duality-modifiers,
|
||||
.duality-result,
|
||||
.dice-title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
@ -67,7 +69,8 @@
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
&.hope, &.fear {
|
||||
&.hope,
|
||||
&.fear {
|
||||
.dice-wrapper {
|
||||
clip-path: polygon(
|
||||
50% 0%,
|
||||
|
|
@ -335,8 +338,10 @@
|
|||
> * {
|
||||
padding: 0 8px;
|
||||
}
|
||||
.message-content {
|
||||
.duality-modifiers, .duality-result, .dice-title {
|
||||
.message-content {
|
||||
.duality-modifiers,
|
||||
.duality-result,
|
||||
.dice-title {
|
||||
display: flex;
|
||||
}
|
||||
.duality-modifiers {
|
||||
|
|
@ -364,7 +369,7 @@
|
|||
}
|
||||
.dice-result {
|
||||
.duality-modifiers {
|
||||
display: flex; // Default => display: none;
|
||||
display: flex; // Default => display: none;
|
||||
gap: 2px;
|
||||
margin-bottom: 4px;
|
||||
.duality-modifier {
|
||||
|
|
@ -375,7 +380,9 @@
|
|||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.dice-formula, > .dice-total, .part-header {
|
||||
.dice-formula,
|
||||
> .dice-total,
|
||||
.part-header {
|
||||
display: none;
|
||||
}
|
||||
.dice-tooltip {
|
||||
|
|
@ -384,7 +391,7 @@
|
|||
.tooltip-part {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
gap: .25rem;
|
||||
gap: 0.25rem;
|
||||
.dice {
|
||||
.dice-rolls {
|
||||
margin-bottom: 0;
|
||||
|
|
|
|||
154
styles/countdown.less
Normal file
154
styles/countdown.less
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
.theme-light {
|
||||
.daggerheart.dh-style.countdown {
|
||||
&.minimized .minimized-view .mini-countdown-container {
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.daggerheart.dh-style.countdown {
|
||||
overflow: hidden;
|
||||
|
||||
fieldset {
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
border-radius: 6px;
|
||||
border-color: light-dark(@dark-blue, @golden);
|
||||
|
||||
legend {
|
||||
font-family: @font-body;
|
||||
font-weight: bold;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
|
||||
a {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.minimized {
|
||||
height: auto !important;
|
||||
max-height: unset !important;
|
||||
max-width: 740px !important;
|
||||
width: auto !important;
|
||||
|
||||
.window-content {
|
||||
display: flex;
|
||||
padding: 4px 8px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.minimized-view {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.mini-countdown-container {
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border: 2px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
padding: 0 4px 0 0;
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
color: light-dark(@beige, @dark);
|
||||
cursor: pointer;
|
||||
|
||||
&.disabled {
|
||||
cursor: initial;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 6px 0 0 6px;
|
||||
}
|
||||
|
||||
.mini-countdown-name {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mini-countdown-value {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.window-content {
|
||||
> div {
|
||||
height: 100%;
|
||||
|
||||
.expanded-view {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.countdowns-menu {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
.flex {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.countdowns-container {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
overflow: auto;
|
||||
max-height: 100%;
|
||||
|
||||
.countdown-fieldset {
|
||||
width: 340px;
|
||||
height: min-content;
|
||||
position: relative;
|
||||
|
||||
.ownership-button {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.countdown-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
img {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
cursor: pointer;
|
||||
|
||||
&.disabled {
|
||||
cursor: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.countdown-inner-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
.countdown-value-container {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
|
||||
input {
|
||||
max-width: 80px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@
|
|||
/* Background */
|
||||
/* Duality */
|
||||
/* Fear */
|
||||
@import '../node_modules/@yaireo/tagify/dist/tagify.css';
|
||||
.daggerheart.sheet.class .editor {
|
||||
height: 500px;
|
||||
}
|
||||
|
|
@ -1297,25 +1296,38 @@
|
|||
.combat-sidebar .encounter-controls.combat {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.combat-sidebar .encounter-controls.combat .encounter-control-fear-container {
|
||||
.combat-sidebar .encounter-controls.combat .encounter-fear-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.combat-sidebar .encounter-controls.combat .encounter-fear-controls .encounter-fear-dice-container {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
}
|
||||
.combat-sidebar .encounter-controls.combat .encounter-fear-controls .encounter-fear-dice-container .encounter-control-fear-container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: black;
|
||||
}
|
||||
.combat-sidebar .encounter-controls.combat .encounter-control-fear-container .dice {
|
||||
height: 24px;
|
||||
.combat-sidebar .encounter-controls.combat .encounter-fear-controls .encounter-fear-dice-container .encounter-control-fear-container .dice {
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
}
|
||||
.combat-sidebar .encounter-controls.combat .encounter-control-fear-container .encounter-control-fear {
|
||||
.combat-sidebar .encounter-controls.combat .encounter-fear-controls .encounter-fear-dice-container .encounter-control-fear-container .encounter-control-fear {
|
||||
position: absolute;
|
||||
font-size: 16px;
|
||||
}
|
||||
.combat-sidebar .encounter-controls.combat .encounter-control-fear-container .encounter-control-counter {
|
||||
.combat-sidebar .encounter-controls.combat .encounter-fear-controls .encounter-fear-dice-container .encounter-control-fear-container .encounter-control-counter {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
.combat-sidebar .encounter-controls.combat .encounter-fear-controls .encounter-countdowns {
|
||||
color: var(--content-link-icon-color);
|
||||
}
|
||||
.combat-sidebar .encounter-controls.combat .control-buttons {
|
||||
width: min-content;
|
||||
}
|
||||
|
|
@ -1891,6 +1903,11 @@ div.daggerheart.views.multiclass {
|
|||
.daggerheart.views.levelup .levelup-section .levelup-container .levelup-inner-container .levelup-posttext {
|
||||
padding: 8px 0;
|
||||
}
|
||||
.daggerheart.views .downtime-container .downtime-header {
|
||||
margin: 0;
|
||||
color: light-dark(#18162e, #f3c267);
|
||||
text-align: center;
|
||||
}
|
||||
.daggerheart.views .downtime-container .activity-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -1906,12 +1923,30 @@ div.daggerheart.views.multiclass {
|
|||
font-weight: bold;
|
||||
}
|
||||
.daggerheart.views .downtime-container .activity-container .activity-title .activity-image {
|
||||
width: 120px;
|
||||
width: 80px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-right: 8px;
|
||||
border: 2px solid black;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.daggerheart.views .downtime-container .activity-container .activity-title .activity-image .activity-select-label {
|
||||
position: absolute;
|
||||
top: -9px;
|
||||
font-size: 14px;
|
||||
border: 1px solid light-dark(#18162e, #f3c267);
|
||||
border-radius: 6px;
|
||||
color: light-dark(#efe6d8, #222);
|
||||
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||
padding: 0 8px;
|
||||
line-height: 1;
|
||||
font-weight: bold;
|
||||
}
|
||||
.daggerheart.views .downtime-container .activity-container .activity-title .activity-image img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.daggerheart.views .downtime-container .activity-container .activity-title .activity-image:hover,
|
||||
.daggerheart.views .downtime-container .activity-container .activity-title .activity-image.selected {
|
||||
filter: drop-shadow(0 0 6px gold);
|
||||
|
|
@ -2496,6 +2531,354 @@ div.daggerheart.views.multiclass {
|
|||
.item-button .item-icon.checked {
|
||||
opacity: 1;
|
||||
}
|
||||
.theme-light .daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .descriptor {
|
||||
background: red;
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
}
|
||||
.theme-light .daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .suggested-traits-container .suggested-trait-container,
|
||||
.theme-light .daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav a .descriptor,
|
||||
.theme-light .daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container .simple-equipment label {
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .window-content {
|
||||
gap: 16px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .tab-navigation nav {
|
||||
flex: 1;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .tab-navigation nav a {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .tab-navigation nav a.disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .nav-section-text {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .finish-marker {
|
||||
position: absolute;
|
||||
align-self: center;
|
||||
top: -8px;
|
||||
padding: 4px;
|
||||
border: 1px solid;
|
||||
border-radius: 50%;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--color-cool-4);
|
||||
content: '';
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .finish-marker.active {
|
||||
background-color: var(--color-warm-2);
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .descriptor {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
font-size: 12px;
|
||||
border-radius: 8px;
|
||||
width: 56px;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
border: 1px solid light-dark(#18162e, #f3c267);
|
||||
border-radius: 6px;
|
||||
color: light-dark(#efe6d8, #222);
|
||||
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .selections-container {
|
||||
width: 140px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .selections-container .card-preview-container {
|
||||
border-color: light-dark(#18162e, #f3c267);
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .selections-outer-container {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
height: 210px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container {
|
||||
border-radius: 8px;
|
||||
border-color: light-dark(#18162e, #f3c267);
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container legend {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
padding: 0 8px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container .section-inner-container {
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
border-color: light-dark(#18162e, #f3c267);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container .section-inner-container legend {
|
||||
font-size: 20px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container .section-inner-container .action-button {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
height: 16px;
|
||||
width: 110px;
|
||||
min-height: unset;
|
||||
border: 1px solid light-dark(#18162e, #f3c267);
|
||||
color: light-dark(#efe6d8, #efe6d8);
|
||||
background-color: light-dark(var(--color-warm-3), var(--color-warm-3));
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container .section-inner-container .action-button:hover {
|
||||
background-color: light-dark(var(--color-warm-2), var(--color-warm-2));
|
||||
filter: drop-shadow(0 0 3px light-dark(var(--color-warm-2), var(--color-warm-2)));
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .suggested-traits-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 176px;
|
||||
gap: 4px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .suggested-traits-container .suggested-trait-container {
|
||||
width: 56px;
|
||||
white-space: nowrap;
|
||||
border: 1px solid light-dark(#18162e, #f3c267);
|
||||
border-radius: 6px;
|
||||
color: light-dark(#efe6d8, #222);
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .traits-inner-container {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
gap: 8px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .traits-inner-container .trait-container {
|
||||
border: 1px solid light-dark(#18162e, #f3c267);
|
||||
padding: 0 4px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .experiences-inner-container {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
text-align: center;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .experiences-inner-container .experience-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .experiences-inner-container .experience-container .experience-description {
|
||||
border-color: light-dark(#18162e, #f3c267);
|
||||
padding-right: 24px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .experiences-inner-container .experience-container .experience-value {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 22px;
|
||||
border-left: 1px solid light-dark(#18162e, #f3c267);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav {
|
||||
flex: 1;
|
||||
gap: 8px;
|
||||
border: 0;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav a {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
border: 1px solid light-dark(#18162e, #f3c267);
|
||||
border-radius: 6px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav a .nav-section-text {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav a .finish-marker {
|
||||
position: absolute;
|
||||
align-self: center;
|
||||
top: -10px;
|
||||
padding: 4px;
|
||||
border: 1px solid;
|
||||
border-radius: 50%;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--color-cool-4);
|
||||
content: '';
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav a .finish-marker.finished {
|
||||
background-color: var(--color-warm-2);
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav a .descriptor {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
font-size: 12px;
|
||||
border-radius: 8px;
|
||||
width: 56px;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
border: 1px solid light-dark(#18162e, #f3c267);
|
||||
border-radius: 6px;
|
||||
color: light-dark(#efe6d8, #222);
|
||||
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section button {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .main-equipment-selection {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 16px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .main-equipment-selection.triple {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border: 2px solid light-dark(#18162e, #f3c267);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection legend {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
padding: 0 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .equipment-subsection {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
gap: 32px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .equipment-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
gap: 8px;
|
||||
height: 100%;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container .simple-equipment {
|
||||
border: 1px solid light-dark(#18162e, #f3c267);
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container .simple-equipment.selectable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container .simple-equipment.inactive {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container .simple-equipment label {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
border: 1px solid light-dark(#18162e, #f3c267);
|
||||
border-radius: 6px;
|
||||
color: light-dark(#efe6d8, #222);
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
padding: 0 2px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container .simple-equipment img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .suggestion-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: min-content;
|
||||
border: 2px solid light-dark(#18162e, #f3c267);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .suggestion-container legend {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
font-size: 12px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .suggestion-container .suggestion-inner-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 6px;
|
||||
cursor: grab;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .suggestion-container .suggestion-inner-container.taken {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .suggestion-container .suggestion-inner-container label {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .suggestion-container .suggestion-inner-container img {
|
||||
width: 120px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .creation-action-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
}
|
||||
.daggerheart.dh-style.dialog.character-creation .creation-action-footer button {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.theme-light .daggerheart.levelup .tiers-container .tier-container {
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
}
|
||||
|
|
@ -2709,6 +3092,27 @@ div.daggerheart.views.multiclass {
|
|||
.daggerheart.levelup .levelup-footer {
|
||||
display: flex;
|
||||
}
|
||||
.daggerheart.views.ownership-selection .ownership-outer-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.daggerheart.views.ownership-selection .ownership-outer-container .ownership-container {
|
||||
display: flex;
|
||||
border: 2px solid light-dark(#18162e, #f3c267);
|
||||
border-radius: 6px;
|
||||
padding: 0 4px 0 0;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.daggerheart.views.ownership-selection .ownership-outer-container .ownership-container img {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 6px 0 0 6px;
|
||||
}
|
||||
.daggerheart.views.ownership-selection .ownership-outer-container .ownership-container select {
|
||||
margin: 4px 0;
|
||||
}
|
||||
:root {
|
||||
--shadow-text-stroke: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
||||
--fear-animation: background 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease, opacity 0.3s ease;
|
||||
|
|
@ -2823,6 +3227,228 @@ div.daggerheart.views.multiclass {
|
|||
#resources:has(.fear-bar) {
|
||||
min-width: 200px;
|
||||
}
|
||||
.theme-light .daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container {
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
}
|
||||
.daggerheart.dh-style.countdown {
|
||||
overflow: hidden;
|
||||
}
|
||||
.daggerheart.dh-style.countdown fieldset {
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
border-radius: 6px;
|
||||
border-color: light-dark(#18162e, #f3c267);
|
||||
}
|
||||
.daggerheart.dh-style.countdown fieldset legend {
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-weight: bold;
|
||||
color: light-dark(#18162e, #f3c267);
|
||||
}
|
||||
.daggerheart.dh-style.countdown fieldset legend a {
|
||||
text-shadow: none;
|
||||
}
|
||||
.daggerheart.dh-style.countdown.minimized {
|
||||
height: auto !important;
|
||||
max-height: unset !important;
|
||||
max-width: 740px !important;
|
||||
width: auto !important;
|
||||
}
|
||||
.daggerheart.dh-style.countdown.minimized .window-content {
|
||||
display: flex;
|
||||
padding: 4px 8px;
|
||||
justify-content: center;
|
||||
}
|
||||
.daggerheart.dh-style.countdown.minimized .minimized-view {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container {
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border: 2px solid light-dark(#18162e, #f3c267);
|
||||
border-radius: 6px;
|
||||
padding: 0 4px 0 0;
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
color: light-dark(#efe6d8, #222);
|
||||
cursor: pointer;
|
||||
}
|
||||
.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container.disabled {
|
||||
cursor: initial;
|
||||
}
|
||||
.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 6px 0 0 6px;
|
||||
}
|
||||
.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container .mini-countdown-name {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .hidden {
|
||||
display: none;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div {
|
||||
height: 100%;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-menu {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-menu .flex {
|
||||
flex: 1;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
overflow: auto;
|
||||
max-height: 100%;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset {
|
||||
width: 340px;
|
||||
height: min-content;
|
||||
position: relative;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .ownership-button {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container img {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container img.disabled {
|
||||
cursor: initial;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container .countdown-inner-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container .countdown-inner-container .countdown-value-container {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container .countdown-inner-container .countdown-value-container input {
|
||||
max-width: 80px;
|
||||
}
|
||||
.daggerheart.dh-style.setting fieldset {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.daggerheart.dh-style.setting fieldset.two-columns {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 10px;
|
||||
}
|
||||
.daggerheart.dh-style.setting fieldset.two-columns.even {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
.daggerheart.dh-style.setting .setting-group-field {
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.daggerheart.dh-style.setting .settings-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.daggerheart.dh-style.setting .settings-items .settings-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border: 1px solid;
|
||||
border-radius: 8px;
|
||||
padding: 0 8px 0 0;
|
||||
}
|
||||
.daggerheart.dh-style.setting .settings-items .settings-item .settings-sub-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.daggerheart.dh-style.setting .settings-items .settings-item .settings-sub-item img {
|
||||
width: 60px;
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
.daggerheart.dh-style.setting .settings-items .settings-item .settings-sub-item i {
|
||||
font-size: 18px;
|
||||
}
|
||||
.daggerheart.dh-style.setting .settings-item-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.daggerheart.dh-style.setting .settings-item-header .profile {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
object-fit: cover;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
}
|
||||
.daggerheart.dh-style.setting .settings-item-header .item-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
text-align: center;
|
||||
width: 80%;
|
||||
}
|
||||
.daggerheart.dh-style.setting .settings-item-header .item-info .item-name input[type='text'] {
|
||||
font-size: 32px;
|
||||
height: 42px;
|
||||
text-align: center;
|
||||
width: 90%;
|
||||
transition: all 0.3s ease;
|
||||
outline: 2px solid transparent;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.daggerheart.dh-style.setting .settings-item-header .item-info .item-name input[type='text']:hover[type='text'],
|
||||
.daggerheart.dh-style.setting .settings-item-header .item-info .item-name input[type='text']:focus[type='text'] {
|
||||
box-shadow: none;
|
||||
outline: 2px solid light-dark(#18162e, #f3c267);
|
||||
}
|
||||
.daggerheart.dh-style.setting .settings-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.daggerheart.dh-style.setting .trait-array-container {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.daggerheart.dh-style.setting .trait-array-container .trait-array-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.daggerheart.dh-style.setting .trait-array-container .trait-array-item label {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
font-size: 12px;
|
||||
font-variant: petite-caps;
|
||||
}
|
||||
.daggerheart.dh-style.setting .trait-array-container .trait-array-item input {
|
||||
text-align: center;
|
||||
}
|
||||
.application.sheet.daggerheart.actor.dh-style.adversary .window-content {
|
||||
overflow: auto;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,12 @@
|
|||
@import './application.less';
|
||||
@import './sheets/sheets.less';
|
||||
@import './dialog.less';
|
||||
@import './characterCreation.less';
|
||||
@import './levelup.less';
|
||||
@import '../node_modules/@yaireo/tagify/dist/tagify.css';
|
||||
@import './ownershipSelection.less';
|
||||
@import './resources.less';
|
||||
@import './countdown.less';
|
||||
@import './settings.less';
|
||||
|
||||
// new styles imports
|
||||
@import './less/actors/character.less';
|
||||
|
|
|
|||
26
styles/ownershipSelection.less
Normal file
26
styles/ownershipSelection.less
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
.daggerheart.views.ownership-selection {
|
||||
.ownership-outer-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.ownership-container {
|
||||
display: flex;
|
||||
border: 2px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
padding: 0 4px 0 0;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
img {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 6px 0 0 6px;
|
||||
}
|
||||
|
||||
select {
|
||||
margin: 4px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
122
styles/settings.less
Normal file
122
styles/settings.less
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
.daggerheart.dh-style.setting {
|
||||
fieldset {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
&.two-columns {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 10px;
|
||||
|
||||
&.even {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.setting-group-field {
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.settings-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.settings-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border: 1px solid;
|
||||
border-radius: 8px;
|
||||
padding: 0 8px 0 0;
|
||||
|
||||
.settings-sub-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
img {
|
||||
width: 60px;
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-item-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.profile {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
object-fit: cover;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.item-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
text-align: center;
|
||||
width: 80%;
|
||||
|
||||
.item-name input[type='text'] {
|
||||
font-size: 32px;
|
||||
height: 42px;
|
||||
text-align: center;
|
||||
width: 90%;
|
||||
transition: all 0.3s ease;
|
||||
outline: 2px solid transparent;
|
||||
border: 1px solid transparent;
|
||||
|
||||
&:hover[type='text'],
|
||||
&:focus[type='text'] {
|
||||
box-shadow: none;
|
||||
outline: 2px solid light-dark(@dark-blue, @golden);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.trait-array-container {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.trait-array-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
label {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
font-size: 12px;
|
||||
font-variant: petite-caps;
|
||||
}
|
||||
|
||||
input {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,26 +2,42 @@
|
|||
.encounter-controls.combat {
|
||||
justify-content: space-between;
|
||||
|
||||
.encounter-control-fear-container {
|
||||
.encounter-fear-controls {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: black;
|
||||
gap: 8px;
|
||||
|
||||
.dice {
|
||||
height: 24px;
|
||||
.encounter-fear-dice-container {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
|
||||
.encounter-control-fear-container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: black;
|
||||
|
||||
.dice {
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
.encounter-control-fear {
|
||||
position: absolute;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.encounter-control-counter {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.encounter-control-fear {
|
||||
position: absolute;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.encounter-control-counter {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
color: var(--color-text-secondary);
|
||||
.encounter-countdowns {
|
||||
color: var(--content-link-icon-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"version": "0.0.1",
|
||||
"compatibility": {
|
||||
"minimum": "13",
|
||||
"verified": "13.344",
|
||||
"verified": "13.345",
|
||||
"maximum": "13"
|
||||
},
|
||||
"authors": [
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
<div class="daggerheart chat downtime">
|
||||
<h2 class="downtime-title-container">
|
||||
<div>{{this.player}} {{localize "DAGGERHEART.Chat.Downtime.Title"}}</div>
|
||||
<div class="downtime-subtitle">{{this.title}}</div>
|
||||
<div>{{title}}</div>
|
||||
</h2>
|
||||
<img class="downtime-image" src="{{this.img}}" />
|
||||
<div>{{{this.description}}}</div>
|
||||
<div class="downtime-refresh-container">
|
||||
<div class="refresh-title">Refreshed Features</div>
|
||||
{{#each this.refreshedFeatures}}
|
||||
<div class="refresh-inner-container">
|
||||
<div>{{this.name}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{#each moves}}
|
||||
<strong>{{this.name}}</strong>
|
||||
<img class="downtime-image" src="{{this.img}}" />
|
||||
<div>{{{this.description}}}</div>
|
||||
{{#if (gt this.actions.length 0)}}<button class="action-use-button">{{localize "Action"}}</button>{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
<div class="component dh-style card-preview-container {{#if (and this.compendium (not this.img))}}selectable{{/if}}" data-action="viewCompendium" data-compendium="{{this.compendium}}" data-path="{{this.path}}" data-limit="{{this.limit}}">
|
||||
<div
|
||||
class="component dh-style card-preview-container {{#if (and (not this.disabled) (and this.compendium (not this.img)))}}selectable{{/if}} {{#if this.disabled}}disabled{{/if}}"
|
||||
{{#if (not disabled)}}data-action="viewCompendium"{{/if}} data-compendium="{{this.compendium}}" data-path="{{this.path}}" data-limit="{{this.limit}}"
|
||||
>
|
||||
{{#if this.img}}
|
||||
<img class="preview-image-container" src="{{this.img}}" />
|
||||
<div class="preview-text-container">{{this.name}}</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
<div>
|
||||
{{formGroup settingFields.schema.fields.displayFear value=settingFields._source.displayFear localize=true}}
|
||||
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.Settings.Menu.Appearance.duality"}}</legend>
|
||||
|
||||
|
|
|
|||
32
templates/settings/automation-settings.hbs
Normal file
32
templates/settings/automation-settings.hbs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<div>
|
||||
<div class="form-group">
|
||||
<label>{{localize "DAGGERHEART.Settings.Automation.FIELDS.hope.label"}}</label>
|
||||
<div class="form-fields">
|
||||
{{formInput settingFields.schema.fields.hope value=settingFields._source.hope }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{localize "DAGGERHEART.Settings.Automation.FIELDS.actionPoints.label"}}</label>
|
||||
<div class="form-fields">
|
||||
{{formInput settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{localize "DAGGERHEART.Settings.Automation.FIELDS.countdowns.label"}}</label>
|
||||
<div class="form-fields">
|
||||
{{formInput settingFields.schema.fields.countdowns value=settingFields._source.countdowns }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="form-footer">
|
||||
<button data-action="reset">
|
||||
<i class="fa-solid fa-arrow-rotate-left"></i>
|
||||
<span>{{localize "Reset"}}</span>
|
||||
</button>
|
||||
<button data-action="save" >
|
||||
<i class="fa-solid fa-floppy-disk"></i>
|
||||
<span>{{localize "Save Changes"}}</span>
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
3
templates/settings/components/action-view-footer.hbs
Normal file
3
templates/settings/components/action-view-footer.hbs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<section class='tab-form-footer'>
|
||||
<button data-action="saveForm"><i class="fa-solid fa-floppy-disk"></i> {{localize "Save"}}</button>
|
||||
</section>
|
||||
6
templates/settings/components/action-view-header.hbs
Normal file
6
templates/settings/components/action-view-header.hbs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<header class='settings-item-header'>
|
||||
<img class='profile' src='{{this.img}}' data-action='editImage' data-edit='img' />
|
||||
<div class='item-info'>
|
||||
<h1 class='item-name'><input type='text' name='name' value='{{this.name}}' /></h1>
|
||||
</div>
|
||||
</header>
|
||||
18
templates/settings/components/action-view.hbs
Normal file
18
templates/settings/components/action-view.hbs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<div>
|
||||
<fieldset>
|
||||
<legend>{{localize "Description"}}</legend>
|
||||
|
||||
<prose-mirror name="description" value="{{description}}" toggled=true>
|
||||
{{{ enrichedDescription }}}
|
||||
</prose-mirror>
|
||||
</fieldset>
|
||||
{{!-- <fieldset>
|
||||
<legend>{{localize "Actions"}} <a data-action="addItem"><i class="fa-solid fa-plus"></i></a></legend>
|
||||
|
||||
<div class="settings-items">
|
||||
{{#each this.actions as |action index|}}
|
||||
{{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" action type="actions" id=index }}
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset> --}}
|
||||
</div>
|
||||
22
templates/settings/components/settings-item-line.hbs
Normal file
22
templates/settings/components/settings-item-line.hbs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<div class="settings-item">
|
||||
<div class="settings-sub-item">
|
||||
<img src="{{this.img}}" />
|
||||
<div>{{this.name}}</div>
|
||||
</div>
|
||||
<div class="settings-sub-item">
|
||||
<a
|
||||
data-action="editItem"
|
||||
data-type="{{this.type}}"
|
||||
data-id="{{this.id}}"
|
||||
>
|
||||
<i class="fa-solid fa-globe"></i>
|
||||
</a>
|
||||
<a
|
||||
data-action="removeItem"
|
||||
data-type="{{this.type}}"
|
||||
data-id="{{this.id}}"
|
||||
>
|
||||
<i class='fas fa-trash'></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
71
templates/settings/homebrew-settings.hbs
Normal file
71
templates/settings/homebrew-settings.hbs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<div>
|
||||
{{formGroup settingFields.schema.fields.maxFear value=settingFields._source.maxFear localize=true}}
|
||||
|
||||
<h4>{{localize "DAGGERHEART.Settings.Homebrew.FIELDS.traitArray.label"}}</h4>
|
||||
<div class="trait-array-container">
|
||||
{{#each settingFields._source.traitArray as |trait index|}}
|
||||
<div class="trait-array-item">
|
||||
<label>{{localize "DAGGERHEART.General.Modifier"}} {{add index 1}}</label>
|
||||
<input type="text" data-dtype="Number" name="{{concat "traitArray." index}}" value="{{this}}" />
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
<fieldset class="two-columns even">
|
||||
<legend>{{localize "DAGGERHEART.Settings.Homebrew.DowntimeMoves"}}</legend>
|
||||
|
||||
<fieldset>
|
||||
<legend>
|
||||
{{localize "DAGGERHEART.Downtime.LongRest.title"}}
|
||||
<a data-action="addItem" data-type="longRest"><i class="fa-solid fa-plus"></i></a>
|
||||
<a data-action="resetMoves" data-type="longRest"><i class="fa-solid fa-arrow-rotate-left"></i></a>
|
||||
</legend>
|
||||
|
||||
<div class="form-group setting-group-field">
|
||||
<label>{{localize "DAGGERHEART.Settings.Homebrew.NrChoices"}}</label>
|
||||
<div class="form-fields">
|
||||
<input type="text" name="restMoves.longRest.nrChoices" value="{{settingFields._source.restMoves.longRest.nrChoices}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-items">
|
||||
{{#each settingFields._source.restMoves.longRest.moves as |move id|}}
|
||||
{{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="longRest" id=id }}
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>
|
||||
{{localize "DAGGERHEART.Downtime.ShortRest.title"}}
|
||||
<a data-action="addItem" data-type="shortRest"><i class="fa-solid fa-plus"></i></a>
|
||||
<a data-action="resetMoves" data-type="shortRest"><i class="fa-solid fa-arrow-rotate-left"></i></a>
|
||||
</legend>
|
||||
|
||||
<div class="form-group setting-group-field">
|
||||
<label>{{localize "DAGGERHEART.Settings.Homebrew.NrChoices"}}</label>
|
||||
<div class="form-fields">
|
||||
<input type="text" name="restMoves.shortRest.nrChoices" value="{{settingFields._source.restMoves.shortRest.nrChoices}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-items">
|
||||
{{#each settingFields._source.restMoves.shortRest.moves as |move id|}}
|
||||
{{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="shortRest" id=id }}
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
|
||||
<footer class="form-footer">
|
||||
<button data-action="reset">
|
||||
<i class="fa-solid fa-arrow-rotate-left"></i>
|
||||
<span>{{localize "Reset"}}</span>
|
||||
</button>
|
||||
<button data-action="save" >
|
||||
<i class="fa-solid fa-floppy-disk"></i>
|
||||
<span>{{localize "Save Changes"}}</span>
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
22
templates/settings/range-measurement-settings.hbs
Normal file
22
templates/settings/range-measurement-settings.hbs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<div>
|
||||
<div class="settings-col">
|
||||
{{formGroup settingFields.schema.fields.enabled value=settingFields._source.enabled localize=true}}
|
||||
{{formGroup settingFields.schema.fields.melee value=settingFields._source.melee localize=true}}
|
||||
{{formGroup settingFields.schema.fields.veryClose value=settingFields._source.veryClose localize=true}}
|
||||
{{formGroup settingFields.schema.fields.close value=settingFields._source.close localize=true}}
|
||||
{{formGroup settingFields.schema.fields.far value=settingFields._source.far localize=true}}
|
||||
{{formGroup settingFields.schema.fields.veryFar value=settingFields._source.veryFar localize=true}}
|
||||
</div>
|
||||
|
||||
<footer class="form-footer">
|
||||
<button data-action="reset">
|
||||
<i class="fa-solid fa-arrow-rotate-left"></i>
|
||||
<span>{{localize "Reset"}}</span>
|
||||
</button>
|
||||
<button data-action="save" >
|
||||
<i class="fa-solid fa-floppy-disk"></i>
|
||||
<span>{{localize "Save Changes"}}</span>
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
<label>{{localize "DAGGERHEART.Settings.Menu.VariantRules.actionTokens"}}</label>
|
||||
|
||||
<div class="form-fields">
|
||||
{{formInput settingFields.schema.fields.actionTokens.fields.enabled value=settingFields._source.actionTokens.enabled}}
|
||||
{{formInput settingFields.schema.fields.actionTokens.fields.tokens value=settingFields._source.actionTokens.tokens disabled=(not settingFields._source.actionTokens.enabled)}}
|
||||
{{formGroup settingFields.schema.fields.actionTokens.fields.enabled value=settingFields._source.actionTokens.enabled localize=true}}
|
||||
{{formGroup settingFields.schema.fields.actionTokens.fields.tokens value=settingFields._source.actionTokens.tokens disabled=(not settingFields._source.actionTokens.enabled) localize=true}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
data-tab='{{tabs.settings.id}}'
|
||||
data-group='{{tabs.settings.group}}'
|
||||
>
|
||||
|
||||
<fieldset class="two-columns">
|
||||
<legend>{{localize tabs.settings.label}}</legend>
|
||||
<span>{{localize "DAGGERHEART.Tiers.singular"}}</span>
|
||||
|
|
|
|||
|
|
@ -52,10 +52,15 @@
|
|||
|
||||
<div class="encounter-controls {{#if hasCombat}}combat{{/if}}">
|
||||
{{#if hasCombat}}
|
||||
<div class="encounter-control-fear-container">
|
||||
<img class="dice " src="../icons/svg/d12-grey.svg"/>
|
||||
<i class="fas fa-skull encounter-control-fear"></i>
|
||||
<div class="encounter-control-counter">{{fear}}</div>
|
||||
<div class="encounter-fear-controls">
|
||||
<div class="encounter-fear-dice-container">
|
||||
<div class="encounter-control-fear-container">
|
||||
<img class="dice " src="../icons/svg/d12-grey.svg"/>
|
||||
<i class="fas fa-skull encounter-control-fear"></i>
|
||||
</div>
|
||||
<div>{{fear}}</div>
|
||||
</div>
|
||||
<a class="encounter-countdowns" data-tooltip="{{localize "DAGGERHEART.Countdown.Title" type=(localize "DAGGERHEART.Countdown.Types.combat")}}" data-action="openCountdowns"><i class="fa-solid fa-stopwatch"></i></a>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
<form class={{cssClass}} autocomplete="off">
|
||||
<div class="form-group">
|
||||
<label>{{localize "DAGGERHEART.Settings.Menu.Automation.HopeLabel"}}</label>
|
||||
<div class="form-fields">
|
||||
<input type="checkbox" name="{{settings.Hope}}" {{checked this.hope}} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{localize "DAGGERHEART.Settings.Menu.Automation.ActionPointsLabel"}}</label>
|
||||
<div class="form-fields">
|
||||
<input type="checkbox" name="{{settings.ActionPoints}}" {{checked this.actionPoints}} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="flexrow">
|
||||
<button type="submit">Save</button>
|
||||
<button>Cancel</button>
|
||||
</footer>
|
||||
</form>
|
||||
4
templates/views/characterCreation/footer.hbs
Normal file
4
templates/views/characterCreation/footer.hbs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<section class="creation-action-footer">
|
||||
<button data-action="close">{{localize "Cancel"}}</button>
|
||||
<button {{#if tabs.setup.finished}}data-action="finish"{{else}}disabled{{/if}}>{{localize "DAGGERHEART.CharacterCreation.FinishCreation"}}</button>
|
||||
</section>
|
||||
13
templates/views/characterCreation/tabs.hbs
Normal file
13
templates/views/characterCreation/tabs.hbs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<section class='tab-navigation'>
|
||||
<line-div></line-div>
|
||||
<nav class='feature-tab sheet-tabs tabs' data-group='primary'>
|
||||
{{#each tabs as |tab|}}
|
||||
<a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'>
|
||||
{{localize tab.label}}
|
||||
<div class="finish-marker {{#if (eq tab.cssClass 'active')}}active{{/if}}">{{#if tab.finished}}<i class="fa-solid fa-check"></i>{{/if}}</div>
|
||||
{{#if tab.optional}}<div class="descriptor">{{localize "DAGGERHEART.CharacterCreation.Tabs.Optional"}}</div>{{/if}}
|
||||
</a>
|
||||
{{/each}}
|
||||
</nav>
|
||||
<line-div></line-div>
|
||||
</section>
|
||||
107
templates/views/characterCreation/tabs/equipment.hbs
Normal file
107
templates/views/characterCreation/tabs/equipment.hbs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
<section
|
||||
class='tab {{tabs.equipment.cssClass}} {{tabs.equipment.id}}'
|
||||
data-tab='{{tabs.equipment.id}}'
|
||||
data-group='{{tabs.equipment.group}}'
|
||||
>
|
||||
<div class="main-selections-container">
|
||||
<div class="main-equipment-selection">
|
||||
<fieldset class="equipment-selection">
|
||||
<legend>{{localize "DAGGERHEART.CharacterCreation.SuggestedArmor"}}</legend>
|
||||
|
||||
<div class="selections-container armor-card">
|
||||
{{#> "systems/daggerheart/templates/components/card-preview.hbs" armor }}
|
||||
{{localize "DAGGERHEART.CharacterCreation.SelectArmor"}}
|
||||
{{/"systems/daggerheart/templates/components/card-preview.hbs"}}
|
||||
</div>
|
||||
{{#if armor.suggestion}}
|
||||
<fieldset class="suggestion-container">
|
||||
<legend>{{armor.suggestion.name}}</legend>
|
||||
<div class="suggestion-inner-container {{#if armor.suggestion.taken}}taken{{/if}}" data-action="viewItem" data-uuid="{{armor.suggestion.uuid}}">
|
||||
<img src="{{armor.suggestion.img}}" />
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="equipment-selection">
|
||||
<legend>{{localize "DAGGERHEART.CharacterCreation.SuggestedWeapons"}}</legend>
|
||||
|
||||
<div class="equipment-subsection">
|
||||
<div class="equipment-wrapper">
|
||||
<div class="selections-container primary-weapon-card">
|
||||
{{#> "systems/daggerheart/templates/components/card-preview.hbs" primaryWeapon }}
|
||||
{{localize "DAGGERHEART.CharacterCreation.SelectPrimaryWeapon"}}
|
||||
{{/"systems/daggerheart/templates/components/card-preview.hbs"}}
|
||||
</div>
|
||||
{{#if primaryWeapon.suggestion}}
|
||||
<fieldset class="suggestion-container">
|
||||
<legend>{{primaryWeapon.suggestion.name}}</legend>
|
||||
<div class="suggestion-inner-container {{#if primaryWeapon.suggestion.taken}}taken{{/if}}" data-action="viewItem" data-uuid="{{primaryWeapon.suggestion.uuid}}">
|
||||
<img src="{{primaryWeapon.suggestion.img}}" />
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="equipment-wrapper">
|
||||
<div class="selections-container secondary-weapon-card">
|
||||
{{#> "systems/daggerheart/templates/components/card-preview.hbs" secondaryWeapon }}
|
||||
{{localize "DAGGERHEART.CharacterCreation.SelectSecondaryWeapon"}}
|
||||
{{/"systems/daggerheart/templates/components/card-preview.hbs"}}
|
||||
</div>
|
||||
{{#if secondaryWeapon.suggestion}}
|
||||
<fieldset class="suggestion-container">
|
||||
<legend>{{secondaryWeapon.suggestion.name}}</legend>
|
||||
<div
|
||||
class="suggestion-inner-container {{#if secondaryWeapon.suggestion.taken}}taken{{/if}}"
|
||||
data-action="viewItem" data-uuid="{{secondaryWeapon.suggestion.uuid}}"
|
||||
>
|
||||
<img src="{{secondaryWeapon.suggestion.img}}" />
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="main-equipment-selection triple">
|
||||
<fieldset class="equipment-selection">
|
||||
<legend>{{localize "DAGGERHEART.CharacterCreation.StartingItems"}}</legend>
|
||||
|
||||
<div class="simple-equipment-container">
|
||||
{{#each inventory.take}}
|
||||
<div class="simple-equipment">
|
||||
<label>{{this.name}}</label>
|
||||
<img src="{{this.img}}" />
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="equipment-selection">
|
||||
<legend>{{localize "DAGGERHEART.CharacterCreation.Choice"}}</legend>
|
||||
|
||||
<div class="simple-equipment-container">
|
||||
{{#each inventory.choiceA.suggestions}}
|
||||
<div class="simple-equipment selectable {{#if (not this.selected)}}inactive{{/if}}" data-action="equipmentChoice" data-path="choiceA" data-uuid="{{this.uuid}}">
|
||||
<label>{{this.name}}</label>
|
||||
<img src="{{this.img}}" />
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="equipment-selection">
|
||||
<legend>{{localize "DAGGERHEART.CharacterCreation.Choice"}}</legend>
|
||||
|
||||
<div class="simple-equipment-container">
|
||||
{{#each inventory.choiceB.suggestions}}
|
||||
<div class="simple-equipment selectable {{#if (not this.selected)}}inactive{{/if}}" data-action="equipmentChoice" data-path="choiceB" data-uuid="{{this.uuid}}" >
|
||||
<label>{{this.name}}</label>
|
||||
<img src="{{this.img}}" />
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
101
templates/views/characterCreation/tabs/setup.hbs
Normal file
101
templates/views/characterCreation/tabs/setup.hbs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
<section
|
||||
class='tab {{tabs.setup.cssClass}} {{tabs.setup.id}}'
|
||||
data-tab='{{tabs.setup.id}}'
|
||||
data-group='{{tabs.setup.group}}'
|
||||
>
|
||||
<div class="main-selections-container">
|
||||
<fieldset class="section-container">
|
||||
<legend>{{localize "TYPES.Item.class"}}</legend>
|
||||
<div class="selections-outer-container">
|
||||
<div class="selections-container class-card">
|
||||
{{#> "systems/daggerheart/templates/components/card-preview.hbs" class }}
|
||||
{{localize "DAGGERHEART.CharacterCreation.SelectClass"}}
|
||||
{{/"systems/daggerheart/templates/components/card-preview.hbs"}}
|
||||
</div>
|
||||
|
||||
<div class="selections-container subclass-card">
|
||||
{{#> "systems/daggerheart/templates/components/card-preview.hbs" subclass disabled=(not class.img) }}
|
||||
{{localize "DAGGERHEART.CharacterCreation.SelectSubclass"}}
|
||||
{{/"systems/daggerheart/templates/components/card-preview.hbs"}}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
{{#if (gte visibility 2)}}
|
||||
<fieldset class="section-container">
|
||||
<legend>{{localize "DAGGERHEART.CharacterCreation.Heritage"}}</legend>
|
||||
<div class="selections-outer-container">
|
||||
<div class="selections-container ancestry-card">
|
||||
{{#> "systems/daggerheart/templates/components/card-preview.hbs" ancestry }}
|
||||
{{localize "DAGGERHEART.CharacterCreation.SelectAncestry"}}
|
||||
{{/"systems/daggerheart/templates/components/card-preview.hbs"}}
|
||||
</div>
|
||||
|
||||
<div class="selections-container community-card">
|
||||
{{#> "systems/daggerheart/templates/components/card-preview.hbs" community }}
|
||||
{{localize "DAGGERHEART.CharacterCreation.SelectCommunity"}}
|
||||
{{/"systems/daggerheart/templates/components/card-preview.hbs"}}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
|
||||
{{#if (gte visibility 3)}}
|
||||
<fieldset class="section-container">
|
||||
<legend>{{localize "DAGGERHEART.CharacterCreation.TraitIncreases"}} {{traits.nrSelected}}/{{traits.nrTotal}}</legend>
|
||||
<div class="traits-container">
|
||||
<fieldset class="section-inner-container">
|
||||
<legend>{{localize "DAGGERHEART.CharacterCreation.SuggestedTraits"}}</legend>
|
||||
<div class="suggested-traits-container">
|
||||
{{#each suggestedTraits}}
|
||||
<div class="suggested-trait-container">{{this}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<button class="action-button" data-action="useSuggestedTraits">{{localize "Use"}}</button>
|
||||
</fieldset>
|
||||
<div class="traits-inner-container">
|
||||
{{#each traits.values}}
|
||||
<div class="trait-container">
|
||||
<div>{{this.name}}</div>
|
||||
<select name="{{concat "traits." this.key ".value"}}" data-dtype="Number">
|
||||
{{selectOptions this.options selected=this.value valueAttr="key" labelAttr="value" blank=""}}
|
||||
</select>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
|
||||
{{#if (gte visibility 4)}}
|
||||
<fieldset class="section-container">
|
||||
<legend>{{localize "DAGGERHEART.CharacterCreation.InitialExperiences"}} {{experience.nrSelected}}/{{experience.nrTotal}}</legend>
|
||||
<div class="experiences-inner-container">
|
||||
{{#each experience.values as |experience id|}}
|
||||
<div class="experience-container">
|
||||
<input class="experience-description" type="text" name="{{concat "experiences." id ".description" }}" value="{{experience.description}}" placeholder="{{localize "DAGGERHEART.CharacterCreation.NewExperience"}}" />
|
||||
<div class="experience-value">{{signedNumber this.value}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
|
||||
{{#if (gte visibility 5)}}
|
||||
<fieldset class="section-container">
|
||||
<legend>{{localize "TYPES.Item.domainCard"}}</legend>
|
||||
<div class="selections-outer-container">
|
||||
{{#each domainCards as |domainCard id|}}
|
||||
<div class="selections-container domain-card" data-card="{{id}}">
|
||||
{{#> "systems/daggerheart/templates/components/card-preview.hbs" domainCard }}
|
||||
{{#each @root.class.system.domains }}
|
||||
<div>{{localize (concat "DAGGERHEART.Domains." this ".label")}}</div>
|
||||
{{/each}}
|
||||
{{/"systems/daggerheart/templates/components/card-preview.hbs"}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
</div>
|
||||
</section>
|
||||
9
templates/views/characterCreation/tabs/story.hbs
Normal file
9
templates/views/characterCreation/tabs/story.hbs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<section
|
||||
class='tab {{tabs.story.cssClass}} {{tabs.story.id}}'
|
||||
data-tab='{{tabs.story.id}}'
|
||||
data-group='{{tabs.story.group}}'
|
||||
>
|
||||
<div>
|
||||
Story
|
||||
</div>
|
||||
</section>
|
||||
42
templates/views/countdowns.hbs
Normal file
42
templates/views/countdowns.hbs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<div>
|
||||
<div class="expanded-view {{#if minimized}}hidden{{/if}}">
|
||||
<div class="countdowns-menu">
|
||||
{{#if canCreate}}<button class="flex" data-action="addCountdown">{{localize "DAGGERHEART.Countdown.AddCountdown"}}</button>{{/if}}
|
||||
{{#if isGM}}<button data-action="openOwnership" data-tooltip="{{localize "DAGGERHEART.Countdown.OpenOwnership"}}"><i class="fa-solid fa-users"></i></button>{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="countdowns-container">
|
||||
{{#each source.countdowns}}
|
||||
<fieldset class="countdown-fieldset">
|
||||
<legend>
|
||||
{{this.name}}
|
||||
{{#if this.canEdit}}<a><i class="fa-solid fa-trash icon-button" data-action="removeCountdown" data-countdown="{{@key}}"></i></a>{{/if}}
|
||||
{{#if @root.isGM}}<a><i class="fa-solid fa-users icon-button" data-action="openCountdownOwnership" data-countdown="{{@key}}" data-tooltip="{{localize "DAGGERHEART.Countdown.OpenOwnership"}}"></i></a>{{/if}}
|
||||
</legend>
|
||||
|
||||
|
||||
<div class="countdown-container">
|
||||
<img src="{{this.img}}" {{#if this.canEdit}}data-action='editImage'{{else}}class="disabled"{{/if}} data-countdown="{{@key}}" />
|
||||
<div class="countdown-inner-container">
|
||||
{{formGroup @root.countdownFields.name name=(concat @root.base ".countdowns." @key ".name") value=this.name localize=true disabled=(not this.canEdit)}}
|
||||
<div class="countdown-value-container">
|
||||
{{formGroup @root.countdownFields.progress.fields.current name=(concat @root.base ".countdowns." @key ".progress.current") value=this.progress.current localize=true disabled=(not this.canEdit)}}
|
||||
{{formGroup @root.countdownFields.progress.fields.max name=(concat @root.base ".countdowns." @key ".progress.max") value=this.progress.max localize=true disabled=(not this.canEdit)}}
|
||||
</div>
|
||||
{{formGroup @root.countdownFields.progress.fields.type.fields.value name=(concat @root.base ".countdowns." @key ".progress.type.value") value=this.progress.type.value localize=true localize=true disabled=(not this.canEdit)}}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="minimized-view {{#if (not minimized)}}hidden{{/if}}">
|
||||
{{#each source.countdowns}}
|
||||
<a class="mini-countdown-container {{#if (not this.canEdit)}}disabled{{/if}}" data-countdown="{{@key}}">
|
||||
<img src="{{this.img}}" />
|
||||
<div class="mini-countdown-name">{{this.name}}</div>
|
||||
<div class="mini-countdown-value">{{this.progress.current}}/{{this.progress.max}}</div>
|
||||
</a>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
<div>
|
||||
<div class="downtime-container">
|
||||
{{#each this.options as |option key|}}
|
||||
<h2 class="downtime-header">{{localize "DAGGERHEART.Downtime.DowntimeHeader" current=nrCurrentChoices max=moveData.nrChoices}}</h2>
|
||||
{{#each moveData.moves as |move key|}}
|
||||
<div class="activity-container">
|
||||
<div class="activity-title">
|
||||
<img class="activity-image {{#if (eq ../selectedActivity.id key)}}selected{{/if}}" src="{{option.img}}" data-action="selectActivity" data-activity="{{key}}" />
|
||||
<div class="activity-image {{#if this.selected}}selected{{/if}}" data-action="selectMove" data-move="{{key}}">
|
||||
{{#if this.selected}}<div class="activity-select-label">{{move.selected}}</div>{{/if}}
|
||||
<img src="{{move.img}}" />
|
||||
</div>
|
||||
|
||||
<span class="activity-title-text">{{localize this.name}}</span>
|
||||
</div>
|
||||
<div class="activity-body">
|
||||
|
|
@ -11,18 +16,9 @@
|
|||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
<div class="activity-container">
|
||||
<div class="activity-title">
|
||||
<img class="activity-image {{#if (eq selectedActivity this.customActivity)}}selected{{/if}}" src="{{this.customActivity.img}}" data-action="selectActivity" data-activity="{{this.customActivity.id}}" />
|
||||
<input class="custom-name-input" type="text" name="name" value="{{this.customActivity.name}}" placeholder="{{localize this.customActivity.namePlaceholder}}" />
|
||||
</div>
|
||||
<div class="activity-body">
|
||||
<textarea class="activity-text-area" name="description" placeholder="{{localize this.customActivity.placeholder}}">{{this.customActivity.description}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="flexrow">
|
||||
<button data-action="takeDowntime" {{#if this.disabledDowntime}}disabled{{/if}}>{{localize "DAGGERHEART.Application.Downtime.TakeDowntime"}}</button>
|
||||
<button data-action="close">{{localize "DAGGERHEART.Application.Cancel"}}</button>
|
||||
<button type="button" data-action="close">{{localize "DAGGERHEART.Application.Cancel"}}</button>
|
||||
<button type="button" data-action="takeDowntime" {{#if this.disabledDowntime}}disabled{{/if}}>{{localize "DAGGERHEART.Application.Downtime.TakeDowntime"}}</button>
|
||||
</footer>
|
||||
</div>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<form class={{cssClass}} autocomplete="off">
|
||||
<div class="form-group">
|
||||
<label>{{localize "DAGGERHEART.Settings.Menu.Homebrew.AbilityArrayLabel"}}</label>
|
||||
<div class="form-fields">
|
||||
<input type="text" name="{{settings.AbilityArray}}" value="{{this.abilityArray}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="flexrow">
|
||||
<button type="submit">Save</button>
|
||||
<button>Cancel</button>
|
||||
</footer>
|
||||
</form>
|
||||
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