148 - Character Setup (#151)

* Added a character setup dialog

* Added optional equipment tab

* Removed temp button to open character setup

* Cleaned up footer from commented code
This commit is contained in:
WBHarry 2025-06-18 20:57:32 +02:00 committed by GitHub
parent a0a5196825
commit f6e077b290
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1560 additions and 142 deletions

View file

@ -77,14 +77,19 @@ Hooks.once('init', () => {
Actors.registerSheet(SYSTEM.id, applications.DhpEnvironment, { types: ['environment'], makeDefault: true }); Actors.registerSheet(SYSTEM.id, applications.DhpEnvironment, { types: ['environment'], makeDefault: true });
CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect; CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect;
DocumentSheetConfig.unregisterSheet( foundry.applications.apps.DocumentSheetConfig.unregisterSheet(
CONFIG.ActiveEffect.documentClass, CONFIG.ActiveEffect.documentClass,
'core', 'core',
foundry.applications.sheets.ActiveEffectConfig foundry.applications.sheets.ActiveEffectConfig
); );
DocumentSheetConfig.registerSheet(CONFIG.ActiveEffect.documentClass, SYSTEM.id, applications.DhActiveEffectConfig, { foundry.applications.apps.DocumentSheetConfig.registerSheet(
makeDefault: true CONFIG.ActiveEffect.documentClass,
}); SYSTEM.id,
applications.DhActiveEffectConfig,
{
makeDefault: true
}
);
CONFIG.Combat.dataModels = { CONFIG.Combat.dataModels = {
base: models.DhCombat base: models.DhCombat

View file

@ -367,39 +367,39 @@
} }
}, },
"Domains": { "Domains": {
"Arcana": { "arcana": {
"label": "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." "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", "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." "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", "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." "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", "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." "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", "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." "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", "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." "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", "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." "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", "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." "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", "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." "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."
} }
@ -417,6 +417,45 @@
"requestingSpotlight": "Requesting The Spotlight", "requestingSpotlight": "Requesting The Spotlight",
"combatStarted": "Active" "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": { "LevelUp": {
"Options": { "Options": {
"trait": "Gain a +1 bonus to two unmarked character traits and mark them.", "trait": "Gain a +1 bonus to two unmarked character traits and mark them.",

View file

@ -0,0 +1,496 @@
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, 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, uuid: item.uuid };
} else if (item.type === 'community' && event.target.closest('.community-card')) {
this.setup.community = { ...item, uuid: item.uuid };
} else if (item.type === 'class' && event.target.closest('.class-card')) {
this.setup.class = { ...item, 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, 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();
}
}

View file

@ -1,4 +1,4 @@
export default class DhActiveEffectConfig extends ActiveEffectConfig { export default class DhActiveEffectConfig extends foundry.applications.sheets.ActiveEffectConfig {
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
classes: ['daggerheart', 'sheet', 'dh-style'] classes: ['daggerheart', 'sheet', 'dh-style']
}; };

View file

@ -221,46 +221,53 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
async _onDrop(event) { async _onDrop(event) {
const data = TextEditor.getDragEventData(event); const data = TextEditor.getDragEventData(event);
const item = await fromUuid(data.uuid); const item = await fromUuid(data.uuid);
const target = event.target.closest('fieldset.drop-section');
if (item.type === 'subclass') { if (item.type === 'subclass') {
await this.document.update({ await this.document.update({
'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid] 'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid]
}); });
} else if (item.type === 'weapon') { } 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) if (!this.document.system.characterGuide.suggestedPrimaryWeapon && !item.system.secondary)
await this.document.update({ await this.document.update({
'system.characterGuide.suggestedPrimaryWeapon': item.uuid '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) if (!this.document.system.characterGuide.suggestedSecondaryWeapon && item.system.secondary)
await this.document.update({ await this.document.update({
'system.characterGuide.suggestedSecondaryWeapon': item.uuid 'system.characterGuide.suggestedSecondaryWeapon': item.uuid
}); });
} }
} else if (item.type === 'armor') { } else if (item.type === 'armor') {
if (event.currentTarget.classList.contains('armor-section')) { if (target.classList.contains('armor-section')) {
if (!this.document.system.characterGuide.suggestedArmor) if (!this.document.system.characterGuide.suggestedArmor)
await this.document.update({ await this.document.update({
'system.characterGuide.suggestedArmor': item.uuid '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 (item.type === 'miscellaneous' || item.type === 'consumable') {
if (this.document.system.inventory.choiceA.length < 2) if (this.document.system.inventory.choiceA.length < 2)
await this.document.update({ 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') { } 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) if (this.document.system.inventory.take.length < 3)
await this.document.update({ 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) if (this.document.system.inventory.choiceB.length < 2)
await this.document.update({ 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
]
}); });
} }
} }

View file

@ -1,56 +1,56 @@
export const domains = { export const domains = {
arcana: { arcana: {
id: 'arcana', id: 'arcana',
label: 'DAGGERHEART.Domains.Arcana.label', label: 'DAGGERHEART.Domains.arcana.label',
src: 'icons/magic/symbols/circled-gem-pink.webp', src: 'icons/magic/symbols/circled-gem-pink.webp',
description: 'DAGGERHEART.Domains.Arcana' description: 'DAGGERHEART.Domains.Arcana'
}, },
blade: { blade: {
id: 'blade', id: 'blade',
label: 'DAGGERHEART.Domains.Blade.label', label: 'DAGGERHEART.Domains.blade.label',
src: 'icons/weapons/swords/sword-broad-crystal-paired.webp', src: 'icons/weapons/swords/sword-broad-crystal-paired.webp',
description: 'DAGGERHEART.Domains.Blade' description: 'DAGGERHEART.Domains.Blade'
}, },
bone: { bone: {
id: 'bone', id: 'bone',
label: 'DAGGERHEART.Domains.Bone.label', label: 'DAGGERHEART.Domains.bone.label',
src: 'icons/skills/wounds/bone-broken-marrow-red.webp', src: 'icons/skills/wounds/bone-broken-marrow-red.webp',
description: 'DAGGERHEART.Domains.Bone' description: 'DAGGERHEART.Domains.Bone'
}, },
codex: { codex: {
id: 'codex', id: 'codex',
label: 'DAGGERHEART.Domains.Codex.label', label: 'DAGGERHEART.Domains.codex.label',
src: 'icons/sundries/books/book-embossed-jewel-gold-purple.webp', src: 'icons/sundries/books/book-embossed-jewel-gold-purple.webp',
description: 'DAGGERHEART.Domains.Codex' description: 'DAGGERHEART.Domains.Codex'
}, },
grace: { grace: {
id: 'grace', id: 'grace',
label: 'DAGGERHEART.Domains.Grace.label', label: 'DAGGERHEART.Domains.grace.label',
src: 'icons/skills/movement/feet-winged-boots-glowing-yellow.webp', src: 'icons/skills/movement/feet-winged-boots-glowing-yellow.webp',
description: 'DAGGERHEART.Domains.Grace' description: 'DAGGERHEART.Domains.Grace'
}, },
midnight: { midnight: {
id: 'midnight', id: 'midnight',
label: 'DAGGERHEART.Domains.Midnight.label', label: 'DAGGERHEART.Domains.midnight.label',
src: 'icons/environment/settlement/watchtower-castle-night.webp', src: 'icons/environment/settlement/watchtower-castle-night.webp',
background: 'systems/daggerheart/assets/backgrounds/MidnightBackground.webp', background: 'systems/daggerheart/assets/backgrounds/MidnightBackground.webp',
description: 'DAGGERHEART.Domains.Midnight' description: 'DAGGERHEART.Domains.Midnight'
}, },
sage: { sage: {
id: 'sage', id: 'sage',
label: 'DAGGERHEART.Domains.Sage.label', label: 'DAGGERHEART.Domains.sage.label',
src: 'icons/sundries/misc/pipe-wooden-straight-brown.webp', src: 'icons/sundries/misc/pipe-wooden-straight-brown.webp',
description: 'DAGGERHEART.Domains.Sage' description: 'DAGGERHEART.Domains.Sage'
}, },
splendor: { splendor: {
id: 'splendor', id: 'splendor',
label: 'DAGGERHEART.Domains.Splendor.label', label: 'DAGGERHEART.Domains.splendor.label',
src: 'icons/magic/control/control-influence-crown-gold.webp', src: 'icons/magic/control/control-influence-crown-gold.webp',
description: 'DAGGERHEART.Domains.Splendor' description: 'DAGGERHEART.Domains.Splendor'
}, },
valor: { valor: {
id: 'valor', id: 'valor',
label: 'DAGGERHEART.Domains.Valor.label', label: 'DAGGERHEART.Domains.valor.label',
src: 'icons/magic/control/control-influence-rally-purple.webp', src: 'icons/magic/control/control-influence-rally-purple.webp',
description: 'DAGGERHEART.Domains.Valor' description: 'DAGGERHEART.Domains.Valor'
} }

View file

@ -5,7 +5,7 @@ import BaseDataActor from './base.mjs';
const attributeField = () => const attributeField = () =>
new foundry.data.fields.SchemaField({ 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 }), bonus: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
tierMarked: new foundry.data.fields.BooleanField({ initial: false }) tierMarked: new foundry.data.fields.BooleanField({ initial: false })
}); });
@ -54,13 +54,7 @@ export default class DhCharacter extends BaseDataActor {
description: new fields.StringField({}), description: new fields.StringField({}),
value: new fields.NumberField({ integer: true, initial: 0 }), value: new fields.NumberField({ integer: true, initial: 0 }),
bonus: 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({ gold: new fields.SchemaField({
coins: new fields.NumberField({ initial: 0, integer: true }), coins: new fields.NumberField({ initial: 0, integer: true }),
@ -235,7 +229,7 @@ export default class DhCharacter extends BaseDataActor {
for (var traitKey in this.traits) { for (var traitKey in this.traits) {
var trait = this.traits[traitKey]; var trait = this.traits[traitKey];
trait.total = trait.value + trait.bonus; trait.total = (trait.value ?? 0) + trait.bonus;
} }
for (var experienceKey in this.experiences) { for (var experienceKey in this.experiences) {

View file

@ -64,7 +64,7 @@ export default class DHSubclass extends BaseDataItem {
} else if (subclassData) { } else if (subclassData) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.SubclassAlreadySelected')); ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.SubclassAlreadySelected'));
return false; 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')); ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.SubclassNotInClass'));
return false; return false;
} }

View file

@ -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
});
}
}

View file

@ -1,4 +1,4 @@
export default class DhMeasuredTemplate extends MeasuredTemplate { export default class DhMeasuredTemplate extends foundry.canvas.placeables.MeasuredTemplate {
_refreshRulerText() { _refreshRulerText() {
super._refreshRulerText(); super._refreshRulerText();

View file

@ -0,0 +1,398 @@
.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(@dark, @beige);
background-color: var(--color-warm-3);
&:hover {
background-color: var(--color-warm-2);
filter: drop-shadow(0 0 3px 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;
}
}
}

View file

@ -2489,6 +2489,345 @@ div.daggerheart.views.multiclass {
.item-button .item-icon.checked { .item-button .item-icon.checked {
opacity: 1; opacity: 1;
} }
.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(#222, #efe6d8);
background-color: var(--color-warm-3);
}
.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container .section-inner-container .action-button:hover {
background-color: var(--color-warm-2);
filter: drop-shadow(0 0 3px 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 { .theme-light .daggerheart.levelup .tiers-container .tier-container {
background-image: url('../assets/parchments/dh-parchment-light.png'); background-image: url('../assets/parchments/dh-parchment-light.png');
} }

View file

@ -8,6 +8,7 @@
@import './application.less'; @import './application.less';
@import './sheets/sheets.less'; @import './sheets/sheets.less';
@import './dialog.less'; @import './dialog.less';
@import './characterCreation.less';
@import './levelup.less'; @import './levelup.less';
@import '../node_modules/@yaireo/tagify/dist/tagify.css'; @import '../node_modules/@yaireo/tagify/dist/tagify.css';
@import './resources.less'; @import './resources.less';

View file

@ -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}} {{#if this.img}}
<img class="preview-image-container" src="{{this.img}}" /> <img class="preview-image-container" src="{{this.img}}" />
<div class="preview-text-container">{{this.name}}</div> <div class="preview-text-container">{{this.name}}</div>

View file

@ -3,7 +3,6 @@
data-tab='{{tabs.settings.id}}' data-tab='{{tabs.settings.id}}'
data-group='{{tabs.settings.group}}' data-group='{{tabs.settings.group}}'
> >
<fieldset class="two-columns"> <fieldset class="two-columns">
<legend>{{localize tabs.settings.label}}</legend> <legend>{{localize tabs.settings.label}}</legend>
<span>{{localize "DAGGERHEART.Tiers.singular"}}</span> <span>{{localize "DAGGERHEART.Tiers.singular"}}</span>

View 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>

View 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>

View 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>

View 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>

View 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>